主コンテンツへ
Oracle® TimesTen In-Memory Database C開発者ガイド
リリース18.1
E98631-04
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

2 ODBCでのTimesTenデータベースの使用

この章では、TimesTenプログラミング機能について取り上げ、TimesTenデータベースに接続して使用するためにODBCを使用する方法について説明します。内容は次のとおりです。


ノート:

  • OCIを使用したCアプリケーションからのTimesTenへのアクセスについては、第3章「TimesTenでのOCIのサポート」を参照してください。

  • Pro*C/C++を使用したCアプリケーションからのTimesTenへのアクセスについては、第4章「TimesTenでのPro*C/C++のサポート」を参照してください。

  • C++アプリケーションからのTimesTenへのアクセスについては、Oracle TimesTen In-Memory Database TTClassesガイドを参照してください。

  • C#アプリケーションからのTimesTenへのアクセスについては、『Oracle Data Provider for .NET Oracle TimesTen In-Memory Databaseサポート・ユーザーズ・ガイド』を参照してください。


TimesTenは、次をサポートしています。

TimesTenデータベース接続の管理

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アプリケーションは、属性(ホストやポート番号など)またはデータソース名(DSN)のいずれかを参照してデータベースに接続できます。TimesTen Classicでは、ユーザーはDSNを直接作成できます。TimesTen Scaleoutでは、グリッドに定義する接続可能オブジェクトごとにDSNが作成されます。

  • TimesTenでは、ユーザー名およびパスワードは、データベースに接続するためのCREATE SESSION権限を付与されている有効なユーザーのものである必要があります。

  • TimesTen接続は、親プロセスから継承することはできません。プロセスが子プロセスを作成(分岐)する前にデータベース接続をオープンした場合、子プロセスではそのデータベース接続を使用しないでください。


この項の以降の内容は次のとおりです。

SQLConnect、SQLDriverConnect、SQLAllocConnect、SQLDisconnectの各関数

データベースへの接続およびそれに関連する機能の実行には、次の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リファレンス』の接続属性に関する説明を参照してください。

例2-3 接続およびストア・レベル・ロックの使用

このコード部分では、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リファレンス』のオープン・ファイルの数の制限に関する項を参照してください。

デフォルトDSNの使用

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 includeファイル

TimesTenの機能を使用するには、必要に応じて、次の表に示すTimesTenファイルを含めます。これらは、TimesTenインストールのincludeディレクトリにあります。

インクルード・ファイル 説明
timesten.h TimesTenのODBC機能

このファイルには、適切なバージョンのsql.hが含まれています。LinuxまたはUNIXシステムではTimesTenバージョン、Windowsシステムではシステム・バージョンです。

このファイルには、sqltypes.hsqlext.hおよびsqlucode.hも含まれています。Windowsシステムでは、windows.hも含まれます。

tt_errCode.h TimesTenエラー・コード(オプション。「ノート」を参照)

このファイルは、TimesTenエラー・コードを定義済の定数マップします。


インクルードされるファイルにアクセスするには、適切なインクルード・パスを設定します。関連情報については、「アプリケーションのコンパイルおよびリンク」を参照してください。


ノート:

  • sql.hを(timesten.hを介してではなく)直接含める場合、WindowsにTimesTenバージョンではなくシステム・バージョンのsql.hを含める必要があります。

  • これまでsqlunix.hにあった型定義は、sqltypes.hに含まれるようになりました。ただし、sqlunix.hは下位互換性のため(空のファイルとして)引き続き存在します。

  • tt_errCode.hを含めるかわりに別の方法もあります。1つは、任意の定数定義をtimesten.hに移動することです。もう1つは、対応する整数値をコード内で直接参照することです。


Cアプリケーション内でのSQL文の実行

SQLを使用してデータを管理する方法については、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のTimesTenデータベースのデータの処理に関する説明を参照してください。この項では、Cアプリケーション内でSQL文を実行するために使用される一般的な書式について説明します。内容は次のとおりです。

SQLExecDirect関数およびSQLExecute関数

SQL文を実行するODBC関数は次の2つです。

  • SQLExecute: SQLPrepareで準備されている文を実行します。実行結果を使用してアプリケーションが実行された後、その結果を破棄して、別のパラメータ値でSQLExecuteを再実行することができます。

    通常、バインド・パラメータを使用するDML文または複数回実行される文に使用します。

  • SQLExecDirect: 文を準備して実行します。

    通常、DDL文、または数回のみ実行される、バインド・パラメータを使用しないDML文に使用します。

これらの関数の詳細は、ODBC APIのリファレンス・マニュアルを参照してください。

