TimesTenのOCIでの機能

この項では、TimesTenのOCIを使用する開発者向けに、次の内容について説明します。

TimesTenの遅延準備

OCIでは、準備コールはクライアントで実行される軽量処理であることが期待されています。

TimesTenでこの期待との一貫性を実現し、クライアントとサーバー間の不要なラウンドトリップを回避するため、TimesTenクライアント・ライブラリに実装されたSQLPrepareでは、遅延準備と呼ばれる処理が実行されますが、この処理では、リクエストは要求されるまでサーバーに送信されません。「TimesTenの遅延準備」を参照してください。

TimesTenのOCIでのパラメータ・バインド機能

OCIアプリケーションからSQLまたはPL/SQLへのパラメータのバインドに関連する機能を示します。

TimesTenのOCIでの重複したパラメータのバインド

TimesTenのOCIでは、ODBCにおいてと同様に、同じパラメータ名が複数回出現する場合はそれらが個別のパラメータとみなされます。ただし、OCIでは複数回出現した同じパラメータをOCIBindByPos()への単一のコールでバインドできます。

「SQL文での重複したパラメータのバインド」を参照してください。

次の問合せについて考えてみましょう:

SELECT * FROM employees
  WHERE employee_id < :a AND manager_id > :a AND salary < :b;

パラメータaが2回出現した場合、それぞれが個別のパラメータとみなされますが、OCIBindByPos()への単一のコールで両方のパラメータをバインドできます。

OCIBindByPos(..., 1, ...); /* both occurrences of :a */
OCIBindByPos(..., 3, ...); /* occurrence of :b */

あるいは、2回出現したaを個別にバインドできます。

OCIBindByPos(..., 1, ...); /* first occurrence of :a */
OCIBindByPos(..., 2, ...); /* second occurrence of :a */
OCIBindByPos(..., 3, ...); /* occurrence of :b */

両方の場合で、パラメータbは位置3に存在するとみなされていることに注意してください。

ノート:

また、OCIでは、OCIBindByName()を使用して位置ではなく、名前でバインドすることもできます。この場合、同じ名前のパラメータに同じ値が使用されます。

TimesTenのOCIでの連想配列のバインド

連想配列(以前の索引付き表またはPL/SQL表)は、TimesTen PL/SQLでは、(たとえばOCIアプリケーションからの)INOUTまたはIN OUTバインド・パラメータとしてサポートされます。これにより、アプリケーションとデータベース間で効率的に配列データを渡すことができます。

連想配列は、キー/値のペアのセットです。TimesTenでは、連想配列バインド(PL/SQL内でのみの連想配列の使用を除く)、キーまたは索引は、整数(BINARY_INTEGERまたはPLS_INTEGER)である必要があります。値は、同じデータ型を持つ単純なスカラー値である必要があります。たとえば、部門番号で索引付けされた部門マネージャの配列になります。索引は、作成された順序ではなく、ソートされた順序で格納されます。

次の例のように、PL/SQLから連想配列の型を宣言し、次に連想配列を宣言できます(INDEX BYにノート)。

declare
   TYPE VARCHARARRTYP IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
   x VARCHARARRTYP;
   ...

Pro*C/C++の場合は、「TimesTenのPro*C/C++での連想配列バインド」を参照してください。

関連情報は、『Oracle TimesTen In-Memory Database PL/SQL開発者ガイド』アプリケーションからの連想配列の使用を参照してください。

ノート:

TimesTenでの次の制限に注意してください。

  • 連想配列のバインドでは、LOB、REF CURSOR、TIMESTAMPROWIDはサポートされていません。

  • パススルー文では連想配列バインドは許可されていません。

  • TimesTen OCIでは、配列の一般的なバルク・バインドはサポートされていません。VARRAYおよびネストした表は、バインド・パラメータとしてはサポートされていません。

TimesTenでは、OCIBindByName()およびOCIBindByPos()関数のmaxarr_lenおよび*curelepパラメータをサポートすることによって、OCIでの連想配列バインドがサポートされています。これらのパラメータは、バインドが連想配列であることを示すために使用されます。

これらの関数の完全なコール・シーケンスは次のとおりです。

sword OCIBindByName ( OCIStmt *stmtp,
                      OCIBind **bindpp,
                      OCIError *errhp,
                      const OraText *placeholder,
                      sb4 placeh_len,
                      void *valuep,
                      sb4 value_sz,
                      ub2 dty,
                      void *indp,
                      ub2 *alenp,
                      ub2 *rcodep,
                      ub4 maxarr_len,
                      ub4 *curelep,
                      ub4 mode );

sword OCIBindByPos ( OCIStmt *stmtp,
                     OCIBind **bindpp,
                     OCIError *errhp,
                     ub4 position,
                     void *valuep,
                     sb4 value_sz,
                     ub2 dty,
                     void *indp,
                     ub2 *alenp,
                     ub2 *rcodep,
                     ub4 maxarr_len,
                     ub4 *curelep,
                     ub4 mode );

maxarr_lenおよび*curelepパラメータで連想配列をバインドする場合、次のように使用されます。(連想配列をバインドしない場合は、0(ゼロ)に設定してください。)

  • maxarr_len: これは、配列の最大長を示す入力パラメータです。これは、連想配列が対応できる要素の最大数です。

  • *curelep: これは、現在の配列の長さを示す入力/出力パラメータです。文の実行前後の連想配列での実際の要素の数へのポインタです。

『Oracle Call Interfaceプログラマーズ・ガイド』OCIBindByName()およびOCIBindByPos()を参照してください。

次の例では、OCIアプリケーションは、PL/SQLプロシージャで、整数配列および文字配列を、対応するOUT連想配列にバインドします。

次のSQL設定を前提とします。

DROP TABLE FOO;
 
CREATE TABLE FOO (CNUM INTEGER,
                  CVC2 VARCHAR2(20));
 
INSERT INTO FOO VALUES ( null,
     'VARCHAR  1');
INSERT INTO FOO VALUES (-102,
     null);
