この章では、ODBCを使用したOracle TimesTen In-Memory Databaseデータ・ストアへの接続および使用の方法について説明します。内容は次のとおりです。
TimesTenデータ・ストアにDSNを作成する方法の詳細は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』を参照してください。作成するDSNのタイプは、アプリケーションがデータ・ストアに直接接続するか、クライアント接続するかによって異なります。
データ・ストアに直接接続する場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータ・ストアの作成に関する項を参照してください。 UNIXまたはWindowsから直接接続するためのDSNの作成方法に関する説明があります。
データ・ストアへのクライアント接続を作成する場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTen ClientおよびTimesTen Serverの使用方法に関する項を参照してください。 UNIXまたはWindowsからクライアント/サーバー接続を行うためのDSNの作成方法に関する説明があります。
|
注意:
|
この項の以降の内容は次のとおりです。
TimesTenデータ・ストアへの接続およびそれに関連する機能の実行には、次のODBC関数を使用できます。
SQLConnect: ドライバをロードしてデータ・ストアに接続します。 接続ハンドルは、ステータス、トランザクションの状態、結果およびエラー情報などの接続情報が格納される場所を指します。
SQLDriverConnect: SQLConnectでサポートされている情報(データソース(TimesTenデータ・ストア)、ユーザー名およびパスワード)よりも詳細な情報が必要な場合に、SQLConnectのかわりに使用します。
SQLAllocConnect: 指定した環境内の接続ハンドルにメモリーを割り当てます。
SQLDisconnect: データ・ストアから切断します。 既存の接続ハンドルを唯一の引数として使用します。
これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
この項では、TimesTenデータ・ストアに対する接続および切断の例を示します。
例1-1 接続および切断(抜粋)
このコード部分では、SQLConnectおよびSQLDisconnectを起動して、FixedDsというデータ・ストアに対する接続および切断を行います。アプリケーションではじめてSQLConnectを起動すると、データ・ストアFixedDsが作成されます。 その後SQLConnectを起動すると、既存のデータ・ストアに接続されます。
#include <sql.h>
SQLRETURN retcode;
SQLHDBC hdbc;
...
retcode = SQLConnect(hdbc,
(SQLCHAR*)"FixedDs", SQL_NTS,
(SQLCHAR*)"johndoe", SQL_NTS,
(SQLCHAR*)"opensesame", SQL_NTS);
...
retcode = SQLDisconnect(hdbc);
...
例1-2 接続および切断(完全なプログラム)
この例には、データ・ストアを作成し、データ・ストアに対して接続および切断を行う完全なプログラムが含まれています。 この例では、SQLConnectではなくSQLDriverConnectを使用して接続を設定し、SQLAllocConnectを使用してメモリーを割り当てます。また、エラー・メッセージの取得方法についても示します。 (「エラー処理」も参照してください。)
#ifdef WIN32
# include <windows.h>
#else
# include <sqlunix.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void chkReturnCode(SQLRETURN rc, SQLHENV henv,
SQLHDBC hdbc, SQLHSTMT hstmt,
char* msg, char* filename,
int lineno, BOOL err_is_fatal);
#define DEFAULT_CONNSTR "DSN=sampledb_1121;PermSize=32"
int
main(int ac, char** av)
{
SQLRETURN rc = SQL_SUCCESS;
/* General return code for the API */
SQLHENV henv = SQL_NULL_HENV;
/* Environment handle */
SQLHDBC hdbc = SQL_NULL_HDBC;
/* Connection handle */
SQLHSTMT hstmt = SQL_NULL_HSTMT;
/* Statement handle */
SQLCHAR connOut[255];
/* Buffer for completed connection string */
SQLSMALLINT connOutLen;
/* Number of bytes returned in ConnOut */
SQLCHAR *connStr = (SQLCHAR*)DEFAULT_CONNSTR;
/* Connection string */
rc = SQLAllocEnv(&henv);
if (rc != SQL_SUCCESS) {
fprintf(stderr, "Unable to allocate an "
"environment handle\n");
exit(1);
}
rc = SQLAllocConnect(henv, &hdbc);
chkReturnCode(rc, henv, SQL_NULL_HDBC,
SQL_NULL_HSTMT,
"Unable to allocate a "
"connection handle\n",
__FILE__, __LINE__, 1);
rc = SQLDriverConnect(hdbc, NULL,
connStr, SQL_NTS,
connOut, sizeof(connOut),
&connOutLen,
SQL_DRIVER_NOPROMPT);
chkReturnCode(rc, henv, hdbc, SQL_NULL_HSTMT,
"Error in connecting to the"
" data store\n",
__FILE__, __LINE__, 1);
rc = SQLAllocStmt(hdbc, &hstmt);
chkReturnCode(rc, henv, hdbc, SQL_NULL_HSTMT,
"Unable to allocate a "
"statement handle\n",
__FILE__, __LINE__, 1);
/* Your application code here */
if (hstmt != SQL_NULL_HSTMT) {
rc = SQLFreeStmt(hstmt, SQL_DROP);
chkReturnCode(rc, henv, hdbc, hstmt,
"Unable to free the "
"statement handle\n",
__FILE__, __LINE__, 0);
}
rc = SQLDisconnect(hdbc);
chkReturnCode(rc, henv, hdbc,
SQL_NULL_HSTMT,
"Unable to close the "
"connection\n",
__FILE__, __LINE__, 0);
rc = SQLFreeConnect(hdbc);
chkReturnCode(rc, henv, hdbc,
SQL_NULL_HSTMT,
"Unable to free the "
"connection handle\n",
__FILE__, __LINE__, 0);
rc = SQLFreeEnv(henv);
chkReturnCode(rc, henv, SQL_NULL_HDBC,
SQL_NULL_HSTMT,
"Unable to free the "
"environment handle\n",
__FILE__, __LINE__, 0);
return 0;
}
static void
chkReturnCode(SQLRETURN rc, SQLHENV henv,
SQLHDBC hdbc, SQLHSTMT hstmt,
char* msg, char* filename,
int lineno, BOOL err_is_fatal)
{
#define MSG_LNG 512
SQLCHAR sqlState[MSG_LNG];
/* SQL state string */
SQLINTEGER nativeErr;
/* Native error code */
SQLCHAR errMsg[MSG_LNG];
/* Error msg text buffer pointer */
SQLSMALLINT errMsgLen;
/* Error msg text Available bytes */
SQLRETURN ret = SQL_SUCCESS;
if (rc != SQL_SUCCESS &&
rc != SQL_NO_DATA_FOUND ) {
if (rc != SQL_SUCCESS_WITH_INFO) {
/*
* It's not just a warning
*/
fprintf(stderr, "*** ERROR in %s, line %d:"
" %s\n",
filename, lineno, msg);
}
/*
* Now see why the error/warning occurred
*/
while (ret == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO) {
ret = SQLError(henv, hdbc, hstmt,
sqlState, &nativeErr,
errMsg, MSG_LNG,
&errMsgLen);
switch (ret) {
case SQL_SUCCESS:
fprintf(stderr, "*** %s\n"
"*** ODBC Error/Warning = %s, "
"TimesTen Error/Warning "
" = %d\n",
errMsg, sqlState,
nativeErr);
break;
case SQL_SUCCESS_WITH_INFO:
fprintf(stderr, "*** Call to SQLError"
" failed with return code of "
"SQL_SUCCESS_WITH_INFO.\n "
"*** Need to increase size of"
" message buffer.\n");
break;
case SQL_INVALID_HANDLE:
fprintf(stderr, "*** Call to SQLError"
" failed with return code of "
"SQL_INVALID_HANDLE.\n");
break;
case SQL_ERROR:
fprintf(stderr, "*** Call to SQLError"
" failed with return code of "
"SQL_ERROR.\n");
break;
case SQL_NO_DATA_FOUND:
break;
} /* switch */
} /* while */
if (rc != SQL_SUCCESS_WITH_INFO && err_is_fatal) {
fprintf(stderr, "Exiting.\n");
exit(-1);
}
}
}
データ・ストアへの接続時に接続文字列を指定して、プログラムで接続属性を設定または上書きできます。
接続属性の全般的な詳細は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』を参照してください。 一般接続属性には、特に権限は必要ありません。 データベースが初めてロードされたときに最初の接続属性が設定され、すべての接続に持続的に使用されます。 最初の接続属性の設定を変更してデータベースをロードできるのは、インスタンス管理者のみです。 特定の接続属性に関する固有の情報については、『Oracle TimesTen In-Memory Databaseリファレンス』を参照してください。
このコード部分では、mydsnというデータ・ストアに接続して、SQLDriverConnectコールで、アプリケーションがデフォルトの行レベル・ロックではなくデータ・ストア・レベル・ロックを使用することを示します。 LockLevelは一般接続属性であることに注意してください。
SQLHDBC hdbc;
SQLCHAR ConnStrOut[512];
SQLSMALLINT cbConnStrOut;
SQLRETURN rc;
rc = SQLDriverConnect(hdbc, NULL,
"DSN=mydsn;LockLevel=1", SQL_NTS,
ConnStrOut, sizeof (ConnStrOut),
&cbConnStrOut, SQL_DRIVER_NOPROMPT);
|
注意: TimesTenデータ・ストアへの接続ごとに、いくつかのファイルがオープンします。つまり、多数のスレッドを使用し、スレッドごとに別々の接続を使用するアプリケーションでは、各スレッドに対して複数のファイルがオープンします。このようなアプリケーションでは、オペレーティング・システムで同時にオープンできるファイル記述子の最大数を超える可能性があります。この場合は、多数のオープン・ファイルを許可するようシステムを設定します。 『Oracle TimesTen In-Memory Databaseリファレンス』のオープン・ファイルの数の制限に関する項を参照してください。 |
この項では、TimesTenデータ・ストアのデータの処理について説明します。内容は次のとおりです。
標準のC #includeファイル以外に、次のTimesTen #includeファイルがアプリケーションに含まれている必要があります。
| インクルード・ファイル | 説明 |
|---|---|
timesten.h |
TimesTen ODBC #includeファイル |
tt_errCode.h |
TimesTenネイティブ・エラー・コード |
SQLを使用してTimesTenデータ・ストアのデータを管理する方法については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータ・ストアのデータの処理に関する項を参照してください。 この項では、Cアプリケーション内でSQL文をコールするために使用される一般的な書式について説明します。内容は次のとおりです。
SQL文を実行するODBC関数は次の2つです。
SQLExecute: すでに準備されている文を実行します。 この関数はSQLPrepareとともに使用します。 実行結果を使用してアプリケーションが実行された後、その結果を破棄して、別のパラメータ値でSQLExecuteを再実行することができます。
通常、バインド・パラメータを使用するDML文または比較的実行回数の多い文に使用します。
SQLExecDirect: 文を準備して実行します。
通常、DDL文、または比較的実行回数が少なくバンド・パラメータを使用しないDML文に使用します。
これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
SQLExecDirect関数は、例1-4に示すように使用できます。
SQLExecute関数とSQLPrepare関数の使用方法は、次の項「問合せの準備および実行とカーソルの使用」に示します。
例1-4 SQLExecDirectを使用したSQL文の実行
次のコード例では、CustIDおよびCustNameという2つの列を持つ、NameIDという表を作成します。この表では、名前(文字)が識別子(整数)にマップされます。
#include <sql.h>
SQLRETURN rc;
SQLHSTMT hstmt;
...
rc = SQLExecDirect(hstmt, (SQLCHAR*)
"CREATE TABLE NameID (CustID INTEGER, CustName VARCHAR(50))",
SQL_NTS);
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
... /* handle error */
この項では、問合せの準備および実行とカーソル使用の基本的な手順を示します。アプリケーションでは、カーソルを使用して問合せの結果がスクロールされ、一度に1つの結果行が確認されます。
ODBC設定では、カーソルは常に結果セットに関連付けられています。 この関連付けはODBCドライバによって行われます。 アプリケーションでは、「SQLSetStmtOptionおよびSQLGetStmtOptionのオプションのサポート」で説明されているSQLSetStmtOptionオプションを使用して、一度にフェッチする行数などのカーソル特性を制御できます。 問合せを実行する場合の手順は、通常は次のとおりです。
SQLPrepareを使用して、SELECTを実行するための文を準備します。
SQLBindParameter(文にパラメータが含まれている場合)を使用して、各パラメータをアプリケーション・アドレスにバインドします。 「SQLBindParameter関数」を参照してください。 (次の例1-5ではパラメータをバインドしていません。)
SQLExecuteをコールして、SELECT文を開始します。 「SQLExecDirect関数およびSQLExecute関数」を参照してください。
SQLBindColをコールして結果の列に記憶域およびデータ型を割り当て、列の結果をアプリケーション内のローカル変数記憶域にバインドします。
SQLFetchをコールして結果をフェッチします。 文ハンドルを指定します。
SQLFreeStmtをコールして文ハンドルを解放します。 文ハンドルおよびSQL_CLOSE、SQL_DROP、SQL_UNBINDまたはSQL_RESET_PARAMSのいずれかを指定します。
これらのODBC関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
例1-5 問合せの実行およびカーソルの使用
この例では、ODBCコールを使用して問合せを準備および実行する方法を示します。 わかりやすくするために、エラーのチェックは省略してあります。 前述のODBC関数以外に、この例では、SQLNumResultColsを使用して結果セット内の列数を返し、SQLDescribeColを使用して結果セットの1つの列の記述(列名、型、精度、スケールおよびNULL値可能)を返し、SQLBindColを使用して記憶域およびデータ型を結果セット内の列に割り当てます。 これらすべての関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
#include <sql.h>
SQLHSTMT hstmt;
SQLRETURN rc;
int i;
SQLSMALLINT numCols;
SQLCHAR colname[32];
SQLSMALLINT colnamelen, coltype, scale, nullable;
SQLULEN collen [MAXCOLS];
SQLLEN outlen [MAXCOLS];
SQLCHAR* data [MAXCOLS];
/* other declarations and program set-up here */
/* Prepare the SELECT statement */
rc = SQLPrepare(hstmt,
(SQLCHAR*) "SELECT * FROM EMP WHERE AGE>20",
SQL_NTS);
/* ... */
/* Determine number of columns in result rows */
rc = SQLNumResultCols(hstmt, &numCols);
/* ... */
/* Describe and bind the columns */
for (i = 0; i < numCols; i++) {
rc = SQLDescribeCol(hstmt,
(SQLSMALLINT) (i + 1),
colname,(SQLSMALLINT)sizeof(colname), &colnamelen, &coltype, &collen[i],
&scale, &nullable);
/* ... */
data[i] = (SQLCHAR*) malloc (collen[i] +1);
rc = SQLBindCol(hstmt, (SQLSMALLINT) (i + 1),
SQL_C_CHAR, data[i],
COL_LEN_MAX, &outlen[i]);
/* ... */
}
/* Execute the SELECT statement */
rc = SQLExecute(hstmt);
/* ... */
/* Fetch the rows */
if (numCols > 0) {
while ((rc = SQLFetch(hstmt)) == SQL_SUCCESS ||
rc == SQL_SUCCESS_WITH_INFO) {
/* ... "Process" the result row */
} /* end of for-loop */
if (rc != SQL_NO_DATA_FOUND)
fprintf(stderr,
"Unable to fetch the next row\n");
/* Close the cursor associated with the SELECT statement */
rc = SQLFreeStmt(hstmt, SQL_CLOSE);
}
標準のODBCでは、結果セットの列記述のようなSQL文に関する情報をアプリケーションで使用可能にしたり、これらの情報にSQLDescribeColなどのコールでアクセスできるように、SQLPrepareコールをSQLエンジンでコンパイルすることが期待されます。 この機能を実現するには、SQLPrepareコールをサーバーに送信して処理する必要があります。
このことは、たとえば、Oracle Call Interface(OCI)で期待される動作とは対照的です。OCIでは、準備コールは単にパラメータの名前と位置を抽出するための、クライアントで実行される軽量処理であることが期待されています。
OCIが期待する動作との一貫性を実現し、クライアントとサーバー間の不要なラウンドトリップを回避するため、TimesTenクライアント・ライブラリに実装されたSQLPrepareでは、遅延準備と呼ばれる処理を実行します。この処理では、リクエストは要求されるまでサーバーに送信されません。 ラウンドトリップが必要になる場合の例を、次に示します。
SQLExecuteコールがある場合。 まだサーバーに送信されていない遅延準備コールがある場合、クライアント上のSQLExecuteコールはSQLExecDirectコールに変換されることに注意してください。
SQLエンジンからのみ取得可能な、問合せに関する情報のリクエストがある場合(SQLDescribeColコールがある場合など)。
標準のODBCのこのようなコールの多くは、以前にSQLPrepareコールで返された情報にアクセスできますが、遅延準備機能を使用している場合は、SQLPrepareコールがサーバーに送信され、情報は必要な場合にのみアプリケーションに返されます。
|
注意: 遅延準備機能はTimesTenの直接ドライバでは実装されておらず、また関連もありません。 |
遅延準備の実装には、アプリケーションまたはユーザー・レベルの変更は必要ありません。ただし、次の関数のいずれかをコールすると、以前に準備された文で必要とされた情報がまだ取得されていなかった場合には、サーバーに対するラウンドトリップが発生する可能性があります。
SQLColAttributes
SQLDescribeCol
SQLDescribeParam
SQLNumResultCols
SQLNumParams
SQLGetStmtOption(SQLエンジンでコンパイルされている文に依存するオプションの場合)
また、これらの関数のいずれかをコールすると、以前のSQLPrepareコールのエラーが、これらのコールの1つが実行されるまで遅延される場合があることに注意してください。 また、これらのコールで、コール自体に固有のエラーの他に、SQLPrepareに固有のエラーも返される場合があります。
この項では、SQL文の入力または出力パラメータをバインドする方法について説明します。内容は次のとおりです。
表1-1に、ODBC SQLデータ型とOracle SQLまたはPL/SQLデータ型とのマッピングを示します。
表1-1 ODBC SQLとTimesTen SQLまたはPL/SQLとのデータ型のマッピング
| ODBCデータ型 | SQLまたはPL/SQLのデータ型 |
|---|---|
|
SQL_BIGINT |
NUMBER |
|
SQL_BINARY |
RAW(p) |
|
SQL_BIT |
PLS_INTEGER |
|
SQL_CHAR |
CHAR(p) |
|
SQL_DATE |
DATE |
|
SQL_DECIMAL |
NUMBER |
|
SQL_DOUBLE |
NUMBER |
|
SQL_FLOAT |
BINARY_DOUBLE |
|
SQL_INTEGER |
PLS_INTEGER |
|
SQL_NUMERIC |
NUMBER |
|
SQL_REAL |
BINARY_FLOAT |
|
SQL_REFCURSOR |
REF CURSOR |
|
SQL_ROWID |
ROWID |
|
SQL_SMALLINT |
PLS_INTEGER |
|
SQL_TIMESTAMP |
TIMESTAMP(s) |
|
SQL_TINYINT |
PLS_INTEGER |
|
SQL_VARBINARY |
RAW(p) |
|
SQL_VARCHAR |
VARCHAR2(p) |
|
SQL_WCHAR |
NCHAR(p) |
|
SQL_WVARCHAR |
NVARCHAR2(p) |
|
注意:
|
ODBCのSQLBindParameter関数を使用して、SQL文のパラメータをバインドします。 IN、OUTまたはIN OUTパラメータを含めることができます。
ODBCを介して入力パラメータをバインドするには、fParamType引数にSQL_PARAM_INPUTを設定してSQLBindParameter関数を使用します。 SQLBindParameter関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。 表1-2に、この引数の簡単な概要を示します。
ODBCを介して出力または入出力パラメータをバインドするには、fParamType引数にSQL_PARAM_OUTPUTまたはSQL_PARAM_INPUT_OUTPUTをそれぞれ設定してSQLBindParameter関数を使用します。 入力パラメータと同様に、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。 また、SQLBindParameterのrgbValue引数、cbValueMax引数およびpcbValue引数も使用します。
表1-2 SQLBindParameterの引数
| 引数 | 型 | 説明 |
|---|---|---|
|
|
SQLHSTMT |
文ハンドル |
|
|
SQLUSMALLINT |
左から右へ順に、1から始まるパラメータ番号 |
|
|
SQLSMALLINT |
入力または出力を示す、SQL_PARAM_INPUT、SQL_PARAM_OUTPUTまたはSQL_PARAM_INPUT_OUTPUT |
|
|
SQLSMALLINT |
パラメータのCデータ型 |
|
|
SQLSMALLINT |
パラメータのSQLデータ型 |
|
|
SQLULEN |
列の精度またはパラメータ・マーカーの式 |
|
|
SQLSMALLINT |
列のスケールまたはパラメータ・マーカーの式 |
|
|
SQLPOINTER |
パラメータのデータに使用するバッファへのポインタ |
|
|
SQLLEN |
|
|
|
SQLLEN* |
パラメータの長さに使用するバッファへのポインタ |
SQLパラメータのデータ型の割当てに関して、TimesTenとOracle Databaseの機能には重要な違いがあります。 TimesTen内で実行する文の場合、TimesTenの問合せオプティマイザがSQLパラメータのデータ型を決定します。 一方、Oracleでは、SQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、アプリケーションでデータ型を指定する必要があります。 TimesTenではこれらの引数は無視されます。 アプリケーションでデータ型の割当てを変更したり、割当てに影響を与えることはできません。
TimesTenをIMDB Cacheとして使用している場合、TimesTenの動作はパススルー文には適用されないことに注意してください。 文をOracleにパススルーして実行する場合は、fSqlType、cbColDefおよびibScaleを必要に応じて使用して、データ型を指定する必要があります。
TimesTenの動作はPL/SQL文にも適用されません。
CパラメータをPL/SQLブロックまたはプロシージャで使用すると、パラメータのCデータ型およびそのコンテキストで使用可能な1つ以上のPL/SQLデータ型に応じて、データ型変換が発生する場合があります。 その組合せがサポートされていない場合、エラーが発生します。 このような変換には、Cデータ型からSQLまたはPL/SQLデータ型(INパラメータ)、SQLまたはPL/SQLデータ型からCデータ型(OUTパラメータ)、またはその両方(IN OUTパラメータ)の変換があります。
INパラメータをTimesTenのPL/SQLで使用するには、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。 これは、前述の「SQLパラメータのデータ型の割当てに関する考慮事項」で説明したとおり、SQL入力パラメータのサポート方法とは対照的です。
また、SQLBindParameterのrgbValue引数、cbValueMax引数およびpcbValue引数も、次のようにINパラメータに使用します。
rgbValue: 文を実行する前に、アプリケーションは、アプリケーションに渡されるパラメータ値を格納するバッファを指します。
cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す入力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。
pcbValue: 文を実行する前に、次のいずれかを含むバッファを指します。
rgbValueが指す値の実際の長さ。 INパラメータの場合、文字データまたはバイナリ・データにのみ有効です。
空文字で終了する文字列の場合は、SQL_NTS。
NULL値の場合は、SQL_NULL_DATA。
ODBCのSQLBindParameter関数のrgbValue引数、cbValueMax引数およびpcbValue引数を、次のようにOUTパラメータに使用します。
rgbValue: 文の実行中に、文から返された値を格納するバッファを指します。
cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す出力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。ODBCでは、データが切り捨てられる場合でも、すべての文字データが空文字で終了することに注意してください。 そのため、OUTパラメータに文字データが含まれる場合、cbValueMaxは受入れ可能な最長値よりも1大きい値になります。 さらに、OUTパラメータに2バイトの空終端文字を使用するC NCHARデータが含まれる場合、cbValueMaxは受入れ可能な最長値よりも2大きい値になります。
pcbValue: 文を実行した後に、次のいずれかを含むバッファを指します。
rgbValueが指す値の実際の長さ(文字データおよびバイナリ・データのみでなく、すべてのCデータ型が対象)。 rgbValueが指すバッファに値が収まるかどうかに関係なく、完全なパラメータ値の長さです。
NULL値の場合は、SQL_NULL_DATA。
例1-6 出力パラメータのバインド
この例では、PL/SQLの無名ブロックを準備、バインドおよび実行する方法を示します。 無名ブロックでは、値abcdeをバインド変数aに、値123をバインド変数bに割り当てます。
SQLPrepareは無名ブロックを準備します。 SQLBindParameterは、最初のパラメータ(a)をデータ型SQL_VARCHARの出力パラメータとしてバインドし、2番目のパラメータ(b)をデータ型SQL_INTEGERの出力パラメータとしてバインドします。 SQLExecuteは無名ブロックを実行します。
{
SQLHSTMT hstmt;
char aval[11];
SQLLEN aval_len;
SQLINTEGER bval;
SQLLEN bval_len;
SQLAllocStmt(hdbc, &hstmt);
SQLPrepare(hstmt,
(SQLCHAR*)"begin :a := 'abcde'; :b := 123; end;",
SQL_NTS);
SQLBindParameter(hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_VARCHAR,
10, 0, (SQLPOINTER)aval, sizeof(aval), &aval_len);
SQLBindParameter(hstmt, 2, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER,
0, 0, (SQLPOINTER)&bval, sizeof(bval), &bval_len);
SQLExecute(hstmt);
printf("aval = [%s] (length = %d), bval = %d\n", aval, (int)aval_len, bval);
}
ODBCのSQLBindParameter関数のrgbValue引数、cbValueMax引数およびpcbValue引数を、次のようにIN OUTパラメータに使用します。
rgbValue: 最初に、「INパラメータのバインド」で説明したように、文を実行する前に使用します。 次に、前の項「OUTパラメータのバインド」で説明したように、文の実行中に使用します。 IN OUTパラメータの場合、文の実行で出力された値は、アプリケーションによって上書きされないかぎり、直後に続く文の実行に対する入力値になります。 また、実行時データの使用中にバインドされるIN OUT値の場合、rgbValueの値は、ODBCのSQLParamData関数で返されるトークンおよび出力値が格納されるバッファへのポインタの両方として機能します。
cbValueMax: 文字データおよびバイナリ・データの場合、最初に、「INパラメータのバインド」で説明したように使用します。 次に、前の項「OUTパラメータのバインド」で説明したように使用します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。ODBCでは、データが切り捨てられる場合でも、すべての文字データが空文字で終了することに注意してください。 そのため、IN OUTパラメータに文字データが含まれる場合、cbValueMaxは受入れ可能な最長値よりも1大きい値になります。 さらに、IN OUTパラメータに2バイトの空終端文字を使用するC NCHARデータが含まれる場合、cbValueMaxは受入れ可能な最長値よりも2大きい値になります。
pcbValue: 最初に、「INパラメータのバインド」で説明したように、文を実行する前に使用します。 次に、前の項「OUTパラメータのバインド」で説明したように、文の実行後に使用します。
|
重要:
|
TimesTenでは、SQL文で重複したパラメータをバインドするための2つのモードがいずれもサポートされます。 (PL/SQL文については、「PL/SQLでの重複したパラメータのバインド」を参照してください。)
Oracleモード: 同じパラメータ名の複数のインスタンスは異なるパラメータとみなされます。
従来のTimesTenモード: 以前のリリースと同様に、同じパラメータ名の複数のインスタンスは同じパラメータとみなされます。
DuplicateBindMode一般接続属性を使用すると、目的のモードを選択できます。 Oracleモードの場合はDuplicateBindMode=0(デフォルト)、TimesTenモードの場合はDuplicateBindMode=1です。 この属性は一般接続属性であるため、同じデータベースに対する複数の同時接続でそれぞれ異なる値を使用できます。 この属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のDuplicateBindModeに関する項を参照してください。
この項の後半では、次の問合せを検討しながら各モードの詳細を説明します。
SELECT * FROM employees WHERE employee_id < :a AND manager_id > :a AND salary < :b;
|
注意:
|
Oracleモード(DuplicateBindMode=0)では、1つのSQL文に含まれる同じパラメータ名の複数のインスタンスは異なるパラメータとみなされます。 パラメータの位置番号が割り当てられるとき、名前の重複に関係なく、パラメータの出現ごとに番号が与えられます。 アプリケーションは、少なくとも各パラメータ名の最初の出現時に値をバインドする必要があります。 特定のパラメータ名の2回目以降の出現に関して、アプリケーションでは次の選択肢があります。
出現ごとに異なる値をバインドします。
出現したパラメータをバインドしないでおきます。この場合には、最初の出現時と同じ値が使用されます。
いずれの場合も、出現ごとに異なるパラメータ位置番号が付けられます。
前述のSQL文のaの2回目の出現に対して異なる値を使用するには、次のように指定します。
SQLBindParameter(..., 1, ...); /* first occurrence of :a */ SQLBindParameter(..., 2, ...); /* second occurrence of :a */ SQLBindParameter(..., 3, ...); /* occurrence of :b */
aの両方の出現に対して同じ値を使用するには、次のように指定します。
SQLBindParameter(..., 1, ...); /* both occurrences of :a */ SQLBindParameter(..., 3, ...); /* occurrence of :b */
いずれの場合も、パラメータbは位置3に存在するとみなされます。
Oracleモードでは、SQLNumParams ODBC関数はこの例のパラメータの数として3を返します。
TimesTenモード(DuplicateBindMode=1)では、重複したパラメータを含むSQL文は、異なるパラメータ名のみが別のパラメータとしてみなされるように解析されます。
バインドは、パラメータ名が最初に出現した位置に基づいて行われます。 その後、このパラメータ名が出現しても、それ専用の位置番号は与えられません。 同じパラメータ名が出現するたびに、すべて同じ値が使用されます。
前述のSQL文の場合、2つのaの出現は1つのパラメータとみなされるため、別々にバインドできません。
SQLBindParameter(..., 1, ...); /* both occurrences of :a */ SQLBindParameter(..., 2, ...); /* occurrence of :b */
TimesTenモードでは、パラメータbは位置3ではなく、位置2に存在するとみなされることに注意してください。
TimesTenモードでは、SQLNumParams ODBC関数はこの例のパラメータの数として2を返します。
前述の説明は、PL/SQL内には当てはまりません。 かわりにPL/SQLセマンティクスが適用され、このセマンティクスを使用してそれぞれの一意のパラメータの値をバインドします。 たとえば、次のブロックをコールするアプリケーションは、:aに対応する1つのパラメータのみをバインドします。
DECLARE x NUMBER; y NUMBER; BEGIN x:=:a; y:=:a; END;
次のブロックをコールするアプリケーションでも、1つのパラメータのみをバインドします。
BEGIN INSERT INTO tab1 VALUES(:a, :a); END
次のブロックをコールするアプリケーションでは、:aをパラメータ#1、:bをパラメータ#2として、2つのパラメータをバインドします。各INSERT文の2番目のパラメータは、最初のINSERT文の最初のパラメータと同じ値を使用します。
BEGIN INSERT INTO tab1 VALUES(:a, :a); INSERT INTO tab1 VALUES(:b, :a); END
REF CURSORはPL/SQLの概念です。REF CURSORはSQL結果セット上のカーソルに対するハンドルであり、PL/SQLとアプリケーションの間で渡されることができます。 TimesTenでは、PL/SQLでカーソルをオープンした後、アプリケーションにREF CURSORを渡すことができます。 結果は、アプリケーション内でODBCコールを使用して処理できます。 これがOUT REF CURSOR(PL/SQLに対するOUTパラメータ)です。 REF CURSORは文ハンドルにアタッチされ、アプリケーションは任意の結果セットと同じAPIを使用して結果セットを記述およびフェッチできます。
REF CURSORを使用するには、次の手順を実行します。 REF CURSOR OUTパラメータを使用してカーソルを返すPL/SQL文を想定しています。 準備、バインド、実行およびフェッチの基本的な手順は、「問合せの準備および実行とカーソルの使用」のカーソルの例と同じです。
SQLPrepareを使用して、最初の文ハンドルと関連付けるPL/SQL文を準備します。
SQLBindParameterを使用して、文の各パラメータをバインドします。 REF CURSOR出力パラメータをバインドする場合、割り当てられた2番目の文ハンドルをrgbValue(データ・バッファへのポインタ)として使用します。
pcbValue引数、ibScale引数、cbValueMax引数およびpcbValue引数は、REF CURSORに対しては無視されます。
SQLBindParameterのこれらの引数および他の引数の詳細は、「SQLBindParameter関数」および「OUTパラメータのバインド」を参照してください。
SQLExecuteをコールして、文を実行します。
SQLBindColをコールして、結果列をローカル変数の記憶域にバインドします。
SQLFetchをコールして結果をフェッチします。 PL/SQLからアプリケーションにREF CURSORが渡された後、アプリケーションは結果セットの場合と同様に、その結果を記述およびフェッチできます。
SQLFreeStmtを使用して文ハンドルを解放します。
これらの手順を、次の例で実際に行ってみます。 これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
|
重要: PL/SQLとアプリケーションの間のREF CURSORの受渡しについて、TimesTenでは、PL/SQLからアプリケーションへのOUT REF CURSORのみと、単一のREF CURSORのみを返す文がサポートされます。 |
例1-7 問合せの実行およびREF CURSORの使用
この例では、REF CURSORを使用して問合せの準備、パラメータのバインド、問合せの実行、ローカル変数記憶域への結果のバインド、および結果のフェッチを行う基本的な手順を示します。 わかりやすくするためにエラー処理は省略しています。 以前に概要を示したODBC関数以外に、この例ではSQLAllocStmtを使用して文ハンドルにメモリーを割り当てます。
refcursor_example(SQLHDBC hdbc)
{
SQLCHAR* stmt_text;
SQLHSTMT plsql_hstmt;
SQLHSTMT refcursor_hstmt;
SQLINTEGER deptid;
SQLINTEGER empid;
SQLCHAR lastname[30];
/* allocate 2 statement handles: one for the plsql statement and
* one for the ref cursor */
SQLAllocStmt(hdbc, &plsql_hstmt);
SQLAllocStmt(hdbc, &refcursor_hstmt);
/* prepare the plsql statement */
stmt_text = (SQLCHAR*)
"begin "
"open :refc for "
"select employee_id, last_name "
"from employees "
"where department_id = :dept; "
"end;";
SQLPrepare(plsql_hstmt, stmt_text, SQL_NTS);
/* bind parameter 1 (:refc) to refcursor_hstmt */
SQLBindParameter(plsql_hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_REFCURSOR,
SQL_REFCURSOR, 0, 0, refcursor_hstmt, 0, 0);
/* bind parameter 2 (:deptid) to local variable deptid */
SQLBindParameter(plsql_hstmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG,
SQL_INTEGER, 0, 0, &deptid, 0, 0);
/* set the value for :deptid */
deptid = 30;
/* execute the plsql statement */
SQLExecute(plsql_hstmt);
/*
* The result set is now attached to refcursor_hstmt.
* Bind the result columns and fetch the result set.
*/
/* bind result column 1 to local variable empid */
SQLBindCol(refcursor_hstmt, 1, SQL_C_SLONG,
(SQLPOINTER)&empid, 0, 0);
/* bind result column 2 to local variable lastname */
SQLBindCol(refcursor_hstmt, 2, SQL_C_CHAR,
(SQLPOINTER)lastname, sizeof(lastname), 0);
/* fetch the result set */
while(SQLFetch(refcursor_hstmt) != SQL_NO_DATA_FOUND){
printf("%d, %s\n", empid, lastname);
}
/* close the ref cursor's statement handle and drop both handles */
SQLFreeStmt(refcursor_hstmt, SQL_DROP);
SQLFreeStmt(plsql_hstmt, SQL_DROP);
}
TimesTenデータベース表の各行には、ROWIDと呼ばれる一意の識別子があります。アプリケーションでは、ROWID擬似列から行のROWIDを取得できます。ROWIDはバイナリまたは文字形式で表すことができます。
アプリケーションでは、SQL文のWHERE句などで、一重引用符で囲んだCHAR定数としてリテラルのROWID値を指定できます。
表1-1に示すように、ODBC SQLデータ型のSQL_ROWIDはSQLデータ型のROWIDに対応します。
パラメータおよび結果セット列では、ROWIDは、Cデータ型のSQL_C_BINARY、SQL_C_WCHARおよびSQL_C_CHARとの間で双方向に変換可能です。 SQL_C_CHARは、ROWIDのデフォルトのCデータ型です。 ROWIDのサイズは、SQL_C_BINARYとしては12バイト、SQL_C_CHARとしては18バイトおよびSQL_C_WCHARとしては36バイトです。
ROWIDおよびROWIDデータ型の使用方法および存続期間などの詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のROWIDデータ型およびROWIDの指定に関する項を参照してください。
|
注意: Oracle TimesTen In-Memory Databaseでは、PL/SQLデータ型のUROWIDはサポートされていません。 |
TimesTenによるODBCの拡張によって、アプリケーションはTimesTenデータ・ストアからODBCドライバのバッファに複数のデータ行をプリフェッチできます。 これにより、コミット読取りレベルまたはシリアライズ可能分離レベルを使用するアプリケーションのパフォーマンスを向上できます。
TT_PREFETCH_COUNT接続オプションによって、SQLFetchコールでプリフェッチする行数が決定されます。 このオプションは、直接アクセスとクライアント/サーバーの両方のアプリケーションで使用できます。
TT_PREFETCH_COUNTは、SQLSetConnectOptionまたはSQLSetStmtOptionへのコールで設定できます。値には0(ゼロ)から128までの任意の整数を設定できます。次に例を示します。
rc = SQLSetConnectOption(hdbc, TT_PREFETCH_COUNT, 100);
この設定を使用すると、最初のSQLFetchコールで100行がプリフェッチされます。 後続のSQLFetchコールは、ODBCバッファが使い果たされるまで、データベースのかわりにODBCバッファからフェッチします。 ODBCバッファが使い果たされると、次のSQLFetchコールでは別の100行がバッファにフェッチされます。
プリフェッチを無効にするには、TT_PREFETCH_COUNTを1に設定します。
プリフェッチ数を0(ゼロ)に設定すると、TimesTenは、データ・ストアに設定した分離レベルに応じて、デフォルトの値を使用します。コミット読取り分離モードでは、デフォルトのプリフェッチの値は5です。シリアライズ可能分離モードでは、デフォルトは128です。デフォルトのプリフェッチの値はほとんどのアプリケーションに最適な設定です。一般的に、値を高く設定すると、リソースの使用量がわずかに増加しますが、大きい結果セットに対するパフォーマンスは向上する可能性があります。
分離レベルは、次のように設定できます。
rc = SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_READ_COMMITTED);
または
rc = SQLSetConnectOption(hdbc, SQL_TXN_ISOLATION, SQL_TXN_SERIALIZABLE);
TimesTenのデフォルトでは、実行したDML変更(更新、挿入または削除)が自動的にコミットされるよう、自動コミットは有効になっています。 ただし、この機能を無効にして、手動で変更をコミット(またはロールバック)することをお薦めします。 自動コミットの詳細は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のトランザクション・セマンティクスに関する項を参照してください。
自動コミットが無効になっている場合は、SQLTransact ODBC関数を使用して手動でトランザクションをコミットまたはロールバックできます。 この関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
|
注意:
|
例1-8 データベースの更新および変更のコミット
この例では、選択した従業員の昇給を行う文を準備して実行した後、手動で変更をコミットします。 自動コミットが事前に無効にされていることを前提としています。
update_example(SQLHDBC hdbc)
{
SQLCHAR* stmt_text;
SQLHSTMT hstmt;
SQLINTEGER raise_pct;
char hiredate_str[30];
SQLLEN hiredate_len;
SQLLEN numrows;
/* allocate a statement handle */
SQLAllocStmt(hdbc, &hstmt);
/* prepare an update statement to give raises to employees hired before a
* given date */
stmt_text = (SQLCHAR*)
"update employees "
"set salary = salary * ((100 + :raise_pct) / 100.0) "
"where hire_date < :hiredate";
SQLPrepare(hstmt, stmt_text, SQL_NTS);
/* bind parameter 1 (:raise_pct) to variable raise_pct */
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
SQL_DECIMAL, 0, 0, (SQLPOINTER)&raise_pct, 0, 0);
/* bind parameter 2 (:hiredate) to variable hiredate_str */
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_TIMESTAMP, 0, 0, (SQLPOINTER)hiredate_str,
sizeof(hiredate_str), &hiredate_len);
/* set parameter values to give a 10% raise to employees hired before
* January 1, 1996. */
raise_pct = 10;
strcpy(hiredate_str, "1996-01-01");
hiredate_len = SQL_NTS;
/* execute the update statement */
SQLExecute(hstmt);
/* print the number of employees who got raises. See */
SQLRowCount(hstmt, &numrows);
printf("Gave raises to %d employees.\n", numrows);
/* drop the statement handle */
SQLFreeStmt(hstmt, SQL_DROP);
/* commit the changes */
SQLTransact(henv, hdbc, SQL_COMMIT);
}
DML RETURNINGと呼ばれるRETURNING INTO句をINSERT、UPDATEまたはDELETE文で使用すると、処理の影響を受けた行の特定の項目を返すことができます。 これにより、処理の影響を受けた対象を確認する場合などに、後続のSELECT文および別個のラウンドトリップが不要になります。
ODBCの場合、DML RETURNINGは単一行処理から項目を返すことに限定されます。 この句では、項目をOUTパラメータのリストに返します。 「パラメータのバインド」の記述に従って、OUTパラメータをバインドします。
TimesTenでのRETURNING INTO句のSQL構文および制限については、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のINSERT、UPDATEおよびDELETEに関する項を参照してください。
DML RETURNINGの詳細は、『Oracle Database PL/SQL言語リファレンス』のRETURNING INTO句に関する項を参照してください。
例1-9 DML RETURNING
この例は、前の項の例1-8を一部変更したものです。
void
update_example(SQLHDBC hdbc)
{
SQLCHAR* stmt_text;
SQLHSTMT hstmt;
SQLINTEGER raise_pct;
char hiredate_str[30];
char last_name[30];
SQLLEN hiredate_len;
SQLLEN numrows;
/* allocate a statement handle */
SQLAllocStmt(hdbc, &hstmt);
/* prepare an update statement to give a raise to one employee hired
before a given date and return that employee's last name */
stmt_text = (SQLCHAR*)
"update employees "
"set salary = salary * ((100 + :raise_pct) / 100.0) "
"where hire_date < :hiredate and rownum = 1 returning last_name into "
":last_name";
SQLPrepare(hstmt, stmt_text, SQL_NTS);
/* bind parameter 1 (:raise_pct) to variable raise_pct */
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG,
SQL_DECIMAL, 0, 0, (SQLPOINTER)&raise_pct, 0, 0);
/* bind parameter 2 (:hiredate) to variable hiredate_str */
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_TIMESTAMP, 0, 0, (SQLPOINTER)hiredate_str,
sizeof(hiredate_str), &hiredate_len);
/* bind parameter 3 (:last_name) to variable last_name */
SQLBindParameter(hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_CHAR,
SQL_CHAR, 0, 0, (SQLPOINTER)last_name,
sizeof(last_name), NULL);
/* set parameter values to give a 10% raise to an employee hired before
* January 1, 1996. */
raise_pct = 10;
strcpy(hiredate_str, "1996-01-01");
hiredate_len = SQL_NTS;
/* execute the update statement */
SQLExecute(hstmt);
/* tell us who the lucky person is */
printf("Gave raise to %s.\n", last_name );
/* drop the statement handle */
SQLFreeStmt(hstmt, SQL_DROP);
/* commit the changes */
SQLTransact(henv, hdbc, SQL_COMMIT);
}
この例では、昇給の対象者としてKingが返されます。
標準のODBC機能を拡張するTimesTen組込みプロシージャについては、『Oracle TimesTen In-Memory Databaseリファレンス』の組込みプロシージャに関する項を参照してください。 これらのプロシージャは、ODBCのコール構文を使用して起動できます。書式は次のとおりです。
rc = SQLExecDirect (hstmt, (SQLCHAR*) "call procedure",SQL_NTS);
次のODBCの例では、ttCkptプロシージャをコールしてファジー・チェックポイントを開始します。
rc = SQLExecDirect (hstmt, (SQLCHAR*) "call ttCkpt",SQL_NTS);
|
注意: TimesTen組込みプロシージャに必要な権限については、『Oracle TimesTen In-Memory Databaseリファレンス』を参照してください。 たとえば、ttCkptにはADMIN権限が必要です。 |
TimesTenには、実行するSQL文またはプロシージャ・コールの時間を制限する方法が2つあり、SQLExecute、SQLExecDirectまたはSQLFetchコールに適用されます。
前者の場合、タイムアウト期間に達すると、文の実行が停止し、エラーがスローされます。 後者の場合、しきい値に達すると、SNMPトラップがスローされますが実行は継続されます。
タイムアウトまでのSQL文の実行時間を制御するには、SQLSetStmtOptionまたはSQLSetConnectOptionコールを使用してSQL_QUERY_TIMEOUTオプションを設定することで、タイムアウト値を秒単位で指定できます。 SQL_QUERY_TIMEOUTという名前にもかかわらず、このタイムアウト値は問合せのみではなく、実行可能なすべてのSQL文に適用されます。
TimesTenでは、DSN属性SqlQueryTimeoutを使用することで、このタイムアウト値をすべての接続、つまりすべての文に対して指定できます。 DSN仕様にSqlQueryTimeoutを設定すると、その値がデータ・ストアに対するその後のすべての接続のデフォルト値になります。 SQL_QUERY_TIMEOUTオプションを指定してSQLSetConnectOptionをコールすると、接続が継承した可能性があるデフォルト値が上書きされ、その接続のすべての文に適用されます。 SQL_QUERY_TIMEOUTオプションを指定してSQLSetStmtOptionをコールすると、接続から継承したデフォルト値も、SQLSetConnectOptionを使用して設定した値も上書きされますが、その文にのみ適用されます。
問合せのタイムアウト制限は、SQL文がアクティブに実行されている場合にのみ有効です。 コミット中またはロールバック中にはタイムアウトは発生しません。多数のUPDATE文、DELETE文またはINSERT文を実行するトランザクションでは、コミットまたはロールバックが完了するまでに時間がかかる場合があります。その間、タイムアウト値は無視されます。
|
注意: ロック・タイムアウト値およびSQLQueryTimeout値の両方が指定されている場合は、まず、2つの値の小さい方の値によってタイムアウトが発生します。
ロック・タイムアウトについては、『Oracle TimesTen In-Memory Databaseリファレンス』のttLockWait(組込みプロシージャ)またはLockWait(接続属性)に関する項、または『Oracle TimesTen In-Memory Databaseトラブルシューティング・プロシージャ・ガイド』のデッドロックとタイムアウトの確認に関する項を参照してください。 |
SQL文の実行が指定期間(秒単位)を超えた場合に、サポート・ログに警告を書き込んでSNMPトラップをスローするように、TimesTenを構成できます。 実行は継続され、しきい値による影響は受けません。
このSNMPトラップの名前はttQueryThresholdWarnTrapです。 SNMPトラップの構成方法については、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』参照してください。 ttQueryThresholdWarnTrapという名前にもかかわらず、このしきい値は実行可能なすべてのSQL文に適用されます。
デフォルトでは、アプリケーションによってQueryThreshold接続属性の設定からしきい値が取得されます。 SQLSetConnectOptionコールでTT_QUERY_THRESHOLDオプションを設定すると、現在の接続の接続属性の設定が上書きされます。
SQLSetConnectOptionでしきい値を設定するには、次のように入力します。
RETCODE SQLSetConnectOption(hdbc, TT_QUERY_THRESHOLD, seconds);
SQLSetStmtOptionコールでTT_QUERY_THRESHOLDオプションを設定すると、接続属性の設定もSQLSetConnectOptionによる設定も上書きされます。 この設定は、ODBC文ハンドルを使用して実行されるSQL文に適用されます。
SQLSetStmtOptionでしきい値を設定するには、次のように入力します。
RETCODE SQLSetStmtOption(hstmt, TT_QUERY_THRESHOLD, seconds);
SQLGetConnectOptionまたはSQLGetStmtOption ODBC関数を使用して、TT_QUERY_THRESHOLDの現在の値を取得できます。
RETCODE SQLGetConnectOption(hdbc, TT_QUERY_THRESHOLD, paramvalue); RETCODE SQLGetStmtOption(hstmt, TT_QUERY_THRESHOLD, paramvalue);
IMDB Cacheでは、FLUSH CACHE GROUP、LOAD CACHE GROUP、REFRESH CACHE GROUPまたはUNLOAD CACHE GROUP文の実行の後、ODBC関数SQLRowCountを使用するとフラッシュ、ロード、リフレッシュまたはアンロードされたキャッシュ・インスタンスの数が返されます。
関連情報については、『Oracle In-Memory Database Cacheユーザーズ・ガイド』の処理の影響を受けるキャッシュ・インスタンスの数の確認に関する項を参照してください。
SQLRowCountの全般的な情報については、ODBC APIのリファレンス・マニュアルを参照してください。
TimesTenによるODBCの拡張によって、アプリケーションで言語ソート、文字列の長さセマンティクス、およびキャラクタ・セット変換中のエラー・レポートに関するオプションを設定できます。これらのオプションは、SQLSetConnectOptionへのコールで使用できます。 オプションはtimesten.h #includeファイル(「TimesTen #includeファイル」を参照)で定義します。
言語ソート、長さセマンティクスおよびキャラクタ・セットの詳細は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のグローバリゼーション・サポートに関する項を参照してください。
この項では、次のTimesTen ODBCグローバリゼーション・サポートについて説明します。
このオプションには、言語比較で使用する照合順番を指定します。 サポートされている言語ソートについては、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』の単一言語ソートおよび多言語ソートに関する項を参照してください。
このオプションには、文字列値を指定します。 デフォルトはBINARYです。
また、同じ機能を持つNLS_SORT接続属性の説明も参照してください(『Oracle TimesTen In-Memory Databaseリファレンス』のNLS_SORTに関する項を参照)。 実行時オプションのTT_NLS_SORTは、NLS_SORT接続属性より優先されることに注意してください。
このオプションには、バイト・セマンティクスを使用するか、キャラクタ・セマンティクスを使用するかを指定します。指定できる値は次のとおりです。
TT_NLS_LENGTH_SEMANTICS_BYTE(デフォルト)
TT_NLS_LENGTH_SEMANTICS_CHAR
また、同じ機能を持つNLS_LENGTH_SEMANTICS接続属性の説明も参照してください(『Oracle TimesTen In-Memory Databaseリファレンス』のNLS_LENGTH_SEMANTICSに関する項を参照)。 実行時オプションのTT_NLS_LENGTH_SEMANTICSは、NLS_LENGTH_SEMANTICS接続属性より優先されることに注意してください。
このオプションでは、SQL処理中に、NCHARまたはNVARCHAR2データとCHARまたはVARCHAR2データの間の暗黙的または明示的なキャラクタ・タイプの変換中にデータが消失した場合に、エラーがレポートされるかどうかを指定します。このオプションは、バインドの結果としてのODBCによる変換には適用されません。
指定できる値は次のとおりです。
TRUE: 変換中のエラーがレポートされます。
FALSE: 変換中のエラーはレポートされません(デフォルト)。
また、同じ機能を持つNLS_NCHAR_CONV_EXCP接続属性の説明も参照してください(『Oracle TimesTen In-Memory Databaseリファレンス』のNLS_NCHAR_CONV_EXCPに関する項を参照)。 実行時オプションのTT_NLS_NCHAR_CONV_EXCPは、NLS_NCHAR_CONV_EXCP接続属性より優先されることに注意してください。
ODBC 2.0以前のリリースで使用されていたデータ型は、ISO 92標準に準拠するために名前が変更されています。 TimesTenに付属のサンプル・プログラムは、SQL 3.0データ型を使用して記述されています。 次の表に、2.0データ型およびそれに対応する3.0データ型を示します。
| ODBC 2.0データ型 | ODBC 3.0データ型 |
|---|---|
| HDBC | SQLHDBC |
| HENV | SQLHENV |
| HSTMT | SQLHSTMT |
| HWND | SQLHWND |
| LDOUBLE | SQLDOUBLE |
| RETCODE | SQLRETURN |
| SCHAR | SQLSCHAR |
| SDOUBLE | SQLFLOATS |
| SDWORD | SQLINTEGER |
| SFLOAT | SQLREAL |
| SWORD | SQLSMALLINT |
| UCHAR | SQLCHAR |
| UDWORD | SQLUINTEGER |
| UWORD | SQLUSMALLINT |
いずれのバージョンのデータ型もTimesTenで制限なしに使用される場合があります。
また、ODBC 2.0のドキュメントに記載されているFAR修飾子は必要ないことにも注意してください。
TimesTenには、表、ビュー、マテリアライズド・ビューおよび順序などのデータベース・オブジェクトについて、オブジェクトレベルの解決法でデータベース・アクセスを制御する機能が含まれています。 これらの機能の概要は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のアクセス制御の管理に関する項を参照してください。
この項では、SQL処理、データ・ストア接続、XLAおよびCユーティリティ関数に関連するアクセス制御について説明します。
このマニュアルで説明するか、または例で使用した問合せ、SQL DML文またはSQL DDL文では、ユーザーが文を実行するための適切な権限を持っていることを前提としています。 たとえば、表に対するSELECT文では、その表の所有権、その表に関して付与されているSELECT権限またはSELECT ANY TABLEシステム権限が必要です。 同様に、DML文にはその表の所有権、その表に関して付与されている適切なDML権限(UPDATEなど)または適切なANY TABLE権限(UPDATE ANY TABLEなど)が必要です。
DDL文の場合、CREATE TABLEにはユーザー・スキーマのCREATE TABLE権限またはその他のスキーマのCREATE ANY TABLE権限が必要です。 ALTER TABLEには、所有権またはALTER ANY TABLEシステム権限が必要です。 DROP TABLEには、所有権またはDROP ANY TABLEシステム権限が必要です。 オブジェクトレベルのALTER権限またはDROP権限はありません。
特定のSQL文に必要な権限については、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のSQL文に関する項を参照してください。
権限は、SQLのGRANT文で付与され、REVOKE文で取り消されます。 一部の権限は、各ユーザーがメンバーであるPUBLICロールを介して、すべてのユーザーに付与されます。 このロールの詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス・ガイド』のPUBLICロールに関する項を参照してください。
また、アクセス制御はこのマニュアルに記載されている次の内容にも影響します。
データ・ストアへの接続。「接続のアクセス制御」を参照してください。
接続属性の設定。 「プログラムでの接続属性の設定」を参照してください。
XLAの構成および管理とXLA関数の使用。 「アクセス制御がXLAに与える影響」を参照してください。 また、第9章「XLAリファレンス」も参照してください。 各XLA関数の説明に必要な権限が記載されています。
Cユーティリティ関数の実行。 第8章「TimesTenユーティリティAPI」を参照してください。 各ユーティリティの説明に、権限が必要かどうかが記載されています。
|
注意:
|
この項の内容は次のとおりです。
この項の内容は次のとおりです。
アプリケーションでは、コールのたびにエラーおよび警告をチェックする必要があります。これによって、開発およびデバッグ時に時間および労力が大幅に節約されます。 TimesTenに付属のデモ・プログラムには、エラー・チェックの例が含まれています。
エラーは、install_dir/include/tt_errCode.hファイルで定義されているTimesTenエラー・コード(エラー番号)またはエラー文字列のいずれかを使用してチェックできます。 エントリの書式は次のとおりです。
#define tt_ErrMemoryLock 712
各メッセージについては、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告のリストに関する項を参照してください。
ODBC関数をコールした後、リターン・コードを確認します。 リターン・コードがSQL_SUCCESSでない場合は、ODBC関数SQLErrorをコールするエラー処理ルーチンを使用して、関連するODBCハンドルのエラーを取得します。 1回のODBCコールで、複数のエラーが返される場合もあります。 アプリケーションは、すべてのエラーがエラー・スタックから読み取られるまでSQLError関数を繰り返しコールしてすべてのエラーを返すように記述されている必要があります。 リターン・コードがSQL_NO_DATA_FOUNDになるまで、SQLErrorをコールし続けます。
SQLError関数およびその引数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
一般的なODBCエラーを処理する関数の作成方法については、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告の取得に関する項を参照してください。
例1-10 ODBC関数コールでのエラーの確認
この例では、SQLAllocConnectをコールした後にエラー状況を確認できることを示しています。 エラーが見つかった場合、エラー・メッセージが表示され、プログラムの実行は終了します。
rc = SQLAllocConnect(henv, &hdbc);
if (rc != SQL_SUCCESS) {
handleError(rc, henv, hdbc, hstmt, err_buf, &native_error);
fprintf(stderr,
"Unable to allocate a connection handle:\n%s\n",
err_buf);
TerminateGracefully(1);
}
TimesTenでは、致命的なエラー、致命的ではないエラーまたは警告が返される可能性があります。
致命的なエラーとは、エラー・リカバリが終わるまでデータ・ストアにアクセスできなくなるエラーのことです。致命的なエラーが発生すると、すべてのデータ・ストアの接続を切断する必要があります。それ以後の処理は完了されません。致命的なエラーは、TimesTenのエラー・コード846および994で示されます。これらのエラーの処理は、標準的なエラーの処理とは異なります。具体的には、アプリケーションのエラー処理コードにデータ・ストアとの接続を切断するコードを含める必要があります。
致命的ではないエラーには、一意制約に違反しているINSERT文などの単純なエラーが含まれます。また、一部のアプリケーション障害およびプロセス障害も、致命的ではないエラーに含まれます。
TimesTenでは、致命的ではないエラーも、通常のエラー処理プロセスによって返されます。また、エラーの検出および識別はアプリケーションで行う必要があります。
致命的ではないエラーによってデータ・ストアに影響が出た場合、エラーが返されることがあり、アプリケーションで適切に対処する必要があります。プロセス障害などの場合、エラーは返されませんが、失敗したプロセスのトランザクションが自動的にロールバックされます。
アプリケーションでは、その処理を変更するか、または障害が発生した1つ以上のトランザクションをロールバックすることによって、致命的ではないエラーに対処できます。
致命的なエラーが発生した場合、TimesTenは、次の完全なクリーンアップおよびリカバリ・プロシージャを実行します。
データ・ストアに対するすべての接続を無効にし、無効になったデータ・ストアからアプリケーションを切断する必要があります。
その後の最初の初期接続時に、チェックポイント・ファイルおよびトランザクション・ログ・ファイルからデータ・ストアをリカバリします。
リカバリされたデータ・ストアには、永続コミットされたすべてのトランザクションの状態が反映されます。また、非永続的にコミットされた一部のトランザクションも反映されます。
コミットされていないトランザクションまたはロールバックされたトランザクションは反映されません。
チェックポイント・ファイルまたはトランザクション・ログ・ファイルがなく、AutoCreate DSN属性が設定されている場合、TimesTenは空のデータ・ストアを作成します。
自動クライアント・フェイルオーバーとは、TimesTenノードの障害の結果として代替ノードへのフェイルオーバー(転送)が発生する際に高可用性シナリオで使用される機能のことです。このフェイルオーバーでは、アプリケーションが新しいノードに自動的に再接続されます。 アクティブ・ノードで障害が発生することにより、スタンバイ・ノードが新しいアクティブ・ノードになります。TimesTenには、このような状況が発生した場合にアプリケーションにアラートを送信できる機能が備えられているため、アプリケーションは適切に対処できます。
この項では、TimesTenでの自動クライアント・フェイルオーバーの実装について説明します。内容は次のとおりです。
自動クライアント・フェイルオーバーはOracle Clusterwareに対する補完機能ですが、この2つの機能は互いに依存しません。 『Oracle TimesTen In-Memory Database TimesTen to TimesTen開発者および管理者ガイド』のOracle Clusterwareを使用したアクティブ・スタンバイ・ペアの管理に関する項も参照してください。
クライアント・フェイルオーバーが発生した場合、接続ハンドル以外の状態は保持されません。 すべてのクライアントの文ハンドルに無効とマークが付けられます。 これらの文ハンドルに対してAPIコールを実行すると、通常は次のように、SQL_ERRORとともに、tt_errCode.hで定義されている区別のためのフェイルオーバー・エラー・コードが返されます。
SQLSTATE = S1000 "General Error", native error = tt_ErrFailoverInvalidation
ただし、SQLErrorおよびSQLFreeStmtコールについては例外で、通常どおりに動作します。
また、次の点にも注意してください。
古いサーバーに対するソケットはクローズされます。 SQLDisconnectのコールは試行されません。
代替のTimesTenノードへの接続では、新しいサーバーDSNを示すように、必要に応じて属性をリセットする以外は、元の接続要求から返された同じ接続文字列が使用されます。
新しい文ハンドルをオープンして必要なSQLPrepareコールを再実行するかどうかは、アプリケーション次第です。
フェイルオーバーがすでに発生済でクライアントがすでに代替サーバーに接続されている場合に、次のフェイルオーバー要求が発生すると、元のサーバーへの再接続が試行されることになります。 再接続に失敗した場合、DSN属性のTTC_TIMEOUTで指定したタイムアウト値に達するまで、この2つのサーバーに対する接続が交互に試行されます。
フェイルオーバー接続は、事前に作成されるのではなく、必要に応じてのみ作成されます。
フェイルオーバーが発生すると、TimesTenは登録されているユーザー定義関数に対してコールバックを行います。 この関数は、フェイルオーバーが発生している状況で実行する必要があるカスタム処理を行います。
|
注意:
|
次に示すパブリック接続オプションは、新しい接続に伝播されます。 該当する場合は、対応する接続属性をカッコ内に示しています。 TT_REGISTER_FAILOVER_CALLBACKオプションは、コールバック関数を登録するために使用します。
SQL_ACCESS_MODE SQL_AUTOCOMMIT SQL_TXN_ISOLATION (Isolation) SQL_OPT_TRACE SQL_QUIET_MODE TT_PREFETCH_CLOSE TT_CLIENT_TIMEOUT (TTC_TIMEOUT) TT_WARN_POSSIBLE_TRUNC_BINDING TT_WARN_SQLCBIGINT_BINDING TT_CONNECTION_CHARACTER_SET (ConnectionCharacterSet) TT_REGISTER_FAILOVER_CALLBACK
次に示すオプションは、接続属性またはSQLSetConnectOptionコールで設定されている場合には新しい接続に伝播されますが、TimesTen組込みプロシージャまたはALTER SESSIONで設定されている場合には新しい接続に伝播されません。
TT_NLS_SORT (NLS_SORT) TT_NLS_LENGTH_SEMANTICS (NLS_LENGTH_SEMANTICS) TT_NLS_NCHAR_CONV_EXCP (NLS_NCHAR_CONV_EXCP) TT_DYNAMIC_LOAD_ENABLE (DynamicLoadEnable) TT_DYNAMIC_LOAD_ERROR_MODE (DynamicLoadErrorMode)
次に示すオプションは、接続ハンドルで設定されている場合には新しい接続に伝播されます。
SQL_QUERY_TIMEOUT TT_PREFETCH_COUNT
次の属性はsys.ttconnect.iniの論理サーバーDSNの属性です。代替サーバー用であることを除けばTTC_Server、TTC_Server_DSNおよびTCP_Portと同等です。
TTC_Server2 TTC_Server_DSN2 TCP_Port2
|
注意:
|
TTC_Server2、TTC_Server_DSN2またはTCP_Port2のいずれかを設定することは、暗黙的に次のことを意味します。
ユーザーは、自動クライアント・フェイルオーバーを使用するつもりである。
ユーザーは、フェイルオーバー・メカニズムをサポートするためにアプリケーションに新しいスレッドが作成されることを理解している。
ユーザーは、アプリケーションをスレッド・ライブラリとリンク済である。
次に示す新しいDSN属性は、フェイルオーバー・スレッドがフェイルオーバー通知をリスニングするポートのポート範囲を指定します。
TTC_FAILOVERPORTRANGE
最小値と最大値をハイフンで区切って設定します。 TimesTenでは、クライアントとサーバー間のファイアウォールに対応するために、ポート範囲の設定がサポートされています。 デフォルトでは、オペレーティング・システムで選択されたポートが使用されます。
|
注意:
|
フェイルオーバーが発生すると、TimesTenは目的の処理を行うためにユーザー定義関数に対してコールバックを行います。 この関数は、代替サーバーへの接続試行が開始されたときにコールされ、接続の試行が完了した後に再度コールされます。 たとえば、文ハンドルを正常にリストアするために、この関数を使用できます。
関数APIは次のように定義します(対応するTAF関数をモデルにしています)。
typedef SQLRETURN (*ttFailoverCallbackFcn_t) (SQLHDBC, /* hdbc */ SQLPOINTER, /* foCtx */ SQLUINTEGER, /* foType */ SQLUINTEGER); /* foEvent */
各要素の意味は次のとおりです。
hdbcは、障害が発生した接続のODBC接続ハンドルです。
foCtxは、アプリケーション定義のデータ構造体に対するポインタで、必要に応じて使用します。
foTypeは、フェイルオーバーのタイプです。 これに対して、TimesTenでサポートされている値はTT_FO_SESSIONのみであり、セッションが再確立されます。 TAFのように、文が再準備されることはありません。
foEventは、発生したイベントが、FANおよびTAFに関してサポートされている次の値で示されます。
TT_FO_BEGIN: フェイルオーバーの開始。
TT_FO_ABORT: フェイルオーバーの失敗。 TTC_TIMEOUTで指定した時間の間、再試行が実行されましたが成功しませんでした。
TT_FO_END: フェイルオーバーの正常終了。
TT_FO_ERROR: フェイルオーバー接続に失敗しましたが、再試行されます。
TT_FO_REAUTHはTimesTenクライアント・フェイルオーバーではサポートされないことに注意してください。
コールバック関数を登録するには、SQLSetConnectOptionコールを使用してTimesTen TT_REGISTER_FAILOVER_CALLBACKオプションを設定します。指定するオプション値はCデータ型の構造体ttFailoverCallback_tへのポインタで、この構造体はtimesten.hファイルに次のように定義されており、コールバック関数を参照します。
typedef struct{
SQLHDBC appHdbc;
ttFailoverCallbackFcn_t callbackFcn;
SQLPOINTER foCtx;
} ttFailoverCallback_t;
各要素の意味は次のとおりです。
appHdbcはODBC接続ハンドルであり、SQLSetConnectOptionコール・シーケンスのhdbcと同じ値である必要があります。 (これは、ドライバ・マネージャを使用している場合のドライバ・マネージャの実装詳細のために、データ構造体で必要になります。)
callbackFcnは、コールバック関数を指定します。 (NULLに設定すると、指定した接続のコールバックが取り消されます。 フェイルオーバーは変わらずに発生しますが、アプリケーションが通知を受けることはありません。)
foCtxは、前述の関数で説明したように、アプリケーション定義のデータ構造体に対するポインタです。
コールバックを必要とする接続ごとに、TT_REGISTER_FAILOVER_CALLBACKを設定します。 ttFailoverCallback_t構造体の値は、SQLSetConnectOptionコールが実行されるときにコピーされます。 アプリケーションで構造体を保持する必要はありません。 TT_REGISTER_FAILOVER_CALLBACKが接続に対して複数回設定されている場合、最後の設定が優先されます。
|
注意:
|
例1-11 フェイルオーバーのコールバック関数および登録
この例で示される内容は、次のとおりです。
グローバルに定義されるユーザー構造体タイプFOINFO、およびタイプFOINFOの構造体変数foStatus。
コールバック関数FailoverCallback()。フェイルオーバーが発生すると常にfoStatus構造体を更新します。
登録関数RegisterCallback()。次の処理を行います。
タイプttFailoverCallback_tの構造体failoverCallbackを宣言します。
foStatus値を初期化します。
接続ハンドル、foStatusへのポインタおよびコールバック関数(FailoverCallback)で構成されるfailoverCallbackデータ値を設定します。
SQLSetConnectOptionコールを使用してコールバック関数を登録します。このコールでは、TT_REGISTER_FAILOVER_CALLBACKをfailoverCallbackへのポインタとして設定します。
/* user defined structure */
struct FOINFO
{
int callCount;
SQLUINTEGER lastFoEvent;
};
/* global variable passed into the callback function */
struct FOINFO foStatus;
/* the callback function */
SQLRETURN FailoverCallback (SQLHDBC hdbc,
SQLPOINTER pCtx,
SQLUINTEGER FOType,
SQLUINTEGER FOEvent)
{
struct FOINFO* pFoInfo = (struct FOINFO*) pCtx;
/* update the user defined data */
if (pFoInfo != NULL)
{
pFoInfo->callCount ++;
pFoInfo->lastFoEvent = FOEvent;
printf ("Failover Call #%d\n", pFoInfo->callCount);
}
/* the ODBC connection handle */
printf ("Failover HDBC : %p\n", hdbc);
/* pointer to user data */
printf ("Failover Data : %p\n", pCtx);
/* the type */
switch (FOType)
{
case TT_FO_SESSION:
printf ("Failover Type : TT_FO_SESSION\n");
break;
default:
printf ("Failover Type : (unknown)\n");
}
/* the event */
switch (FOEvent)
{
case TT_FO_BEGIN:
printf ("Failover Event: TT_FO_BEGIN\n");
break;
case TT_FO_END:
printf ("Failover Event: TT_FO_END\n");
break;
case TT_FO_ABORT:
printf ("Failover Event: TT_FO_ABORT\n");
break;
case TT_FO_REAUTH:
printf ("Failover Event: TT_FO_REAUTH\n");
break;
case TT_FO_ERROR:
printf ("Failover Event: TT_FO_ERROR\n");
break;
default:
printf ("Failover Event: (unknown)\n");
}
return SQL_SUCCESS;
}
/* function to register the callback with the failover connection */
SQLRETURN RegisterCallback (SQLHDBC hdbc)
{
SQLRETURN rc;
ttFailoverCallback_t failoverCallback;
/* initialize the global user defined structure */
foStatus.callCount = 0;
foStatus.lastFoEvent = -1;
/* register the connection handle, callback and the user defined structure */
failoverCallback.appHdbc = hdbc;
failoverCallback.foCtx = &foStatus;
failoverCallback.callbackFcn = FailoverCallback;
rc = SQLSetConnectOption (hdbc, TT_REGISTER_FAILOVER_CALLBACK,
(SQLULEN)&failoverCallback);
return rc;
}
フェイルオーバーが発生すると、コールバック関数は次のような出力を生成します。
Failover Call #1 Failover HDBC : 0x8198f50 Failover Data : 0x818f8ac Failover Type : TT_FO_SESSION Failover Event: TT_FO_BEGIN