ヘッダーをスキップ
Pro*C/C++プログラマーズ・ガイド
11gリリース2(11.2)
B61343-01
  ドキュメント・ライブラリへ
ライブラリ
製品リストへ
製品
目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

15 Oracle動的SQL: 方法4

この章では、Oracle動的SQL方法4を実現する方法について説明します。この方法を使用すると、ホスト変数を含む動的SQL文の受取りや作成ができます。含まれるホスト変数の個数は様々です。既存のアプリケーションは、この方法を使用してサポートしてください。新しいアプリケーションでは、すべてANSI動的SQL方法4を使用してください。

Oracle動的SQL方法4は、オブジェクト型、カーソル変数、構造体の配列、DML RETURNING句、Unicode変数およびLOBをサポートしていません。かわりにANSI動的SQL方法4を使用してください。この章の項目は、次のとおりです。

方法4の特殊要件

方法4の必要条件を学習する前に、選択リスト項目とプレースホルダという用語を理解する必要があります。選択リスト項目とは、問合せ内でキーワードSELECTの後に続く列または式のことです。たとえば、次の動的問合せは3つの選択リスト項目を含んでいます。

SELECT ename, job, sal + comm FROM emp WHERE deptno = 20
 

プレースホルダはダミーのバインド変数です。プレースホルダは実際のバインド変数用にSQL文内に場所を確保するためのものです。プレースホルダは宣言が不要で、任意の名前を指定できます。

バインド変数のプレースホルダはSET句、VALUES句およびWHERE句で最もよく使用されます。たとえば、次の動的SQL文はそれぞれ2つのプレースホルダを含んでいます。

INSERT INTO emp (empno, deptno) VALUES (:e, :d) 
DELETE FROM dept WHERE deptno = :num OR loc = :loc 

方法4が特別な理由

方法1、2、3とは異なり、動的SQL方法4ではプログラムで次のことができます。

  • 選択リスト項目数とプレースホルダ数が不明な動的SQL文の受取りまたは作成

  • OracleとC言語の間のデータ型変換の明示的な制御

プログラムにこのような柔軟性を持たせるには、Oracleランタイム・ライブラリに補足情報を追加する必要があります。

Oracleに必要な情報

Pro*C/C++プリコンパイラでは、すべての実行可能な動的SQL文についてOracleコールが生成されます。動的SQL文に選択リスト項目またはプレースホルダが指定されていない場合、Oracleにはその文を実行するための補足情報は必要ありません。次のDELETE文がこのカテゴリに該当します。

DELETE FROM emp WHERE deptno = 30 

ただし、ほとんどの動的SQL文には、次のUPDATE文のように、選択リスト項目、またはバインド変数のプレースホルダが含まれています。

UPDATE文:

UPDATE emp SET comm = :c WHERE empno = :e
 

バインド変数のためのプレースホルダまたは選択リスト項目を含む動的SQL文を実行するには、入力(バインド)値および問合せを実行するときにFETCHされた値を保持するプログラム変数についての情報が必要です。Oracleは次の情報を必要とします。

  • バインド変数の数と選択リスト項目の数

  • 各バインド変数と選択リスト項目の長さ

  • 各バインド変数と選択リスト項目のデータ型

  • 各バインド変数のアドレスと、各選択リスト項目の値が設定される出力変数のアドレス

情報の格納位置

選択リスト項目またはバインド変数のプレースホルダについてOracleで必要な情報は、その値以外はすべて、SQL記述子領域(SQLDA)というプログラム・データ構造体に格納されます。SQLDA構造体はsqlda.hヘッダー・ファイルに定義されています。

選択リスト項目の記述は選択記述子に格納されます。また、バインド変数に対するプレースホルダの記述はバインド記述子に格納されます。

選択リスト項目の値は出力変数に代入されます。これに対し、バインド変数の値は入力変数に代入されます。これらの変数のアドレスを選択SQLDAまたはバインドSQLDAに格納すると、出力値を書き込む位置および入力値を読み込む位置がOracleに認識されます。

値はどのようにしてこれらのデータ変数に代入されるのでしょうか。出力値は、カーソルを使用してFETCHされます。入力値は、通常はユーザーが対話形式で入力した情報をもとに、プログラムによって代入されます。

SQLDAの参照方法

バインド記述子および選択記述子は、通常はポインタによって参照されます。動的SQLプログラムでは、少なくとも1つのバインド記述子と1つの選択記述子のポインタを次のように宣言する必要があります。

#include <sqlda.h>
... 
SQLDA *bind_dp; 
SQLDA *select_dp; 

この後にSQLSQLDAAlloc()関数を使用すると、次のように記述子を割り当てることができます。

bind_dp = SQLSQLDAAlloc(runtime_context, size, name_length, ind_name_length); 

Oracle8以前のバージョンでは、SQLSQLDAAlloc()はsqlaldt()に相当します。

定数SQL_SINGLE_RCTXは、(dvoid*)0として定義されます。これは、アプリケーションがシングル・スレッドの場合に、runtime_contextに使用します。


関連項目


情報の取得方法

DESCRIBE文を使用すると、Oracleに必要な情報が得られます。

DESCRIBE SELECT LIST文は、各選択リスト項目を検査して名前とその長さを判断します。次にこの情報を使用できるように選択SQLDAに格納します。たとえば、選択リスト名を印刷時に列のヘッダーとして使用できます。選択リスト項目の合計数も、DESCRIBE文によりSQLDAに格納されます。

DESCRIBE BIND VARIABLES文は、それぞれのプレースホルダを調べて名前と長さを決定します。続いて、この情報を使用できるように入力バッファとバインドSQLDAに格納します。たとえば、プレースホルダ名を使用してバインド変数の値をユーザーに入力要求できます。

SQLDAの説明

この項では、SQLDAのデータ構造を詳しく説明します。SQLDAの宣言方法、格納されている変数、初期化の方法、プログラム内での使用方法を理解できます。

SQLDAの用途

選択リスト項目の数またはバインド変数のプレースホルダの数が不明の動的SQL文には、方法4を使用する必要があります。このような動的SQL文を処理するには、プログラムでSQLDA(記述子とも呼ばれます)を明示的に宣言する必要があります。記述子はそれぞれ構造体になっています。記述子はプログラムにコピーまたはハードコードする必要があります。

選択記述子には、選択リスト項目の記述、選択リスト項目の名前および値が格納されている出力バッファのアドレスが保持されます。


注意:

選択リスト項目の名前には、列名、列の別名あるいはsal + commなどの式のテキストを指定できます。

バインド記述子には、バインド変数と標識変数の記述、およびバインド変数と標識変数の名前と値が格納されている入力バッファのアドレスが保持されます。

複数のSQLDA

プログラムにアクティブな動的SQL文が2つ以上ある場合は、それぞれの文が専用のSQLDAを持つ必要があります。別の名前で任意の数のSQLDAを宣言できます。たとえば、sel_desc1sel_desc2およびsel_desc3という名前を付けて3つの選択SQLDAを宣言すると、同時にOPENされている3つのカーソルからFETCHできます。ただし、カーソルが同時に実行されなければSQLDAを再利用できます。

SQLDAの宣言

SQLDAを宣言するには、sqlda.hヘッダー・ファイルをインクルードします。SQLDAの内容は、次のとおりです。

struct SQLDA 
{ 
    long    N;          /* Descriptor size in number of entries */ 
    char  **V;        Ptr to Arr of addresses of main variables */ 
    long   *L;              /* Ptr to Arr of lengths of buffers */ 
    short  *T;                /* Ptr to Arr of types of buffers */ 
    short **I;      * Ptr to Arr of addresses of indicator vars */ 
    long    F;         /* Number of variables found by DESCRIBE */ 
    char  **S;          /* Ptr to Arr of variable name pointers */ 
    short  *M;       /* Ptr to Arr of max lengths of var. names */ 
    short  *C;    * Ptr to Arr of current lengths of var. names */ 
    char  **X;         /* Ptr to Arr of ind. var. name pointers */ 
    short  *Y;  /* Ptr to Arr of max lengths of ind. var. names */ 
    short  *Z;  /* Ptr to Arr of cur lengths of ind. var. names */ 
}; 