INSERT INTO FOO VALUES ( 103,
     'VARCHAR  3');
INSERT INTO FOO VALUES (-104,
     'VARCHAR  4');
INSERT INTO FOO VALUES ( 105,
     'VARCHAR  5');
INSERT INTO FOO VALUES ( 106,
     'VARCHAR  6');
INSERT INTO FOO VALUES ( 107,
     'VARCHAR  7');
INSERT INTO FOO VALUES ( 108,
     'VARCHAR  8');
 
COMMIT;

このPL/SQLパッケージ定義を前提とします。これには、プロシージャP1の定義内の出力連想配列c1およびc2のそれぞれに使用される、INTEGER連想配列タイプNUMARRTYPおよびVARCHAR2連想配列タイプVCHARRTYPがあります。

CREATE OR REPLACE PACKAGE PKG1 AS
  TYPE NUMARRTYP IS TABLE OF INTEGER INDEX BY BINARY_INTEGER;
  TYPE VCHARRTYP IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER;
 
  PROCEDURE P1(c1 OUT NUMARRTYP,c2 OUT VCHARRTYP);
 
END PKG1;
/
 
CREATE OR REPLACE PACKAGE BODY PKG1 AS
 
  CURSOR CUR1 IS SELECT CNUM, CVC2 FROM FOO;
 
  PROCEDURE P1(c1 OUT NUMARRTYP,c2 OUT VCHARRTYP) IS
  BEGIN
    IF NOT CUR1%ISOPEN  THEN
      OPEN CUR1;
    END IF;
    FOR i IN 1..8 LOOP
      FETCH CUR1 INTO c1(i), c2(i);
      IF CUR1%NOTFOUND THEN
        CLOSE CUR1;
        EXIT;
      END IF;
    END LOOP;
  END P1;
 
END PKG1;

次のOCIプログラムは、PKG1.P1をコールし、配列をP1出力連想配列にバインドし、バインドした連想配列の内容を出力します。特にOCIBindByName()関数は、バインドを行うためにコールすることに注意してください。

static OCIEnv *envhp;
static OCIServer *srvhp;
static OCISvcCtx *svchp;
static OCIError *errhp;
static OCISession *authp;
static OCIStmt *stmthp;
static OCIBind *bndhp[MAXCOLS];
static OCIBind *dfnhp[MAXCOLS];
 
STATICF VOID outbnd_1()
{
  int   i;
  int   num[MAXROWS];
  char*   vch[MAXROWS][20];
 
  unsigned int    numcnt   = 5;
  unsigned int    vchcnt = 5;
 
  unsigned short    alen_num[MAXROWS];
  unsigned short    alen_vch[MAXROWS];
  unsigned short    rc_num[MAXROWS];
  unsigned short    rc_vch[MAXROWS];
 
  short    indp_num[MAXROWS];
  short    indp_vch[MAXROWS];
 
/* Assume the process is connected and srvhp, svchp, errhp, authp, and stmthp 
   are all allocated/initialized/etc. */
 
  char  *sqlstmt = (char *)"BEGIN PKG1.P1(:c1, :c2); END; ";
 
  for (i = 0; i < MAXROWS; i++)
  {
    alen_num[i] = 0;
    alen_vch[i] = 0;
    rc_num[i] = 0;
    rc_vch[i] = 0;
    indp_num[i] = 0;
    indp_vch[i] = 0;
  }
 
  DISCARD printf("Running outbnd_1.\n");
  DISCARD printf("\n----> %s\n", sqlstmt);
  checkerr(errhp, OCIStmtPrepare(stmthp, errhp, sqlstmt, 
           (unsigned int)strlen((char *)sqlstmt),
           (unsigned int) OCI_NTV_SYNTAX, (unsigned int) OCI_DEFAULT));
 
  bndhp[0] = 0;
  bndhp[1] = 0;
 
  checkerr(errhp, OCIBindByName(stmthp, &bndhp[0], errhp,
                  (char *) ":c1", (sb4) strlen((char *) ":c1"),
                  (dvoid *) &num[0], (sb4) sizeof(num[0]), SQLT_INT,
                  (dvoid *) &indp_num[0], (unsigned short *) &alen_num[0],
                  (unsigned short *) &rc_num[0],
                  (unsigned int) MAXROWS, (unsigned int *) &numcnt, 
                  (unsigned int) OCI_DEFAULT));
 
  checkerr(errhp, OCIBindByName(stmthp, &bndhp[1], errhp,
                  (char *) ":c2", (sb4) strlen((char *) ":c2"),
                  (dvoid *) vch[0], (sb4) sizeof(vch[0]), SQLT_CHR,
                  (dvoid *) &indp_vch[0], (unsigned short *) &alen_vch[0],
                  (unsigned short *) &rc_vch[0],
                  (unsigned int) MAXROWS, (unsigned int *) &vchcnt, 
                  (unsigned int) OCI_DEFAULT));
 
  DISCARD printf("\nTo execute the PL/SQL statement.\n");
 
  checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (unsigned int) 1, 
                  (unsigned int) 0, (const OCISnapshot*) 0,
                  (OCISnapshot*) 0,  (unsigned int) OCI_DEFAULT));
 
  DISCARD printf("\nHere are the results:\n\n");
 
  DISCARD printf("Column 1, INTEGER: \n");
  for (i = 0; i < numcnt; i++)
  {
    if (indp_num[i] == -1)
      DISCARD printf("-NULL- ");
    else
      DISCARD printf("%5d, ", num[i]);
    DISCARD printf("ind = %d,  len = %d, rc = %d\n",
                             indp_num[i], alen_num[i], rc_num[i]);
  }
 
  DISCARD printf("\nColumn 2, VARCHAR2(20): \n");
  for (i = 0; i < vchcnt; i++)
  {
    if (indp_vch[i] == -1)
      DISCARD printf("-NULL-      ");
    else
      DISCARD printf("%.*s, ", alen_vch[i], vch[i]);
    DISCARD printf("ind = %d,  len = %d, rc = %d\n",
                             indp_vch[i], alen_vch[i], rc_vch[i]);
  }
 
  DISCARD printf("\nDone\n");
  return;
}