SQL文の実行

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オプションを使用して、一度にフェッチする行数などのカーソル特性を制御できます。問合せを実行する場合のステップは、通常は次のとおりです。

  1. SQLPrepareを使用して、SELECT文を実行するための準備をします。

  2. SQLBindParameter(文にパラメータが含まれている場合)を使用して、各パラメータをアプリケーション・アドレスにバインドします。「SQLBindParameter関数」を参照してください。(次の例2-5ではパラメータをバインドしていません。)

  3. SQLBindColをコールして結果の列に記憶域およびデータ型を割り当て、列の結果をアプリケーション内のローカル変数記憶域にバインドします。

  4. SQLExecuteをコールして、SELECT文を実行します。「SQLExecDirect関数およびSQLExecute関数」を参照してください。

  5. SQLFetchをコールして結果をフェッチします。文ハンドルを指定します。

  6. SQLFreeStmtをコールして文ハンドルを解放します。文ハンドルおよびSQL_CLOSESQL_DROPSQL_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);
}

TimesTenの遅延準備

標準の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_CLOSETT_PREFETCH_CLOSE_ONに設定します。

すべてのトランザクションは、読取り専用トランザクションを含め、実行時にコミットされる必要があります。TT_PREFETCH_CLOSETT_PREFETCH_CLOSE_ONに設定されている場合、カーソルが自動的にクローズされ、読取り専用問合せの結果セットの行がすべてプリフェッチされた後でトランザクションがコミットされます。これによって、クライアントとサーバー間のネットワーク・ラウンドトリップ数が減少するため、パフォーマンスが向上します。

クライアントでは、SQLFreeStmt(SQL_CLOSE)を使用して文を解放し、SQLTransact(SQL_COMMIT)を使用してトランザクションをコミットする必要がありますが、これらのコールは、クライアントで実行されるため、クライアントとサーバー間のネットワーク・ラウンドトリップを必要としません。


ノート:

  • TT_PREFETCH_CLOSETT_PREFETCH_CLOSE_ONに設定されている場合、同じ接続に対して複数の文ハンドルを使用しないでください。サーバーでは、クライアントが終了する前に、結果セットのすべてがフェッチされ、トランザクションがコミットされて文ハンドルがクローズされる可能性があります。

  • このオプションは、TimesTen直接接続およびSELECT FOR UPDATE文では無視されます。


次の例は、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用語に準拠)と同じです。

SQLBindParameter関数

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の引数

引数 説明

hstmt

SQLHSTMT

文ハンドル

ipar

SQLUSMALLINT

左から右へ順に、1から始まるパラメータ番号

fParamType

SQLSMALLINT

入力または出力を示す、SQL_PARAM_INPUTSQL_PARAM_OUTPUTまたはSQL_PARAM_INPUT_OUTPUT

fCType

SQLSMALLINT

パラメータのCデータ型

fSqlType

SQLSMALLINT

パラメータのSQLデータ型

cbColDef

SQLULEN

バイナリ・データに対する最大バイト数、数値に対する最大桁数、文字データに対する最大文字数などのパラメータの精度

ibScale

SQLSMALLINT

該当する場合に、小数点より右の最大桁数を参照する、パラメータのスケール

rgbValue

SQLPOINTER

パラメータのデータに使用するバッファへのポインタ

cbValueMax

SQLLEN

rgbValueバッファの最大長(バイト)

pcbValue

SQLLEN*

パラメータの長さに使用するバッファへのポインタ



ノート:

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関数にコールする際に、その関数のfSqlTypecbColDefおよび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とのデータ型のマッピング

ODBCデータ型(fSqlType) SQLまたはPL/SQLのデータ型 TimesTenサポートのノート

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_INTERVAL_DAY

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_DAY_TO_HOUR

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_DAY_TO_MINUTE

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_DAY_TO_SECOND

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_HOUR

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_HOUR_TO_MINUTE

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_HOUR_TO_SECOND

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_MINUTE

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_MINUTE_TO_SECOND

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_MONTH

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_YEAR

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_YEAR_TO_MONTH

該当なし

この表の後のノートを参照してください。

SQL_INTERVAL_SECOND

該当なし

この表の後のノートを参照してください。

SQL_NUMERIC

NUMBER

なし

SQL_REAL

BINARY_FLOAT

なし

SQL_REFCURSOR

REF CURSOR

なし

SQL_ROWID

ROWID

なし

SQL_SMALLINT

PLS_INTEGER

なし

SQL_TIME

TIME

TimesTenTIMEZONEをサポートしていません。TIMEデータ型の値は、時差の調整を行わずに格納されます。アプリケーションでは、あるタイムゾーンを想定し、データベースに値を送信する前に、TIME値をそのタイムゾーンに変換する必要があります。

SQL_TIMESTAMP

TIMESTAMP(s)

TIMEと同じ考慮事項。

SQL_TINYINT

PLS_INTEGER

なし

SQL_VARBINARY

RAW(p)

なし

SQL_VARCHAR