SQLDAの割当て

SQLDAの宣言後に、次の構文のライブラリ関数SQLSQLDAAlloc()(Oracle8以前のバージョンではsqlaldt()に相当)を使用して、記憶域を割り当てます。

descriptor_name = SQLSQLDAAlloc (runtime_context, max_vars, max_name, max_ind_name); 

パラメータは、次のとおりです。

構文 説明
runtime_context ランタイム・コンテキストへのポインタ
max_vars 記述子が記述できる選択リスト項目またはプレースホルダの最大数。
max_name 選択リスト名またはプレースホルダ名の最大長。
max_ind_name オプション指定でプレースホルダ名に付加される標識変数名の最大長。このパラメータはバインド記述子専用です。したがって、選択記述子を割り当てるときは、このパラメータを0(ゼロ)に設定します。

記述子の他に、SQLSQLDAAlloc()は記述子変数が指すデータ・バッファも割り当てます。


関連項目


図15-1に、変数がSQLSQLDAAlloc()コール、DESCRIBEコマンド、FETCHコマンドまたはプログラム割当てのうち、どの方法で設定されるかを示します。

図15-1 変数の設定方法

変数の設定方法
「図15-1 変数の設定方法」の説明

SQLDA変数の使用

この項では、SQLDA内の各変数の用途と使用方法を説明します。

N変数

Nは、DESCRIBE可能な選択リスト項目またはプレースホルダの最大数を指定します。つまり、Nによって記述子配列に含まれる要素の数が決まります。

オプションのDESCRIBEコマンドを発行する前に、ライブラリ関数SQLSQLDAAlloc()を使用してNを記述子配列の次元に設定する必要があります。DESCRIBEコマンドの発行後は、NをDESCRIBEされた変数の実際の数に再設定する必要があります。この数はF変数に格納されています。

V変数

Vは、選択リストまたはバインド変数の値を格納するデータ・バッファのアドレスからなる配列のポインタです。

記述子を割り当てると、SQLSQLDAAlloc()によってアドレスの配列にあるV[0]からV[N - 1]の要素が0(ゼロ)に設定されます。

選択記述子の場合は、FETCHコマンドを発行する前にデータ・バッファを割り当てて、この配列を設定する必要があります。次に例を示します。

EXEC SQL FETCH ... USING DESCRIPTOR ... 

この文は、FETCHされた選択リストの値を、V[0]からV[N - 1]が指しているデータ・バッファに格納するようにOracleに指示します。Oracleにより、i番目の選択リストの値が、V[i]が指しているデータ・バッファに格納されます。

バインド記述子の場合は、OPENコマンドを発行する前に、この配列を設定する必要があります。次に例を示します。

EXEC SQL OPEN ... USING DESCRIPTOR ...
 

この文では、V[0]からV[N - 1]が指しているバインド変数の値を使用して動的SQL文を実行するようにOracleに指示します。Oracleでは、V[i]が指しているデータ・バッファでi番目のバインド変数の値が参照されます。

L変数

Lは、データ・バッファに格納されている選択リストまたはバインド変数の値の長さからなる配列のポインタです。

選択記述子の場合は、DESCRIBE SELECT LISTにより、長さの配列が各選択リスト項目に予想される最大値に設定されます。ただし、FETCHコマンドを発行する前に長さを再設定することもあります。FETCHは、最大n文字を戻します。この場合、nはFETCHを発行する前のL[i]の値です。

長さの形式はOracleデータ型によって異なります。CHARまたはVARCHAR2の選択リスト項目については、DESCRIBE SELECT LISTはL[i]を選択リスト項目の最大長に設定します。NUMBER型の選択リスト項目については、位取りと精度が変数の下位バイトとその次の上位側バイトにそれぞれ戻されます。精度と位取りの値をL[i]から抽出するには、ライブラリ関数SQLNumberPrecV6()を使用できます。「精度と位取りの抽出」も参照してください。

FETCHする前に、L[i]を必要なデータ・バッファの長さに再設定する必要があります。たとえば、NUMBERをC言語のchar文字列に強制変換する場合は、L[i]を精度の数値に符号と小数点のための2を加えたものに設定します。NUMBER型をC言語のfloat型に強制変換する場合は、L[i]をシステム上のfloat型の長さに設定します。強制変換したデータ型の長さの詳細は、「データの変換」を参照してください。

バインド記述子の場合は、OPENコマンドを発行する前に、配列の長さを設定する必要があります。たとえば、strlen()を使用してユーザーが入力したバインド変数文字列の長さを取得してから、適切な配列要素を設定します。

OracleはV[i]に格納されているアドレスを使用して間接的にデータ・バッファにアクセスします。このためバッファ内の値の長さはOracleには認識されません。i番目の選択リストまたはバインド変数の値についてOracleが使用する長さを変更するときは、L[i]を必要な長さに再設定してください。入力バッファまたは出力バッファには、それぞれ異なる長さを指定できます。

T変数

Tは、選択リストまたはバインド変数の値のデータ型コードからなる配列のポインタです。これらのデータ型コードは、V配列の要素が指示するデータ・バッファにOracleデータが格納されるときのデータの変換方法を決定します。

選択記述子の場合は、DESCRIBE SELECT LISTはデータ型コードの配列を選択リスト内の項目の内部データ型(CHAR、NUMBER、DATEなど)に設定します。

Oracleデータ型の内部形式は処理が複雑なため、FETCHする前にデータ型をいくつか再設定する必要がある場合があります。表示用データのときは、一般には選択リストの値のデータ型をVARCHAR2またはSTRINGに強制変換することをお薦めします。計算用データのときは、Oracleの数値をC言語の形式に強制変換する必要がある場合があります。

T[i]の上位ビットの設定は、i番目の選択リスト項目のNULL/NOT NULLステータスを示します。OPENコマンドまたはFETCHコマンドを発行する前に、常にこのビットをオフにする必要があります。データ型コードを取り出してNULL/NOT NULLビットを消去するには、ライブラリ関数SQLColumnNullCheck()を使用します。

OracleのNUMBER内部データ型は、V[i]が指すC言語のデータ・バッファと互換性のある外部データ型に変更する必要があります。

バインド記述子の場合、データ型コードの配列はDESCRIBE BIND VARIABLESによって0に設定されます。OPENコマンドを発行する前に、各要素に格納されたデータ型を設定する必要があります。コードは、V[i]が指すデータ・バッファの外部(C)データ型を表します。バインド変数の値が文字列に格納され、データ型配列の要素が1(VARCHAR2データ型コード)に設定されることがよくあります。データ型コード5(STRING)を使用することもできます。

i番目の選択リストまたはバインド変数の値のデータ型を変更するには、T[i]を変更するデータ型に再設定してください。

I変数

Iは、標識変数値を格納するデータ・バッファのアドレスの配列へのポインタです。

アドレスの配列のI[0]からI[N - 1]の要素を設定する必要があります。

選択記述子の場合は、FETCHコマンドを発行する前に、アドレスの配列を設定する必要があります。Oracleが次の文を実行する場合の例を次に示します。

EXEC SQL FETCH ... USING DESCRIPTOR ... 

この場合、戻されたi番目の選択リスト値がNULLの場合は、I[i]が指す標識変数値が-1に設定されます。それ以外の場合は、0(値がNULLでない)または正の整数(値が切り捨てられている)に設定されます。

バインド記述子の場合は、OPENコマンドを発行する前に、アドレスの配列とそれに対応付けられた標識変数を設定する必要があります。Oracleが次の文を実行する場合の例を次に示します。

EXEC SQL OPEN ... USING DESCRIPTOR ... 

この場合、I[i]が指しているデータ・バッファはi番目のバインド変数の値がNULLであるかどうかを決定します。標識変数の値が-1のとき、関連するバインド変数の値はNULLです。

F変数

Fは、DESCRIBEによって検出される選択リスト項目またはプレースホルダの実際の個数です。