ノート:

alen_*配列は長さの配列、rc_*配列はリターン・コードの配列、indp_*配列はインジケータの配列です。

TimesTenのOCIでのキャッシュ操作の使用

この項では、TimesTen Classicでのキャッシュ操作の使用に関連するTimesTen OCI機能について説明します。

OCIでのキャッシュ用Oracle Databaseパスワードの指定

キャッシュを使用するには、キャッシュされたOracle Database表からの選択およびその更新が可能なOracle Databaseキャッシュ管理ユーザーと同じ名前のキャッシュ管理ユーザーが、TimesTenデータベースに存在している必要があります。このOracle Databaseキャッシュ管理ユーザーは、かわりにスキーマ・ユーザーになることもできます。TimesTenキャッシュ管理ユーザーのパスワードは、Oracle Databaseキャッシュ管理ユーザーとは別のパスワードにできます。『Oracle TimesTen In-Memory Databaseキャッシュ・ガイド』キャッシュ・インフラストラクチャの設定を参照してください。

OCIをキャッシュ操作で使用する場合、TimesTenでは、ログイン時にOCILogon()またはOCILogon2()コールでパスワード・フィールドにOracle Databaseキャッシュ管理ユーザーのパスワードを追加することで、OCIを介してそのパスワードを渡すことができます。次の例のように、接続文字列で属性OraclePWDを使用します。

text *cacheadmin = (text *)"cacheadmin1";
text *cachepwds = (text *)"ttpassword;OraclePWD=oraclepassword";
text *ttdbname = (text *)"tt_tnsname";
....
OCILogon2(envhp, errhp, &svchp,
       (text *)cacheadmin, (ub4)strlen(cacheadmin),
       (text *)cachepwds, (ub4)strlen(cachepwds),
       (text *)ttdbname, (ub4)strlen(ttdbname), OCI_DEFAULT)); 

Oracle Databaseキャッシュ管理ユーザーのパスワードがTimesTenキャッシュ管理ユーザーのパスワードと同じである場合でも、必ずOraclePWDを指定する必要があります。

この例での指定内容は次のとおりです。

  • TimesTenキャッシュ管理ユーザーの名前(キャッシュされたOracle Database表にアクセスできるOracle Databaseユーザーの名前でもある)は、cacheadmin1です。

  • TimesTenキャッシュ管理ユーザーのパスワードは、ttpasswordです。

  • Oracle Databaseキャッシュ管理ユーザーのパスワードは、oraclepasswordです。

  • 接続するTimesTenデータベースのTNS名は、tt_tnsnameです。

Oracle Databaseは、sys.odbc.iniファイルまたはユーザーodbc.iniファイルのTimesTen OracleNetServiceName一般接続属性を使用して指定します。

あるいは、TNS名を使用するかわりに、簡易接続構文またはTWO_TASK環境変数かLOCAL環境変数を使用することもできます(前述の項を参照)。

アクションの影響を受けるキャッシュ・インスタンスの数の確認

TimesTen OCIでは、FLUSH CACHE GROUPLOAD CACHE GROUPREFRESH CACHE GROUPまたはUNLOAD CACHE GROUP文の実行の後、OCI_ATTR_ROW_COUNT引数を指定したOCI関数OCIAttrGet()を使用すると、フラッシュ、ロード、リフレッシュまたはアンロードされたキャッシュ・インスタンスの数が返されます。

『Oracle TimesTen In-Memory Databaseキャッシュ・ガイド』操作の影響を受けるキャッシュ・インスタンスの数の確認を参照してください。

TimesTen OCIでのLOB

TimesTen ClassicではLOB (ラージ・オブジェクト)がサポートされています。これには、CLOB(Character LOB)、NCLOB(National Character LOB)およびBLOB(Binary LOB)が含まれます。この項では、LOBロケータ、一時LOBおよびOCI LOBのAPIおよび機能に重点を置いています。

OCIに関して、次の内容について説明します。

追加情報は、次の項を参照してください。

  • 「ラージ・オブジェクト(LOB)」。この項はODBC向けですが、LOBの一般的な概要、TimesTenとOracle DatabaseとでのLOBの違い、およびLOBのプログラミング・インタフェースについても説明しています。

  • TimesTenでのLOBに関する追加情報は、『Oracle TimesTen In-Memory Database SQLリファレンス』LOBのデータ型を参照してください。

  • LOB、およびOCIでのその使用方法の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』LOBおよびBFILEの操作を参照してください。TimesTenではBFILE、SecureFile、LOBに対する配列読取りおよび書込み、またはLOBのコールバック関数はサポートされていないことを覚えておいてください。

ノート:

LOBのピース単位のデータ・インタフェースは、OCIアプリケーションには適用されません。(ただし、LOBロケータ・インタフェースの機能を介して、LOBデータを分割して操作することはできます。)

OCIでのLOBロケータ

OCIでは、LOBロケータ・インタフェースが提供されており、インタフェースでは、LOBロケータとLOB値でLOBが構成されます。ロケータは値に対するハンドルとして機能します。アプリケーションがデータベースからLOBを選択すると、ロケータを受信ます。LOBの更新はロケータを介して行われます。 LOBをパラメータとして渡す場合、実際の値ではなくロケータが渡されます。

「OCIでのLOBロケータ・インタフェースの使用について」を参照してください。(OCIでは、ロケータが関与しない簡易データ・インタフェースも使用できます。「OCIにおけるLOB簡易データ・インタフェースの使用」を参照してください。)

LOBを更新するには、トランザクションでLOBが含まれる行に対する排他ロックが必要です。この操作を行うには、SELECT ... FOR UPDATE文でLOBを選択します。これを行うと、ロケータが書込み可能になります。単純なSELECT文では、ロケータは読取り専用です。読取り専用ロケータおよび書込み可能なロケータの動作は、次のとおりです。

  • 読取り専用ロケータは読取り一貫性で、これは、有効期間を通して選択された時点のLOBの内容のみが確認されるという意味です。これには、LOBが選択される前、同じトランザクションでLOBに対して実行したすべてのコミットされていない更新が含まれることに注意してください。

  • 書込み可能なロケータは、ロケータを介して書込みが行われるたびに、データベースからの最新データで更新されます。そのため、各書込みは最新のLOBのデータに対して行われ、他のロケータに対して行われた更新も含まれます。

