この章では、TimesTenプログラミング機能について取り上げ、TimesTenデータベースに接続して使用するためにODBCを使用する方法について説明します。内容は次のとおりです。
|
ノート:
|
TimesTenは、次をサポートしています。
TimesTenでは、ODBC 2.5、拡張レベル1がサポートされており、第10章「TimesTen ODBCサポート」で示すように、拡張レベル2の機能もあります
ODBC 3.51コア・インタフェース準拠
TimesTen Scaleoutの場合、データベースを作成し、直接接続またはクライアント/サーバー接続を使用してデータベースに接続する方法については、『Oracle TimesTen In-Memory Database Scaleoutユーザーズ・ガイド』を参照してください。データベースの作成に関する項およびデータベースへの接続に関する項を参照してください。
TimesTen Classicの場合、データベースにDSNを作成する方法の詳細は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』を参照してください。作成するDSNのタイプは、アプリケーションがデータベースに直接接続するか、クライアント接続するかによって異なります。
データベースに直接接続する場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータベースの管理に関する説明を参照してください。LinuxまたはUNIXから、あるいはWindowsから、直接接続するためのDSNの作成方法に関する説明があります。
データベースへのクライアント接続を作成する場合は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTen ClientおよびTimesTen Serverの使用方法に関する説明を参照してください。LinuxまたはUNIXから、あるいはWindowsから、クライアント/サーバー接続を行うためのDSNの作成方法に関する説明があります。
|
ノート:
|
この項の以降の内容は次のとおりです。
データベースへの接続およびそれに関連する機能の実行には、次のODBC関数を使用できます。
SQLConnect: ドライバをロードしてデータベースに接続します。接続ハンドルは、ステータス、トランザクションの状態、結果およびエラー情報などの接続情報が格納される場所を指します。
SQLDriverConnect: SQLConnectでサポートされている情報(データソース(データベース)、ユーザー名およびパスワード)よりも詳細な情報が必要な場合に、SQLConnectのかわりに使用します。
SQLAllocConnect: 指定した環境内の接続ハンドルにメモリーを割り当てます。
SQLDisconnect: データベースから切断します。既存の接続ハンドルを唯一の引数として使用します。
これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
この項では、データベースに対する接続および切断の例を示します。
例2-1 接続および切断(抜粋)
このコード部分では、SQLConnectおよびSQLDisconnectを起動して、FixedDsというデータベースに対する接続および切断を行います。アプリケーションで初めてSQLConnectを起動すると、データベースFixedDsが作成されます。その後のSQLConnectの起動では、既存のデータベースに接続されます。
#include <timesten.h>
SQLRETURN retcode;
SQLHDBC hdbc;
...
retcode = SQLConnect(hdbc,
(SQLCHAR*)"FixedDs", SQL_NTS,
(SQLCHAR*)"johndoe", SQL_NTS,
(SQLCHAR*)"opensesame", SQL_NTS);
...
retcode = SQLDisconnect(hdbc);
...
例2-2 接続および切断(完全なプログラム)
この例には、データベースを作成し、データベースに対して接続および切断を行う完全なプログラムが含まれています。この例では、SQLConnectではなくSQLDriverConnectを使用して接続を設定し、SQLAllocConnectを使用してメモリーを割り当てます。また、エラー・メッセージの取得方法についても示します。(「エラー処理」も参照してください。)
#include <timesten.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;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"
" database\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オペレーション・ガイド』のTimesTenデータベースの管理に関する項を参照してください。一般接続属性には、特に権限は必要ありません。データベースが初めてロードされたときに最初の接続属性が設定され、すべての接続に持続的に使用されます。最初の接続属性の設定を変更してデータベースをロードできるのは、インスタンス管理者のみです。特定の接続属性に関する固有の情報を含む詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』の接続属性に関する説明を参照してください。
このコード部分では、mydsnというデータベースに接続して、SQLDriverConnectコールで、アプリケーションがパススルー設定の3を使用することを示します。PassThroughは一般接続属性であることに注意してください。
SQLHDBC hdbc;
SQLCHAR ConnStrOut[512];
SQLSMALLINT cbConnStrOut;
SQLRETURN rc;
rc = SQLDriverConnect(hdbc, NULL,
"DSN=mydsn;PassThrough=3", SQL_NTS,
ConnStrOut, sizeof (ConnStrOut),
&cbConnStrOut, SQL_DRIVER_NOPROMPT);
|
ノート: データベースへの直接接続ごとに、いくつかのファイルがオープンします。多数のスレッドを使用し、スレッドごとに別々の接続を使用するアプリケーションでは、各スレッドに対して複数のファイルがオープンします。このようなアプリケーションでは、オペレーティング・システムで同時にオープンできるファイル記述子の許可された最大数(構成されている最大値)を超える可能性があります。この場合は、多数のオープン・ファイルを許可するようシステムを設定します。『Oracle TimesTen In-Memory Databaseリファレンス』のオープン・ファイルの数の制限に関する項を参照してください。 |
TimesTen Classicでは、defaultという名前が付けられているデフォルトDSNは、odbc.iniファイルまたはsys.odbc.iniファイルで定義できます。デフォルトDSNの定義の詳細は、Oracle TimesTen In-Memory Databaseオペレーション・ガイドのTimesTen ClassicでのデフォルトDSNの設定に関する説明を参照してください。
SQLConnectまたはSQLDriverConnectがコールされた場合、次のような状況で関連するデータ・ソースが接続されます。
SQLConnectでは、デフォルトDSNが定義済の場合、ServerNameが検出できないデータソースを指定する場合、nullポインタを指定する場合、または明示的にdefaultの値に設定される場合に使用されます。参考に、SQLConnectコール・シーケンスを示します。
SQLRETURN SQLConnect(
SQLHDBC ConnectionHandle,
SQLCHAR * ServerName,
SQLSMALLINT NameLength1,
SQLCHAR * UserName,
SQLSMALLINT NameLength2,
SQLCHAR * Authentication,
SQLSMALLINT NameLength3);
サーバー名としてdefaultを使用します。ユーザー名と認証値はそのまま使用します。
SQLDriverConnectでは、デフォルトDSNが定義済の場合、接続文字列にDSNキーワードが含まれない場合、またはデータソースが検出されない場合に使用されます。参考に、SQLDriverConnectコール・シーケンスを示します。
SQLRETURN SQLDriverConnect(
SQLHDBC ConnectionHandle,
SQLHWND WindowHandle,
SQLCHAR * InConnectionString,
SQLSMALLINT StringLength1,
SQLCHAR * OutConnectionString,
SQLSMALLINT BufferLength,
SQLSMALLINT * StringLength2Ptr,
SQLUSMALLINT DriverCompletion);
DSNキーワードとしてdefaultを使用します。ユーザー名とパスワードはそのまま使用します。
直接モードまたはドライバ・マネージャを備えたクライアント/サーバー・モードの場合、次のことに注意してください。
ドライバ・マネージャを使用していない場合、TimesTenがこの機能を管理します。デフォルトDSNは、TimesTenデータベースである必要があります。
ドライバ・マネージャを使用している場合、ドライバ・マネージャがこの機能を管理します。デフォルトDSNは、TimesTenデータベースである必要はありません。
この項では、TimesTenデータベースのデータの処理について説明します。内容は次のとおりです。
TimesTenの機能を使用するには、必要に応じて、次の表に示すTimesTenファイルを含めます。これらは、TimesTenインストールのincludeディレクトリにあります。
| インクルード・ファイル | 説明 |
|---|---|
timesten.h |
TimesTenのODBC機能
このファイルには、適切なバージョンの このファイルには、 |
tt_errCode.h |
TimesTenエラー・コード(オプション。「ノート」を参照)
このファイルは、TimesTenエラー・コードを定義済の定数マップします。 |
インクルードされるファイルにアクセスするには、適切なインクルード・パスを設定します。関連情報については、「アプリケーションのコンパイルおよびリンク」を参照してください。
|
ノート:
|
SQLを使用してデータを管理する方法については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータベースのデータの処理に関する説明を参照してください。この項では、Cアプリケーション内でSQL文を実行するために使用される一般的な書式について説明します。内容は次のとおりです。
SQL文を実行するODBC関数は次の2つです。
SQLExecute: SQLPrepareで準備されている文を実行します。実行結果を使用してアプリケーションが実行された後、その結果を破棄して、別のパラメータ値でSQLExecuteを再実行することができます。
通常、バインド・パラメータを使用するDML文または複数回実行される文に使用します。
SQLExecDirect: 文を準備して実行します。
通常、DDL文、または数回のみ実行される、バインド・パラメータを使用しないDML文に使用します。
これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
SQLExecDirect関数は、例2-4に示すように使用できます。
SQLExecute関数とSQLPrepare関数の使用方法は、次の項「問合せの準備および実行とカーソルの使用」に示します。
例2-4 SQLExecDirectを使用したSQL文の実行
次のコード例では、CustIDおよびCustNameという2つの列を持つ、NameIDという表を作成します。この表では、名前(文字)が識別子(整数)にマップされます。
#include <timesten.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つの結果行が確認されます。
|
重要: TimesTenでは、コミットやロールバックなど、トランザクションを終了する操作は、接続と関連付けられているすべてのカーソルをクローズします。 |
ODBC設定では、カーソルは常に結果セットに関連付けられています。この関連付けはODBCドライバによって行われます。アプリケーションでは、「ODBC 2.5 SQLSetStmtOptionおよびSQLGetStmtOptionのオプションのサポート」で説明されているSQLSetStmtOptionオプションを使用して、一度にフェッチする行数などのカーソル特性を制御できます。問合せを実行する場合のステップは、通常は次のとおりです。
SQLPrepareを使用して、SELECT文を実行するための準備をします。
SQLBindParameter(文にパラメータが含まれている場合)を使用して、各パラメータをアプリケーション・アドレスにバインドします。「SQLBindParameter関数」を参照してください。(次の例2-5ではパラメータをバインドしていません。)
SQLBindColをコールして結果の列に記憶域およびデータ型を割り当て、列の結果をアプリケーション内のローカル変数記憶域にバインドします。
SQLExecuteをコールして、SELECT文を実行します。「SQLExecDirect関数およびSQLExecute関数」を参照してください。
SQLFetchをコールして結果をフェッチします。文ハンドルを指定します。
SQLFreeStmtをコールして文ハンドルを解放します。文ハンドルおよびSQL_CLOSE、SQL_DROP、SQL_UNBINDまたはSQL_RESET_PARAMSのいずれかを指定します。
これらのODBC関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。この章全体とTimesTenサンプル・アプリケーションでは例をいくつか示しています。「TimesTenクイック・スタートおよびサンプル・アプリケーション」を参照してください。
|
ノート: デフォルトでは(接続属性がPrivateCommands=0のとき)、TimesTenは接続間で準備済の文を共有するため、共有以降は複数の接続で同じ文の準備が迅速に実行されます。 |
例2-5 問合せの実行およびカーソルの使用
この例では、ODBCコールを使用して問合せを準備および実行する方法を示します。わかりやすくするために、エラーのチェックは省略してあります。前述のODBC関数以外に、この例では、SQLNumResultColsを使用して結果セット内の列数を返し、SQLDescribeColを使用して結果セットの1つの列の記述(列名、型、精度、スケールおよびNULL値可能)を返し、SQLBindColを使用して記憶域およびデータ型を結果セット内の列に割り当てます。これらすべての関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
#include <timesten.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); //Allocate space for column data.
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では、結果セットの列記述のような文に関する情報をアプリケーションで使用可能にしたり、これらの情報に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に固有のエラーも返される場合があります。
TimesTenによるODBCの拡張によって、アプリケーションはODBCドライバのバッファに複数のデータ行をプリフェッチできます。これにより、クライアント/サーバー・アプリケーションのパフォーマンスを向上させることができます。
TT_PREFETCH_COUNT ODBC文オプションによって、1回のSQLFetchコールでプリフェッチする行数が決定されます。このオプションは、TimesTenに直接接続するアプリケーションには何もメリットがないことに注意してください。
コールのTT_PREFETCH_COUNTは、SQLSetStmtOptionまたはSQLSetConnectOptionに設定できます(接続に関連付けられる文すべてのデフォルト値をオプションで設定します)。値には0(ゼロ)から128までの任意の整数を設定できます。次に、その例を示します。
rc = SQLSetConnectOption(hdbc, TT_PREFETCH_COUNT, 100);
この設定を使用すると、接続時の最初のSQLFetchコールで100行がプリフェッチされます。後続のSQLFetchコールは、ODBCバッファが使い果たされるまで、データベースのかわりにODBCバッファからフェッチします。使い果たされると、次のSQLFetchコールでは別の100行がバッファにフェッチされます。
プリフェッチを無効にするには、TT_PREFETCH_COUNTを1に設定します。
プリフェッチ数を0(ゼロ)に設定すると、TimesTenでは、データベースに設定した分離レベルに応じてデフォルトのプリフェッチ数が使用され、その値がTT_PREFETCH_COUNTに設定されます。コミット読取り分離レベルでは、デフォルトのプリフェッチの値は5です。シリアライズ可能分離レベルでは、デフォルトは128です。デフォルトのプリフェッチの値はほとんどのアプリケーションに最適な設定です。一般的に、値を高く設定すると、リソースの使用量がわずかに増加しますが、大きい結果セットに対するパフォーマンスは向上する可能性があります。
TT_PREFETCH_COUNTおよびSQL_TXN_ISOLATIONを含む文オプションの詳細は、ODBC 2.5 SQLSetStmtOptionおよびSQLGetStmtOptionのオプションのサポートも参照してください。
ODBCのTimesTen拡張機能を使用すると、TT_PREFETCH_CLOSE ODBC接続オプションを使用してアプリケーションでクライアント/サーバー・アプリケーションの読取り専用問合せパフォーマンスを最適化できます。SQLSetConnectOptionを使用して、TT_PREFETCH_CLOSEをTT_PREFETCH_CLOSE_ONに設定します。
すべてのトランザクションは、読取り専用トランザクションを含め、実行時にコミットされる必要があります。TT_PREFETCH_CLOSEがTT_PREFETCH_CLOSE_ONに設定されている場合、カーソルが自動的にクローズされ、読取り専用問合せの結果セットの行がすべてプリフェッチされた後でトランザクションがコミットされます。これによって、クライアントとサーバー間のネットワーク・ラウンドトリップ数が減少するため、パフォーマンスが向上します。
クライアントでは、SQLFreeStmt(SQL_CLOSE)を使用して文を解放し、SQLTransact(SQL_COMMIT)を使用してトランザクションをコミットする必要がありますが、これらのコールは、クライアントで実行されるため、クライアントとサーバー間のネットワーク・ラウンドトリップを必要としません。
|
ノート:
|
次の例は、TT_PREFETCH_CLOSEオプションを使用する方法を示しています。
SQLSetConnectOption (hdbc, TT_PREFETCH_CLOSE, TT_PREFETCH_CLOSE_ON);
SQLExecDirect (hstmt, "SELECT * FROM T", SQL_NTS);
while (SQLFetch (hstmt) != SQL_NO_DATA_FOUND)
{
// do the processing and error checking
}
SQLFreeStmt (hstmt, SQL_CLOSE);
SQLTransact(SQL_COMMIT);
この項では、SQL文の入力または出力パラメータをバインドする方法について説明します。内容は次のとおりです。
|
ノート: TimesTen開発者ガイドで使用される「バインド・パラメータ」という用語(ODBC用語に準拠)は、TimesTenのPL/SQLのマニュアルで使用される「バインド変数」という用語(Oracle Database PL/SQL用語に準拠)と同じです。 |
ODBCのSQLBindParameter関数を使用して、SQL文のパラメータをバインドします。これには、入力、出力または入力/出力パラメータを含むことができます。
ODBCを介して入力パラメータをバインドするには、fParamType引数にSQL_PARAM_INPUTを設定してSQLBindParameter関数を使用します。SQLBindParameter関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。表2-1に、この引数の簡単な概要を示します。
ODBCを介して出力または入力/出力パラメータをバインドするには、fParamType引数にSQL_PARAM_OUTPUTまたはSQL_PARAM_INPUT_OUTPUTをそれぞれ設定してSQLBindParameter関数を使用します。入力パラメータと同様に、fSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。
表2-1 SQLBindParameterの引数
| 引数 | 型 | 説明 |
|---|---|---|
|
|
|
文ハンドル |
|
|
|
左から右へ順に、1から始まるパラメータ番号 |
|
|
|
入力または出力を示す、 |
|
|
|
パラメータのCデータ型 |
|
|
|
パラメータのSQLデータ型 |
|
|
|
|
|
|
|
|
|
|
|
パラメータのデータに使用するバッファへのポインタ |
|
|
|
|
|
|
|
パラメータの長さに使用するバッファへのポインタ |
|
ノート: TimesTenのデータ型の精度およびスケールの詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス』のデータ型に関する説明を参照してください。 |
バインド・パラメータのデータ型の割当ては、次のように決定されます。
TimesTenで実行される文のパラメータのデータ型の割当ては、TimesTenが決定します。具体的には、次のようになります。
TimesTen内で実行されるSQL文の場合、TimesTenの問合せオプティマイザがSQLパラメータのデータ型を決定します。
Oracle Databaseで実行されるか、Oracle Databaseの機能に基づいた文に対するパラメータのデータ型の割当ては、次のようにアプリケーションが決定します。
Oracle Database内で実行されるSQL文(TimesTen Application-Tier Database Cache (TimesTen Cache)からのパススルー文)の場合は、アプリケーションは、ODBC SQLBindParameter関数にコールする際に、その関数のfSqlType、cbColDefおよびibScale引数に基づいて(該当する場合)、データ型を指定する必要があります。
TimesTen内で実行されるPL/SQLブロックまたはプロシージャで、PL/SQL実行エンジンにOracle Databaseと同じ基本的な機能がある場合は、アプリケーションは、SQLBindParameterに対するコールでデータ型を指定する必要があります(Oracle Database内で実行されるSQL文の場合と同様)。
そのため、PL/SQLのホスト・バインド(PL/SQLブロック内でコロンより前にある変数やパラメータ)については、fSqlTypeおよび該当するその他の引数に従って、PL/SQLブロック内ではなく、SQLBindParameterへのコールでホスト・バインドのデータ型が事実上宣言されることに注意してください。
ODBCドライバは、C値とSQLまたはPL/SQLのデータ型間で必要な型の変換を行います。サポートされていないCとSQLやCとPL/SQLの組合せでは、エラーが発生します。それらは、C型からSQL型またはPL/SQL型(入力パラメータ)、SQL型またはPL/SQL型からC型(出力パラメータ)または両方(入力/出力パラメータ)の変換の場合です。
|
ノート: TimesTenのバインド・メカニズム(アーリー・バインディング)はOracle Databaseのバインド・メカニズム(レイト・バインディング)とは異なります。TimesTenは、問合せの準備の前にデータ型を必要とします。そのため、各バインド・パラメータのデータ型が指定されていないかSQL文から推測できないと、エラーが発生します。たとえば次のような文が、これに該当します。SELECT 'x' FROM DUAL WHERE ? = ?; この問題には、たとえば次のように対処できます。
SELECT 'x' from DUAL WHERE CAST(? as VARCHAR2(10)) =
CAST(? as VARCHAR2(10));
|
表2-2に、ODBCデータ型とSQL型またはPL/SQLデータ型とのマッピングを示します。
表2-2 ODBC SQLとTimesTen SQLまたはPL/SQLとのデータ型のマッピング
|
ノート:
|
TimesTenのPL/SQLに対する入力パラメータでは、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。これは、「パラメータのデータ型の割当ての決定および変換」の記載されている、SQL入力パラメータのサポート方法とは異なります。
また、SQLBindParameter のrgbValue引数、cbValueMax引数およびpcbValue引数も、入力パラメータでは次のように使用します。
rgbValue: 文を実行する前に、アプリケーションは、アプリケーションに渡されるパラメータ値を格納するバッファを指します。
cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す入力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。
pcbValue: 文を実行する前に、次のいずれかを含むバッファを指します。
rgbValueが指す値の実際の長さ
ノート: 入力パラメータの場合、文字データまたはバイナリ・データにのみ有効です。
空文字で終了する文字列の場合は、SQL_NTS
NULL値の場合は、SQL_NULL_DATA
TimesTenのPL/SQLからの出力パラメータでは、前述の入力パラメータに記載したとおり、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。
また、SQLBindParameterのrgbValue引数、cbValueMax引数およびpcbValue引数も、出力パラメータでは次のように使用します。
rgbValue: 文の実行中に、文から返された値を格納するバッファを指します。
cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す出力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。
ODBCでは、データが切り捨てられる場合でも、すべての文字データが空文字で終了することに注意してください。そのため、出力パラメータに文字データが含まれる場合、cbValueMaxはデータの最長値+空文字を保持できる十分な大きさである必要があります(CHARおよびVARCHARパラメータでは1バイト大きい値、またはNCHARおよびNVARCHARパラメータでは2バイト大きい値)。
pcbValue: 文を実行した後に、次のいずれかを含むバッファを指します。
rgbValueが指す値の実際の長さ(文字データおよびバイナリ・データのみでなく、すべてのCデータ型が対象)
ノート: rgbValueが指すバッファに値が収まるかどうかに関係なく、完全なパラメータ値の長さです。
NULL値の場合は、SQL_NULL_DATA
例2-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);
}
TimesTenのPL/SQLに対する入力/出力パラメータでは、前述の入力パラメータに記載したとおり、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。
また、SQLBindParameterのrgbValue引数、cbValueMax引数およびpcbValue引数も、入力/出力パラメータでは次のように使用します。
rgbValue: 最初に、「入力パラメータのバインド」で説明したとおり、文を実行する前に使用します。次に、前の項「出力パラメータのバインド」で説明したとおり、文の実行中に使用します。入力/出力パラメータの場合、文の実行で出力された値は、アプリケーションによって上書きされないかぎり、直後に続く文の実行に対する入力値になります。また、実行時データの使用中にバインドされる入力/出力値の場合、rgbValueの値は、ODBCのSQLParamData関数で返されるトークンおよび出力値が格納されるバッファへのポインタの両方として機能します。
cbValueMax: 文字データおよびバイナリ・データの場合、最初に、「入力パラメータのバインド」で説明したとおりに使用します。次に、前の項「出力パラメータのバインド」で説明したとおりに使用します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterのfCType引数に指定したCデータ型の長さで決定されます。
ODBCでは、データが切り捨てられる場合でも、すべての文字データが空文字で終了することに注意してください。そのため、出力/出力パラメータに文字データが含まれる場合、cbValueMaxはデータの最長値+空文字を保持できる十分な大きさである必要があります(CHARおよびVARCHARパラメータでは1バイト大きい値、またはNCHARおよびNVARCHARパラメータでは2バイト大きい値)。
pcbValue: 最初に、「入力パラメータのバインド」で説明したとおり、文を実行する前に使用します。次に、前の項「出力パラメータのバインド」で説明したとおり、文の実行後に使用します。
|
重要: 文字データおよびバイナリ・データの場合、cbValueMaxに使用する値を慎重に検討してください。実際のバッファ・サイズよりも小さい値を使用すると、誤った切捨て警告が発生します。実際のバッファ・サイズよりも大きい値を使用すると、ODBCドライバによってrgbValueバッファが上書きされ、メモリーが破損することがあります。 |
TimesTenでは、SQL文に同じパラメータ名が複数ある場合、それぞれ別のパラメータであるとみなされます。(これは、重複したパラメータのバインドに対するOracle Databaseでのサポートと整合性があります。)
|
ノート:
|
次の問合せについて考えてみます。
SELECT * FROM employees WHERE employee_id < :a AND manager_id > :a AND salary < :b;
パラメータの位置番号が割り当てられるとき、名前の重複に関係なく、パラメータの出現ごとに番号が与えられます。アプリケーションは、少なくとも各パラメータ名の最初の出現時に値をバインドする必要があります。特定のパラメータ名の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に存在するとみなされます。
SQLNumParams ODBC関数はこの例のパラメータの数として3を返します。
前の項「SQL文での重複したパラメータのバインド」での説明は、独自のセマンティクスを持つ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
さらに、次のCALL文でも同様です。
...CALL proc(:a, :a)...
次のブロックを実行するアプリケーションでは、:aを1番目のパラメータ、:bを2番目のパラメータとして、2つのパラメータをバインドします。各INSERT文の2番目のパラメータは、最初のINSERT文の最初のパラメータと同じ値を使用します。
BEGIN INSERT INTO tab1 VALUES(:a, :a); INSERT INTO tab1 VALUES(:b, :a); END
BINARY_DOUBLEおよびBINARY_FLOATデータ型は、IEEEの浮動小数点の値Inf、-InfおよびNaNを格納および取得します。アプリケーションでprintf、scanfまたはstrtodのような文字データへの変換を必要とするC言語機能を使用した場合、浮動小数点の値はINF、-INFおよびNANとして返されます。これらのキャラクタ文字列を浮動少数点の値に戻すことはできません。
Windowsのドライバ・マネージャを使用するアプリケーションでは、SQL_WCHARまたはSQL_WVARCHARのfSqlType値を渡す際に、SQLBindParameterからSQL状態S1004(SQLデータ型が範囲外)エラーが発生する場合があります。この問題は、かわりにfSqlTypeに次のいずれかの値を渡すことで回避できます。
SQL_WCHARではなくSQL_WCHAR_DM_SQLBINDPARAMETER_BYPASS
SQL_WVARCHARではなくSQL_WVARCHAR_DM_SQLBINDPARAMETER_BYPASS
これらの値は、意味的にはSQL_WCHARおよびSQL_WVARCHARと同じですが、Windowsのドライバ・マネージャからのエラーを回避します。これらは、ドライバ・マネージャとリンクするアプリケーションまたはTimesTen ODBCの直接ドライバやODBCクライアント・ドライバと直接リンクするアプリケーションで使用できます。
ODBC関数の詳細は、「SQLBindParameter関数」を参照してください。
REF CURSORはPL/SQLの1つの概念で、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文を想定しています。REF CURSORでは、「問合せの準備および実行とカーソルの使用」のカーソルの例と同じ準備、バインド、実行およびフェッチの基本的なステップを使用します。
SQLPrepareを使用して、最初の文ハンドルと関連付けるPL/SQL文を準備します。
SQLBindParameterを使用して、文の各パラメータをバインドします。REF CURSOR出力パラメータをバインドする場合、割り当てられた2番目の文ハンドルをrgbValue(データ・バッファへのポインタ)として使用します。
pcbValue引数、ibScale引数、cbValueMax引数およびpcbValue引数は、REF CURSORに対しては無視されます。
SQLBindParameterのこれらの引数および他の引数の詳細は、「SQLBindParameter関数」および「出力パラメータのバインド」を参照してください。
SQLBindColをコールして、結果列をローカル変数の記憶域にバインドします。
SQLExecuteをコールして、文を実行します。
SQLFetchをコールして結果をフェッチします。PL/SQLからアプリケーションにREF CURSORが渡された後、アプリケーションは結果セットの場合と同様に、その結果を記述およびフェッチできます。
SQLFreeStmtを使用して文ハンドルを解放します。
これらのステップを、次の例で実際に行ってみます。これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。REF CURSORの詳細は、『Oracle TimesTen In-Memory Database PL/SQL開発者ガイド』のPL/SQL REF CURSORに関する説明を参照してください。
|
重要: PL/SQLとアプリケーションの間のREF CURSORの受渡しについて、TimesTenでは、PL/SQLからアプリケーションへのOUT REF CURSORのみと、単一のREF CURSORのみを返す文がサポートされます。 |
例2-7 問合せの実行およびREF CURSORの使用
この例では、REF CURSORをループで使用して、問合せの準備、パラメータのバインド、問合せの実行、ローカル変数記憶域への結果のバインド、および結果のフェッチを行う基本的なステップを示します。わかりやすくするためにエラー処理は省略しています。以前に概要を示したODBC関数以外に、この例ではSQLAllocStmtを使用して文ハンドルにメモリーを割り当てます。
refcursor_example(SQLHDBC hdbc)
{
SQLCHAR* stmt_text;
SQLHSTMT plsql_hstmt;
SQLHSTMT refcursor_hstmt;
SQLINTEGER deptid;
SQLINTEGER depts[3] = {10,30,40};
SQLINTEGER empid;
SQLCHAR lastname[30];
SQLINTEGER i;
/* 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);
/* loop through values for :deptid */
for (i=0; i<3; i++)
{
deptid = depts[i];
/* 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 statement handle */
SQLFreeStmt(refcursor_hstmt, SQL_CLOSE);
}
/* drop both handles */
SQLFreeStmt(plsql_hstmt, SQL_DROP);
SQLFreeStmt(refcursor_hstmt, SQL_DROP);
}
DML RETURNINGと呼ばれるRETURNING INTO句をINSERT、UPDATEまたはDELETE文で使用すると、処理の影響を受けた行の特定の項目を返すことができます。これにより、処理の影響を受けた対象を確認する場合などに、後続のSELECT文および別個のラウンドトリップが不要になります。
ODBCの場合、DML RETURNINGは単一行処理から項目を返すことに限定されます。この句では、項目を出力パラメータのリストに返します。「パラメータのバインドおよび文の実行」の記述に従って、出力パラメータをバインドします。
TimesTenでのRETURNING INTO句のSQL構文および制限については、『Oracle TimesTen In-Memory Database SQLリファレンス』のINSERT、UPDATEおよびDELETEに関する説明を参照してください。
DML RETURNINGの詳細は、『Oracle Database PL/SQL言語リファレンス』のRETURNING INTO句に関する項を参照してください。
例2-8 DML RETURNING
この例は、例2-10を元に、主要な部分を太字で強調表示したものです。
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_VARCHAR, 30, 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が返されます。
データベース表の各行には、ROWIDと呼ばれる一意の識別子があります。アプリケーションでは、ROWID擬似列から行のROWIDを取得できます。ROWIDはバイナリまたは文字形式で表すことができます。
アプリケーションでは、SQL文のWHERE句などで、一重引用符で囲んだCHAR定数としてリテラルのROWID値を指定できます。
表2-2に示すとおり、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擬似列に関する項を参照してください。
|
ノート: TimesTenでは、PL/SQL型UROWIDはサポートされていません。 |
TimesTen ClassicではLOB (ラージ・オブジェクト)がサポートされています。これには、CLOB(Character LOB)、NCLOB(National Character LOB)およびBLOB(Binary LOB)が含まれます。
この項では、次の各項目で、LOBの概要とODBCでの使用について説明します。
次の情報も参照できます。
APIに固有の情報は、「TimesTen OCIでのLOB」および「TimesTen Pro*C/C++でのLOB」を参照してください。
TimesTenでのLOBの詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス』のLOBのデータ型に関する説明を参照してください。
LOBによるプログラミングの一般的な情報(TimesTen機能に固有ではない)は、Oracle Database SecureFileおよびラージ・オブジェクト開発者ガイドを参照してください。
LOBは、ラージ・バイナリ・オブジェクト(BLOB)またはキャラクタ・オブジェクト(CLOBまたはNCLOB)です。TimesTenでは、BLOBは最大16 MBで、CLOBまたはNCLOBは最大4 MBです。他に注記がなければ、TimesTenでのLOBには、Oracle Databaseと基本的に同じ機能があります。(次の項「TimesTen LOBとOracle Database LOBの相違点」を参照。)
LOBは、永続または一時のいずれかです。永続LOBは、データベースのLOB列に存在します。一時LOBは、アプリケーション内にのみ存在します。一時LOBが暗黙的に作成される場合があります。たとえば、SELECT文で追加の文字列が連結されたLOBを指定した場合に、連結されたデータを含むためにTimesTenによって一時LOBが作成されます。TimesTen ODBCでは、すべての一時LOBは暗黙的に管理されます。
一時LOBは、TimesTenの一時データ領域に格納されます。
次のことに注意してください。
TimesTen LOB実装とOracle Database実装の主な違いは、TimesTenでは、アプリケーションで使用されたLOBはトランザクションの終了後は有効ではないことです。アプリケーションで使用されたLOBはすべて(明示的、暗黙的のどちらの場合も)、コミットまたはロールバックの後に無効化されます。これにはDDL文の後も含まれます。
TimesTenでは、BFILE、SecureFile、LOBに対する配列読取りおよび書込みまたはLOBに対するコールバック関数をサポートしていません。
TimesTenでは、LOBに対する配列のバインドをサポートしていません。
TimesTenでは、LOBに対するバッチ処理をサポートしていません。
BLOBに関して、TimesTenでの16進リテラルの使用方法に違いがあります。『Oracle TimesTen In-Memory Database SQLリファレンス』の定数でHexadecimalLiteralに関する説明を参照してください。
CまたはC++プログラムでのTimesTenからLOBへのアクセスには、次の3つのプログラミング方法があります。
簡易データ・インタフェース(ODBC、OCI、Pro*C/C++、TTClasses): その他のスカラー型と同様に、バインドおよび定義を使用してLOBデータを1チャンクで転送します。
ピース単位のデータ・インタフェース(ODBC): バインドおよび定義の拡張書式を使用して複数ピース単位でLOBデータを転送します。これは、ストリーミングまたはdata-at-execの使用(プログラムの実行時)と呼ばれることがあります。TimesTenでは、LOBデータをピース単位で探索するポーリング・ループを使用した、ピース単位のデータ・インタフェースをサポートしています。(ピース単位による別の方法であるコールバック関数の使用は、Oracle Databaseではサポートされていますが、TimesTenではサポートされていません。)
LOBロケータ・インタフェース(OCI、Pro*C/C++): SQLでLOBロケータを選択し、ファイル・システムへのアクセスで使用するAPIと概念的に似ているAPIを介して、LOBデータにアクセスします。LOBロケータ・インタフェースを使用すると、LOBデータを分割してまたは単一チャンクで使用できます。(「TimesTen OCIでのLOB」および「TimesTen Pro*C/C++でのLOB」を参照。)
LOBロケータ・インタフェースは、使用できる場合は最適なユーティリティを提供します。
簡易データ・インタフェースを使用すると、他のスカラー型と同様に、アプリケーションからバインドおよび定義によってLOBデータにアクセスできます。ODBCでの簡易データ・インタフェースでは、パラメータのバインドにSQLBindParameterを使用し、結果列の定義にSQLBindColを使用します。アプリケーションは、SQLデータ型を使用してバインドまたは定義することが可能で、このSQLデータ型は次に示すように対応する変数型と互換性があります。
BLOBデータでは、SQL型SQL_LONGVARBINARYおよびC型SQL_C_BINARYを使用します。
CLOBデータでは、SQL型SQL_LONGVARCHARおよびC型SQL_C_CHARを使用します。
NCLOBデータでは、SQL型SQL_WLONGVARCHARおよびC型SQL_C_WCHARを使用します。
LOBデータに対するSQLBindParameterコールおよびSQLBindColコールは、この章で前述した他のデータ型に対するコールとよく似ています。
|
ノート: CLOBまたはNCLOBでC型のSQL_C_BINARYを使用したバインドは禁止されています。 |
ピース単位のインタフェースを使用すると、アプリケーションから、LOBデータの各部分に個別にアクセスできます。簡易データ・インタフェースで実行されるのと同様のアクションで、アプリケーションによってパラメータがバインドされ、結果が定義されますが、プログラム実行時(at exec)にデータが提供されたものか取得されたものであるかが示されます。TimesTenでは、すべてのLOBデータが読み取られるか書き込まれるまで継続されるポーリング・ループを介してピース単位のデータ・インタフェースを実装できます。
ODBCでのピース単位のデータ・インタフェースでは、SQLParamDataとSQLPutDataをポーリング・ループで使用してパラメータをバインドし(次の例2-9を参照)、SQLGetDataをポーリング・ループで使用して結果を取得します。BLOB、CLOBおよびNCLOBでサポートされるSQLおよびCのデータ型の詳細は、前述の項「ODBCでのLOBの簡易データ・インタフェースの使用」を参照してください。
|
ノート: 様々なAPIに対する同様のピース単位のデータ・アクセスは、TimesTenの以前のリリースからサポートされています(varデータ型の場合)。 |
例2-9 SQLPutDataの使用、ODBCのピース単位のデータ・インタフェース
このプログラムの抜粋では、SQLPutDataとSQLParamDataをポーリング・ループで使用して、LOBデータをピース単位でデータベースに挿入します。コードが実行されるとき、CLOB列には"123ABC"の値が含まれています。
...
/* create a table */
create_stmt = "create table clobtable ( c clob )";
rc = SQLExecDirect(hstmt, (SQLCHAR *)create_stmt, SQL_NTS);
if(rc != SQL_SUCCESS){/* ...error handling... */}
/* initialize an insert statement */
insert_stmt = "insert into clobtable values(?)";
rc = SQLPrepare(hstmt, (SQLCHAR *)insert_stmt, SQL_NTS);
if(rc != SQL_SUCCESS){/* ...error handling... */}
/* bind the parameter and specify that we will be using
* SQLParamData/SQLPutData */
rc = SQLBindParameter(
hstmt, /* statement handle */
1, /* colnum number */
SQL_PARAM_INPUT, /* param type */
SQL_C_CHAR, /* C type */
SQL_LONGVARCHAR, /* SQL type (ignored) */
2, /* precision (ignored) */
0, /* scale (ignored) */
0, /* putdata token */
0, /* ignored */
&pcbvalue); /* indicates use of SQLPutData */
if(rc != SQL_SUCCESS){/* ...error handling... */}
pcbvalue = SQL_DATA_AT_EXEC;
/* execute the statement -- this should return SQL_NEED_DATA */
rc = SQLExecute(hstmt);
if(rc != SQL_NEED_DATA){/* ...error handling... */}
/* while we still have parameters that need data... */
while((rc = SQLParamData(hstmt, &unused)) == SQL_NEED_DATA){
memcpy(char_buf, "123", 3);
rc = SQLPutData(hstmt, char_buf, 3);
if(rc != SQL_SUCCESS){/* ...error handling... */}
memcpy(char_buf, "ABC", 3);
rc = SQLPutData(hstmt, char_buf, 3);
if(rc != SQL_SUCCESS){/* ...error handling... */}
}
...
パススルーLOB(TimesTenを介してアクセスされるOracle DatabaseのLOB)は、TimesTen LOBとして公開され、TimesTenによってTimesTen LOBとほぼ同じようにサポートされますが、次の点に注意してください。
TimesTen LOBのサイズ制限は、パススルーによるOracle DatabaseのLOBの格納には適用されません。
TimesTenのローカルLOBと同様に、アプリケーションで使用されるパススルーLOBは、トランザクションの終了後は有効ではありません。
実行したDML変更(更新、挿入または削除)が自動的にコミットされるよう、自動コミットはデフォルトで有効になっています(ODBCの指定に従って)。ただし、この機能を無効にして、変更を明示的にコミット(またはロールバック)することをお薦めします。これを設定するには、次のようにSQLSetConnectOptionコールでSQL_AUTOCOMMITオプションを使用します。
rc = SQLSetConnectOption(hdbc, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
自動コミットが無効になっている場合は、次のコミットの例のように、SQLTransact ODBC関数を使用してトランザクションをコミットまたはロールバックできます。
rc = SQLTransact(henv, hdbc, SQL_COMMIT);
これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
|
ノート:
|
トランザクションの追加情報は、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のトランザクションの概要に関する説明を参照してください。
例2-10 データベースの更新および変更のコミット
この例では、選択した従業員の昇給を行う文を準備して実行した後、手動で変更をコミットします。自動コミットが事前に無効にされていることを前提としています。
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 */
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);
}
前述の項では、TimesTenデータを管理するための主な機能について説明しました。この項では、次にリストするその他の機能について説明します。
TimesTen Classicでは、スタンドアロンまたはパッケージの一部であるPL/SQLプロシージャ(procname)またはPL/SQLファンクション(funcname)をコールするため、またはTimesTenの組込みプロシージャ(procname)をコールするための、すべてのプログラミング・インタフェースの次の各構文書式がサポートされています。
CALL procname[(argumentlist)] CALL funcname[(argumentlist)] INTO :returnparam CALL funcname[(argumentlist)] INTO ?
TimesTen ODBCでは、次の各構文書式もサポートされています。
{ CALL procname[(argumentlist)] }
{ ? = [CALL] funcname[(argumentlist)] }
{ :returnparam = [CALL] funcname[(argumentlist)] }
次のODBCの例では、TimesTen ttCkpt組込みプロシージャをコールします。
rc = SQLExecDirect (hstmt, (SQLCHAR*) "call ttCkpt",SQL_NTS);
これらの例では、PL/SQLプロシージャmyprocを2つのパラメータでコールします。
rc = SQLExecDirect(hstmt, (SQLCHAR*) "{ call myproc(:param1, :param2) }",SQL_NTS);
rc = SQLExecDirect(hstmt, (SQLCHAR*) "{ call myproc(?, ?) }",SQL_NTS);
PL/SQLファンクションmyfuncをコールするいくつかの方法を次に示します。
rc = SQLExecDirect (hstmt, (SQLCHAR*) "CALL myfunc() INTO :retparam",SQL_NTS);
rc = SQLExecDirect (hstmt, (SQLCHAR*) "CALL myfunc() INTO ?",SQL_NTS);
rc = SQLExecDirect (hstmt, (SQLCHAR*) "{ :retparam = myfunc() }",SQL_NTS);
rc = SQLExecDirect (hstmt, (SQLCHAR*) "{ ? = myfunc() }",SQL_NTS);
CALL構文の詳細は、『Oracle TimesTen In-Memory Database SQLリファレンス』のCALLに関する説明を参照してください。
|
ノート:
|
TimesTenには、SQL文またはプロシージャ・コールの実行時間を制限する方法として、タイムアウト期間の設定と、しきい値期間の設定の2つがあります。これは、SQLExecuteコール、SQLExecDirectコールまたはSQLFetchコールに適用されます。
タイムアウト期間に達すると、文の実行が停止し、エラーがスローされます。しきい値期間に達すると、サポート・ログに警告が書き込まれますが、実行は続行されます。
この項では、次のトピックを取り扱います:
タイムアウトまでのSQL文の実行時間を制御するには、SQLSetStmtOptionまたはSQLSetConnectOptionコールを使用してSQL_QUERY_TIMEOUTオプションを設定することで、タイムアウト値を秒単位で指定できます。値0はタイムアウトが発生しないことを示します。このような名前であるにもかかわらず、このタイムアウト値は問合せのみではなく、実行可能なすべてのSQL文に適用されます。
TimesTenでは、SQLQueryTimeout一般接続属性(秒)またはSQLQueryTimeoutMsec一般接続属性(ミリ秒)を使用することで、タイムアウト値を接続、つまり接続の任意の文に対して指定できます。各デフォルト値は0で、タイムアウトはありません。(Oracle TimesTen In-Memory DatabaseリファレンスのSQLQueryTimeoutおよびSQLQueryTimeoutMsecに関する項も参照してください。)
名前によらず、これらのタイムアウト値は問合せだけでなく実行可能なSQL文に適用されます。
SQL_QUERY_TIMEOUTオプションを指定したSQLSetConnectOptionのコールは、前の問合せタイムアウトの設定を上書きします。SQL_QUERY_TIMEOUTオプションを指定したSQLSetStmtOptionのコールは、特定の文の接続設定を上書きします。
問合せのタイムアウト制限は、SQL文がアクティブに実行されている場合にのみ有効です。コミット中またはロールバック中にはタイムアウトは発生しません。多数の行に対して更新、削除または挿入を行うトランザクションでは、コミットまたはロールバックが完了するまでに時間がかかる場合があります。その間、タイムアウト値は無視されます。
SQL問合せタイムアウトと他のタイムアウト設定に関する考慮事項については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のSQLおよびPL/SQLタイムアウト値の選択に関する項を参照してください。
|
ノート: ロック・タイムアウト値およびSQL問合せタイムアウト値の両方が指定されている場合は、まず、2つの値の小さい方の値によってタイムアウトが発生します。ロック・タイムアウトについては、『Oracle TimesTen In-Memory Databaseリファレンス』のttLockWait(組込みプロシージャ)またはLockWait(一般接続属性)に関する説明、またはOracle TimesTen In-Memory Databaseトラブルシューティング・ガイドのデッドロックとタイムアウトの確認に関する説明を参照してください。 |
SQL文の実行時間が、指定した期間(秒)を超えたときに、サポート・ログに警告を書き込むように、TimesTenを構成できます。実行は継続され、しきい値による影響は受けません。
デフォルトでは、アプリケーションはしきい値をQueryThreshold一般接続属性設定から取得します(『Oracle TimesTen In-Memory Databaseリファレンス』のQueryThresholdに関する説明を参照)。デフォルト値は0で、警告は書き込まれません。SQLSetConnectOptionコールでTT_QUERY_THRESHOLDオプションを設定すると、現在の接続の接続属性の設定が上書きされます。このような名前であっても、しきい値は実行可能なすべてのSQL文に適用されます。
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);
この項では、TimesTen ClassicでのTimesTen Cacheの使用に関する機能について説明します。
TimesTen Cacheの詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』を参照してください。
一般接続属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のパススルーに関する説明を参照してください。パススルー設定の詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』のパススルー・レベルの設定に関する説明を参照してください。
TimesTenでは、パススルー・レベルを一時的に設定するためのPassThroughフラグを含む、様々なフラグを設定するためのttOptSetFlag組込みプロシージャが提供されています。次の例のとおり、ttOptSetFlagを使用して、CアプリケーションでPassThroughを設定できます(この例では、パススルー・レベルは1に設定されます)。この設定によって、トランザクションが終了するまで、準備されたすべての文に適用されます。
rc = SQLExecDirect (hstmt, "call ttOptSetFlag ('PassThrough', 1)",SQL_NTS);
この組込みプロシージャの詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のttOptSetFlagに関する説明を参照してください。
SQL文がTimesTenデータベースで実行されるか、またはOracle Databaseにパススルーされて実行されるかを確認するには、TT_STMT_PASSTHROUGH_TYPE文オプションを指定したODBC関数SQLGetStmtOptionをコールします。これは次の例のようになります。
rc = SQLGetStmtOption(hStmt, TT_STMT_PASSTHROUGH_TYPE, &passThroughType);
SQL文の準備の後に、このコールを実行できます。これは、PassThrough設定1または2の場合(文が実際にパススルーされるかどうかの確認がコンパイル時まで行われない)に有効です。TT_STMT_PASSTHROUGH_NONEが返された場合、文はTimesTenで実行されます。TT_STMT_PASSTHROUGH_ORACLEが返された場合、文はOracle Databaseにパススルーされて実行されます。
PassThrough設定の詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』のパススルー・レベルの設定に関する項を参照してください。
|
ノート: TT_STMT_PASSTHROUGH_TYPEは、SQLGetStmtOptionでのみサポートされていて、SQLSetStmtOptionではサポートされていません。 |
TimesTen Cacheでは、FLUSH CACHE GROUP、LOAD CACHE GROUP、REFRESH CACHE GROUPまたはUNLOAD CACHE GROUP文の実行の後、ODBC関数SQLRowCountを使用するとフラッシュ、ロード、リフレッシュまたはアンロードされたキャッシュ・インスタンスの数が返されます。
詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』の処理の影響を受けるキャッシュ・インスタンスの数の確認に関する項を参照してください。
SQLRowCountの全般的な情報については、ODBC APIのリファレンス・マニュアルを参照してください。
TimesTenによるODBCの拡張によって、アプリケーションで言語ソート、文字列の長さセマンティクス、および文字セット変換中のエラー・レポートに関するオプションを設定できます。これらのオプションは、SQLSetConnectOptionへのコールで使用できます。オプションは、timesten.hファイル(「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接続属性より優先されることに注意してください。
レプリケーションを使用するアプリケーションの場合、パラレル・レプリケーションを使用することでパフォーマンスを向上させることができます。パラレル・レプリケーションでは、並行して動作する複数のスレッドを使用して、レプリケーション・スキーム内のデータベースにトランザクションの変更をレプリケートおよび適用します。TimesTen Classicでは、次のタイプのパラレル・レプリケーションをサポートしています。
自動パラレル・レプリケーション(ReplicationApplyOrdering=0): トランザクションの依存性と、コミット順に適用されたすべての変更を自動的に適用する、複数のスレッドにおけるパラレル・レプリケーション。これはデフォルトです。
コミット依存性を無効にした自動パラレル・レプリケーション(ReplicationApplyOrdering=2): トランザクションの依存性を自動的に強制するが、サブスクライバ・データベース上でマスター・データベース上と同じ順序でコミットされるトランザクションは強制しない、複数スレッドでのパラレル・レプリケーション。このモードでは、オプションでレプリケーション・トラックを指定できます。
詳細および使用シナリオについては、『Oracle TimesTen In-Memory Database開発者および管理者ガイド』のパラレル・レプリケーションの構成に関する説明を参照してください。
パラレル・レプリケーションを使用し、レプリケーション・トラックを指定するODBCアプリケーションでは、TT_REPLICATION_TRACK接続オプションを使用した接続時のトランザクションのトラック番号を指定できます(「ODBC 2.5 SQLSetConnectOptionおよびSQLGetConnectOptionのオプションのサポート」を参照)。(または、一般接続属性ReplicationTrackまたはALTER SESSIONのパラメータREPLICATION_TRACKを使用します。)
この項の内容は次のとおりです。
アプリケーションでは、コールのたびにエラーおよび警告をチェックする必要があります。これによって、開発およびデバッグ時に時間および労力が大幅に節約されます。TimesTenに付属のサンプル・アプリケーションには、エラー・チェックの例が含まれています。「TimesTenクイック・スタートおよびサンプル・アプリケーション」を参照してください。
エラーは、installation_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をコールし続けます。(SQL_NO_DATA_FOUNDは、sqlext.h (timesten.hに含まれています)に定義されています。)
SQLError関数およびその引数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。
一般的なODBCエラーを処理する関数の作成方法については、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』のエラーおよび警告の取得に関する項を参照してください。
例2-11 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);
exit(-1);
}
操作が完全には成功しない場合、TimesTenは、致命的なエラー、致命的ではないエラーまたは警告を返すことができます。
致命的なエラーとは、エラー・リカバリが終わるまでデータベースにアクセスできなくなるエラーのことです。致命的なエラーが発生すると、すべてのデータベースの接続を切断する必要があります。それ以後の処理は完了されません。致命的なエラーは、TimesTenのエラー・コード846および994で示されます。これらのエラーの処理は、標準的なエラーの処理とは異なります。特に、アプリケーションのエラー処理コードは、現行のトランザクションをロールバックし、データベースから切断する必要があります。
「致命的なエラーからのリカバリ」も参照してください。
致命的ではないエラーには、一意制約に違反しているINSERT文などの単純なエラーが含まれます。また、一部のアプリケーション障害およびプロセス障害も、致命的ではないエラーに含まれます。
TimesTenは、通常のエラー処理プロセスを通して致命的ではないエラーを返します。アプリケーションはエラーがないか確認し、適切に処理する必要があります。
致命的ではないエラーによってデータベースに影響が出た場合、エラーが返されることがあり、アプリケーションで適切に対処する必要があります。
アプリケーションでは、その処理を変更するか、または障害が発生した1つ以上のトランザクションをロールバックすることによって、致命的ではないエラーに対処できます。
致命的なエラーが発生した場合、TimesTenは、次の完全なクリーンアップおよびリカバリ・プロシージャを実行します。
データベースに対するすべての接続を無効化します。サーバーでのメモリー不足状態を回避するには、無効になったデータベースからアプリケーションを切断する必要があります。古いTimesTenインスタンスの共有メモリーは、エラー発生時にアクティブだったすべての接続が切断されるまで解放されません。古いTimesTenインスタンスに依然として接続されている非アクティブなアプリケーションは、手動で終了する必要があることがあります。
その後の最初の初期接続時に、チェックポイント・ファイルおよびトランザクション・ログ・ファイルからデータベースをリカバリします。
リカバリされたデータベースには、永続コミットされたすべてのトランザクションの状態が反映されます。また、非永続的にコミットされた一部のトランザクションも反映されます。
コミットされていないトランザクションまたはロールバックされたトランザクションは反映されません。
TimesTenはほとんどの一時的なエラーを自動的に解決します(これは、TimesTen Scaleoutで特に重要です)が、アプリケーションが次のSQLSTATE値を検出した場合には現在のトランザクションを再試行することをお薦めします。
TT005: Transient transaction failure due to unavailability of resource.Roll back the transaction and try it again.
|
ノート:
|
ODBC 3.5では、SQLSTATEはSQLGetDiagRec関数によって返されるか、SQLGetDiagField関数のSQL_DIAG_SQLSTATEフィールドに示されます。ODBC 2.5では、SQLSTATEはSQLError関数によって返されます。このSQLSTATEは、次のいずれかの関数で発生する可能性があります。特に指定しないかぎり、これらの関数はODBC 2.5またはODBC 3.5に適用されます。
カタログ関数(SQLTablesやSQLColumnsなど)
SQLCancel
SQLCloseCursor (ODBC 3.5)
SQLDisconnect
SQLExecDirect
SQLExecute
SQLFetch
SQLFetchScroll (ODBC 3.5)
SQLFreeStmt (ODBC 2.5)
SQLGetData
SQLGetInfo
SQLPrepare
SQLPutData
SQLEndTran (ODBC 3.5)
SQLTransact (ODBC 2.5)
自動クライアント・フェイルオーバーは、TimesTen ScaleoutまたはTimesTen Classicのいずれかの高可用性シナリオで使用されます。TimesTen Classicには、2つのシナリオがあります。1つは、アクティブ・スタンバイ・ペアのレプリケーションを持ち、もう1つは汎用自動クライアント・フェイルオーバーと呼ばれます。
クライアントが接続しているデータベースまたはデータベース要素に障害が発生した場合は、代替のデータベースまたはデータベース要素にフェイルオーバー(接続転送)します。
TimesTen Scaleoutの場合、フェイルオーバーは、グリッド内の使用可能な要素についてTimesTenによって返されるリストからの要素に対して行われます。
アクティブ・スタンバイ・レプリケーションを使用するTimesTen Classicの場合、フェイルオーバーは新しいアクティブ・データベース(元のスタンバイ)に対して行われます。
汎用自動クライアント・フェイルオーバーを使用するTimesTen Classicの場合、スキーマおよびデータが両方のデータベースで一貫していることを確認できるため、フェイルオーバーはクライアントodbc.iniファイルで構成されているリストからのデータベースに対して行われます。
汎用自動フェイルオーバーの一般的なユースケースは、読取り専用キャッシュを使用する一連のデータベースで、各データベースのキャッシュ・データのセットは同じです。たとえば、読取り専用キャッシュ・グループが複数ある場合は、フェイルオーバー・サーバーのリストに含まれるすべてのTimesTen Classicデータベースに、同じ読取り専用キャッシュ・グループを作成します。クライアント接続が代替のTimesTenデータベースにフェイルオーバーされると、TimesTenキャッシュは必要に応じてOracleデータベースからデータを自動的にリフレッシュするため、キャッシュされたデータの一貫性が保たれます。
アプリケーションは新しいデータベースまたはデータベース要素に自動的に再接続されます。TimesTenは自動クライアント・フェイルオーバーが発生したときに、アプリケーションに警告を渡す機能を提供しているため、アプリケーションは適切な処理を行うことができます。
この項では、アプリケーション開発者に適用されるTimesTenでの自動クライアント・フェイルオーバーの実装を取り上げ、次のことについて説明します。
TimesTen Scaleoutの詳細は、『Oracle TimesTen In-Memory Database Scaleoutユーザーズ・ガイド』のクライアントの接続フェイルオーバーに関する項を参照してください。TimesTen Classicの場合、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』の自動クライアント・フェイルオーバーの使用に関する項を参照してください。
|
ノート:
|
クライアントが接続しているデータベースまたはデータベース要素に障害が発生すると、代替のデータベースまたはデータベース要素へのフェイルオーバーが発生します。フェイルオーバーが発生した場合、次の点に注意してください。
クライアントの接続は新しくなりますが、使用するODBC接続ハンドルは同じままです。ただし、ハンドル自体を除き、元の接続の状態は保持されません。アプリケーションは、新しいODBC文ハンドルおよび記述子ハンドルをオープンする必要があります。
フェイルオーバーのコールバック関数を登録すると(フェイルオーバーのコールバック関数を参照)、フェイルオーバー・リスナー・スレッドがクライアント・プロセス内に作成され、フェイルオーバー・イベントをリスニングしてコールバック関数を起動します。
元の接続からのすべてのクライアント文ハンドルは、無効とマークされます。これらの文ハンドルに対してAPIコールを実行すると、通常は次のように、tt_errCode.hで定義されている個別のフェイルオーバー・エラー・コードがSQL_ERRORとともに返されます。
ネイティブ・エラー30105 (SQL状態08006)
ネイティブ・エラー47137
これに対する例外は、SQLError、SQLFreeStmt、SQLGetDiagRec、SQLGetDiagFieldの各コールに対するもので(ODBCのバージョンによって異なります)、いずれも正常に動作します。
また、次の点にも注意してください。
元のデータベースまたはデータベース要素へのソケットはクローズされます。SQLDisconnectをコールする必要はありません。TimesTenは同等の処理を実行して、接続ハンドルをクリーン・アップし、リソースを解放します。
新しいTimesTenデータベースまたはデータベース要素に接続する場合、元の接続要求からの同じ接続文字列およびDSN定義が適切なサーバー名とともに使用されます。
新しい文ハンドルをオープンして必要なSQLPrepareコールを再実行するかどうかは、アプリケーション次第です。
フェイルオーバーがすでに発生し、クライアントが新しいデータベースまたはデータベース要素に接続されている場合は、次のようになります。
TimesTen Scaleoutの場合、次のフェイルオーバー・リクエストによって、元の接続時にTimesTenから返されたリストにある次の要素への接続が試行されます。
アクティブ・スタンバイ・レプリケーションを使用するTimesTen Classicの場合、次のフェイルオーバー・リクエストによって元のアクティブ・データベースへの再接続が試行されます。再接続に失敗した場合、タイムアウトになるまで2つのサーバーへの接続の試行が交互に行われ、この間は接続はブロックされます。
汎用自動クライアント・フェイルオーバーを使用するTimesTen Classicの場合、次のフェイルオーバー・リクエストにより、クライアントのodbc.iniファイル内に構成されているリスト内の次のデータベースへの接続が試行されます。これは、自動クライアント・フェイルオーバーの構成で説明されているTTC_Random_Selection接続属性の設定により、次のデータベースになる場合も、リストからランダムに選択されたデータベースになる場合もあります。
タイムアウト値は、TimesTenクライアント接続属性TTC_Timeout (デフォルトは60秒)に従います。(この属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_Timeoutに関する説明を参照してください。)
フェイルオーバー接続は、事前に作成されるのではなく、必要に応じてのみ作成されます。
フェイルオーバーの間、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_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) TT_NO_RECONNECT_ON_FAILOVER (TTC_NoReconnectOnFailover)
次に示すオプションは、接続ハンドルで設定されている場合には新しい接続に伝播されます。
SQL_QUERY_TIMEOUT TT_PREFETCH_COUNT
TimesTen接続属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』の接続属性に関する項を参照してください。
|
ノート: 最初のデータベース接続後の任意の時点でALTER SESSION文を発行する場合は、フェイルオーバー後にこの文を再発行する必要があります。 |
TimesTenでのクライアント接続フェイルオーバーの管理の詳細は、Oracle TimesTen In-Memory Databaseオペレーション・ガイドのTimesTen Classicの自動クライアント・フェイルオーバーの構成に関する項、またはOracle TimesTen In-Memory Database Scaleoutユーザーズ・ガイドのクライアント接続フェイルオーバーに関する項を参照してください。
TimesTen Classicでは、フェイルオーバーDSNは、具体的にTTC_Server2接続属性とTTC_Servern接続属性を使用して構成する必要があります。
|
ノート: TTC_Server2、TTC_Server_DSN2、TTC_ServernまたはTCP_Port2を設定すると、自動クライアント・フェイルオーバーを使用することになります。アクティブ・スタンバイ・ペアのシナリオの場合は、アプリケーションでフェイルオーバー・メカニズムをサポートするために新しいスレッドが作成されます。 |
次のTimesTen接続属性に注意してください。
TTC_NoReconnectOnFailover: これが1 (有効)に設定されている場合、TimesTenは自動再接続を除くすべての通常のクライアント・フェイルオーバー処理を実行するようになります。(たとえば、文ハンドルおよび接続ハンドルは無効としてマークされます。)これは、アプリケーションが自身の接続プーリングを行うか、フェイルオーバー後にデータベースへの自身の再接続を管理する場合に便利です。デフォルト値は0 (再接続)です。『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_NoReconnectOnFailoverに関する項も参照してください。
TTC_REDIRECT: これが0に設定されていて、目的のデータベースまたはデータベース要素に対する最初の接続試行が失敗した場合、エラーが返され、それ以上の接続試行は行われません。これは、その接続の後続のフェイルオーバーには影響しません。『Oracle TimesTen In-Memory Databaseリファレンス』のTTC_REDIRECTに関する項も参照してください。
TTC_Random_Selection: 汎用自動クライアント・フェイルオーバーを使用するTimesTen Classicの場合、デフォルト設定の1 (有効)では、フェイルオーバーが発生したときに、クライアントがTTC_Servern属性の設定で指定されたリストから代替サーバーをランダムに選択します。選択したサーバーにクライアントが接続できない場合、クライアントはリストされているサーバーのいずれかに正常に接続するまでリダイレクトし続けます。0を設定すると、TimesTenはTTC_Servernサーバーのリストを順番に処理します。Oracle TimesTen In-Memory DatabaseリファレンスのTTC_Random_Selectionも参照してください。
|
ノート: これらをodbc.iniまたは接続文字列内で設定すると、設定がフェイルオーバー接続に適用されます。ODBC接続オプションまたはALTER SESSION属性としては設定できません。 |
フェイルオーバーの発生時に行うカスタム・アクションがある場合、TimesTenではこのようなアクションのためのユーザー定義関数にコールバックを行うことができます。この関数は、新しいデータベースまたはデータベース要素への接続試行が開始されたときにコールされ、接続の試行が完了した後に再度コールされます。たとえば、文ハンドルを正常にリストアするために、この関数を使用できます。
関数APIの定義は次のとおりです。
typedef SQLRETURN (*ttFailoverCallbackFcn_t) (SQLHDBC, /* hdbc */ SQLPOINTER, /* foCtx */ SQLUINTEGER, /* foType */ SQLUINTEGER); /* foEvent */
各要素の意味は次のとおりです。
hdbcは、障害が発生した接続のODBC接続ハンドルです。
foCtxは、アプリケーション定義のデータ構造体に対するポインタで、必要に応じて使用します。
foTypeは、フェイルオーバーのタイプです。これに対して、TimesTenでサポートされている値はTT_FO_SESSIONのみであり、セッションが再確立されます。この場合、文の再準備は行われません。
foEventは、サポートされている次の値を使用して、発生したイベントを示します。
TT_FO_BEGIN: フェイルオーバーの開始。
TT_FO_ABORT: フェイルオーバーの失敗。TTC_Timeoutで指定した時間(アクティブ・スタンバイ・フェイルオーバーの最小値である60秒)の間、再試行が実行されましたが成功しませんでした。
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が接続に対して複数回設定されている場合、最後の設定が優先されます。
|
注意:
|
例2-12 フェイルオーバーのコールバック関数および登録
この例では次の機能を示しています。
グローバルに定義されるユーザー構造体タイプFOINFO、およびタイプFOINFOの構造体変数foStatus
フェイルオーバーが発生すると常にfoStatus構造体を更新するコールバック関数FailoverCallback()
登録関数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
この項の内容は次のとおりです。
アプリケーションでの操作に応えて「自動クライアント・フェイルオーバーの機能」に示されたいずれかのエラー状態になった場合は、アプリケーション・フェイルオーバーが進行中です。次のリカバリ・アクションを実行します。
接続に対してロールバックを発行します。これを行うまで、接続に対してそれ以上の処理は実行できません。
前の接続からすべてのオブジェクトをクリーンアップします。前の接続に関連付けられた状態やオブジェクトはいずれも保持されませんが、関連するAPIコールによる適切なクリーン・アップをお薦めします。
TTC_NoReconnectOnFailover=0 (デフォルト)とすると、次の項「フェイルオーバー遅延および再試行設定」で説明しているように、短い間スリープします。TTC_NoReconnectOnFailover=1の場合は、かわりにアプリケーションを代替のデータベースまたはデータベース要素に手動で再接続する必要があります。
接続に関連するすべてのオブジェクトを再作成および再準備します。
進行中のトランザクションを最初から再起動します。
自動クライアント・フェイルオーバー中に別のデータベースまたはデータベース要素に再接続する場合、時間がかかることがあります。TimesTenがクライアント・フェイルオーバー・プロセスを完了する前に、アプリケーションがリカバリ・アクションを試みると、「自動クライアント・フェイルオーバーの機能」に示すように、別のフェイルオーバー・エラー状態になる場合があります。
このため、アプリケーションではすべてのリカバリ・アクションを1つのループ内に配置し、後続の各試行の前に発生する遅延を短くすることをお薦めします。このようにすると、合計試行回数が制限されます。試行回数を制限しないと、クライアント・フェイルオーバー・プロセスが正常に完了しない場合には、アプリケーションがハングしたように見えることがあります。たとえば、リカバリ・ループでの再試行遅延を100ミリ秒にして、再試行の最大回数を100に制限できます。理想的な値は、特定のアプリケーションおよび構成によって異なります。
例2-13に、このポイントを示します(さらに一時的なエラーの再試行も示します。これについては「一時的なエラー後の再試行(ODBC)」を参照してください)。
例2-13 一時的なエラーとクライアント・フェイルオーバー・エラーの処理
/*
* The following code snippet is a simple illustration of how you might handle
* the retrying of transient and connection failover errors in a C/ODBC
* application. In the interests of simplicity code that is not directly
* relevant to the example has been omitted (...). A real application
* would of course be more complex.
*
* This example uses the ODBC 3.5 API.
*/
// define maximum retry counts and failover retry delay
#define MAX_TE_RETRIES 30
#define MAX_FO_RETRIES 100
#define FO_RETRY_DELAY 100 // milliseconds
// function return values
#define SUCCESS 0
#define FAILURE (-1)
// constants for categorising errors
#define ERR_OTHER 1
#define ERR_TRANSIENT 2
#define ERR_FAILOVER 3
// SQLSTATES and native errors
#define SQLSTATE_TRANSIENT "TT005"
#define SQLSTATE_FAILOVER "08006"
#define NATIVE_FAILOVER1 47137
#define NATIVE_FAILOVER2 30105
// SQL statements
SQLCHAR * sqlQuery = (SQLCHAR *)"SELECT ...";
SQLCHAR * sqlUpdate = (SQLCHAR *)"UPDATE ...";
// Database connection handle
SQLHDBC dbConn = SQL_NULL_HDBC;
// Statement handles
SQLHSTMT stmtQuery = SQL_NULL_HSTMT;
SQLHSTMT stmtUpdate = SQL_NULL_HSTMT;
// ODBC return code
SQLRETURN rc;
// Retry counters
int teRetries; // transient errors
int foRetries; // failover errors
int foDelay = FO_RETRY_DELAY; // failover retry delay in ms
// Function to sleep for a specified number of milliseconds
void
sleepMs( unsigned int ms)
{
struct timespec rqtm, rmtm;
rqtm.tv_sec = (time_t)(ms / 1000);
rqtm.tv_nsec = (long)(ms % 1000000);
while ( nanosleep( &rqtm, &rmtm ) )
rqtm = rmtm;
} // sleepMs
// Function to check error stack for transient or failover errors.
// In a real application lots of other kinds of checking would also
// go in here to identify other errors of interest. We'd probably also
// log the errors to an error log.
int
errorCategory( SQLHANDLE handle, SQLSMALLINT handleType )
{
SQLRETURN rc;
SQLSMALLINT i = 1;
SQLINTEGER native_error;
SQLCHAR sqlstate[LEN_SQLSTATE+1];
SQLCHAR msgbuff[1024];
SQLSMALLINT msglen;
native_error = 0;
sqlstate[0] = '\0';
rc = SQLGetDiagRec( handleType, handle, i, sqlstate, &native_error,
msgbuff, sizeof(msgbuff), &msglen );
while ( rc == SQL_SUCCESS )
{
if ( strcmp( sqlstate, SQLSTATE_TRANSIENT ) == 0 )
return ERR_TRANSIENT;
else
if ( native_error == NATIVE_FAILOVER1 )
return ERR_FAILOVER;
else
if ( ( strcmp( sqlstate, SQLSTATE_FAILOVER ) == 0 ) &&
(native_error == NATIVE_FAILOVER2) )
return ERR_FAILOVER;
rc = SQLGetDiagRec( handleType, handle, ++i, sqlstate,
&native_error, msgbuff, sizeof(msgbuff),
&msglen );
}
return ERR_OTHER;
} // errorCategory
// Function to perform a rollback
void
rollBack( SQLHDBC hDbc )
{
SQLRETURN rc;
rc = SQLEndTran( SQL_HANDLE_DBC, hDbc, SQL_ROLLBACK );
// Report/log errors (a rollback failure is very, very bad).
...
} // rollBack
// Function to prepare all statements, bind parameters and bind
// columns.
int
prepareAll( void )
{
SQLRETURN rc;
// Prepare the SQL statements and check for errors.
rc = SQLPrepare( stmtQuery, sqlQuery, SQL_NTS );
if ( rc != SQL_SUCCESS )
{
rollBack( dbConn );
return errorCategory( stmtQuery, SQL_HANDLE_STMT );
}
rc = SQLPrepare( stmtUpdate, sqlUpdate, SQL_NTS );
...
// Bind parameters and colums
...
return SUCCESS; // indicate success
} // prepareAll
// Function to execute a specific application transaction handling
// retries.
int
txnSomeTransaction( ... )
{
SQLRETURN rc;
SQLLEN rowcount = 0;
int needReprepare = 0;
int result;
// Initialize retry counters
teRetries = MAX_TE_RETRIES;
foRetries = MAX_FO_RETRIES;
// main retry loop
while ( ( teRetries > 0 ) && ( foRetries > 0 ) )
{
// Do we need to re-prepare?
while ( needReprepare && ( foRetries > 0 ) )
{
msSleep( retryDelay ); // delay before proceeding
result = prepareAll();
if ( result == SUCCESS )
needReprepare = 0;
else
if ( result != ERR_FAILOVER )
goto err;
else
foRetries--;
}
// First execute the query
// Set input values for query
...
// Execute query
rc = SQLExecute( stmtQuery );
if ( rc != SQL_SUCCESS )
{
result = errorCategory( stmtQuery, SQL_HANDLE_STMT );
rollBack( dbConn );
switch ( result )
{
case ERR_OTHER:
goto err;
break;
case ERR_TRANSIENT:
teRetries--;
continue; // retry loop
break;
case ERR_FAILOVER:
foRetries--;
needReprepare = 1;
continue; // retry loop
break;
}
}
// Process results
while ( (rc = SQLFetch( stmtQuery )) == SQL_SUCCESS )
{
// process next row
...
}
if ( (rc != SQL_SUCCESS) && (rc != SQL_NO_DATA) )
{
result = errorCategory( stmtQuery, SQL_HANDLE_STMT );
rollBack( dbConn );
switch ( result )
{
case ERR_OTHER:
goto err;
break;
case ERR_TRANSIENT:
teRetries--;
continue; // retry loop
break;
case ERR_FAILOVER:
foRetries--;
needReprepare = 1;
continue; // retry loop
break;
}
}
// Now execute the update
// Set input values for update
...
// Execute update
rc = SQLExecute( stmtUpdate );
if ( rc != SQL_SUCCESS )
{
...
}
// Check number of rows affected
rc = SQLRowCount( stmtUpdate, &rowcount );
if ( rc != SQL_SUCCESS )
{
...
}
// Check rowcount and handle unexpected cases
if ( rowcount != 1 )
{
...
}
// Finally, commit
rc = SQLEndTran( SQL_HANDLE_DBC, dbConn, SQL_COMMIT );
if ( rc != SQL_SUCCESS )
{
...
}
return SUCCESS; // all good
} // retry loop
err:
// if we get here, we ran out of retries or had some other non-retryable
// error. Report/log it etc. then return failure
...
return FAILURE;
} // txnSomeTransaction
// Main code
int
main ( int argc, char * argv[] )
{
int status = 0; // final exit code
....
// Open the connection to the database and allocate statement handles
...
// Disable auto-commit (this is essential)
rc = SQLSetConnectAttr( dbConn,
SQL_ATTR_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF,
0 );
...
// Prepare all statements, bind etc.
if ( prepareAll() != SUCCESS )
{
...
}
// Do stuff until we are finished
while ( ... )
{
...
if ( txnSomeTransaction( ... ) != SUCCESS )
{
...
goto fini;
}
...
}
fini: // cleanup etc.
// Release all resources (ODBC and non-ODBC)
...
// Disconnect from database
...
// Return final exit code
return status;
} //main
パフォーマンスを高めるために、TimesTen Scaleoutではクライアント・アプリケーションがハッシュ分散キーのキー値に基づいて要素に接続をルーティングできます。キー値を指定すると、TimesTen Scaleoutが要素ID (またはレプリカ・セットID)の配列を返します。その値は、データベースによって割り当てられたものです。これにより、指定したキー値に一致する行を格納している要素にクライアント・アプリケーションが接続できるため、行を格納している要素とアプリケーションに接続された要素との間で不要な通信を回避できます。
|
ノート: この機能はドライバ・マネージャではサポートされていません。 |
この項の内容は次のとおりです。
TimesTen Scaleoutでは、timesten.hファイルにクライアント・ルーティング用の新しいオブジェクトが2つ含まれています。
TTGRIDMAP: グリッド・マップは、グリッドのトポロジをマップする参照表です。グリッド・マップを作成するには、有効なODBC接続を指定したttGridMapCreate関数をコールします。この関数は、TTGRIDMAPオブジェクトへのハンドルを返します。
|
ノート:
|
グリッド・マップを解放するには、ttGridMapFree関数を使用します。
TTGRIDDIST: グリッド分散は、1つ以上の表の分散キー列を表す、順序付きの型と値のセットです。複数の列で構成される分散キーの場合、型と値の順序は表の分散キー列のものと同じである必要があります。
グリッド分散を作成するには、表の分散キー列のCデータ型、SQL型、長さ、スケールおよび精度を指定したttGridDistCreate関数をコールします。この関数は、TTGRIDDISTオブジェクトへのハンドルを返します。表2-3に、ttGridDistCreate関数の引数の概要を示します。
|
ノート:
|
表2-3 ttGridDistCreateの引数
| 引数 | 型 | 説明 |
|---|---|---|
|
|
|
接続ハンドル |
|
|
|
グリッド・マップ・ハンドル |
|
|
|
分散キー列と同じ順序で並んだCバインド・タイプの配列 |
|
|
|
分散キー列と同じ順序で並んだSQLバインド・タイプの配列 |
|
|
|
分散キー列と同じ順序で並んだ精度値の配列 |
|
|
|
分散キー列と同じ順序で並んだスケール値の配列 |
|
|
|
分散キー列と同じ順序で並んだ最大列サイズ値の配列 |
|
|
|
分散キーの列数 |
|
|
|
グリッド分散ハンドル(OUT) |
|
ノート: ttGridDistCreateのパラメータは、後続のSQLBindParameter ODBCコールで使用されるパラメータと似ています。 |
グリッド分散を解放するには、ttGridDistFree関数を使用します。
例2-14 グリッド・マップとグリッド分散の作成
この例では、TTGRIDMAPオブジェクトとTTGRIDDISTオブジェクトを作成します。次に、ttGridMapCreate関数をコールし、既存のODBC接続を使用してグリッド・マップを作成します。その後、ttGridDistCreate関数をコールして、2つの列で構成される分散キーに基づいてグリッド分散を作成します。最後に、グリッド分散とグリッド・マップをそれぞれttGridDistFree関数とttGridMapFree関数で解放します。
TTGRIDMAP map;
TTGRIDDIST dist;
ttGridMapCreate(hdbc, &map);
SQLSMALLINT cTypes[] = { SQL_C_LONG, SQL_C_CHAR };
SQLSMALLINT sqlTypes[] = { SQL_INTEGER, SQL_CHAR };
SQLLEN maxSizes[] = { 4, 20 };
ttGridDistCreate(hdbc, map, cTypes, sqlTypes, NULL, NULL, maxSizes, 2, &dist);
...
ttGridDistFree(hdbc, dist);
ttGridMapFree(hdbc, map);
グリッド・マップとグリッド分散を定義したら、それらが割り当てられる要素を決定するためにキー値を設定します。ttGridDistValueSet関数をコールして、分散キーの列のいずれかにキー値を設定します。複数の列で構成される分散キーの場合、分散キーの列ごとにこの関数を1回コールします。表2-4に、ttGridDistValueSet関数の引数の概要を示します。
分散キー値を設定したら、要素IDまたはレプリカ・セットIDによってキー値の位置をコールできます。
ttGridDistElementGet関数をコールして、指定されたキー値の位置を表す対応する要素IDを取得します。この関数は、要素IDの配列を返します。アプリケーションは、戻り配列を割り当てます。配列の長さは、グリッドのK-safetyの値に基づいています。たとえば、K-safetyが2に設定されているグリッドでは、配列に少なくとも2つの要素が必要です。表2-5に、ttGridDistElementGet関数の引数の概要を示します。
表2-5 ttGridDistElementGetの引数
| 引数 | 型 | 説明 |
|---|---|---|
|
|
|
接続ハンドル |
|
|
|
グリッド分散ハンドル |
|
|
|
キー値が割り当てられる要素IDの配列(IN/OUT) |
|
|
|
K-safetyの値 |
例2-16 現在のキー値の要素IDの配列の取得
この例では、ttGridDistElementGet関数をコールして、現在のキー値(ttGridDistValueSet関数によって設定されたもの)に関連付けられた要素IDの配列を取得します。
SQLSMALLINT elementIds[2]; ttGridDistElementGet(hdbc, dist, elementIds, 2);
|
ノート: elementIds配列の長さは、グリッドのK-safetyの値以上である必要があります。 |
使用可能なキー値セットの位置により、アプリケーションは要素IDを使用して、要素のいずれかへの接続を選択し、文を作成し、値をバインドして、その文を実行できます。
|
注意: 接続試行はフェイルオーバー・イベントの対象になることがあり、そのためにアプリケーションが目的の要素に接続できない可能性があります。 |
例2-17に、ほとんどのオブジェクトおよび関数が使用しているクライアント・ルーティングAPIを示します。
例2-17 クライアント・ルーティングAPI
#include <timesten.h>
...
TTGRIDMAP map;
TTGRIDDIST dist;
/* Create a grid map using any existing connection. */
ttGridMapCreate(hdbc, &map);
/* The distribution key has two columns: one with TT_INTEGER as data type and
* one with CHAR(20), in that order. Precision and scale are not necessary. */
SQLSMALLINT cTypes[] = { SQL_C_LONG, SQL_C_CHAR };
SQLSMALLINT sqlTypes[] = { SQL_INTEGER, SQL_CHAR };
SQLLEN maxSizes[] = { 4, 20 };
/* Create grid distribution from the grip map and the specified distribution
* key column paremeters. */
ttGridDistCreate(hdbc, map, cTypes, sqlTypes, NULL, NULL, maxSizes, 2, &dist);
/* Execution loop. */
while ( ... )
{
SQLSMALLINT elementIds[2];
/* Clear the existing key values from the distribution map */
ttGridDistClear(hdbc, dist);
/* Set the key values for the grid distribution. */
ttGridDistValueSet(hdbc, dist, 1, key1, sizeof(key1));
ttGridDistValueSet(hdbc, dist, 2, key2, SQL_NTS);
/* Get the corresponding element IDs for current key values*/
ttGridDistElementGet(hdbc, dist, elementIds, 2);
/* The application uses the element IDs to select a connection to
* one of the elements, prepare a statement, bind values, and execute
* the statement. */
...
}
/* Free the grid distribuion and map. */
ttGridDistFree(hdbc, dist);
ttGridMapFree(hdbc, map);
例2-18に、要素IDを接続文字列に関連付ける際に役立つ問合せを示します。
例2-18 各要素IDの接続文字列
この例では、SYS.V$DISTRIBUTION_CURRENTシステム・ビューを問い合せて、データベースの要素ごとに接続文字列をアセンブルします。接続文字列にはTTC_REDIRECT=0属性が含まれていて、指定した要素またはそのレプリカへの接続が保証されます。すべてのレプリカへの接続が失敗すると、接続エラーが返されます。
select 'TTC_REDIRECT=0; TTC_SERVER='||hostexternaladdress||'/'||serverport,mappedelementid from SYS.V$DISTRIBUTION_CURRENT; < TTC_REDIRECT=0;TTC_SERVER=ext-host3.example.com/6625, 1 > < TTC_REDIRECT=0;TTC_SERVER=ext-host4.example.com/6625, 2 > < TTC_REDIRECT=0;TTC_SERVER=ext-host5.example.com/6625, 3 > < TTC_REDIRECT=0;TTC_SERVER=ext-host6.example.com/6625, 4 > < TTC_REDIRECT=0;TTC_SERVER=ext-host7.example.com/6625, 5 > < TTC_REDIRECT=0;TTC_SERVER=ext-host8.example.com/6625, 6 > 6 rows found.
ttGridDistReplicaGet関数をコールして、指定されたキー値の位置を表す対応するレプリカ・セットIDを取得します。表2-6に、ttGridDistReplicaGet関数の引数の概要を示します。
表2-6 ttGridDistReplicaGetの引数
| 引数 | 型 | 説明 |
|---|---|---|
|
|
|
接続ハンドル |
|
|
|
グリッド分散ハンドル |
|
|
|
キー値が割り当てられるレプリカ・セットID (OUT) |
例2-19 現在のキー値のレプリカ・セットIDの取得
この例では、ttGridDistReplicaGet関数をコールして、(ttGridDistValueSet関数によって設定された)現在のキー値に関連付けられたレプリカ・セットIDの配列を取得します。
SQLSMALLINT replicaSetId; ttGridDistReplicaGet(hdbc, dist, replicaSetId);
例2-18の要素IDの場合と同じく、SYS.V$DISTRIBUTION_CURRENTシステム・ビューでレプリカ・セットIDを使用して、そのレプリカ・セットで要素の通信パラメータを参照できます。
TTGRIDDISTオブジェクトは、ODBCで使用可能なCデータ型およびSQLデータ型を使用して作成されます。表2-7に、サポートされているCデータ型およびSQLデータ型をその対応するデータベースSQLデータ型とともに示します。
表2-7 サポートされているデータ型のリスト
| Cデータ型 | ODBC SQLデータ型 | データベースSQLデータ型 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TTGRIDDISTオブジェクトでは、符号付きと符号なしのすべてのデータ型バリアントがサポートされています。たとえば、SQL_C_SLONGとSQL_C_ULONGの両方がサポートされています。
ttGridDistValueSet関数のvalueLenパラメータにSQL_NULL_DATAを指定して、NULL値を設定できます。NULL値は、常に同じレプリカ・セットまたは要素IDにマップされます。
クライアント・ルーティングには次の制限があります。
暗黙的な接続または文の管理はありません。
日付、時間またはタイムスタンプのデータ型はサポートされていません。
明示的な型変換はサポートされていません。アプリケーションでは、キー値を正規のバイト形式で指定する必要があります。
文字セット変換はサポートされていません。接続文字セットは無視されます。
グリッドのトポロジを変更するには、アプリケーションがグリッド・マップを解放して再作成する必要があります。
クライアント・ルーティングAPIは、次のシナリオでエラーを返すことがあります。
表の分散キー列を説明するデータ型および値が正しくありません。この場合、APIは要素IDの配列を計算しますが、目的のキー値の実際の位置に対応していない可能性があります。
型コードを認識できません。認識されない型コードでttGridDistCreate関数をコールすると、エラーが返されます。
グリッド分散に十分な値が設定されていません。ttGridDistValueSet関数を使用して分散キーに十分な値を指定しない場合、ttGridDistElementGet関数またはttGridDistReplicaGet関数はエラーを返します。
要素ID配列のサイズが無効です。K-safetyの値のサイズ以上の配列を指定しない場合、ttGridDistElementGet関数はエラーを返します。