FはDESCRIBEによって設定されます。Fの値が負のときは、割り当てられた記述子のサイズに対してDESCRIBEが検出した選択リスト項目またはプレースホルダが多すぎることを示しています。たとえば、Nを10に設定したときに、DESCRIBEで11個の選択リスト項目またはプレースホルダが検出されると、Fは-11に設定されます。この機能を使用すると、必要に応じて選択リスト項目またはプレースホルダに大きい記憶域を動的に再割当てすることができます。

S変数

Sは、データ・バッファのアドレスの配列へのポインタです。動的SQL文で選択リストまたはプレースホルダの名前が見つかると、それをそのデータ・バッファに格納します。

SQLSQLDAAlloc()を使用してデータ・バッファを割り当ててから、S配列にそれらのアドレスを格納します。

DESCRIBEは、S[i]が指すデータ・バッファ内にi番目の選択リスト項目またはプレースホルダの名前を格納するように、Oracleに指示します。

M変数

Mは、選択リストまたはプレースホルダの名前を格納するデータ・バッファの最大長からなる配列へのポインタです。このデータ・バッファのアドレスは、S配列の要素によって指定されます。

記述子を割り当てると、SQLSQLDAAlloc()は要素M[0]からM[N - 1]までを最大長の配列に設定します。S[i]が指しているデータ・バッファに格納するとき、i番目の名前は必要に応じてM[i]の長さに切り捨てられます。

C変数

Cは、選択リストまたはプレースホルダの名前の現行の長さからなる配列へのポインタです。

DESCRIBEは、現行の長さの配列の中にC[0]からC[N - 1]の要素を設定します。DESCRIBE文を実行すると、配列には選択リスト名またはプレースホルダ名の文字数が格納されます。

X変数

Xは、標識変数の名前を格納するデータ・バッファのアドレスからなる配列へのポインタです。標識変数の値を選択リスト項目およびバインド変数に対応付けることができます。ただし、標識変数の名前はバインド変数にしか対応付けられません。したがって、Xはバインド記述子専用です。

データ・バッファを割り当て、そのアドレスをX配列に保存するには、SQLSQLDAAlloc()を使用します。

DESCRIBE BIND VARIABLESは、i番目の標識変数名をX[i]が指すデータ・バッファに格納するようにOracleに指示します。

Y変数

Yは、標識変数の名前を格納するデータ・バッファの最大長からなる配列へのポインタです。Xと同様に、Yもバインド記述子専用です。

SQLSQLDAAlloc()を使用して要素Y[0]からY[N - 1]までを最大長の配列に割り当てます。X[i]が指しているデータ・バッファに格納するとき、i番目の名前は必要に応じてY[i]の長さに切り捨てられます。

Z変数

Zは、標識変数の名前の現行の長さからなる配列へのポインタです。XおよびYと同様に、Zもバインド記述子専用です。

DESCRIBE BIND VARIABLESは、現行の長さの配列にZ[0]からZ[N - 1]の要素を設定します。DESCRIBEを実行すると、それぞれの標識変数名の文字数がこの配列に格納されます。

予備知識

動的SQL方法4を実装するには次の処理についての知識が必要です。

データの変換

この項では、T(データ型)の記述子配列について詳しく説明します。データ型の同値化と動的SQL方法4のどちらも使用しないホスト・プログラムでは、Oracleの内部データ型と外部データ型との変換方法はプリコンパイル時に決定されます。デフォルトでは、プリコンパイラは宣言部内のそれぞれのホスト変数に特定の外部データ型を割り当てます。たとえば、プリコンパイラはint型のホスト変数にINTEGER外部データ型を割り当てます。

ただし、方法4を使用すると、データの変換および形式を制御できます。データの変換方法を指定するには、T記述子配列にデータ型コードを設定します。

内部データ型

内部データ型は、Oracleがデータベース表に列値を格納するための形式と、疑似列値を表すための形式を指定します。

DESCRIBE SELECT LISTコマンドを発行すると、Oracleはそれぞれの選択リスト項目に対する内部データ型コードをT記述子配列に戻します。たとえば、i番目の選択リスト項目に対するデータ型コードはT[i]に戻されます。

表15-1に、Oracleの内部データ型とそのコードを示します。

表15-1 Oracle内部データ型

Oracle内部データ型 コード

VARCHAR2

1

NUMBER

2

LONG

8

BINARY_FLOAT

100

BINARY_DOUBLE

101

ROWID

11

DATE

12

RAW

23

LONG RAW

24

CHARACTER(またはCHAR)

96

ユニバーサルROWID

104


外部データ型

外部データ型は、入力ホスト変数と出力ホスト変数に値を格納するための形式を指定します。

DESCRIBE BIND VARIABLESコマンドはデータ型コードのT配列を0(ゼロ)に設定します。このため、OPENコマンドを発行する前に、それらのコードを再設定する必要があります。これらのコードは、各種バインド変数について想定される外部データ型をOracleに指示します。i番目のバインド変数については、必要な外部データ型をT[i]に再設定してください。

表15-2に、Oracleの外部データ型とそのコード、および各外部データ型で通常使用するC言語のデータ型を示します。

表15-2 Oracle外部データ型とデータ型コード

外部データ型 コード C言語のデータ型

VARCHAR2

1

char[n]

NUMBER

2

char[n](n <= 22)

INTEGER

3

int

FLOAT

4

float

STRING

5

char[n+1]

VARNUM

6

char[n](n <= 22)

DECIMAL

7

float

LONG

8

char[n]

SQLT_BFLOAT

21

float

SQLT_BDOUBLE

22

double

VARCHAR

9

char[n+2]

ROWID

11

char[n]

DATE

12

char[n]

VARRAW

15

char[n]

RAW

23

unsigned char[n]

LONG RAW

24

unsigned char[n]

UNSIGNED

68

unsigned int

DISPLAY

91

char[n]

LONG VARCHAR

94

char[n+4]

LONG VARRAW

95

unsigned char[n+4]

CHAR

96

char[n]

CHARF

96

char[n]

CHARZ

97

char[n+1]



関連項目


データ型の強制変換

選択記述子の場合、DESCRIBE SELECT LISTはOracleの内部データ型をどれでも戻すことができます。文字データの場合と同様に、内部データ型は使用する外部データ型と正確に対応している場合が多くあります。ただし、外部データ型にマップされるいくつかの内部データ型は処理が困難です。そのため、T記述子配列の一部の要素を再設定する必要がある場合があります。たとえば、NUMBER値をC言語のfloat値に対応するFLOAT値に再設定する場合があります。Oracleでは、内部データ型と外部データ型の間の変換に必要な処理がFETCH時に実行されます。このため、データ型の再設定は必ずDESCRIBE SELECT LISTの、FETCHのに行ってください。

バインド記述子の場合は、DESCRIBE BIND VARIABLESによってバインド変数のデータ型は戻されません。バインド変数の数と名前のみが戻されます。したがって、データ型コードのT配列を明示的に設定することで、それぞれのバインド変数の外部データ型をOracleに通知する必要があります。Oracleでは、OPEN時に外部データ型と内部データ型間で必要な変換がすべて実行されます。

T記述子配列でデータ型コードを再設定すると、データ型を強制変換することになります。たとえば、i番目の選択リスト値をSTRINGに強制変換するには、次の文を使用します。

/* Coerce select-list value to STRING. */ 
select_des->T[i] = 5; 

データ表示用にNUMBERの選択リスト値をSTRINGに強制変換するときは、値の精度と位取りのバイトを抽出し、それらを使用して最大表示長を算出する必要もあります。FETCHの前に、L(長さ)記述子配列の該当する要素を再設定し、使用するバッファの長さをOracleに通知する必要があります。

たとえば、DESCRIBE SELECT LISTによってi番目の選択リスト項目のデータ型がNUMBER型であるとわかっているとします。このときfloat型で宣言されているC変数に戻り値を格納する場合は、T[i]には4を、L[i]にはシステムが定めるfloatの長さを設定するのみで済みます。


注意:

DESCRIBE SELECT LISTによって戻される内部データ型が、期待する結果と合わない場合もあります。DATE型とNUMBER型がその例です。DATE型の選択リスト項目をDESCRIBEすると、Oracleではデータ型コード12がT記述子配列に戻されます。FETCHの前にコードを再設定しないかぎり、日付の値はその7バイト内部形式で戻されます。日付を文字形式(DD-MON-YY)で取得するには、12に設定されているデータ型コードを1(VARCHAR2)または5(STRING)に変更し、7に設定されているL値を9または10に増やします。

NUMBER型の選択リスト項目を同じ要領でDESCRIBEすると、Oracleではデータ型コード2がT配列に戻されます。FETCHの前にコードを再設定しないかぎり、数値はその内部形式で戻されるため、おそらく求めている値とは異なります。そのときは、2に設定されているコードを1(VARCHAR2)、3(INTEGER)、4(FLOAT)、5(STRING)またはその他の適切なデータ型に変更します。


精度と位取りの抽出

ライブラリ関数SQLNumberPrecV6()(従来のsqlprc())は、精度と位取りを抽出します。一般には、この関数はDESCRIBE SELECT LISTの後に使用します。その最初の引数はL[i]です。次の構文で、SQLNumberPrecV6()をコールします。


注意:

プラットフォームの正しいプロトタイプは、プラットフォーム固有のSQLNumberPrecV6ヘッダー・ファイルを参照してください。

SQLNumberPrecV6(dvoid *runtime_context, int *length, int *precision,
                int *scale); 

パラメータは、次のとおりです。

構文 説明
runtime_context ランタイム・コンテキストへのポインタ
length OracleのNUMBER値を保存する長い整変数へのポインタ。長さはL[i]に格納されます。値の位取りと精度は下位バイトと次の上位バイトにそれぞれ格納されます。
precision NUMBER値の精度を戻す整変数へのポインタ。精度とは有効桁数を指します。選択リスト項目がサイズの指定されていないNUMBERを参照している場合、精度は0(ゼロ)に設定されます。このときはサイズ指定がないため、最大の精度(38)を想定してください。
scale NUMBER値の位取りを戻す整変数へのポインタ。位取りには四捨五入する位置を指定します。たとえば位取りが2のときは、1/100の倍数の近似値に値が四捨五入される(3.456は3.46になる)ことを意味します。また位取りが-3のときは、1000の倍数の近似値に値が四捨五入される(3456が3000になる)ことを意味します。

位取りが負の場合は、その絶対値を長さに追加してください。たとえば、精度に3、位取りに-2を指定すると、99900までの値が有効になります。

次の例に、SQLNumberPrecV6()を使用して、STRINGに強制変換するNUMBER値の最大値表示長を計算する方法を示します。

/* Declare variables for the function call. */ 
sqlda         *select_des;  /* pointer to select descriptor */ 
int            prec;        /* precision                    */ 
int            scal;        /* scale                        */ 
extern void SQLNumberPrecV6();  /* Declare library function. */ 
/* Extract precision and scale. */ 
SQLNumberPrecV6(SQL_SINGLE_RCTX, &(select_des->L[i]), &prec, &scal); 
/* Allow for maximum size of NUMBER. */ 
if (prec == 0) 
    prec = 38; 
/* Allow for possible decimal point and sign. */ 
select_des->L[i] = prec + 2; 
/* Allow for negative scale. */ 
if (scal < 0) 
    select_des->L[i] += -scal; 

この関数コールの最初の引数は長さの配列のi番目の要素を指します。また、パラメータは3つともすべてアドレスであることに注意してください。

SQLNumberPrecV6()関数では、一部のSQLデータ型の精度と位取りの値に0(ゼロ)が戻されます。SQLNumberPrecV7()関数も同様で、次に示すSQLデータ型の場合を除けば引数リストも戻り値も同じです。

表15-3 SQLデータ型の精度と位取り

SQLデータ型 2進数精度 位取り

FLOAT

126

-127

FLOAT(N)

N(範囲は1〜126)

-127

REAL

63

-127

DOUBLE PRECISION

126

-127


NULL/NOT NULLデータ型の処理

すべての選択リスト列(式は不可)について、DESCRIBE SELECT LISTは選択記述子のデータ型配列TにNULL/NOT NULLインジケータを戻します。i番目の選択リスト列にNOT NULL制約が指定されていると、T[i]の上位ビットはオフにされます。それ以外の場合は上位ビットが設定されます。

OPEN文またはFETCH文でデータ型を使用する前に、すでにNULL/NOT NULLビットが設定されているときは、そのビットをオフにする必要があります。(このビットは絶対にオンにしないでください。)

列にNULLが有効かどうかを調べ、データ型のNULL/NOT NULLビットを消去するには、ライブラリ関数SQLColumnNullCheck()(従来のsqlnul())を使用します。次の構文で、SQLColumnNullCheck()をコールします。

SQLColumnNullCheck(dvoid *context, unsigned short *value_type, 
      unsigned short *type_code, int *null_status);

パラメータは、次のとおりです。

構文 説明
context ランタイム・コンテキストへのポインタ
value_type 選択リスト列のデータ型コードを格納する符号なしshort int型変数へのポインタ。データ型はT[i]に格納されます。
type_code 選択リスト列のデータ型コードを戻す符号なしshort int型変数へのポインタ。上位ビットはオフにされています。
null_status 選択リスト列のNULL状態を戻すint型変数へのポインタ。1は列がNULLを許可し、0は許可しないことを意味します。

次の例に、SQLColumnNullCheck()の使用方法を示します。

/* Declare variables for the function call. */ 
sqlda  *select_des;      /* pointer to select descriptor */ 
unsigned short   dtype;  /* datatype without null bit    */ 
int   nullok;            /* 1 = null, 0 = not null       */ 
extern void SQLColumnNullCheck();    /* Declare library function.    */ 
/* Find out whether column is not null. */ 
SQLColumnNUllCheck(SQL_SINGLE_RCTX, (unsigned short *)&(select_des->T[i]), &dtype, &nullok); 
if (nullok) 
{ 
    /* Nulls are allowed. */ 
    ... 
    /* Clear the null/not null bit. */ 
SQLColumnNullCheck(SQL_SINGLE_RCTX, &(select_des->T[i]), &(select_des->T[i]), &nullok); 
} 

SQLColumnNullCheck()関数の2回目のコールで指定されている1番目と2番目の引数は、データ型配列のi番目の要素を指します。また、パラメータは3つともアドレスであることに注意してください。

基本ステップ

方法4はあらゆる動的SQL文に使用できます。次の例には問合せの処理が示されているため、入力ホスト変数と出力ホスト変数の両方の処理方法が理解できます。

このサンプル・プログラムでは次のステップに従って動的問合せを処理します。

  1. 問合せのテキストを保持するためのホスト文字列を宣言部で宣言します。

  2. 選択SQLDAとバインドSQLDAを宣言します。

  3. 選択記述子とバインド記述子に対する記憶域を割り当てます。

  4. DESCRIBEできる選択リスト項目とプレースホルダの最大数を設定します。

  5. 問合せのテキストをホスト文字列に設定します。

  6. ホスト文字列から問合せをPREPAREします。

  7. 問合せ用の(FOR)カーソルをDECLAREします。

  8. バインド記述子に(INTO)バインド変数をDESCRIBEします。

  9. プレースホルダの最大数をDESCRIBEによって実際に検出された数に再設定します。

  10. DESCRIBEで検出されたバインド変数の値を取得し、それらの変数に対する記憶域を割り当てます。

  11. バインド記述子を使用して(USING)カーソルをOPENします。

  12. 選択記述子に(INTO)選択リストをDESCRIBEします。

  13. 選択リスト項目の最大数をDESCRIBEによって実際に検出された数に再設定します。

  14. 表示用にそれぞれの選択リスト項目の長さとデータ型を再設定します。

  15. 選択記述子が指している割当て済のデータ・バッファに(INTO)データベースの行をFETCHします。

  16. FETCHによって戻された選択リストの値を処理します。

  17. 選択リスト項目、プレースホルダ、標識変数および記述子に対する記憶域の割当てを解除します。

  18. カーソルをCLOSEします。