次の例は、同じLOBに対する2つの書込み可能なロケータの動作を示します。

  1. LOB列に「XY」が含まれています。

  2. ロケータL1を選択して更新します。

  3. ロケータL2を選択して更新します。

  4. L1を介してオフセット1に「Z」を書き込みます。

  5. ロケータL1を介して読み取ります。「ZY」が戻されます。

  6. ロケータL2を介して読み取ります。L2は書込みに使用されるまで読取り一貫性を維持するため、「XY」が戻されます。

  7. L2を介してオフセット2に「W」を書き込みます。

  8. ロケータL2を介して読み取ります。「ZW」が戻されます。前述のステップの書込みの前に、ロケータが最新データ(「ZY」)で更新されました。

OCIでの一時LOB

一時LOBはアプリケーション内にのみ存在し、TimesTen OCIでは、存続期間は作成されたトランザクションを超えません(TimesTenでのすべてのLOBロケータの存続期間と同様)。一時LOBをLOBデータのスクラッチ領域と考えることができます。

OCIアプリケーションは、アプリケーション内で使用するために、適切なAPIを介して、一時LOBをインスタンス化できます。(「OCIでのLOBロケータ・インタフェースの使用について」を参照してください。)一時LOBは、TimesTenによって暗黙的に作成されることもあります。たとえば、SELECT文で追加の文字列が連結されたLOBを選択した場合に、連結されたデータを含むためにTimesTenによって一時LOBが作成され、OCIアプリケーションは、その一時LOBのロケータを受信します。

一時LOBは、TimesTenの一時データ領域に格納されます。

OCIにおけるTimesTenのLOBとOracle DatabaseのLOBの相違点

TimesTenとOracle DatabaseでのLOB実装の主な違いは、TimesTenではLOBロケータはトランザクションの終了後は有効ではないことです。すべてのLOBロケータは、(明示的、暗黙的のどちらの場合も)コミットまたはロールバックの後は無効化されます。これにはDDL文の後も含まれます。

「TimesTenにおけるLOBとOracle DatabaseにおけるLOBの相違点」も参照してください。

OCIにおけるLOB簡易データ・インタフェースの使用

簡易データ・インタフェースでは、その他のスカラー・データ型と同様に、バインドおよび定義することによって、アプリケーションによるLOBデータへのアクセスが可能です。アプリケーションでは、対応する変数タイプと互換性のあるLOB型を使用できます。

OCIStmtPrepare()を使用して、文を準備します。パラメータのバインドには、OCIBindByName()またはOCIBindByPos()を使用します。結果列の定義には、OCIDefineByPos()を使用します。

たとえば、OCIアプリケーションは、データ型がSQLT_CHROCIBindByName()をコールすることによって、CLOBパラメータをバインドできます。OCIStmtExecute()を使用して文を実行します。NCLOBパラメータの場合は、データ型SQLT_CHRを使用し、OCIのcsform属性(OCI_ATTR_CHARSET_FORM)をSQLCS_NCHARに設定します。BLOBパラメータの場合は、データ型SQLT_BINを使用できます。

OCIを介した簡易データ・インタフェースの使用例を次に示します。

ノート:

簡易データ・インタフェース(OCIBindByName()OCIBindByPos()またはOCIDefineByPos())のバインドのサイズは64KBに制限されます。

以降の例では、ここに表示されている表および変数を前提にします。

person(ssn number, resume clob)
 
OCIEnv *envhp;
OCIServer *srvhp;
OCISvcCtx *svchp;
OCIError *errhp;
OCISession *authp;
OCIStmt *stmthp;
 
/* Bind Handles */
OCIBind *bndp1 = (OCIBind *) NULL; 
OCIBind *bndp2 = (OCIBind *) NULL;
 
/* Define Handles */
OCIDefine *defnp1 = (OCIDefine *) NULL;
OCIDefine *defnp2 = (OCIDefine *) NULL;
 
#define DATA_SIZE 50
#define PIECE_SIZE 10
#define NPIECE (DATA_SIZE/PIECE_SIZE)
 
char col2[DATA_SIZE];
char col2Res[DATA_SIZE];
ub2 col2len = DATA_SIZE;
sb4 ssn = 123456;
...

text *ins_stmt = (text *)"INSERT INTO PERSON VALUES (:1, :2)";
text *sel_stmt = (text *)"SELECT * FROM PERSON_1 ORDER BY SSN";
...

次の例では、OCIでの簡易データ・インタフェースを使用してINSERT文を実行します。

for (i=0;i<DATA_SIZE;i++) 
  col2[i] = 'A';
 
/* prepare SQL insert statement */
OCIStmtPrepare (stmthp, errhp, ins_stmt, strlen(ins_stmt), OCI_NTV_SYNTAX,
    OCI_DEFAULT);
 
/* bind parameters 1 and 2 using OCI_DEFAULT (not data-at-exec) */
OCIBindByPos (stmthp, &bndp1, errhp, 1, (dvoid *) &ssn, sizeof(ssn), 
 SQLT_INT, 0, 0, 0, 0, 0, OCI_DEFAULT);
OCIBindByPos (stmthp, &bndp2, errhp, 2, (dvoid *) col2, col2len,
                           SQLT_CHR, 0, 0, 0, 0, 0, OCI_DEFAULT);
 
/* execute insert statement */
OCIStmtExecute (svchp, stmthp, errhp, 1, 0, 0, 0, OCI_DEFAULT);

次の例では、OCIでの簡易データ・インタフェースを使用してSELECT文を実行します。これは、上で定義した変数sel_stmtを介してSELECT文を使用します。

