この項では、それぞれのステップを詳しく説明します。章の終わりには、方法4を使用したコメント付きの完全なプログラム例を示します。
EXEC SQL PREPARE statement_name
FROM { :host_string | string_literal };
EXEC SQL DECLARE cursor_name CURSOR FOR statement_name;
EXEC SQL DESCRIBE BIND VARIABLES FOR statement_name
INTO bind_descriptor_name;
EXEC SQL OPEN cursor_name
[USING DESCRIPTOR bind_descriptor_name];
EXEC SQL DESCRIBE [SELECT LIST FOR] statement_name
INTO select_descriptor_name;
EXEC SQL FETCH cursor_name
USING DESCRIPTOR select_descriptor_name;
EXEC SQL CLOSE cursor_name;
方法4では、スクロール可能カーソルも使用できます。スクロール可能カーソルには、埋込みSQL文を次の順序で使用する必要があります。
EXEC SQL PREPARE statement_name
FROM { :host_string | string_literal };
EXEC SQL DECLARE cursor_name SCROLL CURSOR FOR statement_name;
EXEC SQL DESCRIBE BIND VARIABLES FOR statement_name
INTO bind_descriptor_name;
EXEC SQL OPEN cusor_name
[ USING DESCRIPTOR bind_descriptor_name];
EXEC SQL DESCRIBE [ SELECT LIST FOR] statement_name
INTO select_descriptor_name;
EXEC SQL FETCH [ FIRST| PRIOR|NEXT|LAST|CURRENT | RELATIVE fetch_offset
|ABSOLUTE fetch_offset ] cursor_name USING DESCRIPTOR
select_descriptor_name;
EXEC SQL CLOSE cursor_name;
動的問合せの選択リスト項目の数がわかっているときは、DESCRIBE SELECT LISTを省略するとともに次の方法3のFETCH文を使用できます。
EXEC SQL FETCH cursor_name INTO host_variable_list;
または、動的SQL文のバインド変数に対するプレースホルダの数が明確な場合は、DESCRIBE BIND VARIABLESを省略するとともに次の方法3のOPEN文を使用できます。
EXEC SQL OPEN cursor_name [USING host_variable_list];
次にこれらの文により、ホスト・プログラムで記述子を使用して動的SQL文を受け入れ、それを処理する方法を説明します。
注意:
以降では、図を使用して説明します。図が複雑になるのを避けるために、次の条件を満たすことが必要です。
記述子配列は3要素までに制限します。
名前の最大長は5文字以内に制限します。
値の最大長は10文字以内に制限します。
プログラムには、動的SQL文のテキストを格納するためのホスト変数が必要です。ホスト変数(ここではselect_stmt)は文字列として宣言する必要があります。
... int emp_number; VARCHAR emp_name[10]; VARCHAR select_stmt[120]; float bonus;
ここでは、SQLDAのデータ構造体をハードコードするかわりに、次のようにINCLUDEを使用してSQLDAをプログラムにコピーします。
#include <sqlda.h>
問合せに含まれる選択リスト項目の数またはバインド変数のプレースホルダの数が不明なため、次のように選択記述子とバインド記述子のポインタを宣言します。
sqlda *select_des; sqlda *bind_des;
記述子用の記憶域を割り当てるには、SQLSQLDAAlloc()ライブラリ関数を使用することを思い出してください。ANSI C表記法による構文は次のとおりです。
SQLDA *SQLSQLDAAlloc(dvoid *context, unsigned int max_vars, unsigned int max_name, unsigned int max_ind_name);
SQLSQLDAAlloc()関数は、記述子構造体およびポインタ変数V、L、T、Iによってアドレス指定される配列を割り当てます。
max_nameが0(ゼロ)以外のときは、ポインタ変数S、M、Cによってアドレス指定される配列が割り当てられます。max_ind_nameが0(ゼロ)以外のときは、ポインタ変数X、Y、Zによってアドレス指定される配列が割り当てられます。max_nameとmax_ind_nameが0(ゼロ)のときは、領域は割り当てられません。
SQLSQLDAAlloc()は、成功すると構造体のポインタを戻します。SQLSQLDAAlloc()は、失敗すると0(ゼロ)を戻します。
この例では、選択記述子とバインド記述子を次のように割り当てます。
select_des = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t) 5, (size_t) 0); bind_des = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, (size_t) 5, (size_t) 4);
選択記述子には、Xによってアドレス指定される配列に領域が割り当てられないようにするため、常にmax_ind_nameを0(ゼロ)に設定します。
次の例では、ユーザーにSQL文の入力を求め、入力された文字列をselect_stmtに格納します。
printf("\n\nEnter SQL statement: ");
gets(select_stmt.arr);
select_stmt.len = strlen(select_stmt.arr);
このときユーザーが次の文字列を入力したと仮定します。
"SELECT ename, empno, comm FROM emp WHERE comm < :bonus"
DECLARE CURSORは名前を指定し、特定のSELECT文に対応付けることにより、カーソルを定義します。
静的問合せ用のカーソルを宣言するには、次の構文を使用します。
EXEC SQL DECLARE cursor_name CURSOR FOR SELECT ...
動的問合せ用にカーソルを宣言するには、PREPAREにより動的問合せに指定された文の名前で静的問合せを置換します。例では、DECLARE CURSORはemp_cursorという名前のカーソルを定義し、このカーソルをsql_stmtに対応付けます。
EXEC SQL DECLARE emp_cursor CURSOR FOR sql_stmt;
注意:
問合せのみでなく、すべての動的SQL文のカーソルを宣言できます。また、問合せ以外の場合も、カーソルのOPENにより動的SQL文を実行します。
DESCRIBE BIND VARIABLESは、バインド記述子にプレースホルダの記述を設定します。例では、DESCRIBEは次のようにbind_desを準備します。
EXEC SQL DESCRIBE BIND VARIABLES FOR sql_stmt INTO bind_des;
bind_desはコロンで始めることはできません。
DESCRIBE BIND VARIABLES文はPREPARE文の後で、かつOPEN文の前に指定する必要があります。
図15-4に、DESCRIBE実行後のバインド記述子を示します。SQL文の実行で検出されたプレースホルダの実際の数が、DESCRIBEによってFに設定されています。
プログラムでは、SQL文で検出されたバインド変数に対する値を取得し、メモリーを割り当てる必要があります。値はどのように取得してもかまいません。たとえば値をハードコードしたり、ファイルから読み込んだり、または対話形式で入力することもできます。
例では、問合せのWHERE句のプレースホルダbonusに置換されるバインド変数に値を割り当てる必要があります。そこで、ユーザーに値の入力を求め、入力された値を次のように処理します。
for (i = 0; i < bind_des->F; i++)
{
printf("\nEnter value of bind variable %.*s:\n? ",
(int) bind_des->C[i], bind_des->S[i]);
gets(hostval);
/* Set length of value. */
bind_des->L[i] = strlen(hostval);
/* Allocate storage for value and null terminator. */
bind_des->V[i] = malloc(bind_des->L[i] + 1);
/* Allocate storage for indicator value. */
bind_des->I[i] = (unsigned short *) malloc(sizeof(short));
/* Store value in bind descriptor. */
strcpy(bind_des->V[i], hostval);
/* Set value of indicator variable. */
*(bind_des->I[i]) = 0; /* or -1 if "null" is the value */
/* Set datatype to STRING. */
bind_des->T[i] = 5;
}
ここでは、ユーザーがbonusの値として625を入力したと想定します。図15-5に、結果として得られるバインド記述子を示します。値はヌル文字で終了します。
動的問合せに使用するOPEN文は、カーソルがバインド記述子に対応付けられることを除けば静的問合せに使用するものと同じです。実行時に決定され、バインド記述子表の要素でアドレス指定したバッファに格納された値を使用して、SQL文を評価します。問合せの場合は、アクティブ・セットの識別にも同じ値を使用します。
例では、OPENは次のようにemp_cursorをbind_desに対応付けます。
EXEC SQL OPEN emp_cursor USING DESCRIPTOR bind_des;
bind_desはコロンで始めることはできません。
OPENはSQL文を実行します。問合せのときは、OPENはアクティブ・セットを決定するとともにカーソルを先頭行に位置づけます。
動的SQL文が問合せのときは、DESCRIBE SELECT LIST文はOPEN文の後で、かつFETCH文の前に指定する必要があります。
DESCRIBE SELECT LISTは、選択記述子に選択リスト項目の記述を設定します。例では、DESCRIBEは次のようにselect_desを準備します。
EXEC SQL DESCRIBE SELECT LIST FOR sql_stmt INTO select_des;
Oracleのデータ・ディクショナリにアクセスすることで、DESCRIBEは各選択リストの値の長さとデータ型を設定します。
図15-6に、DESCRIBE実行後の選択記述子を示します。問合せの選択リストで検出された項目の実際の数がDESCRIBEによってFに設定されています。SQL文が問合せでないときは、Fは0(ゼロ)に設定されます。
また、NUMBER型の長さはまだ使用できないことに注意してください。NUMBERと定義した列には、ライブラリ関数SQLNumberPrecV6()を使用して精度と位取りを抽出する必要があります。
関連項目:
例では、選択リストの値をFETCHする前に、ライブラリ関数malloc()を使用して、記憶域を割り当てます。また、表示用の長さとデータ型の配列の要素のいくつかを再設定します。
for (i=0; i<select_des->F; i++)
{
/* Clear null bit. */
SQLColumnNullCheck(SQL_SINGLE_RCTX, (unsigned short *)&(select_des->T[i]),
(unsigned short *)&(select_des->T[i]), &nullok);
/* Reset length if necessary. */
switch(select_des->T[i])
{
case 1: break;
case 2: SQLNumberPrecV6(SQL_SINGLE_RCTX, (unsigned long *)
&(select_des->L[i]), &prec, &scal);
if (prec == 0) prec = 40;
select_des->L[i] = prec + 2;
if (scal < 0) select_des->L[i] += -scal;
break;
case 8: select_des->L[i] = 240;
break;
case 11: select_des->L[i] = 18;
break;
case 12: select_des->L[i] = 9;
break;
case 23: break;
case 24: select_des->L[i] = 240;
break;
}
/* Allocate storage for select-list value. */
select_des->V[i] = malloc(select_des->L[i]+1);
/* Allocate storage for indicator value. */
select_des->I[i] = (short *)malloc(sizeof(short *));
/* Coerce all datatypes except LONG RAW to STRING. */
if (select_des->T[i] != 24) select_des->T[i] = 5;
}
図15-7に、結果として得られる選択記述子を示します。NUMBERの長さはこのとき使用可能となります。データ型はすべてSTRINGです。L[1]およびL[2]の長さはそれぞれ6と9になっています。これは、DESCRIBEされた長さ4と7にそれぞれ符号と小数点のための2を加算したためです。
FETCHはアクティブ・セットから1行を戻し、データ・バッファに選択リストの値を格納してから、カーソルをアクティブ・セットの次の行に進めます。行がなくなると、FETCHは「データが見つかりません。」のOracleエラー・コードをsqlca.sqlcodeに設定します。例では、次に示すように、FETCHによってENAME、EMPNOおよびCOMMの列の値をselect_desに戻します。
EXEC SQL FETCH emp_cursor USING DESCRIPTOR select_des;
図15-8に、FETCH実行後の選択記述子を示します。Oracleは選択リストの値とインジケータの値を、VとIの要素によってアドレス指定されるデータ・バッファに格納しています。
データ型1の出力バッファについては、OracleはL配列に格納された長さを使用し、CHARまたはVARCHAR2のデータを左揃えにしてから、NUMBERデータを右揃えにします。データ型5(STRING)の出力バッファについては、値を左揃えにし、CHAR、VARCHAR2およびNUMBERのデータにヌル文字を付けます。
値'MARTIN'は、EMP表のVARCHAR2(10)列から取り出されました。L[0]の長さを使用して、Oracleは10バイトのフィールドの値を左揃えにしてバッファを埋めます。
値7654はNUMBER(4)列から取り出され、'7654'に強制変換されています。しかし、符号と小数点を使用可能にするため、L[1]の長さが2のみ増えています。そこでOracleは6バイトのフィールドの値を左揃えにしてから、ヌル文字を付けます。
値482.50はNUMBER(7,2)列から取り出され、'482.50'に強制変換されています。L[2]の長さが2のみ増えています。Oracleは9バイトのフィールドの値を左揃えにしてから、ヌル文字を付けます。
malloc()によって割り当てられた記憶域を解除するには、free()ライブラリ関数を使用します。構文は次のとおりです。
free(char *pointer);
例では、選択リスト項目、バインド変数および標識変数の値に対する記憶域の割当てを次のように解除します。
for (i = 0; i < select_des->F; i++) /* for select descriptor */
{
free(select_des->V[i]);
free(select_des->I[i]);
}
for (i = 0; i < bind_des->F; i++) /* for bind descriptor */
{
free(bind_des->V[i]);
free(bind_des->I[i]);
}
記述子の記憶域の割当てを解除するには、次の構文のライブラリ関数SQLSQLDAFree()を使用します。
SQLSQLDAFree(context, descriptor_name);
記述子はSQLSQLDAAlloc()を使用して割り当ててください。そうしないと結果は予測できなくなります。
例では、選択記述子とバインド記述子に対する記憶域の割当てを次のように解除します。
SQLSQLDAFree(SQL_SINGLE_RCTX, select_des); SQLSQLDAFree(SQL_SINGLE_RCTX, bind_des);
方法4で入力ホスト配列または出力ホスト配列を使用するには、オプションのFOR句を使用してホスト配列のサイズをOracleに通知する必要があります。
関連項目:
FOR句の詳細は、Host Arraysを参照してください
次の構文を使用して、i番目の選択リスト項目またはバインド変数に記述子エントリを設定する必要があります。
V[i] = array_address; L[i] = element_size;
array_addressはホスト配列のアドレスで、element_sizeはある配列要素のサイズです。
EXECUTE文またはFETCH文(どちらか適切な方)にFOR句を指定して、処理対象の配列要素の数をOracleに通知する必要があります。Oracleがホスト配列のサイズを認識する方法は他にないので、この手順は必須です。
次の完全なプログラム例では、3つの入力ホスト配列を使用してEMP表に行をINSERTします。方法4による問合せ以外のDML文にもEXECUTEを使用できます。
#include <stdio.h>
#include <sqlcpr.h>
#include <sqlda.h>
#include <sqlca.h>
#define NAME_SIZE 10
#define INAME_SIZE 10
#define ARRAY_SIZE 5
/* connect string */
char *username = "scott/tiger";
char *sql_stmt =
"INSERT INTO emp (empno, ename, deptno) VALUES (:e, :n, :d)";
int array_size = ARRAY_SIZE; /* must have a host variable too */
SQLDA *binda;
char names[ARRAY_SIZE][NAME_SIZE];
int numbers[ARRAY_SIZE], depts[ARRAY_SIZE];
/* Declare and initialize indicator vars. for empno and deptno columns */
short ind_empno[ARRAY_SIZE] = {0,0,0,0,0};
short ind_dept[ARRAY_SIZE] = {0,0,0,0,0};
main()
{
EXEC SQL WHENEVER SQLERROR GOTO sql_error;
/* Connect */
EXEC SQL CONNECT :username;
printf("Connected.\n");
/* Allocate the descriptors and set the N component.
This must be done before the DESCRIBE. */
binda = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 3, NAME_SIZE, INAME_SIZE);
binda->N = 3;
/* Prepare and describe the SQL statement. */
EXEC SQL PREPARE stmt FROM :sql_stmt;
EXEC SQL DESCRIBE BIND VARIABLES FOR stmt INTO binda;
/* Initialize the descriptors. */
binda->V[0] = (char *) numbers;
binda->L[0] = (long) sizeof (int);
binda->T[0] = 3;
binda->I[0] = ind_empno;
binda->V[1] = (char *) names;
binda->L[1] = (long) NAME_SIZE;
binda->T[1] = 1;
binda->I[1] = (short *)0;
binda->V[2] = (char *) depts;
binda->L[2] = (long) sizeof (int);
binda->T[2] = 3;
binda->I[2] = ind_dept;
/* Initialize the data buffers. */
strcpy(&names[0] [0], "ALLISON");
numbers[0] = 1014;
depts[0] = 30;
strcpy(&names[1] [0], "TRUSDALE");
numbers[1] = 1015;
depts[1] = 30;
strcpy(&names[2] [0], "FRAZIER");
numbers[2] = 1016;
depts[2] = 30;
strcpy(&names[3] [0], "CARUSO");
numbers[3] = 1017;
ind_dept[3] = -1; /* set indicator to -1 to insert NULL */
depts[3] = 30; /* value in depts[3] is ignored */
strcpy(&names[4] [0], "WESTON");
numbers[4] = 1018;
depts[4] = 30;
/* Do the INSERT. */
printf("Adding to the Sales force...\n");
EXEC SQL FOR :array_size
EXECUTE stmt USING DESCRIPTOR binda;
/* Print rows-processed count. */
printf("%d rows inserted.\n\n", sqlca.sqlerrd[2]);
EXEC SQL COMMIT RELEASE;
exit(0);
sql_error:
/* Print Oracle error message. */
printf("\n%.70s", sqlca.sqlerrm.sqlerrmc);
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK RELEASE;
exit(1);
}