VARCHAR2(p)

なし

SQL_WCHAR

NCHAR(p)

なし

SQL_WVARCHAR

NVARCHAR2(p)

なし



ノート:

  • (p)という表記は、精度がSQLBindParameterの引数cbColDefに従うことを示します。

  • (s)という表記は、スケールがSQLBindParameterの引数ibScaleに従うことを示します。

  • SQL_INTERVAL_xxxx型は、SQL式などの計算値に対してのみサポートされており、データベースの列型としてはサポートされません。

  • ほとんどのアプリケーションでは、文字データのバインドにSQL_CHARではなくSQL_VARCHARを使用してください。SQL_CHARを使用すると、パラメータ・タイプの完全な精度に不要な空白文字が含まれる可能性があります。

  • たとえば、TIMEおよびTIMESTAMPについて、アプリケーションはタイムゾーンを太平洋標準時とみなすことができます。アプリケーションで太平洋夏時間または東部標準時間のTIMEおよびTIMESTAMP値を使用している場合は、TIMEおよびTIMESTAMPを太平洋標準時間に変換する必要があります。


入力パラメータのバインド

TimesTenのPL/SQLに対する入力パラメータでは、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。これは、「パラメータのデータ型の割当ての決定および変換」の記載されている、SQL入力パラメータのサポート方法とは異なります。

また、SQLBindParameterrgbValue引数、cbValueMax引数およびpcbValue引数も、入力パラメータでは次のように使用します。

  • rgbValue: 文を実行する前に、アプリケーションは、アプリケーションに渡されるパラメータ値を格納するバッファを指します。

  • cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す入力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterfCType引数に指定したCデータ型の長さで決定されます。

  • pcbValue: 文を実行する前に、次のいずれかを含むバッファを指します。

    • rgbValueが指す値の実際の長さ

      ノート: 入力パラメータの場合、文字データまたはバイナリ・データにのみ有効です。

    • 空文字で終了する文字列の場合は、SQL_NTS

    • NULL値の場合は、SQL_NULL_DATA

出力パラメータのバインド

TimesTenのPL/SQLからの出力パラメータでは、前述の入力パラメータに記載したとおり、ODBCのSQLBindParameter関数のfSqlType引数、cbColDef引数およびibScale引数を(必要に応じて)使用して、データ型を指定します。

また、SQLBindParameterrgbValue引数、cbValueMax引数およびpcbValue引数も、出力パラメータでは次のように使用します。

  • rgbValue: 文の実行中に、文から返された値を格納するバッファを指します。

  • cbValueMax: 文字データおよびバイナリ・データの場合、rgbValueが指す出力値の最大長(バイト)を示します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterfCType引数に指定した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引数を(必要に応じて)使用して、データ型を指定します。

また、SQLBindParameterrgbValue引数、cbValueMax引数およびpcbValue引数も、入力/出力パラメータでは次のように使用します。

  • rgbValue: 最初に、「入力パラメータのバインド」で説明したとおり、文を実行する前に使用します。次に、前の項「出力パラメータのバインド」で説明したとおり、文の実行中に使用します。入力/出力パラメータの場合、文の実行で出力された値は、アプリケーションによって上書きされないかぎり、直後に続く文の実行に対する入力値になります。また、実行時データの使用中にバインドされる入力/出力値の場合、rgbValueの値は、ODBCのSQLParamData関数で返されるトークンおよび出力値が格納されるバッファへのポインタの両方として機能します。

  • cbValueMax: 文字データおよびバイナリ・データの場合、最初に、「入力パラメータのバインド」で説明したとおりに使用します。次に、前の項「出力パラメータのバインド」で説明したとおりに使用します。他のすべてのデータ型の場合、cbValueMaxは無視され、rgbValueが指す値の長さはSQLBindParameterfCType引数に指定したCデータ型の長さで決定されます。

    ODBCでは、データが切り捨てられる場合でも、すべての文字データが空文字で終了することに注意してください。そのため、出力/出力パラメータに文字データが含まれる場合、cbValueMaxはデータの最長値+空文字を保持できる十分な大きさである必要があります(CHARおよびVARCHARパラメータでは1バイト大きい値、またはNCHARおよびNVARCHARパラメータでは2バイト大きい値)。

  • pcbValue: 最初に、「入力パラメータのバインド」で説明したとおり、文を実行する前に使用します。次に、前の項「出力パラメータのバインド」で説明したとおり、文の実行後に使用します。


重要:

文字データおよびバイナリ・データの場合、cbValueMaxに使用する値を慎重に検討してください。実際のバッファ・サイズよりも小さい値を使用すると、誤った切捨て警告が発生します。実際のバッファ・サイズよりも大きい値を使用すると、ODBCドライバによってrgbValueバッファが上書きされ、メモリーが破損することがあります。