/* prepare select statement */
OCIStmtPrepare (stmthp, errhp, sel_stmt, strlen(sel_stmt), OCI_NTV_SYNTAX,
    OCI_DEFAULT);
 
/* define result columns 1 and 2 using OCI_DEFAULT (not data-at-exec) */
OCIDefineByPos (stmthp, &defnp1, errhp, 1, (dvoid*) &ssn, sizeof(ssn), 
                              SQLT_INT, 0, 0, 0, OCI_DEFAULT);
OCIDefineByPos (stmthp, &defnp2, errhp, 2, (dvoid *) col2Res, sizeof(col2), 
                              SQLT_CHR, 0, &col2len, 0, OCI_DEFAULT);
 
/* execute select statement */
OCIStmtExecute (svchp, stmthp, errhp, (ub4)1, (ub4)0, (OCISnapshot *) NULL, 
                              (OCISnapshot *) NULL, OCI_DEFAULT));
 
/* col2Res should now have a DATA_SIZE sized string of 'A's. */

OCIでのLOBロケータ・インタフェースの使用について

OCI LOBロケータ・インタフェースは、データベースまたは一時LOB(ピース単位またはチャンク全体)のどちらとも使用できます。

LOBロケータ・インタフェースを使用するには、アプリケーションに有効なLOBロケータが必要です。一時LOBの場合、これは、OCILobCreateTemporary()コールにより明示的に、または一時LOBを作成するSQL文(たとえば、SELECT c1 || c2 FROM myclob)により暗黙的に取得できます。永続LOBの場合は、SQL文を使用してデータベースからLOBロケータを取得します。(例はこの項で後述します。)

バインド・タイプは、CLOBではSQLT_CLOB、BLOBではSQLT_BLOBです。NCLOBの場合は、SQLT_CLOBを使用し、OCIのcsform属性(OCI_ATTR_CHARSET_FORM)をSQLCS_NCHARに設定します。

次の各項では、様々なシナリオでのLOBロケータの使用について説明します。

OCIのLOB関数の詳細とその他の例は、『Oracle Call Interfaceプログラマーズ・ガイド』LOB関数を参照してください。TimesTenではBFILE、SecureFile、LOBに対する配列読取りおよび書込み、またはLOBのコールバック関数の専用の機能はサポートされていないことに留意してください。

ヒント:

LOBロケータを使用してAPIを介してLOBを操作すると、TimesTenの一時領域を使用することになります。このような操作を多数行うことで、TimesTenの一時データ領域のサイズ増加が必要となる場合があります。『Oracle TimesTen In-Memory Databaseリファレンス』TempSizeを参照してください。

ノート:

  • 無効のLOBロケータが、OCILobLocatorAssign()を使用して、別のLOBロケータに割り当てられている場合、割当て先も解放され、無効のマークが設定されます。

  • OCILobLocatorAssign()は一時LOBに対して使用できますが、OCILobAssign()は使用できません。

OCIでの一時LOBの作成

OCIアプリケーションは、ロケータを割り当てるためのOCIDescriptorAlloc()の最初のコール後に、LOBロケータの入力/出力パラメータを含むOCILobCreateTemporary()関数を使用して一時LOBを作成できます。終了後に、OCIDescriptorFree()を使用してロケータの割当てを解放し、OCILobFreeTemporary()を使用して一時LOB自身を解放します。

ヒント:

TimesTenでは、一時LOBを作成すると、データベース・トランザクションがまだ進行していない場合は、データベース・トランザクションが作成されます。エラーとなる状況を回避するには、コミットまたはロールバックを実行してトランザクションをクローズする必要があります。

TimesTenでは、Oracle Databaseによってサポートされるすべての期間(OCI_DURATION_SESSIONOCI_DURATION_TRANSACTIONまたはOCI_DURATION_CALL)は、OCILobCreateTemporary()で許容されますが、TimesTenでは、一時LOBの存続期間はトランザクション存続期間を超えることはありません。

次のシナリオでは、一時LOBの存続期間はトランザクション存続期間より短くなることに注意してください。

  • OCI_DURATION_CALLが指定されている場合

  • トランザクションの終了前に、アプリケーションが、ロケータでOCILobFreeTemporary()をコールした場合

  • アプリケーションで、一時LOBのユーザーが指定した期間を開始するためにOCIDurationBegin()をコールし、トランザクションの終了前にOCIDurationEnd()をコールした場合

前述したOCI LOB関数の一部の例を次に示します。『Oracle Call Interfaceプログラマーズ・ガイド』一時LOBのサポートを参照してください。

if (OCIDescriptorAlloc((void*)envhp, (void **)&tblob,(ub4)OCI_DTYPE_LOB,
    (size_t)0, (void**)0))
{
   printf("failed in OCIDescriptor Alloc in select_and_createtemp \n");
   return OCI_ERROR;
}
 
...
 
if (OCILobCreateTemporary(svchp, errhp, tblob, (ub2)0, SQLCS_IMPLICIT,
    OCI_TEMP_BLOB, OCI_ATTR_NOCACHE, OCI_DURATION_TRANSACTION))
{
   (void) printf("FAILED: OCILobCreateTemporary() \n");
   return OCI_ERROR;
}
 
...
 
if(OCILobFreeTemporary(svchp,errhp,tblob))
{
   printf ("FAILED: OCILobFreeTemporary() call \n");
   return OCI_ERROR;
}

OCIでの永続LOBのロケータへのアクセス

アプリケーションでは通常、SQL文を使用してLOBロケータを取得またはロケータにアクセスし、次に、適切なAPI関数をロケータに渡すことによって、データベースからLOBにアクセスします。

EMPTY_CLOB()またはEMPTY_BLOB() SQL関数を使用して作成されたLOBには、有効なロケータがあり、アプリケーションはこのロケータを使用し、選択することでLOBにデータを挿入します。

次の表定義を想定します。