注意:

動的SQL文に含まれる選択リスト項目またはプレースホルダの数が明確である場合、一部の手順は必要ありません。

各ステップの詳細

この項では、それぞれのステップを詳しく説明します。章の終わりには、方法4を使用したコメント付きの完全なプログラム例を示します。

方法4では、埋込みSQL文を次のような順序で使用します。

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文を受け入れ、それを処理する方法を説明します。


注意:

次の説明には、図を使用します。図が複雑になるのを避けるために、次の条件を満たす事が必要です。

ホスト文字列の宣言

プログラムには、動的SQL文のテキストを格納するためのホスト変数が必要です。ホスト変数(ここではselect_stmt)は文字列として宣言する必要があります。

    ... 
    int      emp_number; 
    VARCHAR  emp_name[10]; 
    VARCHAR  select_stmt[120]; 
    float    bonus; 

SQLDAの宣言

ここでは、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()関数は、記述子構造体およびポインタ変数VLTIによってアドレス指定される配列を割り当てます。

max_nameが0(ゼロ)以外のときは、ポインタ変数SMCによってアドレス指定される配列が割り当てられます。max_ind_nameが0(ゼロ)以外のときは、ポインタ変数XYZによってアドレス指定される配列が割り当てられます。max_namemax_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(ゼロ)に設定します。

DESCRIBEへの最大数の設定

DESCRIBEできる選択リスト項目またはプレースホルダの最大数を次のように設定します。

select_des->N = 3; 
bind_des->N = 3; 

図15-2図15-3に、結果として得られる記述子を示します。


注意:

選択記述子の場合(図15-2)、標識変数名の選択は消されて使用されないことを示します。

図15-2 初期化された選択記述子

初期化された選択記述子
「図15-2 初期化された選択記述子」の説明

図15-3 初期化されたバインド記述子

初期化されたバインド記述子
「図15-3 初期化されたバインド記述子」の説明

ホスト文字列への問合せテキストの設定

次の例では、ユーザーに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" 

ホスト文字列からの問合せのPREPARE

PREPAREはこのSQL文を解析して名前を指定します。例では、PREPAREはホスト文字列select_stmtを解析してから、それにsql_stmtという名前を指定します。

EXEC SQL PREPARE sql_stmt FROM :select_stmt; 

カーソルの宣言

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

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に設定されています。

図15-4 DESCRIBE後のバインド記述子

DESCRIBE後のバインド記述子
「図15-4 DESCRIBE後のバインド記述子」の説明

プレースホルダの最大数の再設定

次に、プレースホルダの最大数を、DESCRIBEによって実際に検出された数に再設定する必要があります。

bind_des->N = bind_des->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に、結果として得られるバインド記述子を示します。値はヌル文字で終了します。

図15-5 値を割り当てた後のバインド記述子

値を割り当てた後のバインド記述子
「図15-5 値を割り当てた後のバインド記述子」の説明

カーソルのOPEN

動的問合せに使用するOPEN文は、カーソルがバインド記述子に対応付けられることを除けば静的問合せに使用するものと同じです。実行時に決定され、バインド記述子表の要素でアドレス指定したバッファに格納された値を使用して、SQL文を評価します。問合せの場合は、アクティブ・セットの識別にも同じ値を使用します。

例では、OPENは次のようにemp_cursorbind_desに対応付けます。

EXEC SQL OPEN emp_cursor USING DESCRIPTOR bind_des; 

bind_desはコロンで始めることはできません。

OPENはSQL文を実行します。問合せ時に、OPENはアクティブ・セットを決定するとともにカーソルを先頭行に位置付けます。

選択リストのDESCRIBE

動的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()を使用して精度と位取りを抽出する必要があります。

図15-6 DESCRIBE実行後の選択記述子

DESCRIBE実行後の選択記述子
「図15-6 DESCRIBE実行後の選択記述子」の説明

選択リスト項目の最大数の再設定

次に、選択リスト項目の最大数を、DESCRIBEによって実際に検出された数に再設定する必要があります。

select_des->N = select_des->F; 

各選択リスト項目の長さとデータ型の再設定

例では、選択リストの値を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を加算したためです。

図15-7 FETCH前の選択記述子

FETCH前の選択記述子
「図15-7 FETCH前の選択記述子」の説明

アクティブ・セットからの行のFETCH

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は選択リストの値とインジケータの値を、VIの要素によってアドレス指定されるデータ・バッファに格納しています。

データ型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バイトのフィールドの値を左揃えにしてから、ヌル文字を付けます。

選択リストの値の取得と処理

FETCH後、プログラムで戻り値を処理できます。例では、列ENAME、EMPNOおよびCOMMの値が処理されます。

図15-8 FETCH後の選択記述子

FETCH後の選択記述子
「図15-8 FETCH後の選択記述子」の説明

記憶域の割当て解除

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); 

カーソルのCLOSE

CLOSEはカーソルを使用禁止にします。例では、CLOSEは次のようにemp_cursorを使用禁止にします。

EXEC SQL CLOSE emp_cursor; 

ホスト配列の使用

方法4で入力ホスト配列または出力ホスト配列を使用するには、オプションのFOR句を使用してホスト配列のサイズをOracleに通知する必要があります。


関連項目

FOR句の詳細は、第8章「ホスト配列」を参照してください。

次の構文を使用して、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); 
} 

sample12.pc

配列のフェッチを使用した簡単な動的SQLの例は、demoディレクトリのsample12.pcファイルにあります。

サンプル・プログラム: 動的SQL方法4

このプログラムでは、動的SQL方法4を使用するために必要な基本ステップを示します。Oracleへの接続後に、プログラムでは次のことが実行されます。

入力されたSQL文が問合せのときは、プログラムは各行のデータをFETCHしてからカーソルをCLOSEします。このプログラムはdemoディレクトリのファイルsample10.pcにあり、オンラインで利用できます。

/*******************************************************************
Sample Program 10:  Dynamic SQL Method 4

This program connects you to ORACLE using your username and
password, then prompts you for a SQL statement.  You can enter
any legal SQL statement.  Use regular SQL syntax, not embedded SQL.
Your statement will be processed.  If it is a query, the rows
fetched are displayed.
You can enter multiline statements.  The limit is 1023 characters.
This sample program only processes up to MAX_ITEMS bind variables and
MAX_ITEMS select-list items.  MAX_ITEMS is #defined to be 40.
*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <sqlda.h>
#include <stdlib.h>
#include <sqlcpr.h>

/* Maximum number of select-list items or bind variables. */
#define MAX_ITEMS         40

/* Maximum lengths of the _names_ of the
   select-list items or indicator variables. */
#define MAX_VNAME_LEN     30
#define MAX_INAME_LEN     30

#ifndef NULL
#define NULL  0
#endif

/* Prototypes */
#if defined(__STDC__)
  void sql_error(void);
  int oracle_connect(void);
  int alloc_descriptors(int, int, int);
  int get_dyn_statement(void);
  void set_bind_variables(void);
  void process_select_list(void);
  void help(void);
#else
  void sql_error(/*_ void _*/);
  int oracle_connect(/*_ void _*/);
  int alloc_descriptors(/*_ int, int, int _*/);
  int get_dyn_statement(/* void _*/);
  void set_bind_variables(/*_ void -*/);
  void process_select_list(/*_ void _*/);
  void help(/*_ void _*/);
#endif

char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",
                        "UPDATE", "update", "DELETE", "delete"};

EXEC SQL INCLUDE sqlda;
EXEC SQL INCLUDE sqlca;

EXEC SQL BEGIN DECLARE SECTION;
    char    dyn_statement[1024];
    EXEC SQL VAR dyn_statement IS STRING(1024);
EXEC SQL END DECLARE SECTION;
 
SQLDA *bind_dp;
SQLDA *select_dp;

/* Define a buffer to hold longjmp state info. */
jmp_buf jmp_continue;

