この章では、OracleによるANSI動的SQL(SQL92動的SQLとも呼ばれます)の実装について説明します。ANSI動的SQLは新しい方法4のアプリケーションで使用されます。これは、旧バージョンのOracle動的SQL方法4の拡張版です。詳細は、第13章を参照してください。
ANSI方法4では、すべてのOracle型がサポートされます。旧バージョンのOracle方法4では、オブジェクト型、カーソル変数、構造体の配列、DML RETURNING句、Unicode変数およびLOBはサポートされませんでした。
ANSI動的SQLでは、記述子はOracleによって内部的に保持されます。一方、旧バージョンのOracle動的SQL方法4では、記述子はユーザーによりPro*C/C++プログラムで定義されます。どちらの場合も、方法4ではPro*C/C++プログラムを使用してホスト変数を含む動的SQL文を受け取ったり作成できます。含まれるホスト変数の個数は様々です。
この章の項目は、次のとおりです。
次のSQL文について考えます。
SELECT ename, empno FROM emp WHERE deptno = :deptno_data
ANSI動的SQLを使用する手順は、次のとおりです。
変数および実行する文を保持する文字列を宣言します。
入力および出力変数の記述子を割り当てます。
文を準備します。
入力記述子の入力を記述します。
入力記述子(上の例の入力ホスト・バインド変数は、deptno_data
)を設定します。
動的カーソルを宣言およびオープンします。
出力記述子(上の例の出力ホスト変数は、ename
およびempno
)を設定します。
GET DESCRIPTORを使用して、各行からename
およびempno
データ・フィールドを取り出し、データを反復的にフェッチします。
取り出したデータを利用(データの出力など)します。
動的カーソルのクローズと入力および出力記述子の割当てを解除します。
マイクロ・プリコンパイラ・オプションDYNAMICをANSIに設定するか、マクロ・オプションMODEをANSIに設定してください。これにより、DYNAMICのデフォルト値がANSIに設定されます。DYNAMICのもう1つの設定値は、ORACLEです。
ANSI型コードを使用するには、プリコンパイラ・マイクロ・オプションTYPE_CODEをANSIに設定するか、マクロ・オプションMODEをANSIに設定します。これにより、TYPE_CODEのデフォルト設定がANSIに変更されます。TYPE_CODEをANSIに設定する場合、DYNAMICもANSIに設定する必要があります。
表14-1「ANSI SQLデータ型」に記載されているANSI SQL型のOracleによる実装は、ANSI規格と完全には一致していません。たとえば、INTEGERとして宣言された列の記述では、NUMERICのコードが戻されます。OracleをANSI規格に近づけると、動作にわずかな変更が必要になる場合があります。使用中のアプリケーションをデータベース・プラットフォーム間で移植できるようにして、可能なかぎりANSI準拠にする場合、TYPE_CODEプリコンパイラ・オプションを設定したANSI型を使用してください。このような変更ができない場合は、TYPE_CODEをANSIに設定しないでください。
動的SQL文で記述子を使用する前に、記述子領域を割り当てます。
ALLOCATE DESCRIPTOR文の構文は次のとおりです。
EXEC SQL ALLOCATE DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} [WITH MAX {:occurrences | numeric_literal}];
グローバル記述子は、プログラム内のどのモジュールでも使用できます。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。デフォルト値は、LOCALです。
記述子名のdesc_nam
には、引用符で囲んだリテラルまたはホスト変数に格納した文字値を代入できます。
occurrences
は、記述子が保持できるバインド変数または列数の最大値です。数値リテラルを指定する必要があります。デフォルトは100です。
記述子が必要でない場合、割当てを解除するとメモリーを節約できます。それ以外の場合には、アクティブなデータベース接続がなくなった時点で自動的に割当てが解除されます。
割当て解除文は次のとおりです。
EXEC SQL DEALLOCATE DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal};
準備済のSQL文の情報を取得するには、DESCRIBE文を使用します。準備済の動的文のバインド変数を記述するには、DESCRIBE INPUTを使用します。出力列の数、型および長さを取得するには、DESCRIBE OUTPUT(デフォルト)を使用します。構文を簡略化すると次のようになります。
EXEC SQL DESCRIBE [INPUT | OUTPUT] sql_statement USING [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal};
SQL文に入力値および出力値がある場合は、記述子を2つ割り当てる必要があります。1つは入力用に、もう1つは出力用に割り当てます。
SELECT ename, empno FROM emp ;
この例のように、入力値がない場合には、入力記述子は必要ありません。
INSERTS、UPDATES、DELETESおよびSELECT文のWHERE句の入力値を指定するには、SET DESCRIPTOR文を使用します。入力記述子内にDESCRIBEしていないときに入力バインド変数の数(COUNT
に格納されています)を設定するには、SET DESCRIPTOR文を使用します。
EXEC SQL SET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} COUNT = {:kount | numeric_literal};
kount
には、ホスト変数または数値リテラル(5など)を設定できます。SET DESCRIPTOR文を使用して、各ホスト変数に少なくともデータ・ソースを指定してください。
EXEC SQL SET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} VALUE item_number DATA = :hv3;
また、入力ホスト変数の型および長さも設定できます。
注意: TYPE_CODE=ORACLEのとき、SET文を使用して明示的に、またはDESCRIBE OUTPUTによって暗黙的にTYPEおよびLENGTHを指定していない場合、プリコンパイラではホスト変数から導出された値が使用されます。TYPE_CODE=ANSIのときは、表14-1「ANSI SQLデータ型」の値を使用してTYPEを設定する必要があります。また、ANSIデフォルト長はホスト変数に一致しないことがあるため、LENGTHも設定する必要があります。 |
EXEC SQL SET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} VALUE item_number TYPE = :hv1, LENGTH = :hv2, DATA = :hv3;
hv1
、hv2
およびhv3
などの識別子は、ホスト変数から値を供給する必要があることをユーザーが忘れないようにするために使用します。item_numberは、入力変数のSQL文内での位置を表します。
TYPE_CODEがANSIに設定されている場合、TYPEは次の表から選択される型コードになります。
表14-1 ANSI SQLデータ型
データ型 | 型コード |
---|---|
CHARACTER |
1 |
CHARACTER VARYING |
12 |
DATE |
9 |
DECIMAL |
3 |
DOUBLE PRECISION |
8 |
FLOAT |
6 |
INTEGER |
4 |
NUMERIC |
2 |
REAL |
7 |
SMALLINT |
5 |
DATAは、入力されるホスト変数の値です。
インジケータ、精度および位取りなど、他の入力値も設定できます。
SET DESCRIPTOR文の数値は、int
またはshort int
で宣言する必要があります。ただし、インジケータおよび戻された長さの値はshort int
として宣言する必要があります。
たとえば、次の例でempno
を取得する場合、empno
は動的SQL文の2番目の出力ホスト変数であるため、値をVALUE = 2に設定します。ホスト変数empno_typ
は、3(Oracleタイプの整数値)に設定します。ホスト整数の長さを表すempno_len
は、4に設定します。この値はホスト変数のサイズです。DATAはホスト変数empno_data
と等しくなります。この変数は値をデータベース表から受け取ります。コード例は、次のようになります。
... char *dyn_statement = "SELECT ename, empno FROM emp WHERE deptno = :deptno_number" ; int empno_data ; int empno_typ = 3 ; int empno_len = 4 ; ... EXEC SQL SET DESCRIPTOR 'out' VALUE 2 TYPE = :empno_typ, LENGTH = :empno_len, DATA = :empno_data ;
入力値を設定後に、入力記述子を使用して文を実行またはオープンします。文中に出力値がある場合、FETCHを行う前に出力値を設定してください。DESCRIBE OUTPUTを実行している場合は、ホスト変数の実際の型と長さのテストが必要になる場合があります。DESCRIBEを実行すると、ホスト変数の外部型および長さとは異なる内部型と長さが生成されます。
出力記述子をFETCHした後、戻されたデータにアクセスするには、GET DESCRIPTORを使用します。簡略化された構文は次のとおりです。構文の詳細は、この章の後半部分を参照してください。
EXEC SQL GET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} VALUE item_number :hv1 = DATA, :hv2 = INDICATOR, :hv3 = RETURNED_LENGTH ;
desc_nam
およびitem_number
には、リテラルまたはホスト変数を指定できます。記述子には、'out'などリテラルの名前を指定できます。項目番号には、2などの数値リテラルを指定できます。
hv1、hv2およびhv3
は、ホスト変数です。これらはホスト変数であり、リテラルではありません。例では、ホスト変数が3つのみ使用されています。
数値すべてにlong、intまたはshortのいずれかを指定します。ただし、記述子または戻された長さの値はshort
にする必要があります。
次の例でANSI動的SQLの使用例を示します。ここでは入力記述子('in')および出力記述子('out')を割り当ててSELECT文を実行します。入力値はSET DESCRIPTOR文を使用して設定します。カーソルはオープンおよびフェッチされ、結果の出力値はGET DESCRIPTOR文を使用して取得されます。
... char* dyn_statement = "SELECT ename, empno FROM emp WHERE deptno = :deptno_data" ; int deptno_type = 3, deptno_len = 2, deptno_data = 10 ; int ename_type = 97, ename_len = 30 ; char ename_data[31] ; int empno_type = 3, empno_len = 4 ; int empno_data ; long SQLCODE = 0 ; ... main () { /* Place preliminary code, including connection, here. */ ... EXEC SQL ALLOCATE DESCRIPTOR 'in' ; EXEC SQL ALLOCATE DESCRIPTOR 'out' ; EXEC SQL PREPARE s FROM :dyn_statement ; EXEC SQL DESCRIBE INPUT s USING DESCRIPTOR 'in' ; EXEC SQL SET DESCRIPTOR 'in' VALUE 1 TYPE = :deptno_type, LENGTH = :deptno_len, DATA = :deptno_data ; EXEC SQL DECLARE c CURSOR FOR s ; EXEC SQL OPEN c USING DESCRIPTOR 'in' ; EXEC SQL DESCRIBE OUTPUT s USING DESCRIPTOR 'out' ; EXEC SQL SET DESCRIPTOR 'out' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len, DATA = :ename_data ; EXEC SQL SET DESCRIPTOR 'out' VALUE 2 TYPE = :empno_type, LENGTH = :empno_len, DATA = :empno_data ; EXEC SQL WHENEVER NOT FOUND DO BREAK ; while (SQLCODE == 0) { EXEC SQL FETCH c INTO DESCRIPTOR 'out' ; EXEC SQL GET DESCRIPTOR 'out' VALUE 1 :ename_data = DATA ; EXEC SQL GET DESCRIPTOR 'out' VALUE 2 :empno_data = DATA ; printf("\nEname = %s Empno = %s", ename_data, empno_data) ; } EXEC SQL CLOSE c ; EXEC SQL DEALLOCATE DESCRIPTOR 'in' ; EXEC SQL DEALLOCATE DESCRIPTOR 'out' ; ... }
ANSI動的SQLとともにスクロール可能カーソルを使用することもできます。ANSI動的SQLをスクロール可能カーソルとともに使用するには、カーソルをSCROLLモードでDECLAREします。FETCH文に様々なフェッチ方向を使用して、結果セットにアクセスします。
次の拡張機能について説明します。
SET文のデータ項目の参照セマンティクス。
配列を使用したバルク操作。
オブジェクト型、NCHAR列およびLOBのサポート。
ANSI規格では値セマンティクスが指定されています。パフォーマンスを改善するために、Oracleはこの規格を拡張して参照セマンティクスを可能にしています。
値セマンティクスではホスト変数データのコピーが生成されます。参照セマンティクスでは、ホスト変数のアドレスを使用することでコピーを回避しています。このように参照セマンティクスを使用すると、大容量データを処理する際のパフォーマンスが向上します。
フェッチ速度を速めるには、データ句の前にREFキーワードを使用してください。
EXEC SQL SET DESCRIPTOR 'out' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len, REF DATA = :ename_data ; EXEC SQL DESCRIPTOR 'out' VALUE 2 TYPE = :empno_type, LENGTH = :empno_len, REF DATA = :empno_data ;
ホスト変数は、取得結果を受け取ります。GET文は必要ありません。取得されたデータは、FETCHのたびにename_data
およびempno_data
に直接書き込まれます。
次のコード例のように、REFキーワードは、DATA、INDICATORおよびRETURNED_LENGTH項目(フェッチされた行ごとに異なります)の前でのみ使用できます。
int indi, returnLen ; ... EXEC SQL SET DESCRIPTOR 'out' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len, REF DATA = :ename_data, REF INDICATOR = :indi, REF RETURNED_LENGTH = :returnLen ;
フェッチするたびに、returnLen
によりename
フィールドの実際に取得した長さが保持されます。このフィールドはCHAR
またはVARCHAR2
データで使用すると便利です。
ename_len
は、戻された長さを受け取りません。またFETCH文によっても変更されません。DESCRIBE文とそれに続くGET文を使用して、データ行をフェッチする前に列の最大幅を調べてください。
処理速度を速くするために、SQL文のSELECT以外の型でREFキーワードを使用することもできます。参照セマンティクスの場合、記述子領域にコピーされた値ではなくホスト変数が使用されます。SET時ではなく、SQL文の実行時のホスト変数データが使用されます。次に例を示します。
int x = 1 ; EXEC SQL SET DESCRIPTOR 'value' VALUE 1 DATA = :x ; EXEC SQL SET DESCRIPTOR 'reference' VALUE 1 REF DATA = :x ; x = 2 ; EXEC SQL EXECUTE s USING DESCRIPTOR 'value' ; /* Will use x = 1 */ EXEC SQL EXECUTE s USING DESCRIPTOR 'reference' ; /* Will use x = 2 */
Oracleによりバルク操作機能が追加され、SQL92 ANSI動的規格が拡張されました。バルク操作を行うには、配列サイズを指定したFOR
句を使用して、処理する入力データ量または行数を指定します。
データまたは行数の最大値を指定するには、ALLOCATE文でFOR
句を使用します。たとえば、最大配列サイズを100に指定する場合、次のようにします。
EXEC SQL FOR 100 ALLOCATE DESCRIPTOR 'out' ;
または
int array_size = 100 ; ... EXEC SQL FOR :array_size ALLOCATE DESCRIPTOR 'out' ;
FOR
句は、記述子にアクセスする後続の文で使用されます。出力記述子でのFETCH文の配列サイズは、ALLOCATE文で指定した配列サイズ以下にする必要があります。
EXEC SQL FOR 20 FETCH c1 USING DESCRIPTOR 'out' ;
同じ記述子の後続のGET文では、FETCH文と同じ配列サイズを指定する必要があります。GET文を使用すると、DATA、INDICATORまたはRETURNED_LENGTH値を取得できます。
int val_data[20] ; short val_indi[20] ; ... EXEC SQL FOR 20 GET DESCRIPTOR 'out' VALUE 1 :val_data = DATA, :val_indi = INDICATOR ;
ただし、行ごとに異なることのない他の項目(LENGTH、TYPE、COUNTなど)を参照するGET文では、FOR
句を使用しないでください。
int cnt, len ; ... EXEC SQL GET DESCRIPTOR 'out' :cnt = COUNT ; EXEC SQL GET DESCRIPTOR 'out' VALUE 1 :len = LENGTH ;
これは参照セマンティクスを使用するSET文でも同様です。FETCH文の前に置かれ、DATA、INDICATORまたはRETURNED_LENGTHの参照セマンティクスを使用するSET文には、FETCHと同じ配列サイズを指定する必要があります。
int ref_data[20] ; short ref_indi[20] ; ... EXEC SQL FOR 20 SET DESCRIPTOR 'out' VALUE 1 REF DATA = :ref_data, REF INDICATOR = :ref_indi ;
同様に、入力に使用する記述子を使用して、行の集まりなどを挿入する場合、EXECUTEまたはOPEN文には、ALLOCATE文で使用するサイズ以下の配列サイズを指定する必要があります。値セマンティクスと参照セマンティクス両方のSET文で、EXECUTE文と同じサイズの配列を使用する必要があります。このSET文を使用してDATA、INDICATORまたはRETURNED_LENGTHにアクセスします。
FOR句は、DEALLOCATE文またはPREPARE文では使用されません。
次のサンプル・コードでは、出力記述子がないバルク操作について説明します。このサンプル・コードでは出力は行われず、emp
表への挿入操作のみが実行されます。COUNT
の値は2です。INSERT文にはename_arr
とempno_arr
の2つのホスト変数があります。データ配列ename_arr
には、順番に「Tom」、「Dick」および「Harry」という3つの文字列が保持されます。インジケータ配列ename_ind
の2番目の要素には、-1という値が指定されるため、「Dick」のかわりにNULLが挿入されます。データ配列empno_arr
には、従業員番号が3つ含まれています。DML RETURNING句を使用すると、実際に挿入された名前を確認できます。
... char* dyn_statement = "INSERT INTO emp (ename) VALUES (:ename_arr)" ; char ename_arr[3][6] = {Tom","Dick","Harry"} ; short ename_ind[3] = {0,-1,0} ; int ename_len = 6, ename_type = 97, cnt = 2 ; int empno_arr[3] = {8001, 8002, 8003} ; int empno_len = 4 ; int empno_type = 3 ; int array_size = 3 ; EXEC SQL FOR :array_size ALLOCATE DESCRIPTOR 'in' ; EXEC SQL SET DESCRIPTOR 'in' COUNT = :cnt ; EXEC SQL SET DESCRIPTOR 'in' VALUE 1 TYPE = :ename_type, LENGTH = :ename_len ; EXEC SQL SET DESCRIPTOR 'in' VALUE 2 TYPE = :empno_type, LENGTH = :empno_len ; EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 1 DATA = :ename_arr, INDICATOR = :ename_ind ; EXEC SQL FOR :array_size SET DESCRIPTOR 'in' VALUE 2 DATA = :empno_arr ; EXEC SQL PREPARE s FROM :dyn_statement ; EXEC SQL FOR :array_size EXECUTE s USING DESCRIPTOR 'in' ; ...
上のコードを実行すると、次の値が挿入されます。
EMPNO ENAME 8001 Tom 8002 8003 Harry
HOST_STRIDE_LENGTHを構造体のサイズに、INDICATOR_STRIDE_LENGTHをインジケータ構造体のサイズに、そしてRETURNED_LENGTH_STRIDEを戻された長さの構造体のサイズに設定する必要があります。
構造体の配列は、ANSI動的SQLによってサポートされていますが、旧バージョンのOracle動的SQLではサポートされていません。
独自に定義したオブジェクト型では、Oracle TYPEを108にして使用してください。オブジェクト型の列では、DESCRIBE文を使用してUSER_DEFINED_TYPE_VERSION、USER_DEFINED_TYPE_NAME、USER_DEFINED_TYPE_NAME_LENGTH、USER_DEFINED_TYPE_SCHEMAおよびUSER_DEFINED_TYPE_SCHEMA_LENGTHを取得します。
DESCRIBE文を使用しないでこれらの値を取得する場合は、SET DESCRIPTOR文を使用して自分で設定を行う必要があります。
マクロ・オプションのMODEを使用すると、ANSIと互換性のある特性の設定や、多くの機能の制御ができます。このオプションではANSIまたはORACLEの値を使用できます。個々の機能に対しては、マイクロ・オプションを使用します。このオプションはMODE設定を上書きします。「MODE」も参照してください。
動的SQLでの記述子の動作を指定する場合は、プリコンパイラ・マイクロ・オプションDYNAMICを使用します。ANSIとORACLEのどちらのデータ型を使用するかを指定する場合は、プリコンパイラ・マイクロ・オプションTYPE_CODEを使用します。
マクロ・オプションMODEをANSIに設定すると、マイクロ・オプションDYNAMICも自動的にANSIになります。同様にMODEをORACLEに設定すると、DYNAMICもORACLEになります。
DYNAMICとTYPE_CODEはインラインでは使用できません。
次の表に機能とDYNAMICの設定がその機能に与える影響を示します。
表14-2 DYNAMICオプションの設定
機能 | DYNAMIC = ANSI | DYNAMIC = ORACLE |
---|---|---|
記述子の生成。 |
ALLOCATE文を使用する必要があります。 |
関数SQLSQLDAAlloc()を使用する必要があります。「SQLLIBパブリック関数の新しい名前」を参照してください。 |
記述子の破壊。 |
DEALLOCATE文が使用可能です。 |
関数SQLLDAFree()が使用可能です。「SQLLIBパブリック関数の新しい名前」を参照してください。 |
データの取得。 |
FETCHとGET文の両方が使用可能です。 |
FETCH文のみを使用する必要があります。 |
入力データの設定。 |
DESCRIBE INPUT文が使用可能です。SET文を使用する必要があります。 |
コードに記述子の値を設定する必要があります。DESCRIBE BIND VARIABLES文を使用する必要があります。 |
記述子の表現。 |
引用符で囲んだリテラルまたは記述子名を含むホスト識別子。 |
SQLDAへのポインタであるホスト変数。 |
使用可能なデータ型。 |
BIT以外のすべてのANSI型およびすべてのOracle型。 |
オブジェクト、LOB、構造体の配列およびカーソル変数以外のOracle型。 |
マイクロ・オプションTYPE_CODEは、プリコンパイラによりマクロ・オプションMODEと同じ設定にされます。TYPE_CODEがANSIに等しくなるのは、DYNAMICがANSIに等しい場合のみです。
TYPE_CODE設定に対応する機能は次のとおりです。
用途
この文を使用してSQL記述子領域を割り当てます。記述子、ホスト・バインド項目発生数の最大値および配列サイズを指定します。この文は、ANSI動的SQLでのみ使用できます。
構文
EXEC SQL [FOR [:]array_size] ALLOCATE DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} [WITH MAX occurrences] ;
変数
array_size
これは配列処理をサポートするオプション句(Oracle拡張機能)です。この句により、配列処理で記述子が使用可能であることがプリコンパイラに通知されます。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子の名前。ローカル記述子は、モジュール内で一意にしてください。前回の割当てを解除せずに記述子を割り当てた場合は、ランタイム・エラーが生成されます。グローバル記述子は、アプリケーション全体で一意にしてください。そうでない場合はランタイム・エラーが発生します。
occurrences
記述子で使用可能なホスト変数の最大値です。この値は、0〜64KBの整数定数にする必要があります。それ以外の場合はエラーが戻されます。デフォルト値は100です。この句はオプションです。これらの規則に違反するとプリコンパイラ・エラーが戻されます。
例
EXEC SQL ALLOCATE DESCRIPTOR 'SELDES' WITH MAX 50 ; EXEC SQL FOR :batch ALLOCATE DESCRIPTOR GLOBAL :binddes WITH MAX 25 ;
用途
以前に割り当てられたSQL記述子の割当てを解除してメモリーを解放する場合に、この文を使用します。この文は、ANSI動的SQLでのみ使用できます。
構文
EXEC SQL DEALLOCATE DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} ;
変数
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
同じ名前およびスコープの記述子が割り当てられていない場合、または割当てが解除されている場合は、ランタイム・エラーが発生します。
例
EXEC SQL DEALLOCATE DESCRIPTOR GLOBAL 'SELDES' ; EXEC SQL DEALLOCATE DESCRIPTOR :binddes ;
用途
この文を使用してSQL記述子領域から情報を取得します。
構文
EXEC SQL [FOR [:]array_size] GET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} { :hv0 = COUNT | VALUE item_number :hv1 = item_name1 [ {, :hvN = item_nameN}] } ;
変数
array_size
FOR array_size
は、オプションのOracle拡張機能です。array_size
は、FETCH文のarray_size
フィールドと等しくする必要があります。
COUNT
バインド変数の総数。
desc_nam
記述子の名前。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
VALUE item_number
SQL文内での項目の位置。item_number
には変数または定数を指定できます。item_number
の値がCOUNT
より大きい場合、「データが見つかりません。」という条件が戻されます。item_number
には0より大きい値を指定する必要があります。
hv1 .. hvN
値が転送されるホスト変数。
item_name1 .. item_nameN
ホスト変数に対応する記述子項目名。使用可能なANSI記述子項目名は次のとおりです。
表14-4 GET DESCRIPTORの記述子項目名の定義
記述子項目名 | 意味 |
---|---|
|
ANSIデータ型が表にない場合およびTYPE_CODE=ANSIの場合は、負の値のOracle型コードを使用してください。 |
|
列データの長さ。NCHARでは文字数、その他の場合はバイト数で表されます。DESCRIBE OUTPUTによって設定されます。 |
|
データの長さ。バイト数で表します。 |
|
FETCH後の実際のデータ長。 |
|
戻されたデータの長さ。バイト数で表します。 |
|
桁数。 |
|
正確な数値型の場合は、小数点以下の桁数。 |
|
1の場合、列にNULL値を指定できます。0の場合、列にはNULL値を指定できません。 |
|
対応付けられたインジケータの値。 |
|
データの値。 |
|
列の名前。 |
|
列のキャラクタ・セット。 |
追加されたOracle記述子項目の名前は次のとおりです。
表14-5 Oracle拡張機能により追加されたGET DESCRIPTORの記述子項目名の定義
記述子項目名 | 意味 |
---|---|
|
2の場合、NCHARまたはNVARCHAR2です。1の場合、文字です。0の場合は文字以外の値です。 |
|
内部の長さ。バイト数で表します。 |
|
ホスト構造体のサイズ。バイト数で表します。 |
|
インジケータ構造体のサイズ。バイト数で表します。 |
|
戻された長さの構造体のサイズ。バイト数で表します。 |
|
オブジェクト型バージョンを表す文字。 |
|
オブジェクト型の名前。 |
|
オブジェクト型の名前の長さ。 |
|
オブジェクト・スキーマを表す文字。 |
|
|
|
2の場合、NCHARまたはNVARCHAR2です。1の場合、文字です。0の場合は文字以外の値です。 |
使用上の注意
FOR句は、DATA、INDICATORおよびRETURNED_LENGTH項目を含むGET DESCRIPTOR文でのみ使用してください。
内部型は、DESCRIBE OUTPUT文によって指定されます。その型を入力と出力の両方でホスト変数の外部型に設定する必要があります。
TYPEはANSI SQLデータ型のコードです。ANSIの型が表に含まれていない場合は、負の値のOracle型コードを使用してください。
LENGTHには、固定幅の各国語キャラクタ・セットを持つフィールドの列の長さを表す文字数が含まれます。他の文字列はバイト数で表されます。DESCRIBE OUTPUTで設定されます。
RETURNED_LENGTHは、FETCH文によって設定された実際のデータ長です。これは、LENGTHの場合と同様にバイト数または文字数で表されます。フィールドOCTET_LENGTHおよびRETURNED_OCTET_LENGTHの長さはバイト数で表されます。
NULLABLE = 1は列でNULLを使用できることを意味し、NULLABLE = 0はNULLを使用できないことを意味します。
CHARACTER_SET_NAMEは、文字列でのみ使用します。他の型については定義されていません。DESCRIBE OUTPUT文を使用して値を取得します。
DATAおよびINDICATORは、列のデータおよびインジケータの状態を表します。データがNULLでもインジケータが要求されていない場合は、ランタイム・エラーが生成されます(「DATA EXCEPTION, NULL VALUE, NO INDICATOR PARAMETER」)。
Oracle固有の記述子項目名
列がNCHARまたはNVARCHAR2の場合は、NATIONAL_CHARACTER = 2に設定されます。列が文字列(ただし各国語キャラクタではない)の場合、この項目は1に設定されます。文字以外の列の場合、DESCRIBE OUTPUTの実行後この項目は0になります。
INTERNAL_LENGTHは、Oracle動的方法4との互換性を保つためのものです。この項目にはOracle SQL記述子領域の長さメンバーと同じ値が設定されています。
次の3つの項目はDESCRIBE OUTPUT文によって戻されません。
ホスト変数構造体のサイズを示すHOST_STRIDE_LENGTH。
標識変数の構造体のサイズを示すINDICATOR_STRIDE_LENGTH。
戻された長さの変数構造体のサイズを示すRETURNED_LENGTH_STRIDE
次の項目は、プリコンパイラ・オプションOBJECTSがYESに設定されているときにのみオブジェクト型に適用されます。
タイプ・バージョンを表す文字を含むUSER_DEFINED_TYPE_VERSION。
型の名前を表す文字を示すUSER_DEFINED_TYPE_NAME。
型の名前の長さをバイト数で示すUSER_DEFINED_TYPE_NAME_LENGTH。
型のスキーマ名を表す文字を示すUSER_DEFINED_TYPE_SCHEMA。
型のスキーマ名の長さを文字数で示すUSER_DEFINED_TYPE_SCHEMA_LENGTH。
例
EXEC SQL GET DESCRIPTOR :binddes :n = COUNT ; EXEC SQL GET DESCRIPTOR 'SELDES' VALUE 1 :t = TYPE, :l = LENGTH ; EXEC SQL FOR :batch GET DESCRIPTOR LOCAL 'SELDES' VALUE :sel_item_no :i = INDICATOR, :v = DATA ;
用途
この文を使用してホスト変数からの情報を記述子領域に設定します。SET DESCRIPTOR文は、項目名のホスト変数のみをサポートしています。
構文
EXEC SQL [FOR array_size] SET DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} {COUNT = :hv0 | VALUE item_number [REF] item_name1 = :hv1 [{, [REF] item_nameN = :hvN}]} ;
変数
array_size
このOracleオプション句で配列を使用できるのは、記述子項目DATA、INDICATORおよびRETURNED_LENGTHの設定時のみです。FOR句を含むSET DESCRIPTORでは他の項目を使用できません。ホスト変数配列サイズはすべて一致している必要があります。FETCH文で使用するのと同じ配列サイズをSET文で使用してください。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子名。この項目には、ALLOCATE DESCRIPTORの規則が適用されます。
COUNT
バインド(入力)または定義(出力)変数の数。
VALUE item_number
ホスト変数の動的SQL文における位置。
hv1 .. hvN
設定したホスト変数(定数ではありません)。
item_name1
desc_item_name
では、GET DESCRIPTOR構文と同様の方法でこれらの値が使用されます。
表14-6 SET DESCRIPTORの記述子項目名
記述子項目名 | 意味 |
---|---|
|
対応するANSI型がない場合は、負の値のOracle型を使用します。 |
|
列データの最大長。 |
|
対応付けられたインジケータの値。参照セマンティクス用に設定します。 |
|
設定されるデータの値。参照セマンティクス用に設定します。 |
|
列のキャラクタ・セット。 |
|
対応するANSI型がない場合は、負の値のOracle型を使用します。 |
Oracle拡張機能により追加された記述子項目の名前は次のとおりです。
表14-7 Oracle拡張機能により追加されたSET DESCRIPTORの記述子項目名の定義
記述子項目名 | 意味 |
---|---|
|
FETCH後に戻された長さ。参照セマンティクスを使用している場合に設定します。 |
|
入力ホスト変数がNCHARまたはNVARCHAR2型の場合、2に設定します。 各国語キャラクタ設定が設定されていない場合、0に設定します。 |
|
ホスト変数構造体のサイズ。バイト数で表します。 |
|
標識変数のサイズ。バイト数で表します。 |
|
戻された長さの構造体のサイズ。バイト数で表します。 |
|
オブジェクト型の名前。 |
|
オブジェクト型の名前の長さ。 |
|
オブジェクト・スキーマを表す文字。 |
|
|
使用上の注意
参照セマンティクスは、パフォーマンスを向上させる別のOracle拡張機能です。REFキーワードは、DATA、INDICATORおよびRETURNED_LENGTHの記述子項目名の前にのみ使用します。REFキーワードの使用時には、GET文を使用する必要はありません。複合データ型(オブジェクト型、コレクション型、構造体の配列およびDML RETURNING句)はすべて、SET DESCRIPTORのREF形式を必要とします。
プログラムで別のSQLのDESCRIPTORを再利用する場合、DESCRIPTORの古い値は残ります。
REFの使用時には、対応付けられたホスト変数自体がSETで使用されます。この場合、GETは必要ありません。値ではなくREFを使用している場合にかぎり、RETURNED_LENGTHを設定できます。
FETCH文で使用するのと同じ配列サイズをSET文またはGET文で使用してください。
NCHARホスト入力値用にNATIONAL_CHARフィールドを2に設定します。
古いSQLでNCAHRホスト入力値用にDESCRIPTORが使用されるとき、NATIONAL_CHARACTERフィールドを0に設定します。
オブジェクト型の特性の設定時には、USER_DEFINED_TYPE_NAMEおよびUSER_DEFINED_TYPE_NAME_LENGTHを設定する必要があります。
この操作を省略すると、USER_DEFINED_TYPE_SCHEMAおよびUSER_DEFINED_TYPE_SCHEMA_LENGTHのデフォルトが現行の接続になります。
クライアント側Unicodeサポートには、CHARACTER_SET_NAMEをUTF16に設定します。データはUCS2エンコーディングになり、RETURNED_LENGTHはCHARSです。
例
int bindno = 2 ; short indi = -1 ; char data = "ignore" ; int batch = 1 ; EXEC SQL FOR :batch ALLOCATE DESCRIPTOR 'binddes' ; EXEC SQL SET DESCRIPTOR GLOBAL :binddes COUNT = 3 ; EXEC SQL FOR :batch SET DESCRIPTOR :bindes VALUE :bindno INDICATOR = :indi, DATA = :data ; ...
用途
この方法で使用されているPREPARE文は、他の動的SQL方法で使用されているPREPARE文と同じです。Oracle拡張機能により、変数と同様にSQL文で引用符付きの文字列を使用できます。
構文
EXEC SQL PREPARE statement_id FROM :sql_statement ;
変数
statement_id
これを宣言しないでください。これは未宣言のSQL識別子です。
sql_statement
埋込みSQL文を保持する文字列(定数または変数)。
例
char* statement = "SELECT ENAME FROM emp WHERE deptno = :d" ; EXEC SQL PREPARE S1 FROM :statement ;
用途
この文はバインド変数についての情報を戻します。
構文
EXEC SQL DESCRIBE INPUT statement_id USING [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} ;
変数
statement_id
PREPAREおよびDESCRIBE OUTPUTと同じように使用します。これを宣言しないでください。これは未宣言のSQL識別子です。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子名。
使用上の注意
DESCRIBE INPUTでは、COUNTおよびNAME項目のみが設定されます。
例
EXEC SQL DESCRIBE INPUT S1 USING SQL DESCRIPTOR GLOBAL :binddes ; EXEC SQL DESCRIBE INPUT S2 USING DESCRIPTOR 'input' ;
用途
PREPAREされた文の出力列についての情報を取得する場合は、この文を使用します。ANSI構文は、旧バージョンのOracle構文と異なります。SQL記述子領域に格納された情報には、戻り値の数、型、長さおよび名前など、対応付けられた情報が含まれます。
構文
EXEC SQL DESCRIBE [OUTPUT] statement_id USING [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} ;
変数
statement_id
PREPAREで使用されているものと同じです。これを宣言しないでください。これは未宣言のSQL識別子です。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子名。
OUTPUTはデフォルトで、省略可能です。
例
char* desname = "SELDES" ; EXEC SQL DESCRIBE S1 USING SQL DESCRIPTOR 'SELDES' ; /* Or, */ EXEC SQL DESCRIBE OUTPUT S1 USING DESCRIPTOR :desname ;
用途
EXECUTE文では、まず準備済のSQL文の入力および出力変数を一致させ、それから文が実行されます。EXECUTEのANSIバージョンは、1つの文中に2つの記述子を割り当てることでDML RETURNING句をサポートできる点で、旧バージョンのEXECUTE文とは異なります。
構文
EXEC SQL [FOR :array_size] EXECUTE statement_id [USING [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal}] [INTO [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal}] ;
変数
array_size
文によって処理される行数。
statement_id
PREPAREで使用されているものと同じです。これを宣言しないでください。これは未宣言のSQL識別子です。この項目にリテラルを指定することも可能です。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子名。
使用上の注意
INTO句により、INSERT、UPDATEおよびDELETEのDML RETURNING句が実装されます。
例
EXEC SQL EXECUTE S1 USING SQL DESCRIPTOR GLOBAL :binddes ; EXEC SQL EXECUTE S2 USING DESCRIPTOR :bv1 INTO DESCRIPTOR 'SELDES' ;
用途
リテラルまたはSQL文を含むホスト変数文字列を実行します。この文のANSI SQL形式は、旧バージョンのOracle動的SQLと同じです。
構文
EXEC SQL EXECUTE IMMEDIATE {:sql_statement | string_literal}
変数
sql_statement
文字列内のSQL文またはPL/SQLブロック。
例
EXEC SQL EXECUTE IMMEDIATE :statement ;
用途
問合せ文に対応付けられたカーソルを宣言します。これは汎用DECLARE CURSOR文の形式です。
構文
EXEC SQL DECLARE cursor_name CURSOR FOR statement_id;
変数
cursor_name
カーソル変数(ホスト変数ではなくSQL識別子)。
statement_id
未宣言のSQL識別子。
例
EXEC SQL DECLARE C1 CURSOR FOR S1 ;
用途
OPEN文は、入力パラメータとカーソルを対応付けてからカーソルをオープンします。
構文
EXEC SQL [FOR :array_size] OPEN dyn_cursor [[USING [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam1 | string_literal}] [INTO [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam2 | string_literal}]] ;
変数
array_size
記述子の割当て時には、この制限は指定数以下になります。
dyn_cursor
カーソル変数。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子名。
使用上の注意
カーソルに対応付けられた準備済の文にコロンまたは疑問符が含まれる場合は、USING句を指定する必要があります。指定しなければ、ランタイム・エラーが発生します。DML RETURNING句がサポートされています。
例
EXEC SQL OPEN C1 USING SQL DESCRIPTOR :binddes ; EXEC SQL FOR :limit OPEN C2 USING DESCRIPTOR :b1, :b2 INTO SQL DESCRIPTOR :seldes ;
用途
動的DECLARE文で宣言されたカーソルの行をフェッチします。
構文
EXEC SQL [FOR :array_size] FETCH cursor INTO [SQL] DESCRIPTOR [GLOBAL | LOCAL] {:desc_nam | string_literal} ;
変数
array_size
文によって処理される行数。
cursor
以前に宣言された動的カーソル。
GLOBAL | LOCAL
デフォルトでは、オプション句のスコープはLOCALに設定されています。ローカル記述子には、割り当てられたファイル内でのみアクセスできます。グローバル記述子は、コンパイル・ユニット内のどのモジュールでも使用できます。
desc_nam
記述子の名前。
使用上の注意
FOR句のarray_size
オプションは、ALLOCATE DESCRIPTOR文で指定された数以下にする必要があります。
例
EXEC SQL FETCH FROM C1 INTO DESCRIPTOR 'SELDES' ; EXEC SQL FOR :arsz FETCH C2 INTO DESCRIPTOR :desc ;
用途
動的カーソルをクローズします。構文は、旧バージョンのOracle方法4から変更されていません。
構文
EXEC SQL CLOSE cursor ;
変数
cursor
以前に宣言された動的カーソル。
例
EXEC SQL CLOSE C1 ;
ANSI動的SQLインタフェースは、Oracle動的方法4がサポートしたすべてのデータ型に加え、次の機能をサポートしています。
ANSI動的SQLによるオブジェクト型、結果セットおよびLOB型を含むすべてのデータ型のサポート。
ANSIモードによる内部SQL記述子領域を使用した入力および出力情報の格納。この内部SQL記述子領域は、旧バージョンのOracle動的方法4で使用された外部SQLDAの拡張版です。
新しい埋込みSQL文(ALLOCATE DESCRIPTOR、DEALLOCATE DESCRIPTOR、DESCRIBE、GET DESCRIPTORおよびSET DESCRIPTOR)の導入。
DESCRIBE文では、ANSI動的SQLの標識変数名は戻されません。
ANSI動的SQLでは、戻された列名または式の最大値を指定できません。デフォルト・サイズは128です。
記述子名は、引用符で囲んだ識別子または前にコロンが付いたホスト変数のいずれかにする必要があります。
出力時に、DESCRIBE文のSELECT LIST FOR句はオプション・キーワードOUTPUTに置き換えられます。INTO句はUSING DESCRIPTOR句に置き換えられます。この句には、オプション・キーワードSQLを含めることができます。
入力時に、DESCRIBE文のオプションのBIND VARIABLES FOR句をキーワードINPUTに置き換えることができます。INTO句はUSING DESCRIPTOR句に置き換えられます。この句には、オプション・キーワードSQLを含めることができます。
オプション・キーワードSQLをEXECUTE、FETCHおよびOPEN文のUSING句のキーワードDESCRIPTORの前に置くことができます。
次の2つのプログラムは、demoディレクトリにあります。
このプログラムは、ANSI動的SQLを使用したSQL文の処理方法を示します。このSQL文は実行時まで不明です。このプログラムは、ANSI動的SQLを使用した最も簡単なプログラミング(最も効率的というわけではありません)を紹介することを目的としています。ここでは、ANSIと互換性のある値セマンティクスおよびANSI型コードを使用しています。ANSI SQLSTATEは、エラー番号用に使用されています。記述子名はリテラルです。すべての入力および出力には、ANSI可変キャラクタ・タイプが使用されます。
このプログラムでは、自分のユーザー名とパスワードを使用してORACLEに接続した後、SQL文を入力します。埋込み型ではなく通常のSQL構文を使用して有効なSQLまたはPL/SQL文を入力し、各文の終わりにセミコロンを付けてください。入力した文が処理されます。問合せのときはフェッチされた行が表示されます。
複数行の文を入力できます。最大1023文字まで入力できます。変数のサイズには制限があり、MAX_VAR_LENが255に定義されています。このプログラムでは、バインド変数は40、選択リスト項目も40まで処理できます。DML RETURNING句およびユーザー定義型は、値セマンティクスではサポートされていません。
次のように、mode = ansiに設定してプログラムをプリコンパイルします。
proc mode=ansi ansidyn1
mode=ansi
に指定すると、動的およびtype_code
がANSIに設定されます。
/******************************************************************* ANSI Dynamic Demo 1: ANSI Dynamic SQL with value semantics, literal descriptor names and ANSI type codes This program demonstates using ANSI Dynamic SQL to process SQL statements which are not known until runtime. It is intended to demonstrate the simplest (though not the most efficient) approach to using ANSI Dynamic SQL. It uses ANSI compatible value semantics and ANSI type codes. ANSI Sqlstate is used for error numbers. Descriptor names are literals. All input and output is through ANSI the varying character type. The program connects you to ORACLE using your username and password, then prompts you for a SQL statement. Enter legal SQL or PL/SQL statements using regular, not embedded, SQL syntax and terminate each statement with a seimcolon. Your statement will be processed. If it is a query, the fetched rows are displayed. You can enter multiline statements. The limit is 1023 characters. There is a limit on the size of the variables, MAX_VAR_LEN, defined as 255. This program processes up to 40 bind variables and 40 select-list items. DML returning statments and user defined types are not supported with value semantics. Precompile the program with mode=ansi, for example: proc mode=ansi ansidyn1 Using mode=ansi will set dynamic and type_code to ansi. *******************************************************************/ #include <stdio.h> #include <string.h> #include <setjmp.h> #include <stdlib.h> #include <sqlcpr.h> #define MAX_OCCURENCES 40 #define MAX_VAR_LEN 255 #define MAX_NAME_LEN 31 #ifndef NULL #define NULL 0 #endif /* Prototypes */ #if defined(__STDC__) void sql_error(void); int oracle_connect(void); int get_dyn_statement(void); int process_input(void); int process_output(void); void help(void); #else void sql_error(/*_ void _*/); int oracle_connect(/*_ void _*/); int get_dyn_statement(/* void _*/); int process_input(/*_ void _*/); int process_output(/*_ void _*/); void help(/*_ void _*/); #endif EXEC SQL INCLUDE sqlca; char SQLSTATE[6]; /* global variables */ EXEC SQL BEGIN DECLARE SECTION; char dyn_statement[1024]; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; /* Define a buffer to hold longjmp state info. */ jmp_buf jmp_continue; /* A global flag for the error routine. */ int parse_flag = 0; /* A global flag to indicate statement is a select */ int select_found; void main() { /* Connect to the database. */ if (oracle_connect() != 0) exit(1); EXEC SQL WHENEVER SQLERROR DO sql_error(); /* Allocate the input and output descriptors. */ EXEC SQL ALLOCATE DESCRIPTOR 'input_descriptor'; EXEC SQL ALLOCATE DESCRIPTOR 'output_descriptor'; /* 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. */ 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; /* Call the function that processes the input. */ if (process_input()) exit(1); /* Open the cursor and execute the statement. */ EXEC SQL OPEN C USING DESCRIPTOR 'input_descriptor'; /* Call the function that processes the output. */ if (process_output()) exit(1); /* Close the cursor. */ EXEC SQL CLOSE C; } /* end of for(;;) statement-processing loop */ /* Deallocate the descriptors */ EXEC SQL DEALLOCATE DESCRIPTOR 'input_descriptor'; EXEC SQL DEALLOCATE DESCRIPTOR 'output_descriptor'; EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL COMMIT WORK; puts("\nHave a good day!\n"); EXEC SQL WHENEVER SQLERROR DO sql_error(); return; } 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'; select_found = 0; } fgets(linebuf, sizeof linebuf, stdin); cp = strrchr(linebuf, '\n'); if (cp && cp != linebuf) *cp = ' '; else if (cp == linebuf) continue; if ((strncmp(linebuf, "SELECT", 6) == 0) || (strncmp(linebuf, "select", 6) == 0)) { select_found=1;; } 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; } int process_input() { int i; EXEC SQL BEGIN DECLARE SECTION; char name[31]; int input_count, input_len, occurs, ANSI_varchar_type; char input_buf[MAX_VAR_LEN]; EXEC SQL END DECLARE SECTION; EXEC SQL DESCRIBE INPUT S USING DESCRIPTOR 'input_descriptor'; EXEC SQL GET DESCRIPTOR 'input_descriptor' :input_count = COUNT; ANSI_varchar_type=12; for (i=0; i < input_count; i++) { occurs = i +1; /* occurence is 1 based */ EXEC SQL GET DESCRIPTOR 'input_descriptor' VALUE :occurs :name = NAME; printf ("\nEnter value for input variable %*.*s: ", 10,31, name); fgets(input_buf, sizeof(input_buf), stdin); input_len = strlen(input_buf) - 1; /* get rid of new line */ input_buf[input_len] = '\0'; /* null terminate */ EXEC SQL SET DESCRIPTOR 'input_descriptor' VALUE :occurs TYPE = :ANSI_varchar_type, LENGTH = :input_len, DATA = :input_buf; } return(sqlca.sqlcode); } int process_output() { int i, j; EXEC SQL BEGIN DECLARE SECTION; int output_count, occurs, type, len, col_len; short indi; char data[MAX_VAR_LEN], name[MAX_NAME_LEN]; EXEC SQL END DECLARE SECTION; if (!select_found) return(0); EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR 'output_descriptor'; EXEC SQL GET DESCRIPTOR 'output_descriptor' :output_count = COUNT; printf ("\n"); type = 12; /* ANSI VARYING character type */ len = MAX_VAR_LEN; /* use the max allocated length */ for (i = 0; i < output_count; i++) { occurs = i + 1; EXEC SQL GET DESCRIPTOR 'output_descriptor' VALUE :occurs :name = NAME; printf("%-*.*s ", 9,9, name); EXEC SQL SET DESCRIPTOR 'output_descriptor' VALUE :occurs TYPE = :type, LENGTH = :len; } printf("\n"); /* FETCH each row selected and print the column values. */ EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop; for (;;) { EXEC SQL FETCH C INTO DESCRIPTOR 'output_descriptor'; for (i=0; i < output_count; i++) { occurs = i + 1; EXEC SQL GET DESCRIPTOR 'output_descriptor' VALUE :occurs :data = DATA, :indi = INDICATOR; if (indi == -1) printf("%-*.*s ", 9,9, "NULL"); else printf("%-*.*s ", 9,9, data); /* simplified output formatting */ /* truncation will occur, but columns will line up */ } printf ("\n"); } end_select_loop: return(0); } 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\nANSI sqlstate: %s: ", SQLSTATE); 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); } 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; }
このプログラムは、ANSI動的SQLを使用したSQL文の処理方法を示します。このSQL文は実行時まで不明です。このプログラムでは、バッチ処理および参照セマンティクスのOracle拡張機能が使用されます。
このプログラムでは、自分のユーザー名とパスワードを使用してORACLEに接続した後、SQL文を入力します。埋込み型ではなく対話型のSQL構文を使用して有効なSQLまたはPL/SQL文を入力し、各文の終わりにセミコロンを付けてください。入力した文が処理されます。問合せのときはフェッチされた行が表示されます。
複数行の文を入力できます。最大1023文字まで入力できます。変数のサイズには制限があり、MAX_VAR_LENが255に定義されています。このプログラムでは、バインド変数は40、選択リスト項目も40まで処理できます。
次のように、dynamic = ansi
に設定してプログラムをプリコンパイルします。
proc dynamic=ansi ansidyn2 /******************************************************************* ANSI Dynamic Demo 2: ANSI Dynamic SQL with reference semantics, batch processing and global descriptor names in host variables This program demonstates using ANSI Dynamic SQL to process SQL statements which are not known until runtime. It uses the Oracle extensions for batch processing and reference semantics. The program connects you to ORACLE using your username and password, then prompts you for a SQL statement. Enter legal SQL or PL/SQL statement using interactive, not embedded, SQL syntax, terminating the statement with a seimcolon. Your statement will be processed. If it is a query, the fetched rows are displayed. If your statement has input bind variables (other than in a where clause), the program will ask for an input array size and then allow you to enter that number of input values. If your statment has output, the program will ask you for an output array size and will do array fetchng using that value. It will also output the rows fetched in one batch together, so using a small value for the output array size will improve the look of the output. For example, connected as scott/tiger, try select empno, ename from emp with an output array size of 4; You can enter multiline statements. The limit is 1023 characters. There is a limit on the size of the variables, MAX_VAR_LEN, defined as 255. This program processes up to 40 bind variables and 40 select-list items. Precompile with program with dynamic=ansi, for example: proc dynamic=ansi ansidyn2 *******************************************************************/ #include <stdio.h> #include <string.h> #include <setjmp.h> #include <stdlib.h> #include <sqlcpr.h> #define MAX_OCCURENCES 40 #define MAX_ARRSZ 100 #define MAX_VAR_LEN 255 #define MAX_NAME_LEN 31 #ifndef NULL #define NULL 0 #endif /* Prototypes */ #if defined(__STDC__) void sql_error(void); int oracle_connect(void); int get_dyn_statement(void); int process_input(void); int process_output(void); void rows_processed(void); void help(void); #else void sql_error(/*_ void _*/); int oracle_connect(/*_ void _*/); int get_dyn_statement(/* void _*/); int process_input(/*_ void _*/); int process_output(/*_ void _*/); void rows_processed(/*_ void _*/); void help(/*_ void _*/); #endif EXEC SQL INCLUDE sqlca; /* global variables */ char dyn_statement[1024]; /* statement variable */ EXEC SQL VAR dyn_statement IS STRING(1024); char indesc[]="input_descriptor"; /* descriptor names */ char outdesc[]="output_descriptor"; char input[MAX_OCCURENCES][MAX_ARRSZ][MAX_VAR_LEN +1 ], /* data areas */ output[MAX_OCCURENCES][MAX_ARRSZ][MAX_VAR_LEN + 1]; short outindi[MAX_OCCURENCES][MAX_ARRSZ]; /* output indicators */ short *iptr; int in_array_size; /* size of input batch, that is, number of rows */ int out_array_size; /* size of input batch, that is, number of rows */ int max_array_size=MAX_ARRSZ; /* maximum arrays size used for allocates */ char *dml_commands[] = {"SELECT", "select", "INSERT", "insert", "UPDATE", "update", "DELETE", "delete"}; int select_found, cursor_open = 0; /* 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() { /* Connect to the database. */ if (oracle_connect() != 0) exit(1); EXEC SQL WHENEVER SQLERROR DO sql_error(); /* Allocate the input and output descriptors. */ EXEC SQL FOR :max_array_size ALLOCATE DESCRIPTOR GLOBAL :indesc; EXEC SQL FOR :max_array_size ALLOCATE DESCRIPTOR GLOBAL :outdesc; /* 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. */ 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; /* Call the function that processes the input. */ if (process_input()) exit(1); /* Open the cursor and execute the statement. */ EXEC SQL FOR :in_array_size OPEN C USING DESCRIPTOR GLOBAL :indesc; cursor_open = 1; /* Call the function that processes the output. */ if (process_output()) exit(1); /* Tell user how many rows were processed. */ rows_processed(); } /* end of for(;;) statement-processing loop */ /* Close the cursor. */ if (cursor_open) EXEC SQL CLOSE C; /* Deallocate the descriptors */ EXEC SQL DEALLOCATE DESCRIPTOR GLOBAL :indesc; EXEC SQL DEALLOCATE DESCRIPTOR GLOBAL :outdesc; EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL COMMIT WORK RELEASE; puts("\nHave a good day!\n"); EXEC SQL WHENEVER SQLERROR DO sql_error(); return; } 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'; select_found = 0; } fgets(linebuf, sizeof linebuf, stdin); cp = strrchr(linebuf, '\n'); if (cp && cp != linebuf) *cp = ' '; else if (cp == linebuf) continue; if ((strncmp(linebuf, "SELECT", 6) == 0) || (strncmp(linebuf, "select", 6) == 0)) { select_found=1;; } 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; } int process_input() { int i, j; char name[31]; int input_count, input_len= MAX_VAR_LEN; int occurs, string_type = 5; int string_len; char arr_size[3]; EXEC SQL DESCRIBE INPUT S USING DESCRIPTOR GLOBAL :indesc; EXEC SQL GET DESCRIPTOR GLOBAL :indesc :input_count = COUNT; if (input_count > 0 && !select_found ) { /* get input array size */ printf ("\nEnter value for input array size (max is %d) : ", max_array_size); fgets(arr_size, 4, stdin); in_array_size = atoi(arr_size); } else { in_array_size = 1; } for (i=0; i < input_count; i++) { occurs = i +1; /* occurence is 1 based */ EXEC SQL GET DESCRIPTOR GLOBAL :indesc VALUE :occurs :name = NAME; for (j=0; j < in_array_size; j++) { if (in_array_size == 1) printf ("\nEnter value for input variable %*.*s: ",10,31, name); else printf ("\nEnter %d%s value for input variable %*.*s: ", j +1, ((j==0) ? "st" : (j==1) ? "nd" : (j==2) ? "rd" :"th"), 10,31, name); fgets(input[i][j], sizeof(input[i][j]), stdin); string_len = strlen(input[i][j]); input[i][j][string_len - 1 ] = '\0'; /* change \n to \0 */ } EXEC SQL SET DESCRIPTOR GLOBAL :indesc VALUE :occurs TYPE = :string_type, LENGTH = :input_len; EXEC SQL FOR :in_array_size SET DESCRIPTOR GLOBAL :indesc VALUE :occurs REF DATA = :input[i]; } return(sqlca.sqlcode); } int process_output() { int i, j; int output_count, occurs; int type, output_len= MAX_VAR_LEN; char name[MAX_OCCURENCES][MAX_NAME_LEN]; int rows_this_fetch=0, cumulative_rows=0; char arr_size[3]; if (!select_found) return(0); EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR GLOBAL :outdesc; EXEC SQL GET DESCRIPTOR GLOBAL :outdesc :output_count = COUNT; if (output_count > 0 ) { printf ("\nEnter value for output array size (max is %d) : ", max_array_size); fgets(arr_size, 4, stdin); out_array_size = atoi(arr_size); } if (out_array_size < 1) /* must have at least one */ out_array_size = 1; printf ("\n"); for (i = 0; i < output_count; i++) { occurs = i + 1; EXEC SQL GET DESCRIPTOR GLOBAL :outdesc VALUE :occurs :type = TYPE, :name[i] = NAME; occurs = i + 1; /* occurence is one based */ type = 5; /* force all data to be null terminated character */ EXEC SQL SET DESCRIPTOR GLOBAL :outdesc VALUE :occurs TYPE = :type, LENGTH = :output_len; iptr = (short *)&outindi[i]; /* no mult-dimension non-char host vars */ EXEC SQL FOR :out_array_size SET DESCRIPTOR GLOBAL :outdesc VALUE :occurs REF DATA = :output[i], REF INDICATOR = :iptr; } EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop; /* print the column headings */ for (j=0; j < out_array_size; j++) for (i=0; i < output_count; i++) printf("%-*.*s ", 9,9, name[i]); printf("\n"); /* FETCH each row selected and print the column values. */ for (;;) { EXEC SQL FOR :out_array_size FETCH C INTO DESCRIPTOR GLOBAL :outdesc; rows_this_fetch = sqlca.sqlerrd[2] - cumulative_rows; cumulative_rows = sqlca.sqlerrd[2]; if (rows_this_fetch) for (j=0; j < out_array_size && j < rows_this_fetch; j++) { /* output by columns using simplified formatting */ for (i=0; i < output_count; i++) { if (outindi[i][j] == -1) printf("%-*.*s ", 9, 9, "NULL"); else printf("%-*.*s ", 9, 9, output[i][j]); /* simplified */ /* output formatting may cause truncation */ /* but columns will line up */ } } printf ("\n"); } end_select_loop: /* print any unprinted rows */ rows_this_fetch = sqlca.sqlerrd[2] - cumulative_rows; cumulative_rows = sqlca.sqlerrd[2]; if (rows_this_fetch) for (j=0; j < out_array_size && j < rows_this_fetch; j++) { /* output by columns using simplified formatting */ for (i=0; i < output_count; i++) { if (outindi[i][j] == -1) printf("%-*.*s ",9, 9, "NULL"); else printf("%-*.*s ", 9, 9, output[i][j]); } } return(0); } void rows_processed() { int i; 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 ? ' ' : 's'); break; } } 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); } 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; }