CREATE TABLE clobtable (x NUMBER, y DATE, z VARCHAR2(30), lobcol CLOB);
  1. INSERT文を準備します。例:

    INSERT INTO clobtable ( x, y, z, lobcol )
       VALUES ( 81, sysdate, 'giants', EMPTY_CLOB() )
       RETURNING lobcol INTO :a;

    または、データを使用してLOBを初期化します。

    INSERT INTO clobtable ( x, y, z, lobcol )
       VALUES ( 81, sysdate, 'giants', 'The Giants finally won a World Series' )
       RETURNING lobcol INTO :a;
  2. 記載されているとおり、LOBロケータを:aにバインドします。

  3. 文を実行します。実行後、ロケータは新しく作成されたLOBを参照します。

アプリケーションは、LOBロケータ・インタフェースを使用して、ロケータを介してLOBデータの読取りまたは書込みが可能です。

または、アプリケーションはSELECT文を使用して、既存LOBのロケータにアクセスできます。

次の例では、次の表を使用します。

person(ssn number, resume clob)

PERSON表のLOB列用のロケータを選択します。

text *ins_stmt = (text *)"INSERT INTO PERSON VALUES (:1, :2)";
text *sel_stmt = (text *)"SELECT * FROM PERSON WHERE SSN = 123456";
text *ins_empty = (text *)"INSERT INTO PERSON VALUES ( 1, EMPTY_CLOB())";
 
OCILobLocator *lobp;
 
ub4   amtp = DATA_SIZE;
ub4   remainder = DATA_SIZE;
ub4    nbytes = PIECE_SIZE;
 
/* Allocate lob locator */
OCIDescriptorAlloc (envhp, &lobp, OCI_DTYPE_LOB, 0, 0);
 
/* Insert an empty locator */
OCIStmtPrepare (stmhp, errhp, ins_empty, strlen(ins_empty), OCI_NTV_SYNTAX, 
                               OCI_DEFAULT);
OCIStmtExecute (svchp, stmhp, errhp, 1, 0, 0, 0, OCI_DEFAULT);
 
/* Now select the locator */
 
OCIStmtPrepare (stmhp, errhp, sel_stmt, strlen(sel_stmt), OCI_NTV_SYNTAX,
                OCI_DEFAULT);
 
/* Call define for the lob column */
OCIDefineByPos (stmthp, &defnp2, errhp, 1, &lobp, 0 , SQLT_CLOB, 0, 0, 0,
                OCI_DEFAULT);
 
OCIStmtExecute (svchp, stmhp, errhp, 1, 0, 0, 0, OCI_DEFAULT);

OCIのLOBロケータ・インタフェースを使用したLOBデータの読取りおよび書込み

OCIアプリケーションはOCILobOpen()およびOCILobClose()を使用して、LOBをオープンおよびクローズできます。LOBを明示的にオープンまたはクローズしない場合は、読取りまたは書込みの前に暗黙的にオープンされ、トランザクションの終了時に暗黙的にクローズされます。

アプリケーションでは、OCILobRead()またはOCILobRead2()を使用してLOBデータを読み取り、OCILobWrite()またはOCILobWrite2()を使用してLOBデータを書き込み、OCILobWriteAppend()またはOCILobWriteAppend2()を使用してLOBデータに追加し、OCILobErase()またはOCILobErase2()を使用してLOBデータを消去し、その他の様々なOCI関数を使用してその他の様々なアクションを実行します。

たとえば、「Hello World!」という内容のCLOBについて考えてみます。OCILobWrite()をコールして、オフセット7に「I am a new string」と書き込むようにデータを上書きおよび追加できます。この結果、CLOBの内容が「Hello I am a new string」に更新されます。または、元の「Hello World!」からデータを消去する場合、オフセット7、量(文字数)5などを指定してOCILobErase()をコールすると、CLOBを「Hello !」(空白が6つ)に更新できます。

OCIのLOBロケータ・インタフェースのすべての関数の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』LOB関数を参照してください。

ノート:

  • Oracle DatabaseではLOBに対するOCIの読取りおよび書込み関数で、2のバージョンを重視しています(2のバージョン以外はOracle Database 11.2リリースで非推奨になりました)が、TimesTenでは、OCILobRead2()OCILobWrite2()およびOCILobWriteAppend2() (TimesTenでサポートするサイズより大きいLOBを対象)を使用することによる技術的なメリットはありません。

  • LOBの読取りまたは書込み関数を使用する場合、TimesTenではLOB操作に対するコールバック関数をサポートしていないため、コールバック関数のパラメータがNULLまたは0(ゼロ)に設定されている必要があります。

  • TimesTenではLOBに対する配列のバインドがサポートされていないため、OCILobArrayRead()およびOCILobArrayWrite()関数はサポートされません。

次の例では、OCI LOB関数OCILobWrite()を使用したLOBデータの書込み方法、およびOCILobRead()を使用したデータの読取り方法を示します。前述の項の例で示した表および変数を使用します。

for (i=0;i<DATA_SIZE;i++) 
  col2[i] = 'A';
 
/*************** Writing to the LOB *****************/
 
amt = DATA_SIZE;
offset = 1;
 
/* Write contents of col2 buffer into the LOB in a single chunk via locator lobp */
OCILobWrite (svchp, errhp, lobp, &amt, offset, col2, DATA_SIZE, OCI_ONE_PIECE,
                        0, 0, 0, SQLCS_IMPLICIT);
 
/*************** Reading from the LOB *****************/
 
/* Get the length of the LOB */
OCILobGetLength (svchp, errhp, lobp, &len);
amt = len;
 
/* Read the LOB data in col2Res in a single chunk */
OCILobRead (svchp, errhp, lobp, &amt, offset, col2Res, DATA_SIZE, 0, 0, 0,  
                        SQLCS_IMPLICIT);

OCIクライアント側のバッファリング

OCIでは、LOBごとにクライアント側のバッファリング用の機能が提供されています。この機能は、LOBでOCILobEnableBuffering()をコールすることで有効化され、OCILobDisableBuffering()をコールすること無効化されます。