SQL文での重複したパラメータのバインド

TimesTenでは、SQL文に同じパラメータ名が複数ある場合、それぞれ別のパラメータであるとみなされます。(これは、重複したパラメータのバインドに対するOracle Databaseでのサポートと整合性があります。)


ノート:

  • この説明は、PL/SQL経由などではなく、ODBCから直接発行されるSQL文にのみ当てはまります。(PL/SQL文については、次の項「PL/SQLでの重複したパラメータのバインド」を参照してください。)

  • 重複したパラメータをバインドするためのTimesTenモードと、DuplicateBindMode接続属性は非推奨になっています。

  • パラメータでの?の使用はOracle Databaseではサポートされていませんが、TimesTenではサポートされています。


次の問合せについて考えてみます。

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を返します。

PL/SQLでの重複したパラメータのバインド

前の項「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を格納および取得します。アプリケーションでprintfscanfまたはstrtodのような文字データへの変換を必要とするC言語機能を使用した場合、浮動小数点の値はINF、-INFおよびNANとして返されます。これらのキャラクタ文字列を浮動少数点の値に戻すことはできません。

ドライバ・マネージャを使用したSQL_WCHARおよびSQL_WVARCHARの使用

Windowsのドライバ・マネージャを使用するアプリケーションでは、SQL_WCHARまたはSQL_WVARCHARfSqlType値を渡す際に、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の使用

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では、「問合せの準備および実行とカーソルの使用」のカーソルの例と同じ準備、バインド、実行およびフェッチの基本的なステップを使用します。

  1. SQLPrepareを使用して、最初の文ハンドルと関連付けるPL/SQL文を準備します。

  2. SQLBindParameterを使用して、文の各パラメータをバインドします。REF CURSOR出力パラメータをバインドする場合、割り当てられた2番目の文ハンドルをrgbValue(データ・バッファへのポインタ)として使用します。

    pcbValue引数、ibScale引数、cbValueMax引数およびpcbValue引数は、REF CURSORに対しては無視されます。

    SQLBindParameterのこれらの引数および他の引数の詳細は、「SQLBindParameter関数」および「出力パラメータのバインド」を参照してください。

  3. SQLBindColをコールして、結果列をローカル変数の記憶域にバインドします。

  4. SQLExecuteをコールして、文を実行します。

  5. SQLFetchをコールして結果をフェッチします。PL/SQLからアプリケーションにREF CURSORが渡された後、アプリケーションは結果セットの場合と同様に、その結果を記述およびフェッチできます。

  6. 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句)の使用

DML RETURNINGと呼ばれるRETURNING INTO句をINSERTUPDATEまたは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を取得できます。ROWIDはバイナリまたは文字形式で表すことができます。

アプリケーションでは、SQL文のWHERE句などで、一重引用符で囲んだCHAR定数としてリテラルのROWID値を指定できます。

表2-2に示すとおり、ODBC SQLデータ型のSQL_ROWIDはSQLデータ型のROWIDに対応します。

パラメータおよび結果セット列では、ROWIDは、Cデータ型のSQL_C_BINARYSQL_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はサポートされていません。

LOBの作業

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の概要

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 LOBの相違点

次のことに注意してください。

  • 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に関する説明を参照してください。

LOBプログラミング・インタフェース

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ロケータ・インタフェースは、使用できる場合は最適なユーティリティを提供します。

ODBCでの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を使用したバインドは禁止されています。

ODBCでのLOBのピース単位のデータ・インタフェースの使用

ピース単位のインタフェースを使用すると、アプリケーションから、LOBデータの各部分に個別にアクセスできます。簡易データ・インタフェースで実行されるのと同様のアクションで、アプリケーションによってパラメータがバインドされ、結果が定義されますが、プログラム実行時(at exec)にデータが提供されたものか取得されたものであるかが示されます。TimesTenでは、すべてのLOBデータが読み取られるか書き込まれるまで継続されるポーリング・ループを介してピース単位のデータ・インタフェースを実装できます。

ODBCでのピース単位のデータ・インタフェースでは、SQLParamDataSQLPutDataをポーリング・ループで使用してパラメータをバインドし(次の例2-9を参照)、SQLGetDataをポーリング・ループで使用して結果を取得します。BLOB、CLOBおよびNCLOBでサポートされるSQLおよびCのデータ型の詳細は、前述の項「ODBCでのLOBの簡易データ・インタフェースの使用」を参照してください。


ノート:

様々なAPIに対する同様のピース単位のデータ・アクセスは、TimesTenの以前のリリースからサポートされています(varデータ型の場合)。

例2-9 SQLPutDataの使用、ODBCのピース単位のデータ・インタフェース

このプログラムの抜粋では、SQLPutDataSQLParamDataをポーリング・ループで使用して、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... */}
 
  }