/* A global flag for the error routine. */
int parse_flag = 0;

void main()
{
    int i;

    /* Connect to the database. */
    if (oracle_connect() != 0)
        exit(1);

    /* Allocate memory for the select and bind descriptors. */
    if (alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN) != 0)
        exit(1);

    /* Process SQL statements. */
    for (;;) 
    {
        (void) setjmp(jmp_continue);

        /* Get the statement.  Break on "exit". */
        if (get_dyn_statement() != 0)
            break;

        /* Prepare the statement and declare a cursor. */
        EXEC SQL WHENEVER SQLERROR DO sql_error();

        parse_flag = 1;     /* Set a flag for sql_error(). */
        EXEC SQL PREPARE S FROM :dyn_statement;
        parse_flag = 0;     /* Unset the flag. */

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Set the bind variables for any placeholders in the
           SQL statement. */
        set_bind_variables();

        /* Open the cursor and execute the statement.
         * If the statement is not a query (SELECT), the
         * statement processing is completed after the
         * OPEN.
         */

        EXEC SQL OPEN C USING DESCRIPTOR bind_dp;

        /* Call the function that processes the select-list.
         * If the statement is not a query, this function
         * just returns, doing nothing.
         */
        process_select_list();

        /* Tell user how many rows processed. */
        for (i = 0; i < 8; i++)
        {
           if (strncmp(dyn_statement, dml_commands[i], 6) == 0)
           {
               printf("\n\n%d row%c processed.\n", sqlca.sqlerrd[2],
                       sqlca.sqlerrd[2] == 1 ? '\0' : 's');
               break;
           }
        }
    }       /* end of for(;;) statement-processing loop */

    /* When done, free the memory allocated for
       pointers in the bind and select descriptors. */
    for (i = 0; i < MAX_ITEMS; i++)
    {    
        if (bind_dp->V[i] != (char *) 0)
            free(bind_dp->V[i]);
        free(bind_dp->I[i]);   /* MAX_ITEMS were allocated. */
        if (select_dp->V[i] != (char *) 0)
            free(select_dp->V[i]);
        free(select_dp->I[i]); /* MAX_ITEMS were allocated. */
    }

    /* Free space used by the descriptors themselves. */
    SQLSQLDAFree( SQL_SINGLE_RCTX, bind_dp);
    SQLSQLDAFree( SQL_SINGLE_RCTX, select_dp);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    /* Close the cursor. */
    EXEC SQL CLOSE C;

    EXEC SQL COMMIT WORK RELEASE;
    puts("\nHave a good day!\n");

    EXEC SQL WHENEVER SQLERROR DO sql_error();
    return;
}


int oracle_connect()
{
    EXEC SQL BEGIN DECLARE SECTION;
        VARCHAR  username[128];
        VARCHAR  password[32];
    EXEC SQL END DECLARE SECTION;

    printf("\nusername: ");
    fgets((char *) username.arr, sizeof username.arr, stdin);
    username.arr[strlen((char *) username.arr)-1] = '\0';
    username.len = (unsigned short)strlen((char *) username.arr);

    printf("password: ");
    fgets((char *) password.arr, sizeof password.arr, stdin);
    password.arr[strlen((char *) password.arr) - 1] = '\0';
    password.len = (unsigned short)strlen((char *) password.arr);


    EXEC SQL WHENEVER SQLERROR GOTO connect_error;

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    printf("\nConnected to ORACLE as user %s.\n", username.arr);

    return 0;

connect_error:
    fprintf(stderr, "Cannot connect to ORACLE as user %s\n", username.arr);
    return -1;
}


/*
 *  Allocate the BIND and SELECT descriptors using SQLSQLDAAlloc().
 *  Also allocate the pointers to indicator variables
 *  in each descriptor.  The pointers to the actual bind
 *  variables and the select-list items are realloc'ed in
 *  the set_bind_variables() or process_select_list()
 *  routines.  This routine allocates 1 byte for select_dp->V[i]
 *  and bind_dp->V[i], so the realloc will work correctly.
 */

alloc_descriptors(size, max_vname_len, max_iname_len)
int size;
int max_vname_len;
int max_iname_len;
{
    int i;

    /*
     * The first SQLSQLDAAlloc parameter is the runtime context.

     * The second parameter determines the maximum number of
     * array elements in each variable in the descriptor. In
     * other words, it determines the maximum number of bind
     * variables or select-list items in the SQL statement.
     *
     * The third parameter determines the maximum length of
     * strings used to hold the names of select-list items
     * or placeholders.  The maximum length of column 
     * names in ORACLE is 30, but you can allocate more or less
     * as needed.
     *
     * The fourth parameter determines the maximum length of
     * strings used to hold the names of any indicator
     * variables.  To follow ORACLE standards, the maximum
     * length of these should be 30.  But, you can allocate
     * more or less as needed.
     */

    if ((bind_dp =
       SQLSQLDAAlloc(SQL_SINGLE_RCTX, size, max_vname_len, max_iname_len)) == 
         (SQLDA *) 0)
    {
        fprintf(stderr,
            "Cannot allocate memory for bind descriptor.");
        return -1;  /* Have to exit in this case. */
    }

    if ((select_dp =
        SQLSQLDAAlloc (SQL_SINGLE_RCTX, size, max_vname_len, max_iname_len)) == 
           (SQLDA *) 0)
    {
        fprintf(stderr,
            "Cannot allocate memory for select descriptor.");
        return -1;
    }
    select_dp->N = MAX_ITEMS;

    /* Allocate the pointers to the indicator variables, and the
       actual data. */
    for (i = 0; i < MAX_ITEMS; i++) {
        bind_dp->I[i] = (short *) malloc(sizeof (short));
        select_dp->I[i] = (short *) malloc(sizeof(short));
        bind_dp->V[i] = (char *) malloc(1);
        select_dp->V[i] = (char *) malloc(1);
    }
       
    return 0;
}


int get_dyn_statement()
{
    char *cp, linebuf[256];
    int iter, plsql;


    for (plsql = 0, iter = 1; ;)
    {
        if (iter == 1)
        {
            printf("\nSQL> ");
            dyn_statement[0] = '\0';
        }
        
        fgets(linebuf, sizeof linebuf, stdin);

        cp = strrchr(linebuf, '\n');
        if (cp && cp != linebuf)
            *cp = ' ';
        else if (cp == linebuf)
            continue;

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||
            (strncmp(linebuf, "exit", 4) == 0))
        {
            return -1;
        }

        else if (linebuf[0] == '?' ||
            (strncmp(linebuf, "HELP", 4) == 0) ||
            (strncmp(linebuf, "help", 4) == 0))
        {
            help();
            iter = 1;
            continue;
        }

        if (strstr(linebuf, "BEGIN") ||
            (strstr(linebuf, "begin")))
        {
            plsql = 1;
        }

        strcat(dyn_statement, linebuf);

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
            (!plsql && (cp = strrchr(dyn_statement, ';'))))
        {
            *cp = '\0';
            break;
        }
        else
        {
            iter++;
            printf("%3d  ", iter);
        }
    }
    return 0;
}