LOBロケータに対するバッファリングを有効化することで、512KBの書込みバッファが作成されます。このサイズは設定できません。アプリケーションによってLOBロケータを介して書き込まれたデータはバッファされます。可能な場合、クライアント・ライブラリは、LOBの読取りリクエストにバッファからも対応します。アプリケーションは、OCILobFlushBuffer()をコールすることによってバッファをフラッシュできます。バッファが一杯になっても自動的にはフラッシュされないため、バッファが一杯のときにロケータを介してLOBに書込みを試みると、エラーが発生することに注意してください。

次の制限事項は、クライアント側のバッファリングを使用している場合に適用されます。

  • バッファリングは、OCILobAppend()OCILobCopy()OCILobCopy2()OCILobErase()OCILobGetLength()OCILobTrim()OCILobWriteAppend()およびOCILobWriteAppend2()の各関数とは互換性がありません。

  • アプリケーションでは、LOBの最後に追加する場合にのみOCILobWrite()またはOCILobWrite2()を使用できます。

  • LOBデータは、アプリケーションがバッファをフラッシュした後にのみ、SQLおよびPL/SQL(サーバー側)操作に表示されます。

  • フラッシュされていないクライアント側の書込みがバッファにあるときにLOBが選択されると、そのフラッシュされていないデータは選択に含まれません。

OCIでのLOBのプリフェッチ

クライアント/サーバー接続でサーバーへのラウンドトリップを減らすには、データベースからLOBデータをプリフェッチし、フェッチ操作中にクライアント側にキャッシュできます。OCIでのLOBのプリフェッチには、Oracle Databaseと同じ機能がTimesTenにあります。

次のOCI属性を使用してLOBのプリフェッチを構成します。サイズは、BLOBの場合はバイトで、CLOBまたはNCLOBの場合は文字数で表すことに注意してください。

  • OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE: これを使用してプリフェッチを有効化し、デフォルトのプリフェッチ・サイズを指定します。値0(ゼロ)(デフォルト)はプリフェッチを無効化します。

  • OCI_ATTR_LOBPREFETCH_SIZE: この属性を列定義ハンドルに設定し、特定のLOB列に対するプリフェッチ・サイズを指定します。

  • OCI_ATTR_LOBPREFETCH_LENGTH: この属性はTRUEまたはFALSE(デフォルト)に設定でき、LOBの長さやチャンクのサイズなどのLOBのメタデータをプリフェッチします。

OCI_ATTR_DEFAULT_LOBPREFETCH_SIZEOCI_ATTR_LOBPREFETCH_LENGTHの設定は互いに独立しています。LOBデータのプリフェッチは、LOBメタデータのプリフェッチから切り離して使用できます。

『Oracle Call Interfaceプログラマーズ・ガイド』LOBデータ、長さおよびチャンク・サイズのプリフェッチを参照してください。

ノート:

前述の属性の設定は、データベースへの直接接続では無視されます。

OCIでのパススルーLOB

パススルーLOB (TimesTenを介してアクセスする、Oracle Database内のLOB)は、TimesTenのLOBとして公開され、TimesTenのLOBとほぼ同様にTimesTenでサポートされています。

次の点に留意してください:

  • パススルーLOBの作成に、OCILobCreateTemporary()は使用できません。

  • 1つのTimesTen LOBを別のTimesTen LOBにコピーすること(OCILobCopy()OCILobCopy2()OCILobAppend()などを使用して)に加えて、TimesTen LOBをパススルーLOBに、パススルーLOBからTimesTen LOBに、1つのパススルーLOBから別のパススルーLOBにコピーできます。これらのコピーでは、LOBの値をターゲットの宛先にコピーします。たとえば、パススルーLOBのTimesTen LOBへのコピーでは、LOBの値がTimesTenデータベースにコピーされます。

    パススルーLOBのサイズがTimesTen LOBのサイズ制限より大きい場合、パススルーLOBをTimesTen LOBにコピーしようとするとエラーが発生します。

  • TimesTen LOBのサイズ制限は、パススルーによるOracle DatabaseのLOBの格納には適用されません。パススルーLOBがTimesTen LOBにコピーされた場合、サイズ制限はコピーにも適用されます。

  • TimesTenのローカルLOBと同様に、パススルーLOBのロケータは、トランザクションの終了後は有効ではありません。