...

ODBCでのパススルーLOB

パススルー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のリファレンス・マニュアルを参照してください。


ノート:

  • 自動コミット・モードは、SQLExecuteまたはSQLExecDirectで実行されるトップレベルの文にのみ適用されます。文の内部での実行内容は認識されないため、ネストされた処理の中間自動コミットの機能はありません。

  • 接続時のすべてのオープン・カーソルは、TimesTenでのトランザクションのコミットまたはロールバック時にクローズされます。

  • SQLRowCount関数を使用すると、SQL処理に関する情報を返すことができます。UPDATEINSERTおよびDELETE文の場合、出力の引数によって、影響を受けた行数が返されます。TimesTenの特別な機能については、「キャッシュ・グループの管理」を参照してください。SQLRowCountおよびその引数の全般的な情報については、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データを管理するための主な機能について説明しました。この項では、次にリストするその他の機能について説明します。

プロシージャおよび関数を実行するためのCALLの使用

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組込みプロシージャより優先されますが、このようなネーミングの競合は避けることが最適です。

  • TimesTenでは、CALL文に対するSQL_DEFAULT_PARAMSQLBindParameterの使用はサポートされていません。


SQL文の実行に対するタイムアウトまたはしきい値の設定

TimesTenには、SQL文またはプロシージャ・コールの実行時間を制限する方法として、タイムアウト期間の設定と、しきい値期間の設定の2つがあります。これは、SQLExecuteコール、SQLExecDirectコールまたはSQLFetchコールに適用されます。

タイムアウト期間に達すると、文の実行が停止し、エラーがスローされます。しきい値期間に達すると、サポート・ログに警告が書き込まれますが、実行は続行されます。

この項では、次のトピックを取り扱います:

SQL文に対するタイムアウト期間の設定

タイムアウトまでの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文に対するしきい値期間の設定

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 Cacheで使用する機能

この項では、TimesTen ClassicでのTimesTen Cacheの使用に関する機能について説明します。

TimesTen Cacheの詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』を参照してください。

一般接続属性の詳細は、『Oracle TimesTen In-Memory Databaseリファレンス』のパススルーに関する説明を参照してください。パススルー設定の詳細は、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』のパススルー・レベルの設定に関する説明を参照してください。

ttOptSetFlag組込みプロシージャを使用した一時的なパススルー・レベルの設定

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 GROUPLOAD CACHE GROUPREFRESH 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グローバリゼーション・サポートについて説明します。

TT_NLS_SORT

このオプションには、言語比較で使用する照合順番を指定します。サポートされている言語ソートについては、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』の単一言語ソートに関する説明および多言語ソートに関する説明を参照してください。

このオプションには、文字列値を指定します。デフォルトはBINARYです。

また、同じ機能を持つNLS_SORT一般接続属性の説明も参照してください(『Oracle TimesTen In-Memory Databaseリファレンス』のNLS_SORTに関する説明を参照)。実行時オプションのTT_NLS_SORTは、NLS_SORT接続属性より優先されることに注意してください。

TT_NLS_LENGTH_SEMANTICS

このオプションには、バイト・セマンティクスを使用するか、キャラクタ・セマンティクスを使用するかを指定します。指定できる値は次のとおりです。

  • 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接続属性より優先されることに注意してください。

TT_NLS_NCHAR_CONV_EXCP

このオプションでは、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は警告を発行します。

  • チェックポイントの障害

  • 非推奨の機能の使用

  • 一部のデータの切捨て

  • 接続時のリカバリ処理の実行

  • レプリケーションがRETURN RECEIPTの場合のタイムアウト

アプリケーション開発者は、アプリケーションの問題を指摘することができるように、警告をチェックするコードを含める必要があります。

異常終了

プロセス障害などの場合、エラーは返されませんが、失敗したプロセスのトランザクションが自動的にロールバックされます。

致命的なエラーからのリカバリ

致命的なエラーが発生した場合、TimesTenは、次の完全なクリーンアップおよびリカバリ・プロシージャを実行します。

  • データベースに対するすべての接続を無効化します。サーバーでのメモリー不足状態を回避するには、無効になったデータベースからアプリケーションを切断する必要があります。古いTimesTenインスタンスの共有メモリーは、エラー発生時にアクティブだったすべての接続が切断されるまで解放されません。古いTimesTenインスタンスに依然として接続されている非アクティブなアプリケーションは、手動で終了する必要があることがあります。

  • その後の最初の初期接続時に、チェックポイント・ファイルおよびトランザクション・ログ・ファイルからデータベースをリカバリします。

  • リカバリされたデータベースには、永続コミットされたすべてのトランザクションの状態が反映されます。また、非永続的にコミットされた一部のトランザクションも反映されます。

  • コミットされていないトランザクションまたはロールバックされたトランザクションは反映されません。