void set_bind_variables()
{
    int i, n;
    char bind_var[64];

    /* Describe any bind variables (input host variables) */
    EXEC SQL WHENEVER SQLERROR DO sql_error();

    bind_dp->N = MAX_ITEMS;  /* Initialize count of array elements. */
    EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;

    /* If F is negative, there were more bind variables
       than originally allocated by SQLSQLDAAlloc(). */
    if (bind_dp->F < 0)
    {
        printf ("\nToo many bind variables (%d), maximum is %d\n.",
                    -bind_dp->F, MAX_ITEMS);
        return;
    }

    /* Set the maximum number of array elements in the
       descriptor to the number found. */
    bind_dp->N = bind_dp->F;
 
    /* Get the value of each bind variable as a
     * character string.
     *   
     * C[i] contains the length of the bind variable
     *      name used in the SQL statement.
     * S[i] contains the actual name of the bind variable
     *      used in the SQL statement.
     *
     * L[i] will contain the length of the data value
     *      entered.
     *
     * V[i] will contain the address of the data value
     *      entered.
     *
     * T[i] is always set to 1 because in this sample program
     *      data values for all bind variables are entered
     *      as character strings.
     *      ORACLE converts to the table value from CHAR.
     *
     * I[i] will point to the indicator value, which is
     *      set to -1 when the bind variable value is "null".
     */
    for (i = 0; i < bind_dp->F; i++)
    {
        printf ("\nEnter value for bind variable %.*s:  ",
               (int)bind_dp->C[i], bind_dp->S[i]);
        fgets(bind_var, sizeof bind_var, stdin);

        /* Get length and remove the new line character. */
        n = strlen(bind_var) - 1;

        /* Set it in the descriptor. */
        bind_dp->L[i] = n;

        /* (re-)allocate the buffer for the value.
           SQLSQLDAAlloc() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer. */

         bind_dp->V[i] = (char *) realloc(bind_dp->V[i],
                         (bind_dp->L[i] + 1));            

        /* And copy it in. */
        strncpy(bind_dp->V[i], bind_var, n);

        /* Set the indicator variable's value. */
        if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) ||
                (strncmp(bind_dp->V[i], "null", 4) == 0))
            *bind_dp->I[i] = -1;
        else
            *bind_dp->I[i] = 0;
    
        /* Set the bind datatype to 1 for CHAR. */
        bind_dp->T[i] = 1;
    }
  return;
}



void process_select_list()
{
    int i, null_ok, precision, scale;

    if ((strncmp(dyn_statement, "SELECT", 6) != 0) &&
        (strncmp(dyn_statement, "select", 6) != 0))
    {
        select_dp->F = 0;
        return;
    }

    /* If the SQL statement is a SELECT, describe the
        select-list items.  The DESCRIBE function returns
        their names, datatypes, lengths (including precision
        and scale), and NULL/NOT NULL statuses. */

    select_dp->N = MAX_ITEMS;
    
    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;

    /* If F is negative, there were more select-list
       items than originally allocated by SQLSQLDAAlloc(). */
    if (select_dp->F < 0)
    {
        printf ("\nToo many select-list items (%d), maximum is %d\n",
                -(select_dp->F), MAX_ITEMS);
        return;
    }

    /* Set the maximum number of array elements in the
       descriptor to the number found. */
    select_dp->N = select_dp->F;

    /* Allocate storage for each select-list item.
  
       SQLNumberPrecV6() is used to extract precision and scale
       from the length (select_dp->L[i]).

       sqlcolumnNullCheck() is used to reset the high-order bit of
       the datatype and to check whether the column
       is NOT NULL.

       CHAR    datatypes have length, but zero precision and
               scale.  The length is defined at CREATE time.

       NUMBER  datatypes have precision and scale only if
               defined at CREATE time.  If the column
               definition was just NUMBER, the precision
               and scale are zero, and you must allocate
               the required maximum length.

       DATE    datatypes return a length of 7 if the default
               format is used.  This should be increased to
               9 to store the actual date character string.
               If you use the TO_CHAR function, the maximum
               length could be 75, but will probably be less
               (you can see the effects of this in SQL*Plus).

       ROWID   datatype always returns a fixed length of 18 if
               coerced to CHAR.

       LONG and
       LONG RAW datatypes return a length of 0 (zero),
               so you need to set a maximum.  In this example,
               it is 240 characters.

       */
    
    printf ("\n");
    for (i = 0; i < select_dp->F; i++)
    {
        char title[MAX_VNAME_LEN]; 
        /* Turn off high-order bit of datatype (in this example,
           it does not matter if the column is NOT NULL). */
        SQLColumnNullCheck ((unsigned short *)&(select_dp->T[i]), 
             (unsigned short *)&(select_dp->T[i]), &null_ok);

        switch (select_dp->T[i])
        {
            case  1 : /* CHAR datatype: no change in length
                         needed, except possibly for TO_CHAR
                         conversions (not handled here). */
                break;
            case  2 : /* NUMBER datatype: use SQLNumberPrecV6() to
                         extract precision and scale. */
                SQLNumberPrecV6( SQL_SINGLE_RCTX, 
                      (unsigned long *)&(select_dp->L[i]), &precision, &scale);
                      /* Allow for maximum size of NUMBER. */
                if (precision == 0) precision = 40;
                      /* Also allow for decimal point and
                         possible sign. */
                /* convert NUMBER datatype to FLOAT if scale > 0,
                   INT otherwise. */
                if (scale > 0)
                    select_dp->L[i] = sizeof(float);
                else
                    select_dp->L[i] = sizeof(int);
                break;

            case  8 : /* LONG datatype */
                select_dp->L[i] = 240;
                break;

            case 11 : /* ROWID datatype */
                select_dp->L[i] = 18;
                break;

            case 12 : /* DATE datatype */
                select_dp->L[i] = 9;
                break;
 
            case 23 : /* RAW datatype */
                break;

            case 24 : /* LONG RAW datatype */
                select_dp->L[i] = 240;
                break;
        }
        /* Allocate space for the select-list data values.
           SQLSQLDAAlloc() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer.  */

         if (select_dp->T[i] != 2)
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i] + 1);  
         else
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i]);  

        /* Print column headings, right-justifying number
            column headings. */
        
        /* Copy to temporary buffer in case name is null-terminated */
        memset(title, ' ', MAX_VNAME_LEN);
        strncpy(title, select_dp->S[i], select_dp->C[i]);
        if (select_dp->T[i] == 2)
           if (scale > 0)
             printf ("%.*s ", select_dp->L[i]+3, title);
           else
             printf ("%.*s ", select_dp->L[i], title);
        else
          printf("%-.*s ", select_dp->L[i], title);

        /* Coerce ALL datatypes except for LONG RAW and NUMBER to
           character. */
        if (select_dp->T[i] != 24 && select_dp->T[i] != 2)
            select_dp->T[i] = 1;

        /* Coerce the datatypes of NUMBERs to float or int depending on
           the scale. */
        if (select_dp->T[i] == 2)
          if (scale > 0)
             select_dp->T[i] = 4;  /* float */
          else
             select_dp->T[i] = 3;  /* int */
    }
    printf ("\n\n");

    /* FETCH each row selected and print the column values. */
    EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;

    for (;;)
    {
        EXEC SQL FETCH C USING DESCRIPTOR select_dp;

        /* Since each variable returned has been coerced to a
           character string, int, or float very little processing 
           is required here.  This routine just prints out the 
           values on the terminal. */
        for (i = 0; i < select_dp->F; i++)
        {
            if (*select_dp->I[i] < 0)
                if (select_dp->T[i] == 4) 
                  printf ("%-*c ",(int)select_dp->L[i]+3, ' ');
                else
                  printf ("%-*c ",(int)select_dp->L[i], ' ');
            else
                if (select_dp->T[i] == 3)     /* int datatype */
                  printf ("%*d ", (int)select_dp->L[i], 
                                 *(int *)select_dp->V[i]);
                else if (select_dp->T[i] == 4)     /* float datatype */
                  printf ("%*.2f ", (int)select_dp->L[i], 
                                 *(float *)select_dp->V[i]);
                else                          /* character string */
                  printf ("%-*.*s ", (int)select_dp->L[i],
                            (int)select_dp->L[i], select_dp->V[i]);
        }
        printf ("\n");
    }
end_select_loop:
    return;
}



void help()
{
    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
    puts("Statements can be continued over several lines, except");
    puts("within string literals.");
    puts("Terminate a SQL statement with a semicolon.");
    puts("Terminate a PL/SQL block (which can contain embedded semicolons)");
    puts("with a slash (/).");
    puts("Typing \"exit\" (no semicolon needed) exits the program.");
    puts("You typed \"?\" or \"help\" to get this message.\n\n");
}


void sql_error()
{
    /* ORACLE error handler */
    printf ("\n\n%.70s\n",sqlca.sqlerrm.sqlerrmc);
    if (parse_flag)
        printf
        ("Parse error at character offset %d in SQL statement.\n",
           sqlca.sqlerrd[4]);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK;
    longjmp(jmp_continue, 1);
}

