XLAイベント・ハンドラ・アプリケーションの作成
データベース内の選択した表に対する変更を検出しレポートするXLAアプリケーションの作成のための、一般的な手順を説明します。
この項では、「列データの確認」のような例外もありますが、一般的な手順を示します。この項で説明する手順は、ほぼすべてのXLAアプリケーションに当てはまります。
この項で説明する手順は次のとおりです。
この項のコード例は、xlaSimpleサンプル・アプリケーションに基づいています。
ここで説明するXLA関数については、XLAリファレンスを参照してください。
ヒント:
「TimesTenのインクルード・ファイル」で説明したファイルの他に、XLAアプリケーションにはtt_xla.hをインクルードする必要があります。
ノート:
コード例をわかりやすくするために、各関数コールのルーチン・エラー・チェック・コードは省略されています。「XLAエラー処理」を参照してください。
データベース接続ハンドルの取得
すべてのODBCアプリケーションと同様に、XLAアプリケーションは、ODBCを初期化し、環境ハンドル(henv)を取得し、接続ハンドル(hdbc)を取得して、特定のデータベースと通信する必要があります。
この項では、接続ハンドルを取得する方法について説明します。
環境ハンドルおよび接続ハンドルを初期化します。
SQLHENV henv = SQL_NULL_HENV; SQLHDBC hdbc = SQL_NULL_HDBC;
henvのアドレスをSQLAllocEnv ODBC関数に渡して環境ハンドルを割り当てます。
rc = SQLAllocEnv(&henv);
hdbcのアドレスをSQLAllocConnect ODBC関数に渡して、データベース用の接続ハンドルを割り当てます。
rc = SQLAllocConnect(henv, &hdbc);
SQLDriverConnect ODBC関数をコールして、接続文字列(connStr)で指定されたデータベースに接続しますが、この接続文字列は、この例ではコマンドラインから渡されます。
static char connstr[CONN_STR_LEN];
...
rc = SQLDriverConnect(hdbc, NULL, (SQLCHAR*)connstr, SQL_NTS, NULL, 0,
NULL, SQL_DRIVER_COMPLETE);ノート:
XLAアプリケーションで使用するためにODBC接続ハンドルをオープンすると、ttXlaCloseをコールして対応するXLAハンドルをクローズするまで、ODBCハンドルをODBC処理に使用できません。
SQLSetConnectOption ODBC関数をコールして、自動コミットをオフにします。
rc = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
XLAの初期化およびXLAハンドルの取得
ODBCを初期化し環境および接続ハンドルを取得した後に、XLAを初期化しXLAハンドルを取得してトランザクション・ログにアクセスできます。
「データベース接続ハンドルの取得」を参照してください。
1つのODBC接続に対して1つのみのXLAハンドルを作成します。アプリケーションで複数のXLAリーダー・スレッドを使用する(それぞれが独自のXLAブックマークに接続されている)場合は、スレッドごとに個別のXLAハンドルおよびODBC接続を作成します。
この項では、XLAを初期化する方法について説明します。XLAを初期化する前に、ブックマークを初期化します。その後、XLAハンドルをttXlaHandle_h型として初期化します。
unsigned char bookmarkName [32]; ... strcpy((char*)bookmarkName, "xlaSimple"); ... ttXlaHandle_h xla_handle = NULL;
bookmarkNameおよびxla_handleのアドレスをttXlaPersistOpen関数に渡してXLAハンドルを取得します。
rc = ttXlaPersistOpen(hdbc, bookmarkName, XLACREAT, &xla_handle);
XLACREATオプションは、新しいレプリケートされていないブックマークの作成に使用します。または、XLAREPLオプションを使用して、レプリケートされたブックマークを作成します。どちらの場合も、ブックマークがすでに存在する場合は処理に失敗します。
すでに存在するブックマークを使用するには、次の例で示すように、XLAREUSEオプションを指定してttXlaPersistOpenをコールします。
#include <tt_errCode.h> /* TimesTen Native Error codes */
...
if ( native_error == 907 ) { /* tt_ErrKeyExists */
rc = ttXlaPersistOpen(hdbc, bookmarkName, XLAREUSE, &xla_handle);
...
}ttXlaPersistOpenに無効なパラメータが指定されている場合、またはアプリケーションでハンドル用のメモリーを割り当てることができなかった場合は、SQL_INVALID_HANDLEというコードが返されます。この場合、このエラーまたはこれ以降のエラーの検出にttXlaErrorは使用できません。
ttXlaPersistOpenが失敗しても、まだハンドルを作成する場合は、メモリー・リークを防ぐためにハンドルを閉じる必要があります。
更新を監視する表の指定
XLAを初期化しXLAハンドルを取得した後に、更新イベントを監視する表またはマテリアライズド・ビューを指定できます。
「XLAの初期化およびXLAハンドルの取得」を参照してください。
SYS.XLASUBSCRIPTIONS表を問い合せることによって、ブックマークをサブスクライブする表を確認できます。また、SYS.XLASUBSCRIPTIONSを使用すると、特定の表にサブスクライブしたブックマークを確認することもできます。
ttXlaNextUpdateおよびttXlaNextUpdateWait関数は、DDLイベントに関連するXLAレコードを取得します。DDL XLAレコードはどのXLAブックマークでも使用できます。DDLイベントには、CREATAB、DROPTAB、CREAIND、DROPIND、CREATVIEW、DROPVIEW、CREATSEQ、DROPSEQ、CREATSYN、DROPSYN、ADDCOLS、DRPCOLSおよびTRUNCATEトランザクションが含まれています。「ttXlaUpdateDesc_t」を参照してください。
ttXlaTableStatus関数は、現行のブックマークを指定した表に更新するためにサブスクライブします。または、現在のブックマークが表に関連するDMLレコードをすでに監視しているかどうかを判別します。
ttXlaTableByName関数をコールして、指定した表またはマテリアライズド・ビューのシステム識別子およびユーザー識別子を両方とも取得します。次に、ttXlaTableStatus関数をコールして、表またはマテリアライズド・ビューへの変更を監視するためにXLAを有効にします。
ノート:
XLAでのLOBのサポートは、次のように制限されています。
-
LOB列を含む表にサブスクライブできますが、LOB値自体についての情報は使用できません。
-
ttXlaGetColumnInfoでは、LOB列に関する情報が返されます。 -
LOBを含む列は、空(長さがゼロ)またはNULL(実際に値が
NULLの場合)としてレポートされます。このようにして、NULL列と非NULL列を区別できます。
この例では、MYDATA表への変更を追跡します。
#define TABLE_OWNER "APPUSER"
#define TABLE_NAME "MYDATA"
...
SQLUBIGINT SYSTEM_TABLE_ID = 0;
...
SQLUBIGINT userID;
rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME,
&SYSTEM_TABLE_ID, &userID);表の識別子を取得すると、ttXlaTableStatus関数を使用して、MYDATA表への変更を検出するためのXLA更新追跡を有効にできます。newstatusパラメータを0(ゼロ)以外の値に設定すると、指定した表に行われた変更がXLAによって追跡されます。
SQLINTEGER oldstatus;
SQLINTEGER newstatus = 1;
...
rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0,
&oldstatus, &newstatus);oldstatusパラメータは、コール時の表のステータスを示す出力です。
ttXlaTableStatusでnewstatusをNULLにし、oldstatusのみを返すことによって、表の現行のXLAステータスを返すことができます。例:
rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0,
&oldstatus, NULL);
...
if (oldstatus != 0)
printf("XLA is currently tracking changes to table %s.%s\n",
TABLE_OWNER, TABLE_NAME);
else
printf("XLA is not tracking changes to table %s.%s\n",
TABLE_OWNER, TABLE_NAME);トランザクション・ログからの更新レコードの取得
更新を監視する表を指定した後、ttXlaNextUpdateまたはttXlaNextUpdateWait関数をコールして、トランザクション・ログから一連のレコードを返すことができます。
「ttXlaNextUpdate」および「ttXlaNextUpdateWait」を参照してください。
コミット済のトランザクションのレコードのみが返されます。レコードはコミットされた順序で返されます。不要になり、トランザクション・ログからパージ可能なレコードをXLAが判別できるように、ttXlaAcknowledge関数を定期的にコールして、トランザクションの受信を確認する必要があります。これらの関数は、トランザクション・ログ内のアプリケーション・ブックマークの位置に影響を与えます(「ブックマークのしくみ」を参照)。『Oracle TimesTen In-Memory Databaseリファレンス』のttLogHoldsも参照してください。TimesTen組込みプロシージャは、トランザクション・ログの保留に関する情報を返します。
ノート:
ttXlaAcknowledgeは、必要な場合にのみ使用する必要がある高コストの処理です。
ttXlaNextUpdateによって返されたトランザクション内の各更新レコードは、ttXlaUpdateDesc_t構造体で記述される更新ヘッダーで始まります。この更新ヘッダーには、レコードがトランザクションの最初のレコード(TT_UPDFIRST)か、最後のコミット・レコード(TT_UPDCOMMIT)かを示すフラグが格納されています。また、更新ヘッダーは、更新によって影響を受ける表も識別します。更新ヘッダーの後には、データベース内の表に対して行われた変更について記述する0(ゼロ)から2行の行データが続きます。
次の図5-5に、4つの更新レコードで構成されているトランザクションをトランザクション・ログから返すttXlaNextUpdateへのコールを示します。返されたトランザクションの受信は、ttXlaAcknowledgeをコールすることで確認され、ブックマークが再設定されます。
ノート:
この例は、説明を明確にするために簡略化されています。実際のXLAアプリケーションでは、ttXlaAcknowledgeをコールする前に、複数のトランザクションのレコードを読み取る可能性があります。
この例のxlaSimpleアプリケーションでは、表の更新の監視は、ユーザーが停止するまで続行されます。
この例では、ttXlaNextUpdateWaitをコールする前に、返されるttXlaUpdateDesc_tレコードを保持するバッファへのポインタ(arry)と実際に返されるレコード数を保持する変数(records)を初期化します。この例ではttXlaNextUpdateWaitをコールするため、トランザクション・ログ・バッファでレコードが見つからない場合に待機する秒数(FETCH_WAIT_SECS)も指定します。
次に、ttXlaNextUpdateWaitをコールし、これらの値を渡してarry内の一連のttXlaUpdateDesc_tレコードを取得します。次に、「レコード・ヘッダーの確認および行アドレスの検出」内の例で説明されているように、arry内の各レコードを、HandleChange()関数に渡すことで処理します。すべてのレコードの処理後、ttXlaAcknowledgeをコールして、ブックマークの位置を再設定します。
#define FETCH_WAIT_SECS 5
...
SQLINTEGER records;
ttXlaUpdateDesc_t** arry;
int j;
while (!StopRequested()) {
/* Get a batch of update records */
rc = ttXlaNextUpdateWait(xla_handle, &arry, 100,
&records, FETCH_WAIT_SECS);
if (rc != SQL_SUCCESS {
/* See XLA Error Handling */
}
/* Process the records */
for(j=0; j < records; j++){
ttXlaUpdateDesc_t* p;
p = arry[j];
HandleChange(p); /* Described in the next section */
}
/* After each batch, Acknowledge updates to reset bookmark.*/
rc = ttXlaAcknowledge(xla_handle);
if (rc != SQL_SUCCESS {
/* See XLA Error Handling */
}
} /* end while !StopRequested() */ttXlaNextUpdateまたはttXlaNextUpdateWaitによって返されるレコードの実際の数は、これらの関数のnreturned出力パラメータで示されるように、maxrecordsパラメータの値より小さい場合があります。図5-6に示す例では、maxrecordsが10で、7つのレコードで構成されているトランザクションAT、および3つのレコードで構成されているトランザクションBTがトランザクション・ログに含まれています。この場合、両方のトランザクションが同じバッチで返され、maxrecordsおよびnreturnedの両方の値が10になります。ただし、ログ内のその次の3つのトランザクションは、11個のレコードで構成されているCT、2つのレコードで構成されているDTおよび2つのレコードで構成されているETになります。CTのコミット・レコードの前にDTトランザクションのコミット・レコードが書き込まれるため、次にttXlaNextUpdateをコールすると、DTトランザクションの2つのレコードが返され、nreturnedの値は2になります。その次にttXlaNextUpdateをコールすると、XLAによって、CTトランザクションの合計レコード数がmaxrecordsを超えていることが検出され、このトランザクションのレコードが2つのバッチで返されます。最初のバッチには、CTの最初の10個のレコード(nreturned = 10)が含まれています。2番目のバッチには、CTの最後のレコードおよびETトランザクションの2つのレコード(ETの後のトランザクションのコミット・レコードが次の7つのレコード内で検出されないと想定)が含まれます。
「ttXlaNextUpdate」および「ttXlaNextUpdateWait」を参照してください。
XLAにより、レコードがメモリー・バッファまたはファイル・システム上のトランザクション・ログ・ファイルから読み取られます(「どのようにXLAでトランザクション・ログからレコードが読み取られるか」を参照)。待機時間を最小にするために、メモリー・バッファからのレコードは使用可能になるとすぐに返されますが、バッファにないレコードはバッファが空の場合にのみ返されます。この設計によって、XLAアプリケーションでは、行われた変更を最小の待機時間ですぐに表示できます。代償として、ttXlaNextUpdateまたはttXlaNextUpdateWaitのmaxrecordsパラメータでリクエストした数より、返される変更の数が少なくなる場合があります。
ノート:
スループットを最適化するために、XLAアプリケーションでは、フェッチおよびレコード処理の手順を非同期にする必要があります。たとえば、1つのスレッドを作成してレコードのフェッチおよび保存を行ったり、1つ以上のスレッドを作成して保存したレコードを処理することができます。
レコード・ヘッダーの確認および行アドレスの検出
更新レコードの配列が存在し、その各レコードが示す処理のタイプがわかるため、返された行データを確認できます。
ttXlaNextUpdateまたはttXlaNextUpdateWait関数によって返される各レコードは、ttXlaUpdateDesc_tヘッダーで始まり、このヘッダーには次の情報が記述されています。
-
処理が実行された表
-
レコードがトランザクションの最初のレコードであるか、または最後の(コミット)レコードであるか
-
レコードが示す処理のタイプ
-
返された行データの長さ(行データがある場合)
-
行内の更新された列(該当する列がある場合)
図5-7に、トランザクション・ログ内の更新レコードの例を示します。
ttXlaUpdateDesc_tヘッダーは固定長で、処理のタイプに応じて、データベースから0(ゼロ)から2行の行(タプル)が続きます。ttXlaUpdateDesc_tヘッダーのアドレスを取得し、それにsizeof(ttXlaUpdateDesc_t)を追加することによって、最初に返された行のアドレスを特定できます。
tup1 = (void*) ((char*) ttXlaUpdateDesc_t + sizeof(ttXlaUpdateDesc_t));これを次の例に示します。
ttXlaUpdateDesc_t ->typeフィールドは、更新を生成したSQL処理のタイプについての記述です。UPDATETTUP型のトランザクション・レコードは、UPDATE処理についての記述であるため、更新の前後の行データをレポートするために2行の行を返します。更新後の値が含まれている2番目に返された行のアドレスは、レコード内の1つ目の行のアドレスにその長さに追加することで特定できます。
if (ttXlaUpdateDesc_t->type == UPDATETUP) { tup2 = (void*) ((char*) tup1 + ttXlaUpdateDesc_t->tuple1); }
これも次の例で示します。ここでは、ttXlaNextUpdateWait関数によって返された各レコードをHandleChange()関数に渡し、この関数で、そのレコードがINSERT、UPDATEまたはCREATE VIEW処理に関連しているかどうかを判別しています。この例では、他のすべての処理は無視されます。
HandleChange()関数により、PrintColValues()関数(「PrintColValues()関数ですべてを組み合せる」内の例を参照)をコールする前に、SQL処理のタイプに応じて異なる方法で対応します。
void HandleChange(ttXlaUpdateDesc_t* xlaP)
{
void* tup1;
void* tup2;
/* First confirm that the XLA update is for the table we care about. */
if (xlaP->sysTableID != SYSTEM_TABLE_ID)
return ;
/* OK, it is for the table we are monitoring. */
/* The last record in the ttXlaUpdateDesc_t record is the "tuple2"
* field. Immediately following this field is the first XLA record "row". */
tup1 = (void*) ((char*) xlaP + sizeof(ttXlaUpdateDesc_t));
switch(xlaP->type) {
case INSERTTUP:
printf("Inserted new row:\n");
PrintColValues(tup1);
break;
case UPDATETUP:
/* If this is an update ttXlaUpdateDesc_t, then following that is
* the second XLA record "row".
*/
tup2 = (void*) ((char*) tup1 + xlaP->tuple1);
printf("Updated row:\n");
PrintColValues(tup1);
printf("To:\n");
PrintColValues(tup2);
break;
case DELETETUP:
printf("Deleted row:\n");
PrintColValues(tup1);
break;
default:
/* Ignore any XLA records that are not for inserts/update/delete SQL ops. */
break;
} /* switch (xlaP->type) */
}列データの確認
列データを確認する方法について説明します。
更新レコードで返されるデータ
ttXlaUpdateDesc_t構造体の後に、1つの更新レコードで0行から2行のデータが返される可能性があります。
「ttXlaUpdateDesc_t」およびレコード・ヘッダーの検出と行アドレスの検索を参照してください。
図5-8に示すように、各行のデータの最初の部分は固定長で、その後に可変長データが続きます。
列記述の取得
返された行から列値を読み取るには、まず、その行の各列のオフセットを判別する必要があります。
列オフセットおよびその他の列メタデータは、特定の表に対してttXlaGetColumnInfo関数をコールすると取得でき、この関数は表の列ごとに個別のttXlaColDesc_t構造体を返します。ttXlaGetColumnInfo関数は、初期化プロシージャの一部としてコールする必要があります。「XLAの初期化およびXLAハンドルの取得」の説明では、わかりやすくするためにこのコールは省略されています。
ttXlaGetColumnInfoをコールする場合は、colinfoパラメータを指定して、返されるttXlaColDesc_t構造体のリストを保持するバッファへのポインタを作成します。maxcolsパラメータを使用して、バッファのサイズを定義します。
次に示すxlaSimpleアプリケーションのコード例では、返される列の最大数(MAX_XLA_COLUMNS)を推測し、返されるttXlaColDesc_t構造体を保持するバッファ(xla_column_defs)のサイズを設定します。maxcolsパラメータを設定する場合のもう1つのより正確な方法として、ttXlaGetTableInfo関数をコールし、ttXlaColDesc_t ->columnsに返された値を使用する方法があります。
#define MAX_XLA_COLUMNS 128
...
SQLINTEGER ncols;
...
ttXlaColDesc_t xla_column_defs[MAX_XLA_COLUMNS];
...
rc = ttXlaGetColumnInfo(xla_handle, SYSTEM_TABLE_ID, userID,
xla_column_defs, MAX_XLA_COLUMNS, &ncols);
if (rc != SQL_SUCCESS {
/* See "XLA Error Handling" */
}図5-9に示すように、ttXlaGetColumnInfo関数は次の情報を出力します。
-
ttXlaGetColumnInfoのcolinfoパラメータで指すバッファに格納されるttXlaColDesc_t構造体のリスト(xla_column_defs) -
xla_column_defsバッファに実際に返される列の数を保持するnreturnedの値(ncols)
図5-9 ttXlaGetColumnInfoによって返されるttXlaColDesc_t構造体

「図5-9 ttXlaGetColumnInfoによって返されるttXlaColDesc_t構造体」の説明
ttXlaGetColumnInfoによって返される各ttXlaColDesc_t構造体には、その列のオフセット位置を記述するoffset値が含まれます。列データを読み込む際にこのoffset値をどのように使用するかは、列が固定長データ(CHAR、NCHAR、INTEGER、BINARY、DOUBLE、FLOAT、DATE、TIME、TIMESTAMPなど)を含んでいるか、可変長データ(VARCHAR、NVARCHAR、VARBINARYなど)を含んでいるかによって異なります。
固定長列データの読取り
固定長列データの場合、列のアドレスは、ttXlaColDesc_t構造体のoffset値に行のアドレスを追加したものです。
「ttXlaColDesc_t」を参照してください。
ここで示した計算などの機能する完成例は、「PrintColValues()関数ですべてを組み合せる」を参照してください。
MYDATA表の最初の列はCHAR型です。HandleChange()関数で前に取得したtup1行のアドレス(「レコード・ヘッダーの確認および行アドレスの検出」内の例を参照)と、ttXlaGetColumnInfo関数によって返された最初のttXlaColDesc_t構造体のoffset (「列記述の取得」内の例を参照)を使用する場合は、次のように計算して最初の列の値を取得できます:
char* Column1; Column1 = ((unsigned char*) tup1 + xla_column_defs[0].offset);
MYDATA表の3番目の列はINTEGER型のため、3番目のttXlaColDesc_t構造体のoffsetを使用して値を検出し、次のように計算して、整数として再キャストできます。データは適切に配置されます。
int Column3;
Column3 = *((int*) ((unsigned char*) tup +
xla_column_defs[2].offset));MYDATA表の4番目の列はNCHAR型のため、4番目のttXlaColDesc_t構造体のoffsetを使用して値を検出し、次のように計算して、SQLWCHAR型として再キャストできます。
SQLWCHAR* Column4;
Column4 = (SQLWCHAR*) ((unsigned char*) tup +
xla_column_defs[3].offset);前述の例で取得した列値とは異なり、Column4は2バイトのUnicode文字の配列を指します。「PrintColValues()関数ですべてを組み合せる」内の例のSQL_WCHARの場合のように、この配列内の各要素に対して繰り返し、文字列を取得する必要があります。
その他の固定長データ型は、対応するCの型にキャストできます。複合固定長データ型(DATE、TIME、DECIMAL値など)は、TimesTenの内部形式で格納されますが、アプリケーションでXLA変換関数を使用して、それらに対応するODBC C値に変換できます(「複合データ型の変換」を参照)。
ノート:
XLAによって返される文字列は空文字では終了しません。「返された文字列の空文字での終了」を参照してください。
NOT INLINE可変長列データの読取り
NOT INLINE可変長データ(VARCHAR、NVARCHARおよびVARBINARY)の場合、ttXlaColDesc_t ->offsetにあるデータは、返された行の可変長部分でのデータの場所を指す4バイトのオフセット値です。
オフセット・アドレスをオフセット値に追加すると、行の可変長部分の列データのアドレスを取得できます。この位置にある最初の8バイトはデータの長さで、その後に実際のデータが続きます。可変長データの場合、ttXlaColDesc_t ->size値は列の許容最大サイズです。図5-11に、行内のNOT INLINE可変長データを検出する方法を示します。
ここで示した計算などの機能する完成例は、「PrintColValues()関数ですべてを組み合せる」を参照してください。
引き続きこの例では、返された行(tup1)の2番目の列はVARCHAR型です。行内の可変長データを探すには、まず前述の図5-11で示すように、行の固定長部分で列のttXlaColDesc_t ->offsetの値をみつけます。このアドレスの値は、行の可変長部分のデータの4バイトのオフセットです(VarOffset)。次に、VarOffsetのアドレスにVarOffsetオフセット値を追加して、可変長列データの先頭へのポインタを取得します(DataLength)。DataLengthの位置にある最初の8バイトはデータの長さです。DataLengthの後の次のバイトが、実際のデータの先頭です(Column2)。
void* VarOffset; /* offset of data */
long* DataLength; /* length of data */
char* Column2; /* pointer to data */
VarOffset = (void*) ((unsigned char*) tup1 +
xla_column_defs[1].offset);
/*
* If column is out-of-line, pColVal points to an offset
* else column is inline so pColVal points directly to the string length.
*/
if (xla_column_defs[1].flags & TT_COLOUTOFLINE)
DataLength = (long*)((char*)VarOffset + *((int*)VarOffset));
else
DataLength = (long*)VarOffset;
Column2 = (char*)(DataLength+1);VARBINARY型は、VARCHAR型での方法とほぼ同じ方法で処理されます。Column2がNVARCHAR型だった場合は、それをSQLWCHARとして初期化し、前述のVARCHARの場合と同様に値を取得し、その後、「PrintColValues()関数ですべてを組み合せる」内のNCHAR値CharBufの場合と同様に、Column2配列にわたり繰り返すことができます。
ノート:
前述の例では、DataLengthはデータ型longで、UNIXベースの64ビット・システムでは64ビット(8バイト)型です。Windows 64ビット・システムでは、longは4バイト型であり、8バイト型の__int64がかわりに使用されます。
返された文字列の空文字での終了
レコードの行データから返される文字列は空文字では終了していません。文字列をバッファにコピーし、その文字列の最後の文字の後に\0などの空文字を追加すると、文字列を空文字で終了できます。
文字列を空文字で終了する手順は、文字列が固定長の場合と可変長の場合で少し異なります。次の各例では、NULLで終了する固定長文字列、NULLで終了する既知のサイズの可変長文字列、およびNULLで終了するサイズ不明の可変長文字列の処理を示します。
ここで示した計算などの機能する完成例は、「PrintColValues()関数ですべてを組み合せる」を参照してください。
「固定長列データの読取り」内の例で返された固定長CHAR(10)のColumn1文字列を空文字で終了するには、その文字列と空文字を保持できる十分な大きさのバッファを設定します。次に、ttXlaColDesc_t ->sizeから文字列のサイズを取得し、その文字列をバッファにコピーして、次のような計算でその文字列の末尾を空文字で終了します。これで、バッファの内容を使用できます。この例では、その文字列を出力します。
char buffer[10+1];
int size;
size = xla_column_defs[0].size;
memcpy(buffer, Column1, size);
buffer[size] = '\0';
printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[0].colName), buffer);可変長文字列を空文字で終了する手順は、固定長文字列の手順とほぼ同じです。可変長データのオフセットの先頭にある値は、文字列のサイズのみです(「NOT INLINE可変長列データの読取り」を参照)。
「NOT INLINE可変長列データの読取り」内の例で取得したColumn2文字列がVARCHAR(32)の場合は、その文字列と空文字を保持できる十分な大きさのバッファを設定します。DataLengthオフセットにある値を使用して、次のような計算で文字列のサイズを設定します。
char buffer[32+1];
memcpy(buffer, Column2, *DataLength);
buffer[*DataLength] = '\0';
printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer);すべてのデータ型を読み取るための汎用コードを記述する場合は、返される文字列のサイズを想定できません。サイズがわからない文字列に対しては、返された文字列の大部分を保持できる十分な大きさのバッファを静的に割り当てます。返された文字列がバッファより大きい場合は、すぐ下の例で示すように、正しいサイズのバッファを動的に割り当てます。
「NOT INLINE可変長列データの読取り」内の例で取得したColumn2文字列のサイズがわからない場合は、最大10,000文字の文字列を保持できる十分な大きさのバッファを静的に割り当てます。その後、可変長データ・オフセットの先頭で取得したDataLength値が、バッファのサイズより小さいことを確認します。文字列がバッファより大きい場合は、malloc()を使用してバッファを動的に正しいサイズに割り当てます。
#define STACKBUFSIZE 10000
char VarStackBuf[STACKBUFSIZE];
char* buffer;
buffer = (*DataLength+1 <= STACKBUFSIZE) ? VarStackBuf :
malloc(*DataLength+1);
memcpy(buffer,Column2,*DataLength);
buffer[*DataLength] = '\0';
printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer);
if (buffer != VarStackBuf) /* buffer was allocated */
free(buffer);複合データ型の変換
複合データ型を変換する方法について説明します。
ここで示した計算などの機能する完成例は、「PrintColValues()関数ですべてを組み合せる」を参照してください。
TT_DATEやTT_TIMEなどの複合データ型の値は、TimesTenの内部形式で保存されます。これらは、XLA型変換関数(「XLAデータ型変換関数」を参照)を使用して、対応するODBC Cデータ型に変換できます。
HandleChange()関数で前に取得したtup1行のアドレス(「レコード・ヘッダーの確認および行アドレスの検出」内の例を参照)と、ttXlaGetColumnInfo関数によって返された5番目のttXlaColDesc_t構造体のoffset (「列記述の取得」内の例を参照)を使用する場合は、TIMESTAMP型の列値を検出できます。ttXlaTimeStampToODBCCType関数を使用し、TimesTen形式から列データを変換し、変換されたTIME値をODBC TIMESTAMP_STRUCTに保存します。次のようなコードを使用すると値を出力できます。
void* Column5;
TIMESTAMP_STRUCT timestamp;
Column5 = (void*) ((unsigned char*) tup1 +
xla_column_defs[4].offset);
rc = ttXlaTimeStampToODBCCType(Column5, ×tamp);
if (rc != SQL_SUCCESS) {
/* See XLA Error Handling */
}
printf(" %s: %04d-%02d-%02d %02d:%02d:%02d.%06d\n",
((unsigned char*) xla_column_defs[i].colName),
timestamp.year,timestamp.month, timestamp.day,
timestamp.hour,timestamp.minute,timestamp.second,
timestamp.fraction);前にHandleChange()関数で取得したtup1行のアドレス、およびttXlaGetColumnInfo関数によって返された6番目のttXlaColDesc_t構造体のoffsetを使用すると、DECIMAL型の列値をみつけることができます。ttXlaDecimalToCString関数を使用して、TimesTenのDECIMAL形式から文字列に列データを変換します。次のようなコードを使用すると値を出力できます。
char decimalData[50];
Column6 = (float*) ((unsigned char*) tup +
xla_column_defs[5].offset);
precision = (short) (xla_column_defs[5].precision);
scale = (short) (xla_column_defs[5].scale);
rc = ttXlaDecimalToCString(Column6, (char*)&decimalData,
precision, scale);
if (rc != SQL_SUCCESS) {
/* See XLA Error Handling */
}
printf(" %s: %s\n", ((unsigned char*) xla_column_defs[5].colName), decimalData);NULL値の検出
NULL値可能な表の列の場合、ttXlaColDesc_t ->nullOffsetは、レコード内の列のNULLバイトを指します。列にNULLを指定できない場合、このフィールドは0(ゼロ)で、列にNULLを指定できる場合、このフィールドは0より大きくなります。
NULL値可能な列の場合(ttXlaColDesc_t ->nullOffset > 0)、列がNULLであるかどうかを確認するには、NULLオフセットをttXlaUpdate_t*のアドレスに追加し、そこで(unsigned char)バイトを調べて1 (NULL)または0 (NOT NULL)のどちらであるかを確認します。
Column6がNULLかどうかを次のように確認します。
if (xla_column_defs[5].nullOffset != 0) {
if (*((unsigned char*) tup +
xla_column_defs[5].nullOffset) == 1) {
printf("Column6 is NULL\n");
}
}XLAデータ型変換関数
この項では、TimesTenの内部形式からODBCのCデータ型に変換する、XLAデータ型変換関数を示します。
これらの変換関数は、ttXlaUpdateDesc_t型(UPDATETUP、INSERTTUPおよびDELETETUP)に含まれる行データで使用できます。
表5-2 XLAデータ型変換関数
| 関数 | 変換 |
|---|---|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
|
|
内部 |
PrintColValues()関数ですべてを組み合せる
各列のttXlaColDesc_t ->dataTypeを確認して、データ型がCHAR、NCHAR、INTEGER、TIMESTAMP、DECIMALおよびVARCHARである列を検出し、その値を出力する関数を示します。
これは、考えられる1つの方法にすぎません。たとえば、別の方法として、ttXlaColDesc_t ->ColNameの値を確認して、名前で特定の列をみつける方法もあります。
PrintColValues()関数は、長さが最大50バイトのCHARおよびVARCHARの文字列を処理します。NCHAR文字は、ASCII文字セットに属している必要があります。
この例の関数は、まずttXlaColDesc_t ->nullOffsetを調べて列がNULLかどうかを確認します。次に、ttXlaColDesc_t ->dataTypeフィールドを調べて列のデータ型を確認します。単純な固定長データ(CHAR、NCHARおよびINTEGER)の場合は、ttXlaColDesc_t ->offsetにある値を適切なCデータ型にキャストします。複合データ型(TIMESTAMPおよびDECIMAL)は、ttXlaTimeStampToODBCCType関数およびttXlaDecimalToCString関数を使用して、TimesTen形式からODBC C値に変換されます。
可変長データ(VARCHAR)の場合、この関数は、行の可変長部分でデータを探します(「XLAエラー処理」を参照)。
void PrintColValues(void* tup)
{
SQLRETURN rc ;
SQLINTEGER native_error;
void* pColVal;
char buffer[50+1]; /* No strings over 50 bytes */
int i;
for (i = 0; i < ncols; i++)
{
if (xla_column_defs[i].nullOffset != 0) { /* See if column is NULL */
/* this means col could be NULL */
if (*((unsigned char*) tup + xla_column_defs[i].nullOffset) == 1) {
/* this means that value is SQL NULL */
printf(" %s: NULL\n",
((unsigned char*) xla_column_defs[i].colName));
continue; /* Skip rest and re-loop */
}
}
/* Fixed-length data types: */
/* For INTEGER, recast as int */
if (xla_column_defs[i].dataType == TTXLA_INTEGER) {
printf(" %s: %d\n",
((unsigned char*) xla_column_defs[i].colName),
*((int*) ((unsigned char*) tup + xla_column_defs[i].offset)));
}
/* For CHAR, just get value and null-terminate string */
else if ( xla_column_defs[i].dataType == TTXLA_CHAR_TT
|| xla_column_defs[i].dataType == TTXLA_CHAR) {
pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset);
memcpy(buffer, pColVal, xla_column_defs[i].size);
buffer[xla_column_defs[i].size] = '\0';
printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer);
}
/* For NCHAR, recast as SQLWCHAR.
NCHAR strings must be parsed one character at a time */
else if ( xla_column_defs[i].dataType == TTXLA_NCHAR_TT
|| xla_column_defs[i].dataType == TTXLA_NCHAR ) {
SQLUINTEGER j;
SQLWCHAR* CharBuf;
CharBuf = (SQLWCHAR*) ((unsigned char*) tup + xla_column_defs[i].offset);
printf(" %s: ", ((unsigned char*) xla_column_defs[i].colName));
for (j = 0; j < xla_column_defs[i].size / 2; j++)
{
printf("%c", CharBuf[j]);
}
printf("\n");
}
/* Variable-length data types:
For VARCHAR, locate value at its variable-length offset and null-terminate.
VARBINARY types are handled in a similar manner.
For NVARCHARs, initialize 'var_data' as a SQLWCHAR, get the value as shown
below, then iterate through 'var_len' as shown for NCHAR above */
else if ( xla_column_defs[i].dataType == TTXLA_VARCHAR
|| xla_column_defs[i].dataType == TTXLA_VARCHAR_TT) {
long* var_len;
char* var_data;
pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset);
/*
* If column is out-of-line, pColVal points to an offset
* else column is inline so pColVal points directly to the string length.
*/
if (xla_column_defs[i].flags & TT_COLOUTOFLINE)
var_len = (long*)((char*)pColVal + *((int*)pColVal));
else
var_len = (long*)pColVal;
var_data = (char*)(var_len+1);
memcpy(buffer,var_data,*var_len);
buffer[*var_len] = '\0';
printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer);
}
/* Complex data types require conversion by the XLA conversion methods
Read and convert a TimesTen TIMESTAMP value.
DATE and TIME types are handled in a similar manner */
else if ( xla_column_defs[i].dataType == TTXLA_TIMESTAMP
|| xla_column_defs[i].dataType == TTXLA_TIMESTAMP_TT) {
TIMESTAMP_STRUCT timestamp;
char* convFunc;
pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset);
if (xla_column_defs[i].dataType == TTXLA_TIMESTAMP_TT) {
rc = ttXlaTimeStampToODBCCType(pColVal, ×tamp);
convFunc="ttXlaTimeStampToODBCCType";
}
else {
rc = ttXlaOraTimeStampToODBCTimeStamp(pColVal, ×tamp);
convFunc="ttXlaOraTimeStampToODBCTimeStamp";
}
if (rc != SQL_SUCCESS) {
handleXLAerror (rc, xla_handle, err_buf, &native_error);
fprintf(stderr, "%s() returns an error <%d>: %s",
convFunc, rc, err_buf);
TerminateGracefully(1);
}
printf(" %s: %04d-%02d-%02d %02d:%02d:%02d.%06d\n",
((unsigned char*) xla_column_defs[i].colName),
timestamp.year,timestamp.month, timestamp.day,
timestamp.hour,timestamp.minute,timestamp.second,
timestamp.fraction);
}
/* Read and convert a TimesTen DECIMAL value to a string. */
else if (xla_column_defs[i].dataType == TTXLA_DECIMAL_TT) {
char decimalData[50];
short precision, scale;
pColVal = (float*) ((unsigned char*) tup + xla_column_defs[i].offset);
precision = (short) (xla_column_defs[i].precision);
scale = (short) (xla_column_defs[i].scale);
rc = ttXlaDecimalToCString(pColVal, (char*)&decimalData, precision, scale);
if (rc != SQL_SUCCESS) {
handleXLAerror (rc, xla_handle, err_buf, &native_error);
fprintf(stderr, "ttXlaDecimalToCString() returns an error <%d>: %s",
rc, err_buf);
TerminateGracefully(1);
}
printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName),
decimalData);
}
else if (xla_column_defs[i].dataType == TTXLA_NUMBER) {
char numbuf[32];
pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset);
rc=ttXlaNumberToCString(xla_handle, pColVal, numbuf, sizeof(numbuf));
if (rc != SQL_SUCCESS) {
handleXLAerror (rc, xla_handle, err_buf, &native_error);
fprintf(stderr, "ttXlaNumberToDouble() returns an error <%d>: %s",
rc, err_buf);
TerminateGracefully(1);
}
printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), numbuf);
}
} /* End FOR loop */
}ノート:
-
前述の例では、
var_lenはデータ型longで、UNIXベースの64ビット・システムでは64ビット(8バイト)型です。Windows 64ビット・システムでは、longは4バイト型であり、__int64がかわりに使用されます。 -
TerminateGracefully()メソッドのサンプルは、「XLAアプリケーションの終了」を参照してください。
XLAエラー処理
この項では、XLAのエラーの処理について説明します。
XLAエラーとコード
この項では、XLAエラーとそのエラー・コードについて説明します。
表5-3 XLAエラーとコード
| エラー | コード |
|---|---|
|
|
802 (一時的) |
|
|
6001 (一時的) |
|
|
6002 (一時的) |
|
|
6003 (一時的) |
|
|
6220 (一時的) |
|
|
6221 (一時的) |
|
|
8024 |
|
|
8029 |
|
|
8031 |
|
|
8034 |
|
|
8035 |
|
|
8036 |
|
|
8037 |
|
|
8038 |
|
|
8046 |
|
|
8047 |
XLAエラーの対処方法
ODBCまたはXLA関数をコールするたびに、エラーのリターン・コードを確認する必要があります。エラーが致命的な場合は、プログラムを終了します。
「XLAアプリケーションの終了」を参照してください。
アプリケーションによっては、前述の項の表5-3で示したような特定のXLAエラーへの対処が必要な場合があります。
エラーは、エラー・コード(エラー番号)またはtt_Err文字列のどちらかを使用して確認できます。TimesTenエラー・コードとエラー文字列をすべて示すリストは、timesten_home/install/include/tt_errCode.hファイルを参照してください。各メッセージの説明は、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告のリストを参照してください。
XLA関数のリターン・コードがSQL_SUCCESSでない場合は、ttXlaError関数を使用して、XLAハンドルでXLA固有のエラーを取得します。
「エラーのチェック」も参照してください。
次の例では、XLA関数ttXlaTableByNameをコールした後、リターン・コードがSQL_SUCCESSであるかどうかを確認します。そうでない場合は、XLAエラー処理関数をコールし、その後アプリケーションを終了する関数をコールします。「XLAアプリケーションの終了」を参照してください。
rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME,
&SYSTEM_TABLE_ID, &userID);
if (rc != SQL_SUCCESS) {
handleXLAerror (rc, xla_handle, err_buf, &native_error);
fprintf(stderr,
"ttXlaTableByName() returns an error <%d>: %s", rc, err_buf);
TerminateGracefully(1);
}XLAエラー処理関数は、エラー・スタックからすべてのXLAエラーが読み取られまで繰り返しttXlaErrorをコールする必要があり、ttXlaErrorからのリターン・コードがSQL_NO_DATA_FOUNDになるまで続行します。エラーを再度読み取る必要がある場合は、ttXlaErrorRestart関数をコールしてエラー・スタックのポインタを最初のエラーに再設定します。(SQL_NO_DATA_FOUNDは、sqlext.h (timesten.hに含まれています)に定義されています。)
エラー・スタックは、ttXlaErrorまたはttXlaErrorRestart以外のすべてのXLA関数へのコール後に消去されます。
ノート:
ttXlaPersistOpenがXLAハンドルを作成できない場合は、エラー・コードSQL_INVALID_HANDLEを返します。XLAハンドルが作成されていないため、ttXlaErrorを使用してこのエラーを検出することはできません。SQL_INVALID_HANDLEは、メモリーが割り当てることができない場合または指定したパラメータが無効である場合にのみ返されます。
次の例では、xlaSimpleアプリケーション・プログラムのエラー関数であるhandleXLAerror()を示します。
void handleXLAerror(SQLRETURN rc, ttXlaHandle_h xlaHandle,
SQLCHAR* err_msg, SQLINTEGER* native_error)
{
SQLINTEGER retLen;
SQLINTEGER code;
char* err_msg_ptr;
/* initialize return codes */
rc = SQL_ERROR;
*native_error = -1;
err_msg[0] = '\0';
err_msg_ptr = (char*)err_msg;
while (1)
{
int rc = ttXlaError(xlaHandle, &code, err_msg_ptr,
ERR_BUF_LEN - (err_msg_ptr - (char*)err_msg), &retLen);
if (rc == SQL_NO_DATA_FOUND)
{
break;
}
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
sprintf(err_msg_ptr,
"*** Error fetching error message via ttXlaError(); rc=<%d>.",rc) ;
break;
}
rc = SQL_ERROR;
*native_error = code ;
/* append any other error messages */
err_msg_ptr += retLen;
}
}XLAブックマークを使用する表の削除
XLAブックマークがサブスクライブされている表を削除するには、ブックマークから表をサブスクライブ解除する必要があります。ブックマークから表をサブスクライブ解除するには、アプリケーションがブックマークに接続されているかどうかに応じて、いくつかの方法があります。
XLAアプリケーションが接続されており、削除する表を追跡しているブックマークを使用している場合は、次のタスクを実行します。
-
各XLAアプリケーションで
ttXlaTableStatus関数をコールし、newstatusパラメータを0に設定する必要があります。これにより、アプリケーションで使用中のXLAブックマークから表がサブスクライブ解除されます。 -
表を削除します。
XLAアプリケーションが接続されておらず、削除する表に関連付けられているブックマークを使用している場合は、次のタスクを実行します。
SYS.XLASUBSCRIPTIONSシステム表を問い合せて、削除する表にサブスクライブされているブックマークを確認します。ttXlaUnsubscribe組込みプロシージャを使用して、表にサブスクライブされている各XLAブックマークから表をサブスクライブ解除します。- 表を削除します。
ブックマークを削除すると、XLAブックマークからも表がサブスクライブ解除されます。「ブックマークの削除」を参照してください。
ブックマークの削除
アプリケーションを終了する場合や表を削除する場合に、ブックマークを削除することがあります。
アプリケーションが接続されており、ブックマークを使用している場合は、ttXlaDeleteBookmark関数を使用してXLAブックマークを削除します。
ブックマークは、前の接続がクローズされた後に新しい接続で再利用される可能性があります(「XLAブックマークについて」を参照)。新しい接続は、前の接続が停止した場所からトランザクション・ログの読取りを再開します。次の点に留意してください:
-
ブックマークを削除すると、後続のチェックポイント処理(組込みプロシージャ
ttCkpt、ttCkptBlockingなど)によって、トランザクション・ログ内の未読の更新レコードに関連するファイル・システム領域が解放されます。 -
ブックマークを削除しない場合、XLAアプリケーションは、接続してブックマークを再利用する際に、プログラムの終了以降累積していたすべての未読の更新レコードを読み取ります。これは、更新レコードがTimesTenトランザクション・ログで永続的であるためです。ただし、これらの未読のレコードがトランザクション・ログ・ファイルに累積し、大量のファイル・システム領域を使用すると危険です。
ノート:
-
レプリケーション・エージェントの実行中は、レプリケートされたブックマークを削除できません。
-
ブックマークを再利用する場合は、トランザクション・ログ・ファイル内の初期読取りログ・レコード識別子から開始します。ブックマークを再利用する接続で、前回、接続を切断した場所から読取りが開始されるようにするには、接続を切断する前に
ttXlaAcknowledgeをコールして、ブックマークの位置を現在アクセスしているレコードに再設定する必要があります。 -
『Oracle TimesTen In-Memory Databaseリファレンス』のttLogHoldsを参照してください。TimesTen組込みプロシージャは、トランザクション・ログの保留に関する情報を返します。
-
ttCkptとttCkptBlockingにはADMIN権限が必要です。TimesTenの組込みプロシージャと必要な権限については、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャを参照してください。
次の例に示すように、xlaSimpleアプリケーションのInitHandler()関数は、終了時にXLAブックマークを削除します。
if (deleteBookmark) {
ttXlaDeleteBookmark(xla_handle);
if (rc != SQL_SUCCESS) {
/* See XLA Error Handling */
}
xla_handle = NULL; /* Deleting the bookmark has the */
/* effect of disconnecting from XLA. */
}
/* Close the XLA connection as described in the next section,
Terminating an XLA Application. */
アプリケーションが接続されておらず、XLAブックマークを使用している場合、次のいずれかの方法でブックマークを削除できます。
-
ブックマークをクローズして
ttXlaBookmarkDelete組込みプロシージャをコールします。 -
ブックマークをクローズして
ttIsqlのxladeletebookmarkコマンドを使用します。
XLAアプリケーションの終了
XLAアプリケーションでトランザクション・ログの読取りを終了したら、コミットされていないトランザクションをロールバックし、すべてのハンドルを解放して、正常に終了する必要があります。
これには次の2つの方法があります。
-
すべての表およびマテリアライズド・ビューのサブスクライブを解除し、XLAブックマークを削除し、データベースから切断します。
または:
-
データベースからは切断しますが、XLAブックマークは維持します。後で再接続する際に、ブックマークからレコードの読取りを再開できます。
最初の方法では、次のステップを実行します。
ttXlaTableStatusをコールして各表およびマテリアライズド・ビューをサブスクライブ解除します(newstatusパラメータを0(ゼロ)に設定します)。ttXlaDeleteBookmarkをコールして、ブックマークを削除します。「ブックマークの削除」を参照してください。ttXlaCloseをコールして、XLAハンドルを切断します。SQL_ROLLBACKを指定してODBC関数SQLTransactをコールし、コミットされていないトランザクションをすべてロールバックします。- ODBC関数
SQLDisconnectをコールして、TimesTenデータベースから切断します。 - ODBC関数
SQLFreeConnectをコールして、ODBC接続ハンドルに割り当てられているメモリーを解放します。 - ODBC関数
SQLFreeEnvをコールして、ODBC環境ハンドルを解放します。
2番目の方法では、ブックマークを維持するため、最初の2つのステップを省略し残りのステップを完了します。
割当てとは反対の順序でリソースが解放されることに注意してください。たとえば、ODBC環境ハンドルはODBC接続ハンドルより先に割り当てられているため、クリーンアップでは、接続ハンドルを環境ハンドルより先に解放します。
この例では、xlaSimpleアプリケーションの終了関数であるTerminateGracefully()を示します。
void TerminateGracefully(int status)
{
SQLRETURN rc;
SQLINTEGER native_error ;
SQLINTEGER oldstatus;
SQLINTEGER newstatus = 0;
/* If the table has been subscribed to through XLA, unsubscribe it. */
if (SYSTEM_TABLE_ID != 0) {
rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0,
&oldstatus, &newstatus);
if (rc != SQL_SUCCESS) {
handleXLAerror (rc, xla_handle, err_buf, &native_error);
fprintf(stderr, "Error when unsubscribing from "TABLE_OWNER"."TABLE_NAME
" table <%d>: %s", rc, err_buf);
}
SYSTEM_TABLE_ID = 0;
}
/* Close the XLA connection. */
if (xla_handle != NULL) {
rc = ttXlaClose(xla_handle);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Error when disconnecting from XLA:<%d>", rc);
}
xla_handle = NULL;
}
if (hstmt != SQL_NULL_HSTMT) {
rc = SQLFreeStmt(hstmt, SQL_DROP);
if (rc != SQL_SUCCESS) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr, "Error when freeing statement handle:\n%s\n", err_buf);
}
hstmt = SQL_NULL_HSTMT;
}
/* Disconnect from TimesTen entirely. */
if (hdbc != SQL_NULL_HDBC) {
rc = SQLTransact(henv, hdbc, SQL_ROLLBACK);
if (rc != SQL_SUCCESS) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr, "Error when rolling back transaction:\n%s\n", err_buf);
}
rc = SQLDisconnect(hdbc);
if (rc != SQL_SUCCESS) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr, "Error when disconnecting from TimesTen:\n%s\n", err_buf);
}
rc = SQLFreeConnect(hdbc);
if (rc != SQL_SUCCESS) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr, "Error when freeing connection handle:\n%s\n", err_buf);
}
hdbc = SQL_NULL_HDBC;
}
if (henv != SQL_NULL_HENV) {
rc = SQLFreeEnv(henv);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr, "Error when freeing environment handle:\n%s\n", err_buf);
}
henv = SQL_NULL_HENV;
}
exit(status);
}