一時的なエラー後の再試行(ODBC)

TimesTenはほとんどの一時的なエラーを自動的に解決します(これは、TimesTen Scaleoutで特に重要です)が、アプリケーションが次のSQLSTATE値を検出した場合には現在のトランザクションを再試行することをお薦めします。

  • TT005: Transient transaction failure due to unavailability of resource.Roll back the transaction and try it again.


ノート:

  • 再試行が適しているかどうかを判断する前に、エラー・スタック全体を検索して、これらのSQLの状態を返すエラーがないか確認してください。

  • フェイルオーバーの遅延および再試行の設定例2-13では、一時的なエラーの場合の再試行方法も示しています。


ODBC 3.5では、SQLSTATESQLGetDiagRec関数によって返されるか、SQLGetDiagField関数のSQL_DIAG_SQLSTATEフィールドに示されます。ODBC 2.5では、SQLSTATESQLError関数によって返されます。このSQLSTATEは、次のいずれかの関数で発生する可能性があります。特に指定しないかぎり、これらの関数はODBC 2.5またはODBC 3.5に適用されます。

  • カタログ関数(SQLTablesSQLColumnsなど)

  • 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オペレーション・ガイド』の自動クライアント・フェイルオーバーの使用に関する項を参照してください。


ノート:

  • 自動クライアント・フェイルオーバーは、クライアント/サーバー接続にのみ適用されます。ここで説明する機能は、直接接続には使用できません。

  • Oracle Clusterwareを使用する場合、自動クライアント・フェイルオーバーはOracle Clusterwareに対する補完機能ですが、この2つの機能は互いに依存しません。Oracle Clusterwareの詳細は、『Oracle TimesTen In-Memory Database開発者および管理者ガイド』のOracle Clusterwareを使用してのアクティブ・スタンバイ・ペアの管理に関する項を参照してください。


自動クライアント・フェイルオーバーの機能

クライアントが接続しているデータベースまたはデータベース要素に障害が発生すると、代替のデータベースまたはデータベース要素へのフェイルオーバーが発生します。フェイルオーバーが発生した場合、次の点に注意してください。

  • クライアントの接続は新しくなりますが、使用するODBC接続ハンドルは同じままです。ただし、ハンドル自体を除き、元の接続の状態は保持されません。アプリケーションは、新しいODBC文ハンドルおよび記述子ハンドルをオープンする必要があります。

  • フェイルオーバーのコールバック関数を登録すると(フェイルオーバーのコールバック関数を参照)、フェイルオーバー・リスナー・スレッドがクライアント・プロセス内に作成され、フェイルオーバー・イベントをリスニングしてコールバック関数を起動します。

元の接続からのすべてのクライアント文ハンドルは、無効とマークされます。これらの文ハンドルに対してAPIコールを実行すると、通常は次のように、tt_errCode.hで定義されている個別のフェイルオーバー・エラー・コードがSQL_ERRORとともに返されます。

  • ネイティブ・エラー30105 (SQL状態08006)

  • ネイティブ・エラー47137

