7 OCIでのバインドおよび定義
この章では、OCIでのバインドと定義について説明します。
この章には次のトピックが含まれます:
OCIでのバインドの概要
この章では、バインドと定義の基本概念をもう一度取り上げ、OCIアプリケーションで使用できる様々な種類のバインドと定義を詳しく説明します。
さらに、構造体配列の使用方法、およびバインド、定義、文字変換における諸問題についても説明します。
たとえば、次のようなINSERT
文があるとします。
INSERT INTO emp VALUES (:empno, :ename, :job, :sal, :deptno)
次の変数宣言を行います。
text *ename, *job; sword empno, sal, deptno;
バインド・ステップでは、プレースホルダ名とプログラム変数のアドレス間の関連付けを行います。また、図7-1に示すように、バインドでは、プログラム変数のデータ型と長さを指定します。
バインド変数の値のみを変更した場合は、文を再実行するために再バインドする必要はありません。バインドは参照によるため、変数のアドレスおよびハンドルが有効であるかぎり、再バインドせずに、変数を参照する文を再実行できます。
ノート:
インタフェース・レベルの場合は、バインド変数はすべてIN
であるとみなされるため、適切に初期化する必要があります。変数が純OUT
バインド変数の場合は、この変数を0 (ゼロ)に設定できます。また、NULL
インジケータを使用して、そのインジケータを-1 (NULL
)に設定することもできます。
Oracle Databaseでは、名前付きデータ型、REF
およびLOB用のデータ型が実装されおり、これらは、SQL文中でプレースホルダとしてバインドできます。
ノート:
記述子やロケータなど、サイズが不明で不透明なデータ型の場合は、記述子またはロケータ・ポインタのアドレスを渡してください。サイズ・パラメータに、適切なデータ構造のサイズ(sizeof(structure)
)を設定します。
関連項目:
この例が実装されているコードは、「OCIバインドで使用するステップ」を参照してください
名前付きバインドおよび定位置バインド
名前付きバインドでは、文の各プレースホルダに名前が関連付けられていますが、定位置バインドでは、プレースホルダは名前ではなく文中の位置によって参照されます。
図7-1のSQL文は、名前付きバインドの例です。文の各プレースホルダには、'ename'や'sal'などの名前が関連付けられています。この文を準備し、プレースホルダをアプリケーションで値に関連付けるときは、OCIBindByName()
コールまたはOCIBindByName2()
コールを使用し、placeholderパラメータでプレースホルダの名前を渡して、プレースホルダの名前による関連付けを行います。
2番目のタイプのバインドは、位置指定バインドと呼ばれます。定位置バインドでは、プレースホルダは名前ではなく文中の位置によって参照されます。このバインドでは、OCIBindByPos()
コールまたはOCIBindByPos2()
コールを使用して、入力値とプレースホルダの位置との間の関連付けを行います。
前の項に示した例を定位置バインドに使用するには:
INSERT INTO emp VALUES (:empno, :ename, :job, :sal, :deptno)
5つのプレースホルダは、OCIBindByPos()
またはOCIBindByPos2()
をコールし、position
パラメータでプレースホルダの位置番号を渡すことによって、それぞれバインドされます。たとえば、OCIBindByPos()
またはOCIBindByPos2()
をコールすることによって、:empno
プレースホルダは位置1に、:ename
は位置2にというようにバインドされます。
重複バインドでは、1つのバインド・コールのみが必要です。次のSQL文について考えてみます。このSQL文は、コミッションと給料の両方が指定の金額よりも高い従業員をデータベースで問い合せます。
SELECT empno FROM emp WHERE sal > :some_value AND comm > :some_value
OCIアプリケーションでは、OCIBindByName()
またはOCIBindByName2()
を1回コールするのみで、:some_value
プレースホルダを名前によってバインドし、この文のバインドを完了できます。この場合、:some_value
のすべてのバインド・プレースホルダには、OCIBindByName()
またはOCIBindByName2()
コールで指定したものと同じ値が割り当てられます。
次に、重複する6つ目のプレースホルダが追加される場合を考えてみます。たとえば、:ename
を6つ目のプレースホルダとして前の最初の例に追加します。
INSERT INTO emp VALUES (:empno, :ename, :job, :sal, :deptno, :ename)
OCIBindByName()
またはOCIBindByName2()
コールを使用している場合、両方の:ename
プレースホルダをバインドするには1つのバインド・コールで十分です。文中のすべての:ename
は、同じ値にバインドされます。さらに、新しいバインド・プレースホルダが、既存のバインド・プレースホルダのバインド位置が変わった結果として追加される場合、バインド位置を更新するために、既存のバインド・コールを変更する必要はありません。これは、プログラムが文のテキストにさらに多くのバインド変数を追加するようになった場合に、OCIBindByName()
またはOCIBindByName2()
コールを使用する上で明らかに有利な点です。
ただし、OCIBindByPos()
またはOCIBindByPos2()
コールを使用していれば、重複するバインド・パラメータのバインドに関しては、それが必要な場合、柔軟性が高まります。バインド・パラメータの重複をどれでも別々にバインドするオプションがあります。バインドを解除された重複パラメータは、同じ名前を持つ最初のバインド・パラメータから値を継承します。最初のものは、明示的にバインドする必要があります。
SQL文のコンテキストでは、位置nは、n番目の位置にあるバインド・パラメータを示します。ただし、PL/SQL文のコンテキストでは、OCIBindByPos()
またはOCIBindByPos2()
での位置パラメータの解釈が異なります。つまり、バインド・コールでの位置nは、文を左から右にスキャンしたときのn番目の一意のパラメータ名のバインドを示します。
前の例と同じSQL文のテキストを使用すると、6番目の位置を単独でバインドする場合、位置6でOCIBindByPos()
またはOCIBindByPos2()
をコールすることにより、:ename
プレースホルダがバインドされます。バインド解除のままの場合は、:ename
は、同じ名前を持つ最初のバインド・パラメータ、この場合は位置2の:ename
から値を継承することになります。
OCI配列インタフェース
Oracle Databaseにデータを渡すには、様々な方法があります。
OCIStmtExecute()
ルーチンを使用してSQL文を繰り返し実行し、反復のたびに異なる入力値を指定することもできます。
Oracle配列インタフェースを使用すると、単一の文とOCIStmtExecute()
のコール1回のみで、多数の値を入力できます。その場合、配列を入力プレースホルダにバインドし、itersパラメータで制御して配列全体を同時に渡すことができます。
配列インタフェースを使用すると、大量のデータを更新または挿入する場合に、データベースとのラウンドトリップの回数を大幅に削減できます。この削減により、通信量の多いクライアント/サーバー環境では、大きなパフォーマンスの向上につながります。たとえば、データベースに10行挿入するアプリケーションを考えてみます。OCIStmtExecute()
を10回、それぞれ1個の値を指定してコールすると、すべてのデータの挿入にネットワーク・ラウンドトリップが10回必要です。入力配列を使用すると、OCIStmtExecute()
を1回コールするのみで同じ結果が得られ、ネットワーク・ラウンドトリップは1回ですみます。
Oracle Database 12cリリース2 (12.2)以上では、従来のDMLによるハイブリッド列圧縮(HCC)のサポートが追加されているため、OCIでの配列の挿入中にHCCを使用できます。HCCの従来型配列の挿入は、ASSM表領域のHCC表でのみサポートされます。
ノート:
OCI配列インタフェースを使用して挿入を行う場合は、各行が挿入されるたびに、データベース内の行トリガーが起動されます。
配列DML文で使用できる最大行数は、40億 -1 (3,999,999,999)です。ただし、ub4
のかわりにub8
を使用すると、配列DML文で許可される最大行数は、40億行より増加します。
関連項目:
-
HCCを構成する方法の詳細は、『Oracle Database管理者ガイド』の表の圧縮についてに関する項を参照してください
PL/SQLのプレースホルダのバインドについて
PL/SQLブロックの処理は、単一のSQL文の場合と同じようにそのブロックを文字列変数に入れて、任意の変数をバインドし、ブロックを含む文を実行して行います。
PL/SQLブロックのプレースホルダをプログラム変数にバインドする場合は、OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
を使用して、スカラーまたは配列のいずれかのホスト変数に対して基本バインドを行います。
次の短いPL/SQLブロックには2つのプレースホルダが含まれ、従業員番号と新規給与額に基づき、従業員の給与を更新するプロシージャへのIN
パラメータを表します。
char plsql_statement[] = "BEGIN\ RAISE_SALARY(:emp_number, :new_sal);\ END;" ;
これらのプレースホルダは、SQL文のプレースホルダと同じ方法で入力変数にバインドできます。
PL/SQL文を処理するとき、出力変数もまたバインド・コールを使用してプログラム変数に関連付けられます。
たとえば、次のPL/SQLブロックがあるとします。
BEGIN SELECT ename,sal,comm INTO :emp_name, :salary, :commission FROM emp WHERE empno = :emp_number; END;
このブロックでは、OCIBindByName()
またはOCIBindByName2()
を使用して、変数を:emp_name
、:salary
および:commission
の各出力プレースホルダにバインドし、入力プレースホルダ:emp_number
にも変数をバインドします。
ノート:
バインド・コールでッファ長を0 (ゼロ)に設定するか、あるいは対応するインジケータを-1に設定して、純OUT
バッファの場合も含め、すべてのババッファを初期化する必要があります。
関連項目:
-
PL/SQLプレースホルダのバインドの詳細は、「名前付きデータ型およびREFバインドの情報」を参照してください
OCIバインドで使用するステップ
プレースホルダは複数のステップでバインドされます。
単純なスカラー・バインドまたは配列バインドの場合は、OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
を使用して、プレースホルダとデータとの関連付けを指定するのみです。
バインドが完了すると、SQL文の実行時には、入力データの位置やPL/SQL出力データを入れる位置がOCIライブラリに対して明らかになります。プログラム入力データは、プログラム変数をプレースホルダにバインドする際にプログラム変数内に存在している必要はありませんが、文を実行する際には存在している必要があります。
次の例7-1のコード例は、SQL文の各プレースホルダのハンドル割当てとバインドを示しています。
ノート:
checkerr()
関数によって、OCIアプリケーションからのリターン・コードが評価されます。この関数のコードは、「OCIErrorGet()
」の例にあります。
例7-1 SQL文の各プレースホルダのハンドル割当てとバインド
... /* The SQL statement, associated with stmthp (the statement handle) by calling OCIStmtPrepare2() */ text *insert = (text *) "INSERT INTO emp(empno, ename, job, sal, deptno)\ VALUES (:empno, :ename, :job, :sal, :deptno)"; ... /* Bind the placeholders in the SQL statement, one per bind handle. */ checkerr(errhp, OCIBindByName(stmthp, &bnd1p, errhp, (text *) ":ENAME", strlen(":ENAME"), (ub1 *) ename, enamelen+1, SQLT_STR, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); checkerr(errhp, OCIBindByName(stmthp, &bnd2p, errhp, (text *) ":JOB", strlen(":JOB"), (ub1 *) job, joblen+1, SQLT_STR, (void *) &job_ind, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); checkerr(errhp, OCIBindByName(stmthp, &bnd3p, errhp, (text *) ":SAL", strlen(":SAL"), (ub1 *) &sal, (sword) sizeof(sal), SQLT_INT, (void *) &sal_ind, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); checkerr(errhp, OCIBindByName(stmthp, &bnd4p, errhp, (text *) ":DEPTNO", strlen(":DEPTNO"), (ub1 *) &deptno,(sword) sizeof(deptno), SQLT_INT, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); checkerr(errhp, OCIBindByName(stmthp, &bnd5p, errhp, (text *) ":EMPNO", strlen(":EMPNO"), (ub1 *) &empno, (sword) sizeof(empno), SQLT_INT, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0,OCI_DEFAULT));
OCIプログラムでのPL/SQLブロック
OCI中のPL/SQLブロックで最もよく使用されるのは、おそらくストアド・プロシージャまたはストアド・ファンクションのコールです。
データベースに格納されたRAISE_SALARY
というプロシージャがあり、そのプロシージャに対するコールを無名PL/SQLブロックに埋め込み、PL/SQLブロックを処理するとします。
次に示すプログラムの一部分は、ストアド・プロシージャ・コールをOCIアプリケーションに埋め込む方法の例です。このプログラムでは、raise_salary
というストアド・プロシージャへの入力として、従業員番号と新しい給与が渡されます。
raise_salary (employee_num IN, sal_increase IN, new_salary OUT);
このプロシージャでは、指定された従業員給与に指定された額が上乗せされます。増額された給与はストアド・プロシージャの変数new_salary
に戻され、この値がプログラムで表示されます。
PL/SQL OUT変数をバインドする必要がありますが、PL/SQLプロシージャ引数new_salary
が未定義であることに注意してください。
例7-2は、単一バインド・コールのみが必要な単純なスカラー・バインドを実行する方法を示しています。場合によっては、特定のバインド・データ型または実行モードについて属性を定義するために、追加のバインド・コールが必要です。
例7-2 OCIで使用されるPL/SQL文の定義
/* Define PL/SQL statement to be used in program. */ text *give_raise = (text *) "BEGIN\ RAISE_SALARY(:emp_number,:sal_increase, :new_salary);\ END;"; OCIBind *bnd1p = NULL; /* the first bind handle */ OCIBind *bnd2p = NULL; /* the second bind handle */ OCIBind *bnd3p = NULL; /* the third bind handle */ static void checkerr(); sb4 status; main() { sword empno, raise, new_sal; OCISession *usrhp = (OCISession *)NULL; ... /* attach to Oracle database, and perform necessary initializations and authorizations */ ... /* prepare the statement request, passing the PL/SQL text block as the statement to be prepared */ checkerr(errhp, OCIStmtPrepare2(svchp, &stmthp, errhp, (text *) give_raise, (ub4) strlen(give_raise), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT)); /* bind each of the placeholders to a program variable */ checkerr( errhp, OCIBindByName(stmthp, &bnd1p, errhp, (text *) ":emp_number", -1, (ub1 *) &empno, (sword) sizeof(empno), SQLT_INT, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); checkerr( errhp, OCIBindByName(stmthp, &bnd2p, errhp, (text *) ":sal_increase", -1, (ub1 *) &raise, (sword) sizeof(raise), SQLT_INT, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); /* remember that PL/SQL OUT variables are bound, not defined */ checkerr( errhp, OCIBindByName(stmthp, &bnd3p, errhp, (text *) ":new_salary", -1, (ub1 *) &new_sal, (sword) sizeof(new_sal), SQLT_INT, (void *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT)); /* prompt the user for input values */ printf("Enter the employee number: "); scanf("%d", &empno); /* flush the input buffer */ myfflush(); printf("Enter employee's raise: "); scanf("%d", &raise); /* flush the input buffer */ myfflush(); /* execute PL/SQL block*/ checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT)); /* display the new salary, following the raise */ printf("The new salary is %d\n", new_sal); OCIStmtRelease(stmthp, errhp, NULL, 0, OCI_DEFAULT); }
関連項目:
PL/SQL OUT変数をバインドする必要があり、未定義である理由については、「PL/SQL出力変数の定義について」および「名前付きデータ型、REF定義およびPL/SQL OUTバインドの情報」を参照してください。OCIでの拡張バインド操作
拡張バインド操作には、マルチステップ・バインド、名前付きデータ型のバインド、REF
のバインドなどがあります。
「OCIのプレースホルダのバインドについて」では、OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
を使用して、SQL文のプレースホルダとプログラム変数間の関連付けを行うための基本バインド操作の方法について説明しました。この項では、マルチステップ・バインド、名前付きデータ型のバインド、REF
のバインドなどの拡張バインド操作について説明します。
場合によっては、特定のバインド・データ型または特定の実行モードについて特定の属性を定義するために、追加のバインド・コールが必要です。
次の各項ではこの特別なケースについて説明し、バインドの詳細は、表7-1にまとめてあります。
表7-1 バインド・タイプの情報の概要
バインドのタイプ | バインド・データ型 | ノート |
---|---|---|
スカラー |
スカラー・データ型 |
|
スカラーの配列 |
スカラー・データ型 |
|
名前付きデータ型 |
|
レコードおよびコレクションが含まれます。 次の2つのバインド・コールが必要です。
|
ブール |
|
|
|
|
次の2つのバインド・コールが必要です。
|
LOB
|
|
|
構造体の配列または静的配列 |
状況により異なる |
次の2つのバインド・コールが必要です。
|
ピース単位挿入 |
状況により異なる |
|
|
|
文ハンドル、 |
関連項目:
-
名前付きデータ型(オブジェクト)のバインドの詳細は、「名前付きデータ型のバインド」を参照してください
LOBのバインドについて
LOBをバインドするには、次の2つの方法があります。
-
実際のLOB値ではなく、LOBロケータをバインドします。この場合、LOB値の書込みや読取りは、LOBロケータをOCI LOB関数に渡すことによって行われます。
-
LOBロケータを使用せず、LOB値を直接バインドします。
LOBロケータのバインド
単一のバインド・コール内で、単一のロケータをバインドすることも、ロケータの配列をバインドすることもできます。
いずれの場合も、アプリケーションは、ロケータ自体ではなく、LOBロケータのアドレスを渡す必要があります。たとえば、アプリケーションにより、one_lob
がLOB列に対応するバインド変数である次のSQL文が準備されたとします。
INSERT INTO some_table VALUES (:one_lob)
アプリケーションでは、次の宣言を行います。
OCILobLocator * one_lob;
例7-3のコールは、プレースホルダをバインドし、文を実行するために使用されます。
また、同一のSQL INSERT
文を使用して、配列の挿入ができます。この場合、アプリケーションには、例7-4で示すコードが含まれます。
記述子は、使用する前にOCIDescriptorAlloc()
関数によって割り当てる必要があります。ロケータの配列では、OCIDescriptorAlloc()
を使用して、各配列要素を初期化する必要があります。BLOB
、CLOB
およびNCLOB
を割り当てる場合は、OCI_DTYPE_LOB
をtype
パラメータとして使用します。BFILE
を割り当てる場合は、OCI_DTYPE_FILE
を使用します。
例7-3 単一のロケータを挿入するためのプレースホルダのバインドと文の実行
/* initialize single locator */ one_lob = OCIDescriptorAlloc(...OCI_DTYPE_LOB...); ... /* pass the address of the locator */ OCIBindByName(...,(void *) &one_lob,... SQLT_CLOB, ...); OCIStmtExecute(...,1,...) /* 1 is the iters parameter */
例7-4 ロケータ配列を挿入するためのプレースホルダのバインドと文の実行
OCILobLocator * lob_array[10]; ... for (i=0; i<10, i++) lob_array[i] = OCIDescriptorAlloc(...OCI_DTYPE_LOB...); /* initialize array of locators */ ... OCIBindByName(...,(void *) lob_array,...); OCIStmtExecute(...,10,...); /* 10 is the iters parameter */
この項には次のトピックが含まれます。「LOBロケータのバインドに対する制限」
関連項目:
OCIDescriptorAlloc()LOBロケータのバインドに対する制限
LOBロケータのバインドに対する制限について説明します。
LOBロケータをバインドする際には、次の制限を守ってください。
-
INSERT
またはUPDATE
のピース単位操作およびコールバック操作は、サポートされていません。 -
FILEロケータを
INSERT
文またはUPDATE
文のバインド変数として使用する場合は、INSERT
文またはUPDATE
文を発行する前に、OCILobFileSetName()
を使用してディレクトリ・オブジェクトおよびファイル名で、ロケータを初期化する必要があります。
関連項目:
-
OCI LOB関数の詳細は、「LOBおよびBFILEの操作」を参照してください
LOBデータのバインドについて
0 (ゼロ)以外のあらゆるサイズのLOBでINSERT
とUPDATE
に対するバインドが可能です。
OCIBindByPos()
またはOCIBindByPos2()
、OCIBindByName()
またはOCIBindByName2()
、およびPL/SQLのバインドを使用すると、データをLOB列にバインドできます。
4KBを超えるデータをLOB列にバインドする場合は、一時表領域の一部を使用します。一時表領域が少なくともLOBバインドの長さの合計値に等しい量のデータを格納できる大きさであることを確認してください。一時表領域が拡張可能であれば、既存の領域が一杯になると自動的に拡張されます。次のコマンドを使用して、拡張できる一時表領域を作成します。
CREATE TABLESPACE ... AUTOEXTEND ON ... TEMPORARY ...;
LOBデータのバインドに対する制限
LOBデータのバインドに対する制限について説明します。
LOBデータをバインドする際には、次の制限を守ってください。
-
表に
LONG
列とLOB列の両方がある場合は、LONG
列とLOB列のどちらに対しても4KBを超えるデータのバインドが可能ですが、同一の文で両方を実行することはできません。 -
INSERT
AS
SELECT
操作で、LOB列に対するデータのバインドはできません。 -
LONGまたはLOBのどちらでもないが、SQL文でLOBまたはLONGバインド変数の後にあるバインド変数の最大サイズについて、特別な配慮が必要です。そのようなバインド変数の最大サイズが4000バイトを超えると、
ORA-24816
エラーが発生します。このエラーを回避するには、文字セットの変換後、サーバー側で最大サイズが4000バイトを超えるバインドに対して、OCI_ATTR_MAXDATA_SIZE
を4000バイトに設定する必要があります。または、そのようなバインドがバインド・リスト内でLONGまたはLOBの前に置かれるように、バインドの順番を変更します。 -
4000バイトを超えるデータに対しては、
HEX
からRAW
、RAW
からHEX
などの暗黙的な変換は行われません。次のコード例のPL/SQLコードではこれを説明しています。実行できない一部の暗黙的変換の説明
create table t (c1 clob, c2 blob); declare text varchar(32767); binbuf raw(32767); begin text := lpad ('a', 12000, 'a'); binbuf := utl_raw.cast_to_raw(text); -- The following works: insert into t values (text, binbuf); -- The following does not work because Oracle dpes not do implicit -- hex to raw conversion. insert into t (c2) values (text); -- The following does not work because Oracle does not do implicit -- raw to hex conversion. insert into t (c1) values (binbuf); -- The following does not work because you cannot combine the -- utl_raw.cast_to_raw() operator with the >4k bind. insert into t (c2) values (utl_raw.cast_to_raw(text)); end; /
-
4000バイトを超えるデータを
BLOB
またはCLOB
にバインドし、データがSQL演算子によってフィルタされる場合は、結果のサイズが制限されて4000バイト以下になります。たとえば、次のようにします。
create table t (c1 clob, c2 blob); -- The following command inserts only 4000 bytes because the result of -- LPAD is limited to 4000 bytes insert into t(c1) values (lpad('a', 5000, 'a')); -- The following command inserts only 2000 bytes because the result of -- LPAD is limited to 4000 bytes, and the implicit hex to raw conversion -- converts it to 2000 bytes of RAW data. insert into t(c2) values (lpad('a', 5000, 'a'));
LOBデータのバインドの例
LOBデータのバインドの例いくつか示します。
例7-5から例7-12では、次のSQL文が使用されています。
CREATE TABLE foo (a INTEGER ); CREATE TYPE lob_typ AS OBJECT (A1 CLOB ); CREATE TABLE lob_long_tab (C1 CLOB, C2 CLOB, CT3 lob_typ, L LONG);
例7-5 可能: C1、C2およびL列に、それぞれサイズが最大8000、8000および2000バイトのバインド可変データ値を挿入
void insert() /* A function in an OCI program */ { /* The following is allowed */ ub1 buffer[8000]; text *insert_sql = (text *) "INSERT INTO lob_long_tab (C1, C2, L) \ VALUES (:1, :2, :3)"; OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[1], errhp, 2, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[2], errhp, 3, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-6 可能: C1およびL列に、それぞれサイズが最大2000および8000バイトのバインド可変データ値を挿入
void insert() { /* The following is allowed */ ub1 buffer[8000]; text *insert_sql = (text *) "INSERT INTO lob_long_tab (C1, L) \ VALUES (:1, :2)"; OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[1], errhp, 2, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-7 可能: C1、C2およびL列を、それぞれサイズが最大8000、8000および2000バイトのバインド可変データ値に更新
void update() { /* The following is allowed, no matter how many rows it updates */ ub1 buffer[8000]; text *update_sql = (text *)"UPDATE lob_long_tab SET \ C1 = :1, C2=:2, L=:3"; OCIStmtPrepare(stmthp, errhp, update_sql, strlen((char*)update_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[1], errhp, 2, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[2], errhp, 3, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-8 可能: C1、C2およびL列を、それぞれサイズが最大2000、2000および8000バイトのバインド可変データ値に更新
void update() { /* The following is allowed, no matter how many rows it updates */ ub1 buffer[8000]; text *update_sql = (text *)"UPDATE lob_long_tab SET \ C1 = :1, C2=:2, L=:3"; OCIStmtPrepare(stmthp, errhp, update_sql, strlen((char*)update_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[1], errhp, 2, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[2], errhp, 3, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-9 可能: ピース単位、コールバックおよび配列挿入または更新の操作
void insert() { /* Piecewise, callback and array insert/update operations similar to * the allowed regular insert/update operations are also allowed */ }
例7-10 不可能: LOB列とLONG列の両方に4000バイトを超えるデータを同じINSERT文を使用して挿入
void insert() { /* The following is NOT allowed because you cannot insert >4000 bytes * into both LOB and LONG columns */ ub1 buffer[8000]; text *insert_sql = (text *)"INSERT INTO lob_long_tab (C1, L) \ VALUES (:1, :2)"; OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[1], errhp, 2, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-11 可能: CT3 LOB列にサイズが最大2000バイトのバインド変数データ値を挿入
void insert() { /* Insert of data into LOB attributes is allowed */ ub1 buffer[8000]; text *insert_sql = (text *)"INSERT INTO lob_long_tab (CT3) \ VALUES (lob_typ(:1))"; OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 2000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
例7-12 不可能: 選択操作としての挿入で任意の長さのデータをLOB列にバインド
void insert() { /* The following is NOT allowed because you cannot do insert as * select character data into LOB column */ ub1 buffer[8000]; text *insert_sql = (text *)"INSERT INTO lob_long_tab (C1) SELECT \ :1 from FOO"; OCIStmtPrepare(stmthp, errhp, insert_sql, strlen((char*)insert_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIBindByPos(stmthp, &bindhp[0], errhp, 1, (void *)buffer, 8000, SQLT_LNG, 0, 0, 0, 0, 0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT); }
OCI_DATA_AT_EXECモードでのバインドについて
OCIBindByName()
またはOCIBindByName2()
コール、あるいはOCIBindByPos()
またはOCIBindByPos2()
コールのmode
パラメータがOCI_DATA_AT_EXEC
に設定されているとき、アプリケーションで実行時にデータを提供するコールバック方式を使用する場合は、追加のOCIBindDynamic()
コールが必要です。
OCIBindDynamic()
のコールは、提供されたデータまたはその一部を示すために、必要に応じてコールバック・ルーチンを設定します。OCI_DATA_AT_EXEC
モードを選択した場合でも、コールバックのかわりに標準のOCIのピース単位ポーリング・メソッドを使用する場合は、OCIBindDynamic()
コールは必要ありません。
RETURN
句の変数をバインドするとき、アプリケーションでは、OCI_DATA_AT_EXEC
モードを使用して、コールバックを提供する必要があります。
関連項目:
-
ピース単位操作の詳細は、「OCIでのランタイム・データ割当てとピース単位操作」を参照してください
OCIでの定義の概要
問合せ文は、データベースのデータをアプリケーションに戻します。
問合せを処理するときには、データを検索する選択リストの各項目について、出力変数または出力変数の配列を定義する必要があります。定義ステップでは、戻される結果の格納場所と格納形式を判断する関連付けを行います。
たとえば、プログラムで次の文を処理するには、通常は2つの出力変数を定義する必要があります。1つは、name
列から戻される値を受け取るための変数、もう1つはssn
列から戻される値を受け取るための変数です。
SELECT name, ssn FROM employees WHERE empno = :empnum
name
列の値のみを取り出す場合は、ssn
列用の出力変数を定義する必要はありません。処理されるSELECT
文が問合せに対して複数の行を戻す場合、出力変数はスカラー値のかわりに配列を定義できます。
定義ステップは、アプリケーションに応じて文の実行前または実行後に行います。コンパイル時に選択リスト項目のデータ型がわかっている場合には、文を実行する前に定義を行うことができます。実行時に入力する動的SQL文、あるいは明確に定義された選択リストがない文をアプリケーションで処理する場合、アプリケーションはその文を実行して、記述情報を取り出す必要があります。記述情報を取り出した後、各選択リスト項目の型情報を使用して、出力変数を定義できます。
OCIでは、クライアント側で定義コールをローカルに処理します。定義ステップでは、結果を格納するバッファの位置を指示し、さらに、データがアプリケーションに戻されるときに行う必要があるデータ変換の種類を決定します。
ノート:
出力バッファには、2バイトの位置揃えを行う必要があります。
OCIDefineByPos()
またはOCIDefineByPos2()
コールのdty
パラメータでは、出力変数のデータ型を指定します。OCIでは、データを出力変数にフェッチするときに広範囲のデータ変換を行うことができます。たとえば、Oracle DATE
形式の内部データは、出力時に、自動的にString
データ型に変換できます。
この項には次のトピックが含まれます。「OCIの定義に使用するステップ」
関連項目:
-
データ型および変換の詳細は、「データ型」を参照してください
OCIの定義に使用するステップ
基本的な定義は、定位置コールのOCIDefineByPos()
またはOCIDefineByPos2()
により行われます。
このステップでは、選択リスト項目と出力変数との間の関連付けを行います。特定のデータ型またはフェッチ・モードには追加の定義コールが必要です。定義ステップが完了すると、OCIライブラリでは、データを入れる位置を決定します。SQL文を再準備または再実行することなく、定義コールを再度行って出力変数を再定義できます。
例7-13では、実行および記述操作の後にスカラー出力変数を定義しています。
関連項目:
-
記述ステップの詳細は、「選択リスト項目の記述について」を参照してください
例7-13 実行および記述操作後のスカラー出力変数の定義
SELECT department_name FROM departments WHERE department_id = :dept_input /* The input placeholder was bound earlier, and the data comes from the user input below */ printf("Enter employee dept: "); scanf("%d", &deptno); /* Execute the statement. If OCIStmtExecute() returns OCI_NO_DATA, meaning that no data matches the query, then the department number is invalid. */ if ((status = OCIStmtExecute(svchp, stmthp, errhp, 0, 0, (OCISnapshot *) 0, (OCISnapshot *) 0, OCI_DEFAULT)) && (status != OCI_NO_DATA)) { checkerr(errhp, status); return OCI_ERROR; } if (status == OCI_NO_DATA) { printf("The dept you entered does not exist.\n"); return 0; } /* The next two statements describe the select-list item, dname, and return its length */ checkerr(errhp, OCIParamGet((void *)stmthp, (ub4) OCI_HTYPE_STMT, errhp, (void **)&parmdp, (ub4) 1)); checkerr(errhp, OCIAttrGet((void*) parmdp, (ub4) OCI_DTYPE_PARAM, (void*) &deptlen, (ub4 *) &sizelen, (ub4) OCI_ATTR_DATA_SIZE, (OCIError *) errhp )); /* Use the retrieved length of dname to allocate an output buffer, and then define the output variable. If the define call returns an error, exit the application */ dept = (text *) malloc((int) deptlen + 1); if (status = OCIDefineByPos(stmthp, &defnp, errhp, 1, (void *) dept, (sb4) deptlen+1, SQLT_STR, (void *) 0, (ub2 *) 0, (ub2 *) 0, OCI_DEFAULT)) { checkerr(errhp, status); return OCI_ERROR; }
OCIでの拡張定義操作
この項では、マルチステップ定義、名前付きデータ型およびREF
などの拡張定義操作について説明します。
定義ステップで、OCIDefineByPos()
またはOCIDefineByPos2()
のコール以外の別のコールが必要になる場合があります。たとえば、配列フェッチOCIDefineArrayOfStruct()
または名前付きデータ型フェッチOCIDefineObject()
の属性を定義する場合です。たとえば、1列の名前付きデータ型を含む複数の行をフェッチするには、その列で3つのコールをすべて呼び出す必要があります。スカラー列のみを含む複数の行をフェッチするには、OCIDefineArrayOfStruct()
およびOCIDefineByPos()
またはOCIDefineByPos2()
が適しています。
また、Oracle Databaseには、オブジェクト型属性をマップする事前定義済のCデータ型が用意されています。
LOB出力変数の定義について
LOBを定義するには、次の2つの方法があります。
-
実際のLOB値ではなく、LOBロケータを定義します。この場合、LOB値の書込みや読取りは、LOBロケータをOCI LOB関数に渡すことによって行われます。
-
LOBロケータを使用せず、LOB値を直接定義します。
LOBロケータの定義について
単一の定義コール内では、単一のロケータを定義することも、ロケータの配列を定義することもできます。いずれの場合も、アプリケーションは、ロケータ自体ではなく、LOBロケータのアドレスを渡す必要があります。たとえば、アプリケーションで次のSQL文が準備されたとします。
SELECT lob1 FROM some_table;
この文では、lob1
はLOB列で、one_lob
は次の宣言を伴ったLOB列に対応する定義変数です。
OCILobLocator * one_lob;
次のようなコールを使用して、プレースホルダをバインドし、文を実行します。
/* initialize single locator */ OCIDescriptorAlloc(...&one_lob, OCI_DTYPE_LOB...); ... /* pass the address of the locator */ OCIBindByName(...,(void *) &one_lob,... SQLT_CLOB, ...); OCIStmtExecute(...,1,...); /* 1 is the iters parameter */
また、同一のSQL SELECT
文を使用して、配列の挿入ができます。このケースでは、アプリケーション内に次のコードが組み込まれている必要があります。
OCILobLocator * lob_array[10]; ... for (i=0; i<10, i++) OCIDescriptorAlloc(...&lob_array[i], OCI_DTYPE_LOB...); /* initialize array of locators */ ... OCIBindByName(...,(void *) lob_array,...); OCIStmtExecute(...,10,...); /* 10 is the iters parameter */
記述子は、使用する前にOCIDescriptorAlloc()
関数によって割り当てる必要があります。ロケータの配列では、OCIDescriptorAlloc()
を使用して、各配列要素を初期化する必要があります。BLOB
、CLOB
およびNCLOB
を割り当てる場合は、OCI_DTYPE_LOB
をtype
パラメータとして使用します。BFILE
を割り当てる場合は、OCI_DTYPE_FILE
を使用します。
LOBデータの定義について
0 (ゼロ)以外のあらゆるサイズのLOBでSELECT
の定義が可能です。OCIDefineByPos()およびPL/SQLの定義を使用すると、使用可能な最大サイズのデータをLOB列から選択できます。1行に複数のLOBを含めることができるため、同一のSELECT
文中の各LOBに対して最大サイズのデータを選択できます。
例7-14と例7-15では、次のSQL文が基礎となっています。
CREATE TABLE lob_tab (C1 CLOB, C2 CLOB);
例7-14 実行前のLOBの定義
void select_define_before_execute() /* A function in an OCI program */ { /* The following is allowed */ ub1 buffer1[8000]; ub1 buffer2[8000]; text *select_sql = (text *)"SELECT c1, c2 FROM lob_tab"; OCIStmtPrepare(stmthp, errhp, select_sql, (ub4)strlen((char*)select_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (void *)buffer1, 8000, SQLT_LNG, (void *)0, (ub2 *)0, (ub2 *)0, (ub4) OCI_DEFAULT); OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (void *)buffer2, 8000, SQLT_LNG, (void *)0, (ub2 *)0, (ub2 *)0, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 1, 0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT); }
例7-15 実行後のLOBの定義
void select_execute_before_define() { /* The following is allowed */ ub1 buffer1[8000]; ub1 buffer2[8000]; text *select_sql = (text *)"SELECT c1, c2 FROM lob_tab"; OCIStmtPrepare(stmthp, errhp, select_sql, (ub4)strlen((char*)select_sql), (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT); OCIStmtExecute(svchp, stmthp, errhp, 0, 0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT); OCIDefineByPos(stmthp, &defhp[0], errhp, 1, (void *)buffer1, 8000, SQLT_LNG, (void *)0, (ub2 *)0, (ub2 *)0, (ub4) OCI_DEFAULT); OCIDefineByPos(stmthp, &defhp[1], errhp, 2, (void *)buffer2, 8000, SQLT_LNG, (void *)0, (ub2 *)0, (ub2 *)0, (ub4) OCI_DEFAULT); OCIStmtFetch(stmthp, errhp, 1, OCI_FETCH_NEXT, OCI_DEFAULT); }
PL/SQL出力変数の定義について
PL/SQLブロック内のSQL SELECT
文の選択リスト項目については、出力変数を定義するのに定義コールは使用しません。
かわりに、OCIバインド・コールを使用してください。
関連項目:
PL/SQL出力変数の定義の詳細は、「名前付きデータ型、REF定義およびPL/SQL OUTバインドの情報」を参照してください
OCIでの構造体配列のバインドと定義について
構造体配列の定義では、最初にOCIDefineByPos()
またはOCIDefineByPos2()
をコールする必要があります。
構造体配列操作に必要なskip
パラメータなどの各追加パラメータを設定するには、追加のOCIDefineArrayOfStruct()
コールが必要です。
構造体配列の使用により、複数行、複数列の操作の処理を簡素化できます。関連したスカラー・データ項目の構造体を作成し、データベースの値を構造体配列へフェッチしたり、これらの構造体配列の値をデータベースに挿入できます。
たとえば、あるアプリケーションでNAME
、AGE
およびSALARY
という列から、複数行のデータをフェッチする必要があるとします。アプリケーションでは、個別のフィールド(それぞれが、データベース表で1行に含まれているNAME
、AGE
およびSALARY
のデータを保持)を含む構造体の定義を組み込むことができます。アプリケーションでは、次に、これらの構造体配列へデータをフェッチします。
構造体配列を使用して、複数行、複数列の操作を実行するには、その操作に関連する各列を、構造体の中の1フィールドと関連付けておく必要があります。この関連付けは、OCIDefineArrayOfStruct()
コールおよびOCIBindArrayOfStruct()
コールの一部であり、データの格納場所を指定します。
スキップ・パラメータ
構造体配列間で列データを分割すると、それはデータベース内では連続した列データとして格納されなくなります。
単一の構造体配列は、いくつかのスカラー配列で構成されているかのようにデータを格納します。このため、バインドまたは定義を行う各フィールドにスキップ・パラメータを指定する必要があります。スキップ・パラメータは、構造体配列の中にある同じフィールドまでスキップするために必要なバイト数です。通常は、1つの構造体のバイト数と等しい値です。
図7-2は、スキップ・パラメータの決定方法を示しています。このケースでは、スキップ・パラメータは、field1
(2バイト)、field2
(4バイト)およびfield3
(2バイト)のサイズの合計8バイトです。このサイズは、1つの構造体のサイズと同じです。
オペレーティング・システムによっては、スキップ・パラメータをsizeof
(struct
)ではなく、sizeof
(one_array_element
)に設定することが必要な場合もあります。これは、コンパイラによっては、構造体に余分なバイトを挿入する場合があるからです。
ub4
とub1
という2つのフィールドで構成されたC構造体配列のケースを考えてみます。
struct demo { ub4 field1; ub1 field2; }; struct demo demo_array[MAXSIZE];
コンパイラによっては、この配列内の次の構造体を開始するub4
の位置を正しくするために、ub1
の後に3バイトの埋込みを挿入することがあります。このようなコンパイラでは、次の文を使用すると、誤った値が戻される可能性があります。
skip_parameter = sizeof(struct demo);
一部のオペレーティング・システムでは、これで8バイトの適切なスキップ・パラメータが戻されます。その他のシステムでは、この文でskip_parameter
が5に設定されます。後者の場合、スキップ・パラメータの正しい値を得るには次の文を使用します。
skip_parameter = sizeof(demo_array[0]);
この項には次のトピックが含まれます: 標準配列のスキップ・パラメータ。
構造体配列で使用されるOCIコール
構造体配列に関係する操作を行う際に使用するコールについて説明します。
構造体配列に関係する操作を行う際には、次の2つのOCIコールを使用する必要があります。
-
OCIBindArrayOfStruct()
は、入力変数の構造体配列でフィールドをバインドする場合に使用します。 -
OCIDefineArrayOfStruct()
は、出力変数の構造体配列を定義する場合に使用します。ノート:
構造体の配列のバインドまたは定義には、複数のコールが必要です。
OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
のコールはOCIBindArrayOfStruct()
のコールに先行し、OCIDefineByPos()
またはOCIDefineByPos2()
のコールはOCIDefineArrayOfStruct()
のコールに先行する必要があります。
構造体配列と標識変数
構造体配列を実装することによって、標識変数とリターン・コードの使用も可能になります。
フェッチ、挿入または更新された情報の配列に対応して、列レベルの標識変数とリターン・コードのパラレル配列を宣言できます。これらの配列には、独自のスキップ・パラメータを含めることができ、スキップ・パラメータは、OCIBindArrayOfStruct()
またはOCIDefineArrayOfStruct()
コール中に指定します。
プログラム値および標識変数の構造配列を設定する方法は多数あります。データベースの3列のデータを、3つのフィールドを含む構造配列にフェッチするアプリケーションについて考えてみます。この場合、3つのフィールドの標識変数の構造に対応する配列を設定でき、データベースからフェッチされるいずれかの列に対してそれぞれが列レベルの標識変数になります。インジケータ構造体内のフィールドと選択リスト項目数の間に1対1関係が存在する必要はありません。
複数バッファのバインドと定義について
単一のバインドまたは定義コールで使用する複数のバッファを指定できます。
様々な不連続アドレスに格納されているデータが1つの連続アドレスにコピーされることがないため、ラウンドトリップ数が減少し、パフォーマンスが向上します。これにより、CPU使用時間とメモリー使用量も減少します。
OCIIOV
データ型の定義は、次のとおりです。
typedef struct OCIIOV { void *bfp; /* The pointer to a buffer for the data */ ub4 bfl; /* The size of the buffer */ }OCIIOV;
複数バッファをバインドする場合、OCIBindByPos()
またはOCIBindByPos2()
、あるいはOCIBindByName()
またはOCIBindByName2()
関数でmode
パラメータに値OCI_IOV
を使用します。mode
にこの値を指定する場合は、パラメータvaluep
でOCIIOV
のアドレスを渡す必要があります。データ型のサイズは、パラメータvaluesz
で渡す必要があります。たとえば、次のようにします。
OCIIOV vecarr[NumBuffers]; ... /* For bind at position 1 with data type int */ OCIBindByPos(stmthp, bindp, errhp, 1, (void *)&vecarr[0], sizeof(int), ... OCI_IOV); ...
複数バッファを定義する場合、OCIDefineByPos()
またはOCIDefineByPos2()
関数でmodeパラメータに値OCI_IOV
を使用します。mode
にこの値を指定する場合は、パラメータvaluep
でOCIIOV
のアドレスを渡します。データ型のサイズは、パラメータvaluesz
で渡す必要があります。このモードは、分散または集約のバインドに使用されることが目的であり、複数のバッファを1つの位置にバインドまたは定義できます。たとえば、列Aの最初の10行を1つのバッファに、次の5行を1つのバッファに、そして残りの25行を別のバッファにバインドまたは定義できます。これにより、配列の実行操作を行う場合に、それらのすべてを1つの大きなバッファに割り当ててコピーする必要がなくなります。
例7-16は、構造体OCIIOV
とそのmode
値の使用例です。
例7-16 複数のバインドおよび定義バッファの使用
/* The following macros mention the maximum length of the data in the * different buffers. */ #define LENGTH_DATE 10 #define LENGTH_EMP_NAME 100 /* These two macros represent the number of elements in each bind and define array */ #define NUM_BIND 30 #define NUM_DEFINE 45 /* The bind buffers for inserting dates */ char buf_1[NUM_BIND][LENGTH_DATE], char buf_2[NUM_BIND * 2][LENGTH_DATE], /* The bind buffer for inserting emp name */ char buf_3[NUM_BIND * 3][LENGTH_EMP_NAME], /* The define buffers */ char buf_4[NUM_DEFINE][LENGTH_EMP_NAME]; char buf_5[NUM_DEFINE][LENGTH_EMP_NAME]; /* The size of data value for buffers corresponding to the same column must be the same, and that value is passed in the OCIBind or Define calls. buf_4 and buf_5 above have the same data values; that is, LENGTH_EMP_NAME although the number of elements are different in the two buffers. */ OCIBind *bndhp1 = (OCIBind *)0; OCIBind *bndhp2 = (OCIBind *)0; OCIDefine *defhp = (OCIDefine *)0; OCIStmt *stmthp = (OCIStmt *)0; OCIError *errhp = (OCIError *)0; OCIIOV bvec[2], dvec[2]; /* Example of how to use indicators and return codes with this feature, showing the allocation when using with define. You allocate memory for indicator, return code, and the length buffer as one chunk of NUM_DEFINE * 2 elements. */ short *indname[NUM_DEFINE*2]; /* indicators */ ub4 *alenname[NUM_DEFINE*2]; /* return lengths */ ub2 *rcodename[NUM_DEFINE*2]; /* return codes */ static text *insertstr = "INSERT INTO EMP (EMP_NAME, JOIN_DATE) VALUES (:1, :2)"; static text *selectstr = "SELECT EMP_NAME FROM EMP"; /* Allocate environment, error handles, and so on, and then initialize the environment. */ ... /* Prepare the statement with the insert query in order to show the binds. */ OCIStmtPrepare (stmthp, errhp, insertstr, (ub4)strlen((char *)insertstr), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); /* Populate buffers with values. The following represents the simplest * way of populating the buffers. However, in an actual scenario * these buffers may have been populated by data received from different * sources. */ /* Store the date in the bind buffers for the date. */ strcpy(buf_1[0], "21-SEP-02"); ... strcpy(buf_1[NUM_BIND - 1], "21-OCT-02"); ... strcpy(buf_2[0], "22-OCT-02"); ... strcpy(buf_2[2*NUM_BIND - 1], "21-DEC-02"); ... memset(bvec[0], 0, sizeof(OCIIOV)); memset(bvec[1], 0, sizeof(OCIIOV)); /* Set up the addresses in the IO Vector structure */ bvec[0].bfp = buf_1[0]; /* Buffer address of the data */ bvec[0].bfl = NUM_BIND*LENGTH_DATE; /* Size of the buffer */ /* And so on for other structures as well. */ bvec[1].bfp = buf_2[0]; /* Buffer address of the data */ bvec[1].bfl = NUM_BIND*2*LENGTH_DATE; /* Size of the buffer */ /* Do the bind for date, using OCIIOV */ OCIBindByPos (stmthp, &bindhp2, errhp, 2, (void *)&bvec[0], sizeof(buf_1[0]), SQLT_STR, (void *)inddate, (ub2 *)alendate, (ub2 *)rcodedate, 0, (ub4 *)0, OCI_IOV); /* Store the employee names in the bind buffers, 3 for the names */ strcpy (buf_3[0], "JOHN "); ... strcpy (buf_3[NUM_BIND *3 - 1], "HARRY"); /* Do the bind for employee name */ OCIBindByPos (stmthp, &bindhp1, errhp, 1, buf_3[0], sizeof(buf_3[0]), SQLT_STR, (void *)indemp, (ub2 *)alenemp, (ub2 *)rcodeemp, 0, (ub4 *)0, OCI_DEFAULT); OCIStmtExecute (svchp, stmthp, errhp, NUM_BIND*3, 0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT); ... /* Now the statement to depict defines */ /* Prepare the statement with the select query in order to show the defines */ OCIStmtPrepare(stmthp, errhp, selectstr,(ub4)strlen((char *)selectstr), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); memset(dvec[0], 0, sizeof(OCIIOV); memset(dvec[1], 0, sizeof(OCIIOV)); /* Set up the define vector */ dvec[0].bfp = buf_4[0]; dvec[0].bfl = NUM_DEFINE*LENGTH_EMP_NAME; dvec[1].bfp = buf_5[0]; dvec[1].bfl = NUM_DEFINE*LENGTH_EMP_NAME; /* Pass the buffers for the indicator, length of the data, and the return code. Note that the buffer where you receive the data is split into two locations, each having NUM_DEFINE number of elements. However, the indicator buffer, the actual length buffer, and the return code buffer comprise a single chunk of NUM_DEFINE * 2 elements. */ OCIDefineByPos (stmthp, &defhp, errhp, 1, (void *)&dvec[0], sizeof(buf_4[0]), SQLT_STR, (void *)indname, (ub2 *)alenname, (ub2 *)rcodename, OCI_IOV); OCIStmtExecute (svchp, stmthp, errhp, NUM_DEFINE*2, 0, (OCISnapshot*)0, (OCISnapshot*)0, OCI_DEFAULT); ...
OCIでのRETURNING句を使用したDML
この項では、RETURNING
句を使用してDML文を正しく実装するためのルールについて、その概要を説明します。
OCIでは、SQL INSERT
文、UPDATE
文およびDELETE
文でのRETURNING
句の使用をサポートします。
関連項目:
-
完全な例は、Oracleインストールに含まれているデータベース・デモ・プログラムを参照してください。詳細は、「OCIデモ・プログラム」を参照してください。
-
INSERT
文、UPDATE
文またはDELETE
文でのRETURNING
句の使用の詳細は、『Oracle Database SQL言語リファレンス』を参照してください
2つのSQL文を結合するためのDMLでのRETURNING句の使用について
DML文でRETURNING
句を使用することにより、2つのSQL文を1つに結合でき、サーバー・ラウンドトリップを削減できます。
これは、追加の句を従来のUPDATE
文、INSERT
文およびDELETE
文に追加することで実行できます。追加の句では、問合せをDML文に効果的に追加します。
OCIでは、OUT
バインド変数として値がアプリケーションに戻されます。次の例では、バインド変数の前にコロン":"が付いています。これらの例では、col1
、col2
およびcol3
という列を含む表table1
を想定しています。
次の文では、新しい値をデータベースに挿入して、影響を受ける行の列値をデータベースから取り出します。これにより、挿入された行を操作できます。
INSERT INTO table1 VALUES (:1, :2, :3) RETURNING col1, col2, col3 INTO :out1, :out2, :out3
次の例は、col1
の値がある範囲内にあるすべての列の値を更新し、変更による影響を受けた行を戻します。
UPDATE table1 SET col1 = col1 + :1, col2 = :2, col3 = :3 WHERE col1 >= :low AND col1 <= :high RETURNING col1, col2, col3 INTO :out1, :out2, :out3
DELETE
文では、col1
の値が特定の範囲内にある行を削除し、それらの行からデータを戻します。
DELETE FROM table1 WHERE col1 >= :low AND col2 <= :high RETURNING col1, col2, col3 INTO :out1, :out2, :out3
RETURNING...INTO変数のバインドについて
UPDATE
文およびDELETE
文は表内の複数行に影響し、DML文は1回のOCIStmtExecute()
コールで複数回実行できるため、どれだけのデータが戻ってくるのかを実行時に判断できない場合があります。
そのため、RETURNING
...INTO
プレースホルダに対応する変数は、OCI_DATA_AT_EXEC
モードでバインドする必要があります。アプリケーションでは、ポーリング・メカニズムを使用するのではなく、アプリケーション自体の動的データ処理コールバックを定義する必要があります。
RETURNING句は、LOBを操作するときに特に役立ちます。通常、アプリケーションでは、空のLOBロケータをデータベースに挿入し、再度選択して操作を行います。RETURNING
句を使用することで、アプリケーションではこれら2つのステップを1つの文に結合できます。
INSERT INTO some_table VALUES (:in_locator) RETURNING lob_column INTO :out_locator
OCIアプリケーションは、RETURNING
句のプレースホルダを純OUT
バインド変数として実装します。ただし、RETURNING
句のすべてのバインドの初期値はIN
であるため、適切に初期化する必要があります。有効な値を設定するために、NULL
インジケータを使用して、そのインジケータを-1に設定できます。
アプリケーションでは、RETURNING
句のバインド変数を操作するとき、次のルールに従う必要があります。
-
OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
を使用して、OCI_DATA_AT_EXEC
モードでRETURNING
句のプレースホルダをバインドした後で、各プレースホルダに対してOCIBindDynamic()
をコールします。 -
RETURNING
句のプレースホルダをバインドする場合は、OCIBindDynamic()
コールのocbfp
パラメータとして、有効なOUT
バインド関数を指定します。この関数では、戻されたデータを保持するための記憶域が必要です。 -
OCIBindDynamic()
コールのicbfp
パラメータによって、コール時にNULL
値を戻すデフォルトの関数を提供する必要があります。 -
OCIBindDynamic()
のpiecep
パラメータを、OCI_ONE_PIECE
に設定します。
RETURNING
句を使用したDML文では、重複バインドは使用できず、文のDMLセクションのバインド変数と、RETURNING
セクションのバインド変数の間では重複は認められません。
ノート:
OCIでは、RETURNING
句バインドのコールバック・メカニズムのみがサポートされています。ポーリング・メカニズムはサポートされていません。
OCIエラー処理
OCIBindDynamic()
に提供されたOUT
バインド関数には、エラー時に文の結果の一部を受け取る準備が必要です。
10回実行されるDML文をアプリケーションで発行し、5回目の実行中にエラーが発生した場合、Oracle Databaseは、1回目から4回目までのデータを戻します。コールバック関数は、最初の4回のデータを受け取るためにさらにコールされます。
OCIでのRETURNING REF...INTO句を使用したDML
RETURNING
句を使用して、データベースに挿入するオブジェクトやデータベース内で更新するオブジェクトにREF
を戻すこともできます。
UPDATE extaddr e SET e.zip = '12345', e.state ='AZ' WHERE e.state = 'CA' AND e.zip = '95117' RETURNING REF(e), zip INTO :addref, :zip
前述の文では、オブジェクト表内のオブジェクトの属性をいくつか更新し、RETURNING
句でオブジェクトへのREF
(およびスカラーZIPコード)を戻します。
この項には次のトピックが含まれます。「出力変数のバインド」。
出力変数のバインド
OCIアプリケーションでREF
出力変数をバインドするには、3つのステップが必要です。
例7-17の次の疑似コードは、前の3ステップに必要なバインドを実行する関数を示します。
- 初期バインド情報を、
OCIBindByName()
またはOCIBindByName2()
を使用して設定します。 REF
(型の記述オブジェクト(TDO)を含む)に対する追加のバインド情報は、OCIBindObject()
を使用して設定します。OCIBindDynamic()
に対するコールを作成します。
例7-17 OCIアプリケーションでのREF出力変数のバインド
sword bind_output(stmthp, bndhp, errhp) OCIStmt *stmthp; OCIBind *bndhp[]; OCIError *errhp; { ub4 i; /* get TDO for BindObject call */ if (OCITypeByName(envhp, errhp, svchp, (CONST text *) 0, (ub4) 0, (CONST text *) "ADDRESS_OBJECT", (ub4) strlen((CONST char *) "ADDRESS_OBJECT"), (CONST text *) 0, (ub4) 0, OCI_DURATION_SESSION, OCI_TYPEGET_HEADER, &addrtdo)) { return OCI_ERROR; } /* initial bind call for both variables */ if (OCIBindByName(stmthp, &bndhp[2], errhp, (text *) ":addref", (sb4) strlen((char *) ":addref"), (void *) 0, (sb4) sizeof(OCIRef *), SQLT_REF, (void *) 0, (ub2 *)0, (ub2 *)0, (ub4) 0, (ub4 *) 0, (ub4) OCI_DATA_AT_EXEC) || OCIBindByName(stmthp, &bndhp[3], errhp, (text *) ":zip", (sb4) strlen((char *) ":zip"), (void *) 0, (sb4) MAXZIPLEN, SQLT_CHR, (void *) 0, (ub2 *)0, (ub2 *)0, (ub4) 0, (ub4 *) 0, (ub4) OCI_DATA_AT_EXEC)) { return OCI_ERROR; } /* object bind for REF variable */ if (OCIBindObject(bndhp[2], errhp, (OCIType *) addrtdo, (void **) &addrref[0], (ub4 *) 0, (void **) 0, (ub4 *) 0)) { return OCI_ERROR; } for (i = 0; i < MAXCOLS; i++) pos[i] = i; /* dynamic binds for both RETURNING variables */ if (OCIBindDynamic(bndhp[2], errhp, (void *) &pos[0], cbf_no_data, (void *) &pos[0], cbf_get_data) || OCIBindDynamic(bndhp[3], errhp, (void *) &pos[1], cbf_no_data, (void *) &pos[1], cbf_get_data)) { return OCI_ERROR; } return OCI_SUCCESS; }
OCIコールバックに関するその他のノート
コールバック関数がコールされるとき、バインド・ハンドルのOCI_ATTR_ROWS_RETURNED
属性により、アプリケーションでは、ある特定の反復処理で戻される行数がわかります。
最初のコールバックの反復処理中に、そのバインド変数に対して戻されるすべての行に領域を割り当てることができます。同一の反復処理内で後続のコールバックを実行しているときは、割り当てられた領域内の正確なメモリーまでバッファ・ポインタを増やします。
OCIでのDML RETURNING文に対する配列インタフェース
OCIでは、各反復処理で複数の行を戻す単一行DML操作および配列DML操作に追加の機能性を提供します。
この機能を利用するには、バインド・コールのOUTバッファを、OCIStmtExecute()
コールで指定した反復件数以上の値に指定する必要があります。これは、コールバックを介してバインド・バッファに提供される追加機能です。
いずれかの反復処理で複数の行が戻された場合は、アプリケーションにOCI_SUCCESS_WITH_INFO
リターン・コードが戻されます。この場合、DML操作は正常に完了しています。このとき、アプリケーションでは、トランザクションをロールバックするか、警告を無視します。
関連項目:
OCIバインドおよび定義における文字変換
この項では、クライアントとサーバー間での文字変換に関する問題について説明します。
文字セットの選択について
文字データを含むデータベースの列がCHAR
、VARCHAR2
、NCHAR
またはNVARCHAR2
列として定義されている場合、その列に関連するバインドまたは定義では、文字セット仕様の処理について特別な配慮が必要です。
クライアントとサーバーの文字セットの幅が異なる場合、および適切に文字変換を行うために、これらの考慮が必要です。異なる文字セット間でデータを変換する間に、データ・サイズが、最大で4倍に増加したり4分の1に縮小する場合があります。データを保存するためのバッファのサイズを十分に確保してください。
Oracle Database 12cリリース2 (12.2)以上では、OCIに2つのサービス・コンテキスト・ハンドル属性のOCI_ATTR_MAX_CHARSET_RATIO
およびOCI_ATTR_MAX_NCHARSET_RATIO
が用意されており、それぞれサーバー文字セットからクライアント文字セットまたは各国語文字セットへの最大文字セット拡張率を取得できます。これらの属性を使用すると、変換の前に最適なメモリー・バッファを効率的に割り当てることができるため、データがデータベースから戻されたときにそれを保持するための十分な領域を割り当てることができます。サーバーとクライアント間の文字セットまたは各国語文字セットが異なる場合に、これらの属性を使用すると便利です。
size_t cratio;
OCIAttrGet((void *)svchp, (ub4)OCI_HTYPE_SVCCTX, (size_t *)&cratio, (ub4) 0, OCI_ATTR_MAX_CHARSET_RATIO, errhp);
printf("Conversion ratio from server to client character set is %d\n", cratio);
Conversion ratio from server to client character set is 2
size_t cratio;
OCIAttrGet((void *)svchp, (ub4)OCI_HTYPE_SVCCTX, (size_t *)&cratio, (ub4) 0, OCI_ATTR_MAX_NCHARSET_RATIO, errhp);
printf("Conversion ratio from server to client ncharset is %d\n", cratio);
Conversion ratio from server to client ncharset is 1
アプリケーションでは、CHAR
やVARCHAR2
、またはNCHAR
やNVARCHAR2
データをバイト数(通常の場合)ではなく文字数で処理する方が簡単な場合があります。
文字セット・フォームと文字セットID
各OCIバインドおよび定義ハンドルには、OCI_ATTR_CHARSET_FORM
属性およびOCI_ATTR_CHARSET_ID
属性が関連付けられています。
アプリケーションでは、バインドまたは定義バッファのキャラクタ・フォームおよび文字セットIDを指定するために、OCIAttrSet()
コールを使用してこれらの属性を設定できます。
csform
属性(OCI_ATTR_CHARSET_FORM
)は、バインドの場合はクライアント・バッファの文字セットを示し、定義の場合はフェッチされたデータを格納する文字セットを示します。この属性の値は、次のうちいずれかになります。
-
SQLCS_IMPLICIT
: デフォルト値で、バインド・バッファまたは定義バッファ用のデータベース文字セットIDを示し、文字バッファ・データはサーバーのデータベース文字セットに変換されます。 -
SQLCS_NCHAR
: バインド・バッファまたは定義バッファ用の各国語文字セットIDを示し、クライアントのバッファ・データはサーバーの各国語文字セットに変換されます。
文字セットID属性OCI_ATTR_CHARSET_ID
が指定されていない場合は、csform
の値に応じて、クライアントのデータベース文字セットIDまたは各国語文字セットIDのデフォルト値が使用されます。これらはそれぞれ、環境変数NLS_LANG
またはNLS_NCHAR
で指定された値です。
ノート:
-
クライアント側の文字セットIDに関係なく、データはサーバーのデータベース文字セットIDまたは各国語文字セットIDに従って変換され、データベースに挿入されます。
-
OCI_ATTR_CHARSET_ID
に0 (ゼロ)を設定することはできません。 -
定義ハンドル属性
OCI_ATTR_CHARSET_FORM
およびOCI_ATTR_CHARSET_ID
は、LOBタイプには影響しません。サーバーからフェッチされたLOBロケータは、元のcsform
を保持します。これらの属性に基づく定義変換の一部として、CLOB
/NCLOB
変換は行われません。
関連項目:
-
NCHAR
データの詳細は、『Oracle Database SQL言語リファレンス』 を参照してください
OCIでのクライアント文字セットの設定について
クライアント文字セットは、OCIEnvNlsCreate()
関数のcharset
パラメータおよびncharset
パラメータを使用して設定できます。
両方のパラメータともOCI_UTF16ID
を設定できます。charset
パラメータでは、メタデータおよびCHAR
データのコーディングが制御されます。ncharset
パラメータでは、NCHAR
データのコーディングが制御されます。OCINlsEnvironmentVariableGet()
関数は、NLS_LANG
の文字セットおよびNLS_NCHAR
の各国語文字セットを戻します。
例7-18は、これらの関数の使用例です(OCIには、UTF-16データのバインドと定義を容易にするために、utext
と呼ばれるtypedefが用意されています)。
例7-18 OCIでのクライアント文字セットのOCI_UTF16IDへの設定
OCIEnv *envhp; ub2 ncsid = 2; /* we8dec */ ub2 hdlcsid, hdlncsid; OraText thename[20]; utext *selstmt = L"SELECT ename FROM emp"; /* UTF16 statement */ OCIStmt *stmthp; OCIDefine *defhp; OCIError *errhp; OCIEnvNlsCreate(OCIEnv **envhp, ..., OCI_UTF16ID, ncsid); ... OCIStmtPrepare(stmthp, ..., selstmt, ...); /* prepare UTF16 statement */ OCIDefineByPos(stmthp, defnp, ..., 1, thename, sizeof(thename), SQLT_CHR,...); OCINlsEnvironmentVariableGet(&hdlcsid, (size_t)0, OCI_NLS_CHARSET_ID, (ub2)0, (size_t*)NULL); OCIAttrSet(defnp, ..., &hdlcsid, 0, OCI_ATTR_CHARSET_ID, errhp); /* change charset ID to NLS_LANG setting*/ ...
OCIでの変数のバインドについて
更新操作または挿入操作は、変数のバインドによって行われます。
変数をバインドするときは、バインド・ハンドルのOCI_ATTR_MAXCHAR_SIZE
属性およびOCI_ATTR_MAXDATA_SIZE
属性を指定して、Oracle Databaseにデータを挿入するときに使用するバイト制約と文字制約を指定します。
これらの属性は、次のように定義します。
-
OCI_ATTR_MAXDATA_SIZE
属性では、サーバー側のバッファで許容される最大バイト数を設定します。 -
OCI_ATTR_MAXCHAR_SIZE
属性では、サーバー側のバッファで許容される最大文字数を設定します。
関連項目:
-
詳細は、「OCI_ATTR_MAXDATA_SIZE属性の使用について」を参照してください
-
詳細は、「OCI_ATTR_MAXCHAR_SIZE属性の使用について」を参照してください
OCI_ATTR_MAXDATA_SIZE属性の使用について
すべてのバインド・ハンドルにはOCI_ATTR_MAXDATA_SIZE
属性があり、文字セットの変換後、クライアント側バインド・データを格納するためにサーバーに割り当てるバイト数が指定されます。
通常、アプリケーションでは、使用方法に応じて、OCI_ATTR_MAXDATA_SIZE
に、列の最大サイズまたはPL/SQL変数のサイズを設定します。OCI_ATTR_MAXDATA_SIZE
が変換後のデータを格納するのに十分でない場合、エラーが発生します。
IN/INOUT
バインドの場合、OCI_ATTR_MAXDATA_SIZE
属性を設定するには、バインド・バッファは、文字セットの各文字のバイト数を乗算した文字数を保持できる大きさである必要があります。
OCI_ATTR_MAXCHAR_SIZE
が100などの0 (ゼロ)以外の値に設定されている場合で、文字セットの各文字が2バイトである場合は、割当て可能な最小サイズは200バイトになります。
次のシナリオで、OCI_ATTR_MAXDATA_SIZE
属性のいくつかの使用方法を説明します。
-
シナリオ1:
CHAR
(ソース・データ)から非CHAR
(宛先列)への変換データに対して暗黙的バインド変換が行われます。ソース・バッファ・サイズに、クライアント側とOracle Database側の文字セット間の最悪の膨張率を乗算した値を、
OCI_ATTR_MAXDATA_SIZE
の値として設定することをお薦めします。 -
シナリオ2:
CHAR
(ソース・データ)からCHAR
(宛先列)または非CHAR
(ソース・データ)からCHAR
(宛先列)への変換列のサイズを、
OCI_ATTR_MAXDATA_SIZE
の値として設定することをお薦めします。 -
シナリオ3: CHAR (ソース・データ)からPL/SQL変数への変換
このケースでは、PL/SQL変数のサイズを、
OCI_ATTR_MAXDATA_SIZE
の値として設定することをお薦めします。
OCI_ATTR_MAXCHAR_SIZE属性の使用について
OCI_ATTR_MAXCHAR_SIZE
を使用すると、バイト数ではなく文字数でデータを処理できます。
バインドについては、OCI_ATTR_MAXCHAR_SIZE
属性を使用して、Oracle Databaseに確保する文字数を設定し、バインド・データを格納します。
たとえば、OCI_ATTR_MAXDATA_SIZEが100に設定され、OCI_ATTR_MAXCHAR_SIZE
が0 (ゼロ)に設定されている場合、Oracle Databaseでのデータの変換後に可能な最大サイズは100バイトです。ただし、OCI_ATTR_MAXDATA_SIZE
が300に設定され、OCI_ATTR_MAXCHAR_SIZE
が100などの0 (ゼロ)以外の値に設定されている場合で、文字セットに2バイトまたは2文字がある場合は、割当て可能な最大サイズは200バイトになります。
定義については、クライアント・アプリケーションのバッファに戻すことができる最大文字数をOCI_ATTR_MAXCHAR_SIZE
属性で指定します。導出されたバイト長は、OCIDefineByPos()
またはOCIDefineByPos2()
コールで指定されたmaxlength
パラメータを上書きします。
ノート:
バインド・コールまたは定義コールで指定されたバッファ長は、OCI_ATTR_MAXCHAR_SIZE
属性の値に関係なく、常にバイト数による長さです。また、送受信する実際の長さもバイト数になります。
関連項目:
OCIバインド時のバッファの拡張
OUT
バインドまたはPL/SQLバインドの場合は、OCI_ATTR_MAXDATA_SIZE
を設定しないでください。OCI_ATTR_MAXDATA_SIZE
は、INSERT
文またはUPDATE
文の場合のみ設定してください。
いずれの属性も設定されていない場合、OCIでは最適な見積りを行ってバッファを拡張します。
INバインド
IN
バインドの場合、基礎となる列が文字長セマンティクスを使用して作成されている場合は、OCI_ATTR_MAXCHAR_SIZE
を使用して制約を指定することをお薦めします。
実際のバッファに含まれる文字数がOCI_ATTR_MAXCHAR_SIZE
で指定した文字数より少なければ、OCIレベルでの制約違反は発生しません。
基礎となる列がバイト長セマンティクスを使用して作成されている場合は、バインド・ハンドルのOCI_ATTR_MAXDATA_SIZE
を使用して、サーバーでのバイト制約を指定します。OCI_ATTR_MAXCHAR_SIZE
の値も指定している場合は、この制約がサーバー側の受信バッファの割当て時に適用されます。
動的SQL
動的SQLの場合は、パラメータ・ハンドルのOCI_ATTR_DATA_SIZE
およびOCI_ATTR_CHAR_SIZE
を取得するための明示的な記述を、バインド・ハンドルのOCI_ATTR_MAXDATA_SIZE
属性およびOCI_ATTR_MAXCHAR_SIZE
属性を設定するためのガイドとして使用できます。
OCI_ATTR_MAXDATA_SIZE
およびOCI_ATTR_MAXCHAR_SIZE
を、実際の列幅より少ない値(バイト数または文字数)に設定することをお薦めします。
挿入時のバッファの拡張
OCI_ATTR_MAXDATA_SIZE
を使用して、挿入時のバッファ拡張による予期しない動作の発生を回避します。
データベース列に文字長セマンティクスがあり、その列にユーザーがOCIBindByPos()
またはOCIBindByPos2()
、あるいはOCIBindByName()
またはOCIBindByName2()
を使用してデータの挿入を試み、OCI_ATTR_MAXCHAR_SIZE
のみを3000バイトに設定したとします。データベース文字セットはUTF8で、クライアント文字セットはASCIIです。この場合、クライアント側ではサイズが3000バイトのバッファに3000文字が入りますが、サーバー側ではバッファが拡張されて4000バイトを超えることになります。基礎となる列がLONG
型またはLOB型でないかぎり、サーバーでエラーが発生します。この問題を回避するには、OCI_ATTR_MAXDATA_SIZE
を4000に指定して、Oracle Databaseが4000バイトを決して超えないことを保証します。
定義時の制約チェック
OCIでは、列からクライアント・バッファへのデータ選択に事前定義の変数が使用されます。
OCI_ATTR_MAXCHAR_SIZE
値を設定して、その定義バッファに対して文字長の追加の制約を適用できます。バイト単位のバッファ・サイズはバイト長の制限として機能するため、定義ハンドルに対するOCI_ATTR_MAXDATA_SIZE
属性はありません。OCIDefineByPos()
またはOCIDefineByPos2()
コールで指定された定義バッファのサイズをバイト制約として使用できます。
関連項目:
動的SQLでの選択
動的SQLのバッファ・サイズを指定するときは、切捨てによるデータの損失を回避するために、常に暗黙的な記述でOCI_ATTR_DATA_SIZE
値を使用します。
データベース列が文字長セマンティクスを使用して作成されている場合(OCI_ATTR_CHAR_USED
属性によって判明します)は、OCI_ATTR_MAXCHAR_SIZE
値を使用して、定義バッファに追加の制約を設定できます。OCI_ATTR_MAXCHAR_SIZE
値の最大文字数がバッファに配置されます。
戻される長さ
次の長さの値は、データベースの文字長セマンティクスに関係なく、常にバイト単位で戻されます。
-
alen
で戻される値、つまり、バインドと定義での実際の長さフィールド -
VARCHAR
やLONG
VARCHAR
など、特殊なデータ型の前に付けられる長さの値 -
切捨てが発生した場合のための標識変数の値
この規則の唯一の例外は、OCI_UTF16ID
文字セットIDの文字列バッファで、戻される長さはUTF-16単位になります。
ノート:
バインド・コールと定義コールのバッファ・サイズ、OCIStmtGetPieceInfo()
とOCIStmtSetPieceInfo()
のピース・サイズおよびコールバックは常にバイト単位になります。
OCIでの文字長セマンティクスの互換性に関する一般的な問題
OCIでの文字長セマンティクスは、Oracle Databaseのリリース(リリース9.0以上とリリース8.1以下)に依存します。
-
リリース8.1以下のOracle Databaseと通信するリリース9.0以上のクライアントの場合は、
OCI_ATTR_MAXCHAR_SIZE
はOracle Databaseでは認識されないため、この値は無視されます。この値のみを指定した場合、OCIでは、クライアント側文字セットの各文字の最大バイト数に基づいて、対応するOCI_ATTR_MAXDATA_SIZE
値が導出されます。 -
リリース9.0以上のOracle Databaseと通信するリリース8.1以下のクライアントの場合は、クライアントで
OCI_ATTR_MAXCHAR_SIZE
値を指定できないため、Oracle Databaseではクライアントが常にバイト長セマンティクスを使用しているものとみなします。これは、クライアントでOCI_ATTR_MAXDATA_SIZE
のみを指定した場合に類似しています。
いずれの場合も、Oracle Databaseとクライアントは適切な方法で情報を交換できます。
OCI_ATTR_MAXCHAR_SIZEを使用した挿入と選択のコード例
列がN
個の文字を指定して作成されると、データベースでの実際の割当てでは、最悪のケース・シナリオが検討されます。
この例を例7-19に示します。割り当てられる実際のバイト数は、N
の倍数、つまりN
のM
倍です。現在、UTF8では各文字に割り当てられる最大バイト数として、M
が3に設定されています。
たとえば、例7-19では、EMP
表で、ENAME
列が30文字として定義され、ADDRESS
列が80文字として定義されています。これに対応するデータベースでのバイト長はそれぞれ、M×30または3×30 = 90、およびM×80または3×80 = 240になります。
例7-19 OCI_ATTR_MAXCHAR_SIZE属性を使用した挿入および選択操作
... utext ename[31], address[81]; /* E' <= 30+ 1, D' <= 80+ 1, considering null-termination */ sb4 ename_max_chars = EC=20, address_max_chars = ED=60; /* EC <= (E' - 1), ED <= (D' - 1) */ sb4 ename_max_bytes = EB=80, address_max_bytes = DB=200; /* EB <= M * EC, DB <= M * DC */ text *insstmt = (text *)"INSERT INTO EMP(ENAME, ADDRESS) VALUES (:ENAME, \ :ADDRESS)"; text *selstmt = (text *)"SELECT ENAME, ADDRESS FROM EMP"; ... /* Inserting Column Data */ OCIStmtPrepare(stmthp1, errhp, insstmt, (ub4)strlen((char *)insstmt), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); OCIBindByName(stmthp1, &bnd1p, errhp, (text *)":ENAME", (sb4)strlen((char *)":ENAME"), (void *)ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *)alenp, (ub2 *)rcodep, (ub4)maxarr_len, (ub4 *)curelep, OCI_DEFAULT); /* either */ OCIAttrSet((void *)bnd1p, (ub4)OCI_HTYPE_BIND, (void *)&ename_max_bytes, (ub4)0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); /* or */ OCIAttrSet((void *)bnd1p, (ub4)OCI_HTYPE_BIND, (void *)&ename_max_chars, (ub4)0, (ub4)OCI_ATTR_MAXCHAR_SIZE, errhp); ... /* Retrieving Column Data */ OCIStmtPrepare(stmthp2, errhp, selstmt, strlen((char *)selstmt), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); OCIDefineByPos(stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof (ename), SQLT_STR, (void *)&selname_ind, (ub2 *)alenp, (ub2 *)rcodep, (ub4)OCI_DEFAULT); /* if not called, byte semantics is by default */ OCIAttrSet((void *)dfn1p, (ub4)OCI_HTYPE_DEFINE, (void *)&ename_max_chars, (ub4)0, (ub4)OCI_ATTR_MAXCHAR_SIZE, errhp); ...
UTF-16のバインドと定義のコード例
CHAR
またはVARCHAR2
のバインドおよび定義、またはNCHAR
またはNVARCHAR2
改良型ハンドルにおける文字セットIDは、すべてのデータがUTF-16 (Unicode)エンコーディングで渡されるものとして設定できます。UTF-16を指定するには、OCI_ATTR_CHARSET_ID
= OCI_UTF16ID
を設定します。
OCIには、UTF-16データのバインドと定義を容易にするため、utext
と呼ばれるtypedefが用意されています。utext
の内部表現は、16ビットの符号なし整数(ub2
)です。wchar_t
データ型のコード体系がUTF-16に準拠しているオペレーティング・システムでは、キャスト演算子を使用してutext
をwchar_t
データ型に容易に変換できます。
UTF-16データの場合でも、バインド・コールと定義コールでのバッファ・サイズはバイト単位とみなされます。utext
データ型は、入出力データのバッファとして使用してください。
例7-20では、UTF-16データのバインドと定義を説明する擬似コードを示しています。
例7-20 UTF-16データのバインドと定義
... OCIStmt *stmthp1, *stmthp2; OCIDefine *dfn1p, *dfn2p; OCIBind *bnd1p, *bnd2p; text *insstmt= (text *) "INSERT INTO EMP(ENAME, ADDRESS) VALUES (:ename, :address)"; \ text *selname = (text *) "SELECT ENAME, ADDRESS FROM EMP"; utext ename[21]; /* Name - UTF-16 */ utext address[51]; /* Address - UTF-16 */ ub2 csid = OCI_UTF16ID; sb4 ename_col_len = 20; sb4 address_col_len = 50; ... /* Inserting UTF-16 data */ OCIStmtPrepare (stmthp1, errhp, insstmt, (ub4)strlen ((char *)insstmt), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); OCIBindByName (stmthp1, &bnd1p, errhp, (text*)":ENAME", (sb4)strlen((char *)":ENAME"), (void *) ename, sizeof(ename), SQLT_STR, (void *)&insname_ind, (ub2 *) 0, (ub2 *) 0, (ub4) 0, (ub4 *)0, OCI_DEFAULT); OCIAttrSet ((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); OCIAttrSet((void *) bnd1p, (ub4) OCI_HTYPE_BIND, (void *) &ename_col_len, (ub4) 0, (ub4)OCI_ATTR_MAXDATA_SIZE, errhp); ... /* Retrieving UTF-16 data */ OCIStmtPrepare (stmthp2, errhp, selname, strlen((char *) selname), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT); OCIDefineByPos (stmthp2, &dfn1p, errhp, (ub4)1, (void *)ename, (sb4)sizeof(ename), SQLT_STR, (void *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT); OCIAttrSet ((void *) dfn1p, (ub4) OCI_HTYPE_DEFINE, (void *) &csid, (ub4) 0, (ub4)OCI_ATTR_CHARSET_ID, errhp); ...
関連項目:
OCIでのPL/SQL REF CURSORおよびNESTED TABLE
OCIには、PL/SQL REF
CURSOR
およびNESTED TABLEを、バインドおよび定義する機能があります。
アプリケーションでは、これらの型の変数をハンドルおよび定義するために、文ハンドルを使用できます。次の例で、このPL/SQLブロックを考えてみます。
static const text *plsql_block = (text *) "begin \ OPEN :cursor1 FOR SELECT employee_id, last_name, job_id, manager_id, \ salary, department_id \ FROM employees WHERE job_id=:job ORDER BY employee_id; \ OPEN :cursor2 FOR SELECT * FROM departments ORDER BY department_id; end;";
アプリケーションは、OCIHandleAlloc()
をコールして、バインドのための文ハンドルを割り当て、次のコード例で:cursor1
がstm2p
にバインドされるように、プレースホルダ:cursor1
を文ハンドルにバインドします。
例7-21のこのコードでは、stm1p
はPL/SQLブロックの文ハンドルで、stm2p
は、後のデータ検索のためのREF
CURSOR
としてバインドされる文ハンドルです。SQLT_RSET
の値は、dty
パラメータに渡されます。
別の例として、次のコードを考えてみます。
static const text *nst_tab = (text *) "SELECT last_name, CURSOR(SELECT department_name, location_id \ FROM departments) FROM employees WHERE last_name = 'FORD'";
2番目の位置がネストした表で、例7-22に示すように、OCIアプリケーションで文ハンドルとして定義できます。
実行後、行をstm2p
にフェッチすると、有効な文ハンドルになります。
ノート:
複数のREF
CURSOR
を取り出した場合は、stm2p
へのフェッチをいつ行うか注意する必要があります。1つ目をフェッチすると、そのデータを取り出すためにフェッチを実行できます。ただし、2番目のREF
CURSOR
をstm2p
にフェッチすると、最初のREF
CURSOR
からのデータにはアクセスできなくなります。
OCIでは、スクロール・モードで実行されたPL/SQL REF
CURSOR
はサポートしていません。
OCIでは、REF
CURSOR
によってすでにフェッチされている行にスクロールして戻ることはできないため、スクロール可能REF
CURSOR
はサポートされていません。
例7-21 プレースホルダ:cursor1を文ハンドルstm2pにREF CURSORとしてバインド
status = OCIStmtPrepare (stm1p, errhp, (text *) plsql_block, strlen((char *)plsql_block), OCI_NTV_SYNTAX, OCI_DEFAULT); ... status = OCIBindByName (stm1p, (OCIBind **) &bnd1p, errhp, (text *)":cursor1", (sb4)strlen((char *)":cursor1"), (void *)&stm2p, (sb4) 0, SQLT_RSET, (void *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT);
例7-22 ネストした表(2番目の位置)を文ハンドルとして定義
status = OCIStmtPrepare (stm1p, errhp, (text *) nst_tab, strlen((char *)nst_tab), OCI_NTV_SYNTAX, OCI_DEFAULT); ... status = OCIDefineByPos (stm1p, (OCIDefine **) &dfn2p, errhp, (ub4)2, (void *)&stm2p, (sb4)0, SQLT_RSET, (void *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT);
関連項目:
パッケージ型を含むすべてのPL/SQL型の固有の記述およびバインド
Oracle Databaseリリース12.1より、OCIクライアントでは、すべてのPL/SQL型を固有に記述およびバインドできます。
これには、以前はバインド型としてサポートされていなかった基本スカラー型ブールも含まれます。また、名前付きレコードまたはコレクション型(ネストした表、VARRAYおよび索引表を含む)など、PL/SQLパッケージで宣言された型、あるいはPL/SQLパッケージ仕様の中で宣言された暗黙的なレコードのサブタイプ(%rowtype)も含まれます。これらの機能を固有にサポートすることにより、用意されたクライアント側のAPIのみを使用して、クライアントでPL/SQL型を記述およびバインドできるようになります。
これらのデータ型(ブール、レコード、索引付きBINARY_INTEGER
、PLS_INTEGER
またはBINARY_INTEGER
)のPL/SQL型コードは、表5-10に記載されています。これらのPL/SQL型コードの同等のSQLT
型は表5-11に記載されています。クライアントでそれぞれ指定のSQLT
型の値をバインドのDTYとして使用して指定の型がバインドされるようにする必要があります。たとえば、クライアントでレコードの場合はSQLT_NTY
をバインドのDTYとして使用してパッケージのレコード型(OCI_TYPECODE_RECORD
)をバインドする、コレクションの場合はSQLT_NTY
をバインドのDTYとして使用してすべてのパッケージのコレクション型(OCI_TYPECODE_ITABLE
)をバインドする、ブールの場合はSQLT_BOL
をバインドのDTYとして使用してブール型(OCI_TYPECODE_BOOLEAN
)がバインドされるようにする必要があります。バインドAPI OCIBindByName()
、OCIBindByName2()
、OCIBindByPos()
およびOCIBindByPos2()
では、これらのPL/SQL型コードを表現するバインドのDTYに含まれている各SQLT
型値がサポートされます。
OCIでのランタイム・データ割当てとピース単位操作
OCIを使用して、データのピース単位の挿入、更新およびフェッチを実行できます。
また、OCIを使用すると、バインド値の静的配列を提供するかわりに、配列の挿入または更新の際に動的にデータを提供できます。非常に大きな列を小さいサイズの一連のチャンクとして挿入または取得することにより、クライアント側のメモリー要件を最小限にできます。
個々のピースのサイズは、実行時にアプリケーションにより判断され、統一することもできます。
OCIのピース単位機能は、文字列やバイナリ・データの膨大なブロックで操作(CLOB
、BLOB
、LONG
、RAW
、LONG
RAW
などのデータを格納するデータベース列に関係する操作など)を行う際に特に有効です。
最後のOCIStmtFetch2()
コールがOCI_SUCCESSの値を戻すと、ピース単位フェッチが完了します。
ピース単位のフェッチおよび挿入では、操作を正常に完了するために必要なコールの順序を理解することが重要です。ピース単位挿入では、コールバックが使用されない場合、挿入するピースの数よりも1回多くOCIStmtExecute()
をコールする必要があります。これは、最初のOCIStmtExecute()
コールが、挿入される最初のピースが必要であることを示す値を戻すからです。結果として、n個のピースを挿入する場合は、OCIStmtExecute()
を合計n+1回コールする必要があります。
同様に、ピース単位フェッチを実行するときは、フェッチするピースの数より1回多くOCIStmtFetch2()
をコールする必要があります。
ピース単位操作に有効なデータ型
一部のデータ型のみが、ピース単位で操作できます。
次のデータ型は、OCIアプリケーションでピース単位のフェッチ、挿入または更新を実行できます。
-
VARCHAR2
-
STRING
-
LONG
-
LONG
RAW
-
RAW
-
CLOB
-
BLOB
すべてのデータ型に対してこの機能を使用する別な方法として、配列の挿入または更新のためにデータを動的に提供する方法があります。ピース単位操作をサポートしないデータ型に対しては、コールバックで、そのコールバックのpiecep
パラメータのOCI_ONE_PIECE
を常に指定する必要があります。
ピース単位操作の種類
ピース単位操作を実行できる方法について説明します。
ピース単位操作は、次の2通りの方法で実行できます。
-
ポーリング・パラダイムに基づいてピース単位操作を実行するには、OCIライブラリで提供されているコールを使用します。
-
必要な情報およびデータ・ブロックを提供するには、ユーザー定義のコールバック関数を使用します。
OCIBindByPos()
またはOCIBindByPos2()
、あるいはOCIBindByName()
またはOCIBindByName2()
コールのmode
パラメータをOCI_DATA_AT_EXEC
に設定すると、OCIアプリケーションでは実行時にINSERT
またはUPDATE
操作のためのデータが動的に提供されます。
同様に、OCIDefineByPos()
またはOCIDefineByPos2()
コールのmode
パラメータをOCI_DYNAMIC_FETCH
に設定した場合は、フェッチ時にデータを受け取るための割当てスペースが動的に提供されます。
どちらの場合でも、コールバック関数またはピース単位操作のいずれかの方法によって、INSERT
、UPDATE
またはFETCH
操作のランタイム情報を提供できます。コールバックを使用するときはコールバックを登録するために、追加のバインド・コールまたは定義コールが必要です。
次項以降では、挿入、更新およびフェッチのためのデータ割当てとピース単位操作について具体的に説明します。
ノート:
ピース単位操作は、SQLブロックおよびPL/SQLブロックでも有効です。
実行時のINSERTまたはUPDATEデータの提供について
OCIBindByPos()
またはOCIBindByPos2()
コール、あるいはOCIBindByName()
またはOCIBindByName2()
コールでOCI_DATA_AT_EXEC
モードを指定すると、value_sz
パラメータによって、実行時に提供できるデータの合計サイズが定義されます。
アプリケーションでは、実行時IN
データ・バッファを、要求に応じて操作完了に必要な回数だけOCIライブラリにいつでも提供できるようにしておく必要があります。割り当てたバッファは、不要になった際にクライアント側で解放する必要があります。
ランタイム・データは、次の2つの方法のいずれかで提供されます。
-
OCIBindDynamic()
関数を使用したコールバックを定義でき、実行時にこれをコールすると、データのピースまたはデータ全体を戻します。 -
コールバックを1つも定義していない場合は、SQL文を処理する
OCIStmtExecute()
コールは、OCI_NEED_DATA
エラー・コードを戻します。次に、クライアント・アプリケーションは、使用されるバインドおよびピースを指定するOCIStmtSetPieceInfo()
コールを使用して、IN/OUT
データ・バッファまたはピースを提供します。
この項には次のトピックが含まれます。「ピース単位の挿入または更新の実行」。
ピース単位の挿入または更新の実行
OCI環境が初期化され、データベース接続およびセッションが確立すると、SQL文またはPL/SQL文を準備するコールと入力値をバインドするコールが実行され、ピース単位挿入が開始されます。
ユーザー定義のコールバックではなく、標準のOCIコールを使用するピース単位操作では、OCIBindDynamic()
をコールする必要はありません。
ノート:
ピース単位操作の一部でない追加のバインド変数は、データ型に応じて、追加のバインド・コールが必要な場合があります。
文の準備およびバインドの次に、アプリケーションでOCIStmtExecute()
、OCIStmtGetPieceInfo()
およびOCIStmtSetPieceInfo()
を連続してコールし、ピース単位操作を完了します。それぞれのOCIStmtExecute()
コールは、次に実行する処理を決定する値を戻します。一般的には、アプリケーションで次のピースの挿入を指示する値を取り出し、そのピースをバッファに移してから挿入を行います。最後のピースを挿入すると操作は完了します。
挿入バッファは任意のサイズにでき、実行時に提供されることに注意してください。また、挿入する各ピースは、同じサイズである必要はありません。挿入するピースのサイズは、それぞれOCIStmtSetPieceInfo()
コールで設定します。
ノート:
すべての挿入で同じサイズのピースが使用され、挿入されるデータのサイズがそのピースのサイズで均等に割り切れない場合は、挿入される最後のピースは小さくなります。このことを考慮して、最後のOCIStmtSetPieceInfo()
コールで小さいサイズを指定する必要があります。
その手順は図7-3で示すとおりで、図の次に各ステップを説明しています。
最後のピースが正常に挿入されると、ピース単位操作は完了します。これは、最後の OCIStmtExecute()
コールによるOCI_SUCCESS
戻り値によって示されます。
ピース単位更新も同様の方法で実行します。ピース単位更新操作では、更新対象のデータが挿入バッファに移され、OCIStmtExecute()
がコールされて更新が実行されます。
PL/SQLでのピース単位操作
OCIアプリケーションでは、前に概説した方法と似た方法で、IN
、OUT
およびIN/OUT
バインド変数について、PL/SQLでのピース単位操作を実行できます。
PL/SQL文のすべてのプレースホルダは、定義ではなくバインドされることに注意してください。OCIBindDynamic()
のコールでは、OUT
パラメータまたはIN/OUT
パラメータについて、適切なコールバックを指定します。
関連項目:
PL/SQL索引付き表のバインドのサポート
PL/SQL索引付き表は、OCIを使用してPL/SQL無名ブロックにIN/OUT
バインドとして渡すことができます。
PL/SQL索引付き表をバインドする手順は、SQL文に対して配列バインドを実行する手順に非常によく似ています。OCIプログラムでは、OCIBindByName()
またはOCIBindByName2()
、あるいはOCIBindByPos()
またはOCIBindByPos2()
のいずれかを使用して、次のとおりに配列の場所を配列の他のメタデータにバインドする必要があります。C配列をPL/SQL索引付き表バインド変数にバインドするプロセスでは、バインド・コール時に次の情報を指定する必要があります。
-
void *valuep (IN/OUT)
: クライアント・メモリーで配列の始まりを指定する場所のポインタ -
ub2 dty (IN)
: クライアントで表されている配列の要素のデータ型 -
sb4 value_sz (IN)
: クライアントで表されている配列の各要素の最大サイズ(バイト単位) -
ub4 maxarr_len (IN)
: 配列で存続期間中に保持されるデータ型の最大要素数静的バインド実行のために前もって配列全体を割り当てる場合、配列を
maxarr_len
数の要素(それぞれのサイズがvalue_sz
)を含むのに十分なサイズに設定する必要があります。この情報は、PL/SQLで表示されるように索引付き表に制約をかけるためにも使用されます。PL/SQLでは、指定したこの制限を越えて索引付き表を(読取りまたは書込みのいずれかのために)参照することはできません。 -
ub4 *curelep (IN/OUT)
: 現在有効な配列内の要素の数(配列の最初から)のポインタ。これは、最大配列長以下にする必要があります。この情報は、PL/SQLで表示されるように索引付き表に制約をかけるためにも使用されることに注意してください。
IN
バインドの場合、PL/SQLは指定したこの制限を越えて索引付き表から読み取ることはできません。OUT
バインドの場合、PL/SQLはこの制限を越えて索引付き表に書き込むことはできますが、maxarr_len
制限を越えることはできません。
IN
索引付き表バインドの場合、OCIStmtExecute()
を実行する前に、ユーザーはその実行のために現在の配列長(*curelep
)を設定する必要があります。さらに、ユーザーは、配列の要素ごとに、実際の長さとインジケータを規定どおりに設定することも必要です。
OUT
バインドの場合、OCIでは配列の要素ごとに、現在の配列長(*curelep
)と実際の長さ、インジケータおよびリターン・コードを規定どおりに戻す必要があります。
最高のパフォーマンスを得るには、配列を最大配列長が割り当てられたままにして、実際にやり取りされている要素の数に基づいて、実行と実行の間に現在の配列長を変更します。この方法だと、実行ごとに割当ての解除と再割当てを繰り返す必要はなく、その結果アプリケーション全体のパフォーマンスが向上します。
また、PL/SQL索引付き表に対するOCIのピース単位のコールを使用して、バインドすることもできます。この方法では、配列全体を前もって割り当てる必要はありません。OCIStmtSetPieceInfo()
コールとOCIStmtGetPieceInfo()
コールを使用すれば、個々の要素をピース単位で渡すこともできます。
この項には次のトピックが含まれます。「PL/SQL索引付き表バインド・インタフェースの制限」。
実行時のフェッチ情報の提供について
mode
パラメータにOCI_DYNAMIC_FETCH
を設定してOCIDefineByPos()
またはOCIDefineByPos2()
をコールすると、アプリケーションでは、フェッチ時のデータ・バッファに関する情報を指定できます。
また、OCIDefineDynamic()
をコールして、データ・バッファに関する情報を取得するために呼び出すコールバック関数を設定する必要がある場合があります。
ランタイム・データは、次の2つの方法のいずれかで提供されます。
-
OCIDefineDynamic()
関数を使用したコールバックを定義できます。実行時に提供されるデータの最大サイズは、value_sz
パラメータによって定義されます。クライアント・ライブラリで、フェッチ済データを戻すためのバッファが必要な場合は、このコールバックが呼び出され、ピースまたは全体のデータを戻すための実行時バッファが提供されます。 -
コールバックが1つも定義されていない場合は、
OCI_NEED_DATA
エラー・コードが戻り、クライアント・アプリケーションでOCIStmtSetPieceInfo()
を使用して、OUT
データ・バッファまたはピースを提供します。OCIStmtGetPieceInfo()
コールにより、どの定義およびピースが含まれるかについての情報が提供されます。
この項には次のトピックが含まれます。「ピース単位フェッチの実行」
ピース単位フェッチの実行
フェッチ・バッファは、任意のサイズにすることができます。また、フェッチする各ピースは、同じサイズである必要はありません。
ただし、最後にフェッチするサイズは、残っている最後のピースに一致する必要があります。フェッチする各ピースのサイズは、OCIStmtSetPieceInfo()
コールでそれぞれ設定します。そのプロセスは図7-4で示すとおりで、図の次に各ステップを説明しています。
- OCI環境を初期化して必要なハンドルを割り当て、データベースに接続してユーザーを認証し、文を準備してから
OCIStmtExecute()
を使用して文を実行します。 mode
をOCI_DYNAMIC_FETCH
に設定し、OCIDefineByPos()
またはOCIDefineByPos2()
を使用して出力変数を定義します。この時点では、使用するピースの実際のサイズを指定する必要はありませんが、実行時にフェッチするデータの全体のサイズを指定します。- 初めて
OCIStmtFetch2()
をコールします。データは取得されず、アプリケーションにOCI_NEED_DATA
エラー・コードが戻されます。その他の値が戻された場合は、エラーが発生しています。 OCIStmtGetPieceInfo()
をコールし、フェッチするピースに関する情報を取得します。piecep
パラメータは、そのピースが最初のピース(OCI_FIRST_PIECE
)であるか、その後続のピース(OCI_NEXT_PIECE
)であるか、または最後のピース(OCI_LAST_PIECE
)であるかを示します。OCIStmtSetPieceInfo()
をコールして、フェッチ・バッファを指定します。- 再度、
OCIStmtFetch2()
をコールし、実際のピースを取り出します。OCIStmtFetch2()
でOCI_SUCCESS
が戻された場合は、すべてのピースが正常にフェッチされています。OCIStmtFetch2()
でOCI_NEED_DATA
が戻された場合は、ステップ4に戻って次のピースを処理します。その他の値が戻された場合は、エラーが起きています。
LOBのピース単位のバインドおよび定義
LOBのピース単位のバインドおよび定義を実行する方法について説明します。
次の2つがあります。
-
データ・インタフェースの使用
次の関数の入力データ型として、
SQLT_CHR
(VARCHAR2
)またはSQLT_LNG
(LONG
)を使用して、CLOB
列の文字データをバインドまたは定義できます。次の関数の入力データ型として、SQLT_LBI
(LONG
RAW
)およびSQLT_BIN
(RAW
)を使用して、BLOB
列の行データをバインドまたは定義することもできます。-
OCIDefineByPos()
またはOCIDefineByPos2()
-
OCIBindByName()
またはOCIBindByName2()
-
OCIBindByPos()
またはOCIBindByPos2()
次に説明するピース単位操作はすべて、この場合、
CLOB
列およびBLOB
列に対してサポートされています。 -
-
LOBロケータの使用
次の関数の入力データ型として、
SQLT_CLOB
(CLOB
)またはSQLT_BLOB
(BLOB
)を使用して、CLOB
列およびBLOB
列のLOBロケータをバインドまたは定義できます。-
OCIDefineByPos()
またはOCIDefineByPos2()
-
OCIBindByName()
またはOCIBindByName2()
-
OCIBindByPos()
またはOCIBindByPos2()
次に、OCILob*関数をコールし、データを読み取って操作する必要があります。
OCILobRead2()
およびOCILobWrite2()
は、ピース単位モードおよびコールバック・モードをサポートしています。 -
関連項目:
-
INSERT
文およびUPDATE
文の使用方法および使用例は、「LOBデータのバインドについて」を参照してください -
SELECT
文の使用方法および使用例は、「LOBデータの定義について」を参照してください -
OCILobWrite2()
およびOCILobRead2()
でコールバックを使用したストリームの詳細は、「LOB読取りおよび書込みコールバック」を参照してください