サンプル・プログラム: スクロール可能カーソルを使用する動的SQL方法4

次のデモ・プログラムでは、Oracle動的SQL方法4へ適用されたスクロール可能カーソル機能が記述されています。このプログラムはdemoディレクトリ内のファイルscrolldemo1.pcにあり、オンラインでも使用可能です。

ScrollDemo1.pc

/*
 * This demo program exhibits the scrollable cursor feature
 * used with oracle dynamic method 4. The scrollable cursor
 * feature can also be used with ANSI dynamic method 4.
 * 
 * This program takes as argument the username/passwd. Once
 * logged in, it prompts for a select query.It then prompts
 * for the orientation and prints the results of the query.
 *
 * Before executing this example, make sure that the hr/hr
 * schema exists.
 */

#include <stdio.h> 
#include <sqlca.h> 
#include <sqlda.h> 
 
#include <sqlcpr.h>
#include <stdlib.h>

#include <setjmp.h>


#define MAX_SELECT_ITEMS    200
#define MAX_CHARS           500

/* Maximum size of a select-list item name */
#define MAX_NAME_SIZE       50  
 
SQLDA   *selda; 
SQLDA   *bind_des; 
jmp_buf beginEnv;
jmp_buf loopEnv;

/* Data buffer */
char c_data[MAX_SELECT_ITEMS][MAX_CHARS];
 
char username[60];
char stmt[500];

/* Print the generic error message & exit */

void sql_error()
{
    char msgbuf[512];
    size_t msgbuf_len, msg_len;

    msgbuf_len = sizeof(msgbuf);
    sqlglm(msgbuf, &msgbuf_len, &msg_len);

    printf ("\n\n%.*s\n", msg_len, msgbuf);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK RELEASE;
    exit(EXIT_FAILURE);
}

/* Print the error message and continue to query
   the user */
void sql_loop_error()
{
    char msgbuf[512];
    size_t msgbuf_len, msg_len;
    int code = sqlca.sqlcode;

    msgbuf_len = sizeof(msgbuf);
    sqlglm(msgbuf, &msgbuf_len, &msg_len);

    printf ("\n%.*s\n", msg_len, msgbuf);
    printf("The error code is %d\n", sqlca.sqlcode);
    if (code==-900 || code == -942 || code == -904)
      longjmp(beginEnv, 1);

    longjmp(loopEnv, 1);
}

/* FETCH has returned the "no data found"  error code.
   This means that either we have reached the end of 
   the active set or the offset refers to a row beyond the
   active set */
void no_data_found()
{
  printf("\nNo Data available at the specified offset\n");
  longjmp(loopEnv, 1);
}

void main(int argc, char *argv[]) 
{
    int i, n;
    int sli;     /* select-list item */
    int offset;
    int contFlag;
    char bindVar[20];
    char *u, temp[3];
    char choice;

    /* Error Handler  */
    EXEC SQL WHENEVER SQLERROR DO sql_error();


    if (argc == 1)
    {
      printf("Logging in as default user hr\n");
      strcpy(username, "hr/hr");
    }
    else
      strcpy(username, argv[1]);

    /* Establish a connection to the data base */
    EXEC SQL CONNECT :username;

    u = username;
    while(*++u != '/');
    *u = '\0';

    /* Error Handler */
    EXEC SQL WHENEVER SQLERROR DO sql_loop_error();
    for (;;)
    {

      setjmp(beginEnv);
      printf("[%s] SQL > ", username);
      gets(stmt);
      if (!strlen(stmt))
        continue;

      if (!strcmp(tolower(stmt), "exit"))
        break;

      selda = sqlald(MAX_SELECT_ITEMS, MAX_NAME_SIZE, 0);
      bind_des = sqlald(MAX_SELECT_ITEMS, MAX_NAME_SIZE, 30);

      /* prepare an sql statement for the query*/
      EXEC SQL PREPARE S FROM :stmt;

      /* Declare a cursor as scrollable */
      EXEC SQL DECLARE C SCROLL CURSOR FOR S;

      for (i=0; i<MAX_SELECT_ITEMS; i++)
      {
        bind_des->I[i] = (short *) malloc(sizeof (short));
        bind_des->V[i] = (char *) malloc(1);
      }
      bind_des->N = MAX_SELECT_ITEMS;

      EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_des;

      /* set up the bind variables */
      if (bind_des->F < 0)
      {
        printf("Bind descriptor, value exceeds the limit\n");
        exit(-1);
      }

      bind_des->N = bind_des->F;
      for (i=0; i<bind_des->F; i++)
      {
        printf("Enter the value for bind variable %.*s: ",
               (int)bind_des->C[i], bind_des->S[i]);

        fgets(bindVar, sizeof(bindVar), stdin);
        n = strlen(bindVar) - 1;

        bind_des->L[i] = n;
        bind_des->V[i] = (char *) realloc(bind_des->V[i], 
                         (bind_des->L[i] +1));

        strncpy(bind_des->V[i], bindVar, n);
        if ((strncmp(bind_des->V[i], "NULL", 4) == 0) ||
              (strncmp(bind_des->V[i], "null", 4) == 0))
          *bind_des ->I[i] = -1;
        else
          *bind_des ->I[i] = 0;

        bind_des->T[i] = 1;
      }

      /* open the cursor */
      EXEC SQL OPEN C USING DESCRIPTOR bind_des;
      EXEC SQL DESCRIBE SELECT LIST FOR S INTO selda;

      if (selda->F < 0)
      {
        printf("Select descriptor, value exceeds the limit\n");
        exit(-1);
      }
        
      selda->N = selda->F;
      for (sli = 0; sli < selda->N; sli++)
      {
          /* Set addresses of heads of the arrays 
             in the V element. */
          selda->V[sli] = c_data[sli];
          /* Convert everything to varchar on output. */
          selda->T[sli] = 1;
          /* Set the maximum lengths. */
          selda->L[sli] = MAX_CHARS;
      }

      while(1)
      {

        printf("\n\nEnter the row number to be fetched \n");
        printf("1.ABSOLUTE\n");
        printf("2.RELATIVE\n");
        printf("3.FIRST \n");
        printf("4.NEXT \n");
        printf("5.PREVIOUS \n");
        printf("6.LAST \n");
        printf("7.CURRENT \n");
        printf("Enter your choice --> ");
        scanf("%c",&choice);

        EXEC SQL WHENEVER NOT FOUND DO no_data_found();
        switch(choice)
        {
          case '1': printf("\nEnter Offset :");
                    scanf("%d",&offset);
                    EXEC SQL FETCH ABSOLUTE :offset C USING DESCRIPTOR selda;
                    break;
          case '2': printf("\nEnter Offset :");
                    scanf("%d",&offset);
                    EXEC SQL FETCH RELATIVE :offset C USING DESCRIPTOR selda;
                    break;
          case '3': EXEC SQL FETCH FIRST C USING DESCRIPTOR selda;
                    break;
          case '4': EXEC SQL FETCH NEXT C USING DESCRIPTOR selda;
                    break;
          case '5': EXEC SQL FETCH PRIOR C USING DESCRIPTOR selda;
                    break;
          case '6': EXEC SQL FETCH LAST C USING DESCRIPTOR selda;
                    break;
          case '7': EXEC SQL FETCH CURRENT C USING DESCRIPTOR selda;
                    break;
          default : printf("Invalid choice\n");
                    continue;
        }

        /* print the row */
        for (sli=0; sli<selda->N; sli++)
          printf("%.10s ", c_data[sli]);

        puts("");

        setjmp(loopEnv);
        contFlag = 'x';
        while(contFlag != 'Y' && contFlag != 'N')
        {
          printf("\nContinue with the current fetch? [y/n] : ");
          contFlag = toupper(getchar());
        }

        if (contFlag != 'Y')
          break;
      }

        EXEC SQL CLOSE C;
    }

    EXEC SQL ROLLBACK RELEASE;
    exit(EXIT_SUCCESS);
}