これに対する例外は、SQLErrorSQLFreeStmtSQLGetDiagRecSQLGetDiagFieldの各コールに対するもので(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_Server2TTC_Server_DSN2TTC_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が接続に対して複数回設定されている場合、最後の設定が優先されます。


注意:

  • コールバック関数はアプリケーションのメイン・スレッドに対して非同期で実行されるため、通常、アプリケーションによってポーリングされるフラグの設定などの簡単なタスクのみを実行させる必要があります。ただし、アプリケーションがマルチスレッド用に設計されている場合は、このような制限はありません。この場合、たとえば関数でODBCコールを実行することもできますが、foEventTT_FO_ENDを受け取っている場合にのみ安全に実行できます。

  • foCtx設定が指すデータを管理するかどうかはアプリケーション次第です。


例2-12 フェイルオーバーのコールバック関数および登録

この例では次の機能を示しています。

  • グローバルに定義されるユーザー構造体タイプFOINFO、およびタイプFOINFOの構造体変数foStatus

  • フェイルオーバーが発生すると常にfoStatus構造体を更新するコールバック関数FailoverCallback()

  • 登録関数RegisterCallback()。次の処理を行います。

    • タイプttFailoverCallback_tの構造体failoverCallbackを宣言します。

    • foStatus値を初期化します。

    • 接続ハンドル、foStatusへのポインタおよびコールバック関数(FailoverCallback)で構成されるfailoverCallbackデータ値を設定します。

    • SQLSetConnectOptionコールを使用してコールバック関数を登録し、TT_REGISTER_FAILOVER_CALLBACKfailoverCallbackへのポインタとして設定します。

/* 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

フェイルオーバー時のアプリケーション・アクション

この項の内容は次のとおりです。

フェイルオーバーのためのアプリケーション・ステップ

アプリケーションでの操作に応えて「自動クライアント・フェイルオーバーの機能」に示されたいずれかのエラー状態になった場合は、アプリケーション・フェイルオーバーが進行中です。次のリカバリ・アクションを実行します。

  1. 接続に対してロールバックを発行します。これを行うまで、接続に対してそれ以上の処理は実行できません。

  2. 前の接続からすべてのオブジェクトをクリーンアップします。前の接続に関連付けられた状態やオブジェクトはいずれも保持されませんが、関連するAPIコールによる適切なクリーン・アップをお薦めします。

  3. TTC_NoReconnectOnFailover=0 (デフォルト)とすると、次の項「フェイルオーバー遅延および再試行設定」で説明しているように、短い間スリープします。TTC_NoReconnectOnFailover=1の場合は、かわりにアプリケーションを代替のデータベースまたはデータベース要素に手動で再接続する必要があります。

  4. 接続に関連するすべてのオブジェクトを再作成および再準備します。

  5. 進行中のトランザクションを最初から再起動します。

フェイルオーバー遅延および再試行設定

自動クライアント・フェイルオーバー中に別のデータベースまたはデータベース要素に再接続する場合、時間がかかることがあります。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のクライアント・ルーティングAPI

パフォーマンスを高めるために、TimesTen Scaleoutではクライアント・アプリケーションがハッシュ分散キーのキー値に基づいて要素に接続をルーティングできます。キー値を指定すると、TimesTen Scaleoutが要素ID (またはレプリカ・セットID)の配列を返します。その値は、データベースによって割り当てられたものです。これにより、指定したキー値に一致する行を格納している要素にクライアント・アプリケーションが接続できるため、行を格納している要素とアプリケーションに接続された要素との間で不要な通信を回避できます。


ノート:

この機能はドライバ・マネージャではサポートされていません。

この項の内容は次のとおりです。

グリッド・マップとグリッド分散の作成

TimesTen Scaleoutでは、timesten.hファイルにクライアント・ルーティング用の新しいオブジェクトが2つ含まれています。

  • TTGRIDMAP: グリッド・マップは、グリッドのトポロジをマップする参照表です。グリッド・マップを作成するには、有効なODBC接続を指定したttGridMapCreate関数をコールします。この関数は、TTGRIDMAPオブジェクトへのハンドルを返します。


    ノート:

    • TTGRIDMAPオブジェクトは、HDBC接続に強く関連付けられていません。一方のオブジェクトを解放しても、他方のオブジェクトは解放されません。

    • グリッド・マップは、多くのグリッド分散およびアプリケーション・スレッド間で共有できます。各データベースのアプリケーション・プロセスごとにグリッド・マップが1つのみ必要です。


    グリッド・マップを解放するには、ttGridMapFree関数を使用します。

  • TTGRIDDIST: グリッド分散は、1つ以上の表の分散キー列を表す、順序付きの型と値のセットです。複数の列で構成される分散キーの場合、型と値の順序は表の分散キー列のものと同じである必要があります。

    グリッド分散を作成するには、表の分散キー列のCデータ型、SQL型、長さ、スケールおよび精度を指定したttGridDistCreate関数をコールします。この関数は、TTGRIDDISTオブジェクトへのハンドルを返します。表2-3に、ttGridDistCreate関数の引数の概要を示します。


    ノート:

    • TTGRIDDISTオブジェクトは、特定の表に関連付けられていません。分散キー列に同じ型および値を使用する表に対して、同じTTGRIDDISTオブジェクトを使用できます。

    • グリッド分散はスレッド間で共有できません。ただし、同じグリッド・マップを使用して異なるスレッドに複数のグリッド分散を作成できます。


    表2-3 ttGridDistCreateの引数

    引数 説明

    hdbc

    SQLHDBC

    接続ハンドル

    map

    TTGRIDMAP

    グリッド・マップ・ハンドル

    cTypes[]

    SQLSMALLINT

    分散キー列と同じ順序で並んだCバインド・タイプの配列

    sqlTypes[]

    SQLSMALLINT

    分散キー列と同じ順序で並んだSQLバインド・タイプの配列

    precisions[]

    SQLULEN

    分散キー列と同じ順序で並んだ精度値の配列

    scales[]

    SQLSMALLINT

    分散キー列と同じ順序で並んだスケール値の配列

    maxSizes[]

    SQLLEN

    分散キー列と同じ順序で並んだ最大列サイズ値の配列

    nCols

    SQLUSMALLINT

    分散キーの列数

    *dist

    TTGRIDDIST

    グリッド分散ハンドル(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関数の引数の概要を示します。

表2-4 ttGridDistValueSetの引数

引数 説明

hdbc

SQLHDBC

接続ハンドル

dist

TTGRIDDIST

グリッド分散ハンドル

position

SQLSMALLINT

分散キー内の列の位置

value

SQLPOINTER

キー値ポインタ

valueLen

SQLLEN

キー値の長さ


例2-15 分散キー値の設定

この例では、まずttGridDistClear関数をコールして、分散キー列に対して事前に定義されたキー値をクリアします。次に、分散キーの列ごとにttGridDistValueSet関数をコールし、各列のキー値を設定します。

ttGridDistClear(hdbc, dist);

ttGridDistValueSet(hdbc, dist, 1, empId, sizeof(empId));
ttGridDistValueSet(hdbc, dist, 2, "SALES", SQL_NTS);

一連のキー値に与えられた要素の位置の取得

分散キー値を設定したら、要素IDまたはレプリカ・セットIDによってキー値の位置をコールできます。

要素IDの取得

ttGridDistElementGet関数をコールして、指定されたキー値の位置を表す対応する要素IDを取得します。この関数は、要素IDの配列を返します。アプリケーションは、戻り配列を割り当てます。配列の長さは、グリッドのK-safetyの値に基づいています。たとえば、K-safetyが2に設定されているグリッドでは、配列に少なくとも2つの要素が必要です。表2-5に、ttGridDistElementGet関数の引数の概要を示します。

表2-5 ttGridDistElementGetの引数

引数 説明

hdbc

SQLHDBC

接続ハンドル

dist

TTGRIDDIST

グリッド分散ハンドル

elemIds[]

SQLSMALLINT

キー値が割り当てられる要素IDの配列(IN/OUT)

elemIdSize

SQLSMALLINT

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.

レプリカ・セットIDの取得

ttGridDistReplicaGet関数をコールして、指定されたキー値の位置を表す対応するレプリカ・セットIDを取得します。表2-6に、ttGridDistReplicaGet関数の引数の概要を示します。

表2-6 ttGridDistReplicaGetの引数

引数 説明

hdbc

SQLHDBC

接続ハンドル

dist

TTGRIDDIST

グリッド分散ハンドル

*replicaSetId

SQLSMALLINT

キー値が割り当てられるレプリカ・セット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データ型

SQL_C_TINYINT

SQL_TINYINT

TT_TINYINT

SQL_C_SMALLINT

SQL_SMALLINT

TT_SMALLINT

SQL_C_LONG

SQL_INTEGER

TT_INTEGER

SQL_C_BIGINT

SQL_BIGINT

TT_BIGINT

SQL_C_CHAR

SQL_CHAR

CHAR

SQL_C_CHAR

SQL_VARCHAR

VARCHARVARCHAR2

SQL_C_WCHAR

SQL_WCHAR

NCHAR

SQL_C_WCHAR

SQL_WVARCHAR

NVARCHAR

SQL_C_SQLT_NUM

SQL_DOUBLE

NUMBER

SQL_C_SQLT_NUM

SQL_DECIMAL

NUMBER(p,s)

SQL_C_SQLT_VNU

SQL_DOUBLE

NUMBER

SQL_C_SQLT_VNU

SQL_DECIMAL

NUMBER(p,s)


TTGRIDDISTオブジェクトでは、符号付きと符号なしのすべてのデータ型バリアントがサポートされています。たとえば、SQL_C_SLONGSQL_C_ULONGの両方がサポートされています。

ttGridDistValueSet関数のvalueLenパラメータにSQL_NULL_DATAを指定して、NULL値を設定できます。NULL値は、常に同じレプリカ・セットまたは要素IDにマップされます。

制限事項

クライアント・ルーティングには次の制限があります。

  • 暗黙的な接続または文の管理はありません。

  • 日付、時間またはタイムスタンプのデータ型はサポートされていません。

  • 明示的な型変換はサポートされていません。アプリケーションでは、キー値を正規のバイト形式で指定する必要があります。

  • 文字セット変換はサポートされていません。接続文字セットは無視されます。

  • グリッドのトポロジを変更するには、アプリケーションがグリッド・マップを解放して再作成する必要があります。

障害モード

クライアント・ルーティングAPIは、次のシナリオでエラーを返すことがあります。

  • 表の分散キー列を説明するデータ型および値が正しくありません。この場合、APIは要素IDの配列を計算しますが、目的のキー値の実際の位置に対応していない可能性があります。

  • 型コードを認識できません。認識されない型コードでttGridDistCreate関数をコールすると、エラーが返されます。

  • グリッド分散に十分な値が設定されていませんttGridDistValueSet関数を使用して分散キーに十分な値を指定しない場合、ttGridDistElementGet関数またはttGridDistReplicaGet関数はエラーを返します。

  • 要素ID配列のサイズが無効です。K-safetyの値のサイズ以上の配列を指定しない場合、ttGridDistElementGet関数はエラーを返します。