次の例では、TimesTenのLOBとOracle Database上のパススルーLOBとの間のコピーでの重要機能を紹介します。表およびデータの設定後に、最初の例では、OCILobAppend()を使用してLOBデータをOracle DatabaseからTimesTenにコピーし、2番目目の例では、OCILobCopy()を使用してLOBデータをTimesTenからOracle Databaseにコピーしています。(どちらのコールも、どちらの場合にも使用できます。)次に、対比のために、3番目の例では、UPDATE文を使用してLOBデータをOracle DatabaseからTimesTenにコピーし、4番目の例では、INSERT文を使用してLOBデータをTimesTenからOracle Databaseにコピーしています。

   /* Table and data setup */
  call ttoptsetflag(''passthrough'', 3)';
  DROP TABLE oratab';
  CREATE TABLE oratab (i INT, c CLOB)';
  INSERT INTO oratab VALUES (1, ''Copy from Oracle to TimesTen'')';
  INSERT INTO oratab VALUES (2, EMPTY_CLOB())';
  COMMIT;
 
  call ttoptsetflag(''passthrough'', 0)';
  DROP TABLE tttab';
  CREATE TABLE tttab (i INT, c CLOB)';
  INSERT INTO tttab VALUES (1, ''Copy from TimesTen to Oracle'')';
  INSERT INTO tttab VALUES (2, EMPTY_CLOB())';
  INSERT INTO tttab VALUES (3, NULL)';
  COMMIT;
  /* Table and data setup end */
 
  /*
   * Below are four OCI pseudocode examples, for copying LOBs between
   * TimesTen and Oracle using OCI API and INSERT/UPDATE statements.
   */
 
  /* Init OCI Env */
 
  /* Set the passthrough level to 1 */
  OCIStmtPrepare (..., "call ttoptsetflag(''passthrough'', 1)'", ...);
  OCIStmtExecute (...);
 
  /*
   * 1. Copy a passthrough LOB on Oracle to a TimesTen LOB */
 
  /* Select a passthrough locator on Oracle */
  OCIStmtPrepare (..., "SELECT c FROM oratab WHERE i = 1", ...);
  OCIDefineByPos (..., (dvoid *)&ora_loc_1, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  /* Select a locator on TimesTen for update */
  OCIStmtPrepare (..., "SELECT c FROM tttab WHERE i = 2 FOR UPDATE", ...);
  OCIDefineByPos (..., (dvoid *)&tt_loc_2, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  /* Copy a passthrough LOB on Oracle to a TimesTen LOB */
  OCILobAppend(..., tt_loc_2, ora_loc_1);
 
  /*
   * 2. Copy a TimesTen LOB to a passthrough LOB on Oracle */
 
  /* Select a passthrough locator on Oracle for update */
  OCIStmtPrepare (..., "SELECT c FROM oratab WHERE i = 2 FOR UPDATE", ...);
  OCIDefineByPos (..., (dvoid *)&ora_loc_2, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  /* Select a locator on TimesTen */
  OCIStmtPrepare (..., "SELECT c FROM tttab WHERE i = 1", ...);
  OCIDefineByPos (..., (dvoid *)&tt_loc_1, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  /* Copy a passthrough LOB on Oracle to a TimesTen LOB */
  OCILobCopy(..., ora_loc_2, tt_loc_1, 28, 1, 1);
 
  /*
   * 3. UPDATE a TimesTen LOB with a passthrough LOB on Oracle */
 
  /* A passthrough LOB, (selected above in case 1) is bound to an UPDATE statement
   * on TimesTen table */
  OCIStmtPrepare (..., "UPDATE tttab SET c = :1 WHERE i = 3", ...);
  OCIBindByPos (..., (dvoid *)&ora_loc_1, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  /*
   * 4. INSERT a passthrough table on Oracle with a TimesTen LOB */
 
  /* A TimesTen LOB, (selected above in case 2) is bound to an INSERT statement
   * on a passthough table on Oracle */
  OCIStmtPrepare (..., "INSERT INTO oratab VALUES (3, :1)", ...);
  OCIBindByPos (..., (dvoid *)&tt_loc_1, 0 , SQLT_CLOB, ...);
  OCIStmtExecute (...);
 
  OCITransCommit (...);
 
  /* Cleanup OCI Env */

OCIを使用したクライアント/サーバーでの結果セット・バッファ・サイズの構成

クライアント/サーバーでSELECT文から返されるデータについては、クライアントに返されるデータのバッファ・サイズは構成可能であり、パフォーマンス向上のために調整できます。(以前のリリースでは、バッファ・サイズを変更できませんでした。)

バッファ・サイズは、データの行数またはデータのバイト数の単位で設定できます。下限が優先されます。一方の制限を使用し、もう一方は先に到達しないように十分大きい値に設定することをお薦めします。

次のOCI文属性を使用します。

  • OCI_ATTR_PREFETCH_ROWS: 行単位でのバッファ・サイズ(デフォルトは8192)
  • OCI_ATTR_PREFETCH_MEMORY: バイト単位でのバッファ・サイズ(デフォルトは2097152、つまり2 MB)

これらの属性は、設定はできますが、取得はできません。

次に例を示します。


/* Double the row limit. */
ub4 rowsvalue = 16384;
...
OCIAttrSet(stmthp, handle_type, (dvoid *)&rowsvalue,
           sizeof(ub4), OCI_ATTR_PREFETCH_ROWS, errhp);

ノート:

  • これらの属性は、TimesTen接続属性TT_NetMsgMaxRowsおよびTT_NetMsgMaxBytesに対応しており、TimesTen接続文字列またはDSNで設定して、接続で作成されるすべての文の初期値として使用できます。
  • 各属性の最小値は1であり、少なくとも1行が必ず返されます。どちらの値も、0に設定するとデフォルト値が使用されます。データ型の最大値(32ビット符号なし整数)以外の最大値設定はありません。
  • これらの属性をサポートするクライアント・バージョンが、それらをサポートしていないサーバー・バージョンに接続する場合、設定はすべて無視されます。

OCIでPL/SQLを使用してTimesTen組込みプロシージャをコール

結果セットを返すTimesTen組込みプロシージャは、OCIで直接にはサポートされていません。

この例では、この目的でのPL/SQLの使用方法を示します。

plsql_resultset_example(OCIEnv *envhp, OCIError *errhp, OCISvcCtx *svchp) 
{ 
  OCIStmt   *stmhp; 
  OCIBind   *bindp; 
 
  sb4        passThruValue = -1; 
  char       v_name[255]; 
  text      *stmt_text; 
  
  /* prepare the plsql statement */ 
  stmt_text = (text *) 
    "declare v_name varchar2(255); " 
    "begin execute immediate " 
       "'call ttOptGetFlag(''passthrough'')' into v_name, :rc1; " 
    "end;"; 
  OCIStmtPrepare2(svchp, &stmhp, errhp, (text *)stmt_text, 
                  (ub4)strlen((char *)stmt_text), 
                  (text *)0, (ub4)0, 
                  (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); 
 
  /* bind parameter 1 (:v_name) to varchar2 out-parameter */ 
  OCIBindByPos(stmhp, &bindp, errhp, 1, 
               (dvoid*)&v_name, sizeof(v_name), SQLT_CHR, 
               (dvoid*)0, (ub2*)0, (ub2*)0, (ub4)0, (ub4*)0, 
               OCI_DEFAULT); 
 
  /* execute the plsql statement */ 
  OCIStmtExecute(svchp, stmhp, errhp, (ub4)1, (ub4)0, 
                 (OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT); 
 
  /* convert the passthrough string value to an integer */ 
  passThruValue = (sb4)atoi((const char *)v_name); 
  printf("Value of the passthrough flag is %d\n", passThruValue); 
 
  /* drop the statement handle */ 
  OCIStmtRelease(stmhp, errhp, (text *)0, (ub4)0, (ub4)OCI_DEFAULT); 
}