静的 SQL を使用すると、組み込み SQL ステートメントで使用されているホスト変数は、アプリケーションのコンパイル時に認識されます。動的 SQL を使用した場合には、組み込み SQL ステートメントとその結果としてのホスト変数は、アプリケーションを実行するまで認識されません。このように、動的 SQL アプリケーションの場合は、アプリケーションで使用するホスト変数のリストを扱う必要があります。 (PREPARE を使用して) 準備された SELECT ステートメントのホスト変数情報を得るためには、 DESCRIBE ステートメントを使用し、その情報を SQL 記述子域 (SQLDA) に保管することができます。
注: | Java アプリケーションは SQLDA 構造を使用しないので、 PREPARE または DESCRIBE ステートメントも使用しません。 JDBC アプリケーションでは、 PreparedStatement オブジェクトと executeQuery() 方式を使って ResultSet オブジェクトを生成することができますが、これはホスト言語カーソルと同等です。 SQLJ アプリケーションでは、 SQLJ イテレーター・オブジェクトを CursorByPos または CursorByName カーソルと共に宣言し、 FETCH ステートメントからデータを戻すこともできます。 |
アプリケーション内で DESCRIBE ステートメントを実行すると、データベース・マネージャーはホスト変数を SQLDA に定義します。ホスト変数を SQLDA に定義すると、 FETCH ステートメントにより、カーソルを使用してホスト変数に値を割り当てることができます。
PREPARE、DESCRIBE、および FETCH ステートメントに関する詳細な情報、そして SQLDA の説明については SQL 解説書 を参照してください。
PREPARE、DESCRIBE、および FETCH ステートメントを使用し、 SQLDA を使用しない単純な動的 SQL プログラムの例については、 例: 動的 SQL プログラムを参照してください。 PREPARE、DESCRIBE、および FETCH ステートメントを使用し、対話式 SQL ステートメントを処理する SQLDA を使用する動的 SQL プログラムの例については、 例: ADHOC プログラムを参照してください。
カーソルを動的に処理することは、静的 SQL を用いてカーソルを処理することとほとんど同じです。カーソルを宣言する際に、カーソルは照会と関連付けられます。
静的 SQL の場合、照会は カーソル・ステートメントの宣言 にあるようなテキスト形式の SELECT ステートメントです。
動的 SQL の場合、照会は PREPARE ステートメントで割り当てられたステートメント名と関連付けられます。参照したホスト変数はパラメーター・マーカーで表されます。 表 7 は、動的 SELECT ステートメントに関連付けられた DECLARE ステートメントを示しています。
表 7. 動的 SELECT に関連付けられた DECLARE ステートメント
言語 | ソース・コード例 |
---|---|
C/C++ |
strcpy( prep_string, "SELECT tabname FROM syscat.tables" "WHERE tabschema = ?" ); EXEC SQL PREPARE s1 FROM :prep_string; EXEC SQL DECLARE c1 CURSOR FOR s1; EXEC SQL OPEN c1 USING :host_var; |
Java (JDBC) |
PreparedStatement prep_string = ("SELECT tabname FROM syscat.tables WHERE tabschema = ?" ); prep_string.setCursor("c1"); prep_string.setString(1, host_var); ResultSet rs = prep_string.executeQuery(); |
COBOL |
MOVE "SELECT TABNAME FROM SYSCAT.TABLES WHERE TABSCHEMA = ?" TO PREP-STRING. EXEC SQL PREPARE S1 FROM :PREP-STRING END-EXEC. EXEC SQL DECLARE C1 CURSOR FOR S1 END-EXEC. EXEC SQL OPEN C1 USING :host-var END-EXEC. |
FORTRAN |
prep_string = 'SELECT tabname FROM syscat.tables WHERE tabschema = ?' EXEC SQL PREPARE s1 FROM :prep_string EXEC SQL DECLARE c1 CURSOR FOR s1 EXEC SQL OPEN c1 USING :host_var |
静的カーソルと動的カーソルの主な相違点は、静的カーソルがプリコンパイル時に準備されるのに対して、動的カーソルは実行時に準備されることです。さらに、照会で参照されるホスト変数はパラメーター・マーカーで表され、カーソルをオープンする際に実行時ホスト変数で置き換えられます。
カーソルの使用方法の詳細については、以下の節を参照してください。
このサンプル・プログラムは、動的 SQL ステートメントに基づくカーソル処理を示しています。このプログラムは、名前列に STAFF という値を持つ表を除く SYSCAT.TABLES のすべての表をリストします。このサンプルは、以下のプログラム言語で入手可能です。
CHECKERR マクロ / 関数は、プログラム外部にあるエラー検査ユーティリティーです。エラー検査ユーティリティーの所在は、ご使用のプログラム言語により異なります。
このエラー検査ユーティリティーのソース・コードについては、 プログラム例での GET ERROR MESSAGE の使用を参照してください。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "utilemb.h" EXEC SQL INCLUDE SQLCA; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char table_name[19]; char st[80]; (1) char parm_var[19]; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: DYNAMIC\n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; EMB_SQL_CHECK("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; EMB_SQL_CHECK("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: dynamic [userid passwd]\n\n"); return 1; } /* endif */ strcpy( st, "SELECT tabname FROM syscat.tables" ); strcat( st, " WHERE tabname <> ?" ); EXEC SQL PREPARE s1 FROM :st; (2) EMB_SQL_CHECK("PREPARE"); EXEC SQL DECLARE c1 CURSOR FOR s1; (3) strcpy( parm_var, "STAFF" ); EXEC SQL OPEN c1 USING :parm_var; (4) EMB_SQL_CHECK("OPEN"); do { EXEC SQL FETCH c1 INTO :table_name; (5) if (SQLCODE != 0) break; printf( "Table = %s\n", table_name ); } while ( 1 ); EXEC SQL CLOSE c1; (6) EMB_SQL_CHECK("CLOSE"); EXEC SQL COMMIT; EMB_SQL_CHECK("COMMIT"); EXEC SQL CONNECT RESET; EMB_SQL_CHECK("CONNECT RESET"); return 0; } /* end of program : DYNAMIC.SQC */
import java.sql.*; class Dynamic { static { try { Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance (); } catch (Exception e) { System.out.println ("\n Error loading DB2 Driver...\n"); System.out.println (e); System.exit(1); } } public static void main(String argv[]) { try { System.out.println (" Java Dynamic Sample"); // Connect to Sample database Connection con = null; // URL is jdbc:db2:dbname String url = "jdbc:db2:sample"; if (argv.length == 0) { // connect with default id/password con = DriverManager.getConnection(url); } else if (argv.length == 2) { String userid = argv[0]; String passwd = argv[1]; // connect with user-provided username and password con = DriverManager.getConnection(url, userid, passwd); } else { throw new Exception("\nUsage: java Dynamic [username password]\n"); } // Enable transactions con.setAutoCommit(false); // Perform dynamic SQL SELECT using JDBC try { PreparedStatement pstmt1 = con.prepareStatement( "SELECT tabname FROM syscat.tables " + "WHERE tabname <> ? " + "ORDER BY 1"); (2) // set cursor name for the positioned update statement pstmt1.setCursorName("c1"); (3) pstmt1.setString(1, "STAFF"); ResultSet rs = pstmt1.executeQuery(); (4) System.out.print("\n"); while( rs.next() ) (5) { String tableName = rs.getString("tabname"); System.out.println("Table = " + tableName); }; rs.close(); pstmt1.close(); (7) } catch( Exception e ) { throw e; } finally { // Rollback the transaction System.out.println("\nRollback the transaction..."); con.rollback(); System.out.println("Rollback done."); } } catch( Exception e ) { System.out.println(e); } } }
Identification Division. Program-ID. "dynamic". Data Division. Working-Storage Section. copy "sqlenv.cbl". copy "sql.cbl". copy "sqlca.cbl". EXEC SQL BEGIN DECLARE SECTION END-EXEC. 01 table-name pic x(20). 01 st pic x(80). (1) 01 parm-var pic x(18). 01 userid pic x(8). 01 passwd. 49 passwd-length pic s9(4) comp-5 value 0. 49 passwd-name pic x(18). EXEC SQL END DECLARE SECTION END-EXEC. 77 errloc pic x(80). Procedure Division. Main Section. display "Sample COBOL program: DYNAMIC". display "Enter your user id (default none): " with no advancing. accept userid. if userid = spaces EXEC SQL CONNECT TO sample END-EXEC else display "Enter your password : " with no advancing accept passwd-name. * Passwords in a CONNECT statement must be entered in a VARCHAR format * with the length of the input string. inspect passwd-name tallying passwd-length for characters before initial " ". EXEC SQL CONNECT TO sample USER :userid USING :passwd END-EXEC. move "CONNECT TO" to errloc. call "checkerr" using SQLCA errloc. move "SELECT TABNAME FROM SYSCAT.TABLES - " ORDER BY 1 - " WHERE TABNAME <> ?" to st. EXEC SQL PREPARE s1 FROM :st END-EXEC. (2) move "PREPARE" to errloc. call "checkerr" using SQLCA errloc. EXEC SQL DECLARE c1 CURSOR FOR s1 END-EXEC. (3) move "STAFF" to parm-var. EXEC SQL OPEN c1 USING :parm-var END-EXEC. (4) move "OPEN" to errloc. call "checkerr" using SQLCA errloc. perform Fetch-Loop thru End-Fetch-Loop until SQLCODE not equal 0. EXEC SQL CLOSE c1 END-EXEC. (6) move "CLOSE" to errloc. call "checkerr" using SQLCA errloc. EXEC SQL COMMIT END-EXEC. move "COMMIT" to errloc. call "checkerr" using SQLCA errloc. EXEC SQL CONNECT RESET END-EXEC. move "CONNECT RESET" to errloc. call "checkerr" using SQLCA errloc. End-Main. go to End-Prog. Fetch-Loop Section. EXEC SQL FETCH c1 INTO :table-name END-EXEC. (5) if SQLCODE not equal 0 go to End-Fetch-Loop. display "TABLE = ", table-name. End-Fetch-Loop. exit. End-Prog. stop run.
/* REXX DYNAMIC.CMD */ parse version rexxType . parse source platform . if platform == 'AIX/6000' & rexxType == 'REXXSAA' then do rcy = SysAddFuncPkg("db2rexx") end else do if RxFuncQuery('SQLDBS') <> 0 then rcy = RxFuncAdd( 'SQLDBS', 'db2ar', 'SQLDBS' ) if RxFuncQuery('SQLEXEC') <> 0 then rcy = RxFuncAdd( 'SQLEXEC', 'db2ar', 'SQLEXEC' ) end /* pull in command line arguments */ parse arg userid passwd . /* check to see if the proper number of arguments have been passed in */ PARSE ARG dbname userid password . if ((dbname = "" ) | , (userid <> "" & password = "") , ) then do SAY "USAGE: dynamic.cmd <dbname> [<userid> <password>]" exit -1 end /* connect to database */ SAY SAY 'Connect to' dbname IF password= "" THEN CALL SQLEXEC 'CONNECT TO' dbname ELSE CALL SQLEXEC 'CONNECT TO' dbname 'USER' userid 'USING' password CALL CHECKERR 'Connect to ' SAY "Connected" say 'Sample REXX program: DYNAMIC' st = "SELECT tabname FROM syscat.tables WHERE tabname <> ? ORDER BY 1" call SQLEXEC 'PREPARE s1 FROM :st' (2) call CHECKERR 'PREPARE' call SQLEXEC 'DECLARE c1 CURSOR FOR s1' (3) call CHECKERR 'DECLARE' parm_var = "STAFF" call SQLEXEC 'OPEN c1 USING :parm_var' (4) do while ( SQLCA.SQLCODE = 0 ) call SQLEXEC 'FETCH c1 INTO :table_name' (5) if (SQLCA.SQLCODE = 0) then say 'Table = ' table_name end call SQLEXEC 'CLOSE c1' (6) call CHECKERR 'CLOSE' call SQLEXEC 'CONNECT RESET' call CHECKERR 'CONNECT RESET' CHECKERR: arg errloc if ( SQLCA.SQLCODE = 0 ) then return 0 else do say '--- error report ---' say 'ERROR occurred :' errloc say 'SQLCODE :' SQLCA.SQLCODE /******************************\ * GET ERROR MESSAGE API called * \******************************/ call SQLDBS 'GET MESSAGE INTO :errmsg LINEWIDTH 80' say errmsg say '--- end error report ---' if (SQLCA.SQLCODE < 0 ) then exit else do say 'WARNING - CONTINUING PROGRAM WITH ERRORS' return 0 end end return 0
SQLDA には不定数の SQLVAR 項目が入っており、 図 2 に示されているように、各 SQLVAR 項目は 1 データ行に 1 つの列を記述するフィールドの集まりで構成されます。 SQLVAR 項目には、基本 SQLVAR と 2 次 SQLVAR という 2 つのタイプがあります。その 2 つのタイプについては、 SQL 解説書 を参照してください。
![]() |
必要とされる SQLVAR 項目の数は結果表の列の数によって決まるため、アプリケーションは必要に応じて適切な数の SQLVAR 要素を割り振ることができなければなりません。これは、以下の 2 とおりの方法で行うことができます。言及されている SQLDA のフィールドについては、 SQL 解説書 を参照してください。
上記の方式の場合、最初にいくつの SQLVAR 項目を割り当てればよいかという疑問が生じます。各 SQLVAR 要素は、最高 44 バイトの記憶域を使用します (SQLDATA および SQLIND フィールドに割り振られる記憶域はカウントしていません)。メモリーに十分余裕があれば、 SQLDA の最大サイズを割り振るという最初の方法を実行するのは簡単です。
より小さい SQLDA を割り当てるという 2 番目の方法は、メモリーの動的割り振りをサポートする C および C++ のようなプログラミング言語にしか適用できません。メモリーの動的割り振りをサポートしない COBOL や FORTRAN のような言語の場合、最初の方法を使用することが必要です。
アプリケーションが、 SQLVAR 項目の入っていない minsqlda という名前の SQLDA 構造を宣言する場面を例にとって考えてみましょう。 SQLDA の SQLN フィールドは割り振られる SQLVAR 項目の数を記述します。この場合、SQLN は 0 に設定しなければなりません。次に、文字ストリング dstring から 1 つのステートメントを準備し、そしてその記述を minsqlda の中に入力するには、次の SQL ステートメントを発行します。 (C 構文を使用し、 minsqlda が SQLDA 構造へのポインターとして宣言されているものとします。)
EXEC SQL PREPARE STMT INTO :*minsqlda FROM :dstring;
dstring に含まれるステートメントが、各行に 20 列を戻す SELECT ステートメントであったとします。 PREPARE ステートメント (または DESCRIBE ステートメント) の後の SQLDA の SQLD フィールドには、準備済み SELECT ステートメントの結果行の列数が入っています。
SQLDA の SQLVAR は以下の場合に設定されます。
最初の SQLD SQLVAR 項目が設定され、SQLDOUBLED はブランクに設定されます。
2* SQLD SQLVAR 項目が設定され、SQLDOUBLED は 2 に設定されます。
最初の SQLD SQLVAR 項目が設定され、SQLDOUBLED はブランクに設定されます。 SQLWARN バインド・オプションが YES の場合は、警告 SQLCODE +237 (SQLSTATE 01594) が発行されます。
以下の場合には、SQLDA 内の SQLVAR は設定されません (追加スペースを割り振り、DESCRIBE をもう 1 度指定するよう要求されます)。
SQLVAR 項目は設定されず、SQLDOUBLED はブランクに設定されます。 SQLWARN バインド・オプションが YES の場合は、警告 SQLCODE +236 (SQLSTATE 01005) が発行されます。
DESCRIBE を正常に実行するためには、SQLD SQLVAR を割り振ってください。
SQLVAR 項目は設定されず、SQLDOUBLED はブランクに設定されます。 SQLWARN バインド・オプションが YES の場合は、警告 SQLCODE +239 (SQLSTATE 01005) が発行されます。
特殊タイプの名前を含む DESCRIBE を正常に実行するためには、 2*SQLD SQLVAR を割り振ってください。
SQLVAR 項目は設定されず、SQLDOUBLED はブランクに設定されます。警告 SQLCODE +238 (SQLSTATE 01005) が発行されます (SQLWARN バインド・オプションの設定に関係なく)。
DESCRIBE を正常に実行するためには、2*SQLD SQLVAR を割り振ってください。
BIND コマンドの SQLWARN オプションは、 DESCRIBE (または PREPARE...INTO) が以下の警告を戻すかどうかを制御します。
アプリケーション・コードでは、これらの SQLCODE が戻される可能性のあることを常に考慮に入れておいてください。警告 SQLCODE +238 (SQLSTATE 01005) は、選択リストに LOB 列があり SQLDA に不適当な SQLVAR がある場合に、必ず戻されます。これは、結果セット内に LOB 列があるために SQLVAR の数が 2 倍になっていなければならないということをアプリケーションが認識できる唯一の方法です。
結果表の列数が決まったら、記憶域を 2 番目の、フルサイズの SQLDA に割り振ることができます。たとえば、結果表に 20 列が含まれている (そのどれもが LOB 列でない) 場合、 2 番目の SQLDA 構造である fulsqlda は、少なくとも 20 の SQLVAR 要素 (または結果表に LOB または特殊タイプがある場合には 40 要素) を指定して割り振らなければなりません。この例のその他の部分では、LOB または特殊タイプは結果表に含まれないものとします。
SQLDA 構造の記憶域要件は以下のように構成されています。
fulsqlda に必要な SQLVAR 項目の数は、 minsqlda という SQLD フィールドに指定されました。その値は 20 です。そのため、この例で使用される fulsqlda に必要な記憶域割り振りは、以下のようになります。
16 + (20 * sizeof(struct sqlvar))
注: | 64 ビット・プラットフォームの場合、 sizeof(struct sqlvar) および sizeof(struct sqlvar2) は 56 を戻します。 32 ビット・プラットフォームの場合、 sizeof(struct sqlvar) および sizeof(struct sqlvar2) は 44 を戻します。 |
この値は、ヘッダーのサイズに各 SQLVAR 項目のサイズの 20 倍を加えて、合計 896 バイトであることを表しています。
SQLDASIZE マクロを使用することにより、自分の計算をしないようにし、バージョン固有の従属関係をすべて回避することができます。
fulsqlda に十分なスペースを割り振るためには、アプリケーションに以下のステップを含める必要があります。
DESCRIBE ステートメントを使用するとステートメントを 2 回準備するコストが省けるため、この方法のほうが好んで使用されます。 DESCRIBE ステートメントは、準備操作中に入手した前の情報を再度使用するだけで、その情報を新規の SQLDA 構造に入れます。次のステートメントを発行することができます。
EXEC SQL DESCRIBE STMT INTO :fulsqlda
このステートメントを実行すると、それぞれの SQLVAR 要素には結果表の 1 つの列の記述が含まれます。
SQLDA 構造を用いて結果表の任意の行を取り出すには、アプリケーションは事前に以下の処理を行っておく必要があります。
SELECT が記述されている場合、ラージ・オブジェクト (LOB) の値について、 SQLVAR に指定されるデータ・タイプは SQL_TYP_xLOB であることに注意してください。このデータ・タイプは一般的な LOB ホスト変数と同じであり、すべての LOB は 1 回でメモリーに保管されます。これは (数 MB までの) 小さい LOB の場合に作動しますが、このデータ・タイプを大きい LOB (1 GB) に使用することはできません。 SQLVAR 内のアプリケーションの列定義を変更して、 SQL_TYP_xLOB_LOCATOR または SQL_TYPE_xLOB_FILE のいずれかにすることが必要になります。 (SQLVAR の SQLTYPE フィールドを変更する場合には、 SQLLEN フィールドも変更する必要があるので注意してください。) SQLVAR 内の列定義を変更すると、アプリケーションではその新しいタイプに対して正しい容量の記憶域を割り振ることができます。 LOB の詳細については、オブジェクト関連機能の使用を参照してください。
これらのステップは、各列の記述を分析し、それぞれの SQLDATA フィールドの内容をその列の値を保持するだけの大きさをもつ記憶域と置き換えることによって行われます。長さ属性は、LOB タイプでないデータ項目に対する各 SQLVAR 項目の SQLLEN フィールドから判別されます。タイプが BLOB、CLOB、または DBCLOB の項目の場合、長さ属性は 2 番目の SQLVAR 項目の SQLLONGLEN フィールドから判別されます。
さらに、指定した列にヌルを使用できる場合、アプリケーションは SQLIND フィールドの内容を列の標識変数のアドレスと置き換えなければなりません。
SQLDA 構造の割り振りが適切に行われると、 SELECT ステートメントに関連するカーソルをオープンし、 FETCH ステートメントの USING DESCRIPTOR 文節を指定することによって行を取り出すことができます。
これが終了したら、カーソルをクローズし、動的に割り振ったメモリーを解放してください。
C 言語で SQLDA 構造を作成するには、ホスト言語で INCLUDE SQLDA ステートメントを組み込むか、または SQLDA インクルード・ファイルを組み込んで、構造定義を入手してください。次に、SQLDA のサイズは固定されていないため、アプリケーションは SQLDA へのポインターを宣言し、それに記憶域を割り振らなければなりません。 SQLDA 構造の実際のサイズは、SQLDA を用いて渡される個別データ項目の数によって決まります。 (SQLDA を処理するアプリケーションのコーディング方法の例については、 例: ADHOC プログラムを参照してください。)
C/C++ プログラム言語では、SQLDA の割り振りを簡単に行うためにマクロが提供されています。このマクロの形式は以下のとおりです (例外として、HP-UX プラットフォームの場合は形式が異なります)。
#define SQLDASIZE(n) (offsetof(struct sqlda, sqlvar) + (n) × sizeof(struct sqlvar))
HP-UX プラットフォームの場合、このマクロの形式は以下のとおりです。
#define SQLDASIZE(n) (sizeof(struct sqlda) + (n-1) × sizeof(struct sqlvar))
このマクロを使用することによって、 n 個の SQLVAR 要素に必要な記憶域を計算することができます。
COBOL で SQLDA 構造を作成するには、 INCLUDE SQLDA ステートメントを組み込むか、または COPY ステートメントを使用します。最も多くの SQLVAR を制御し、その結果 SQLDA が使用する記憶域の容量を制御したい場合は、 COPY ステートメントを使用してください。たとえば、SQLVAR の省略時の数を 1489 から 1 に変更するには、以下の COPY ステートメントを使用します。
COPY "sqlda.cbl" replacing --1489-- by --1--.
FORTRAN 言語では、自己定義データ構造または動的割り振りは直接にはサポートされていません。 SQLDA インクルード・ファイルは FORTRAN では使用できません。これは、FORTRAN では SQLDA をデータ構造としてサポートできないためです。 FORTRAN プログラムでは、プリコンパイラーは INCLUDE SQLDA ステートメントを無視します。
ただし、FORTRAN プログラムで静的 SQLDA 構造に似た構造を作成し、これを SQLDA を使用できる任意の場所で使用することができます。 sqldact.f ファイルには、 FORTRAN で SQLDA 構造を宣言するのに役立つ定数が含まれています。
ポインター値を必要とする SQLDA 要素に値を割り当てるには、SQLGADDR の呼び出しを実行してください。
次の表は、SQLVAR 要素を 1 つ持つ SQLDA 構造の宣言および使用方法を示しています。
言語 | ソース・コード例 |
---|---|
C/C++ |
#include <sqlda.h> struct sqlda *outda = (struct sqlda *)malloc(SQLDASIZE(1)); /* DECLARE LOCAL VARIABLES FOR HOLDING ACTUAL DATA */ double sal; short salind; /* INITIALIZE ONE ELEMENT OF SQLDA */ memcpy( outda->sqldaid,"SQLDA ",sizeof(outda->sqldaid)); outda->sqln = outda->sqld = 1; outda->sqlvar[0].sqltype = SQL_TYP_NFLOAT; outda->sqlvar[0].sqllen = sizeof( double );. outda->sqlvar[0].sqldata = (unsigned char *)&sal; outda->sqlvar[0].sqlind = (short *)&salind; |
COBOL |
WORKING-STORAGE SECTION. 77 SALARY PIC S99999V99 COMP-3. 77 SAL-IND PIC S9(4) COMP-5. EXEC SQL INCLUDE SQLDA END-EXEC * Or code a useful way to save unused SQLVAR entries. * COPY "sqlda.cbl" REPLACING --1489-- BY --1--. 01 decimal-sqllen pic s9(4) comp-5. 01 decimal-parts redefines decimal-sqllen. 05 precision pic x. 05 scale pic x. * Initialize one element of output SQLDA MOVE 1 TO SQLN MOVE 1 TO SQLD MOVE SQL-TYP-NDECIMAL TO SQLTYPE(1) * Length = 7 digits precision and 2 digits scale MOVE x"07" TO PRECISION. MOVE x"02" TO SCALE. MOVE DECIMAL-SQLLEN TO O-SQLLEN(1). SET SQLDATA(1) TO ADDRESS OF SALARY SET SQLIND(1) TO ADDRESS OF SAL-IND |
FORTRAN |
include 'sqldact.f' integer*2 sqlvar1 parameter ( sqlvar1 = sqlda_header_sz + 0*sqlvar_struct_sz ) C Declare an Output SQLDA -- 1 Variable character out_sqlda(sqlda_header_sz + 1*sqlvar_struct_sz) character*8 out_sqldaid ! Header integer*4 out_sqldabc integer*2 out_sqln integer*2 out_sqld integer*2 out_sqltype1 ! First Variable integer*2 out_sqllen1 integer*4 out_sqldata1 integer*4 out_sqlind1 integer*2 out_sqlnamel1 character*30 out_sqlnamec1 equivalence( out_sqlda(sqlda_sqldaid_ofs), out_sqldaid ) equivalence( out_sqlda(sqlda_sqldabc_ofs), out_sqldabc ) equivalence( out_sqlda(sqlda_sqln_ofs), out_sqln ) equivalence( out_sqlda(sqlda_sqld_ofs), out_sqld ) equivalence( out_sqlda(sqlvar1+sqlvar_type_ofs), out_sqltype1 ) equivalence( out_sqlda(sqlvar1+sqlvar_len_ofs), out_sqllen1 ) equivalence( out_sqlda(sqlvar1+sqlvar_data_ofs), out_sqldata1 ) equivalence( out_sqlda(sqlvar1+sqlvar_ind_ofs), out_sqlind1 ) equivalence( out_sqlda(sqlvar1+sqlvar_name_length_ofs), + out_sqlnamel1 ) equivalence( out_sqlda(sqlvar1+sqlvar_name_data_ofs), + out_sqlnamec1 ) C Declare Local Variables for Holding Returned Data. real*8 salary integer*2 sal_ind C Initialize the Output SQLDA (Header) out_sqldaid = 'OUT_SQLDA' out_sqldabc = sqlda_header_sz + 1*sqlvar_struct_sz out_sqln = 1 out_sqld = 1 C Initialize VAR1 out_sqltype1 = SQL_TYP_NFLOAT out_sqllen1 = 8 rc = sqlgaddr( %ref(salary), %ref(out_sqldata1) ) rc = sqlgaddr( %ref(sal_ind), %ref(out_sqlind1) ) |
動的なメモリー割り振りをサポートしない言語では、 SQLVAR 要素の希望数を指定した SQLDA をホスト言語で明示的に宣言しなければなりません。 SQLVAR の要素には、アプリケーションの必要に応じて決定されたとおりの十分な数を必ず宣言してください。
ホスト変数のリストを使用してデータを受け渡すよりも、
SQLDA を使用してデータを受け渡すほうが、より高い柔軟性が得られます。たとえば、SQLDA を用いて、固有のホスト言語に対応するものをもたないデータ (C 言語の DECIMAL データなど) を転送することができます。
ADHOC と呼ばれるサンプル・プログラムは、この技法を用いた例です。
(例: ADHOC プログラムを参照。)
数値と記号名がどのように関連付けられているかを示す便利な相互参照リストについては、
表 8 を参照してください。
数値および対応する記号名 | ||||
SQL 列名 | SQLTYPE 数値 | SQLTYPE 記号名1 | ||
---|---|---|---|---|
DATE | 384/385 | SQL_TYP_DATE / SQL_TYP_NDATE | ||
TIME | 388/389 | SQL_TYP_TIME / SQL_TYP_NTIME | ||
TIMESTAMP | 392/393 | SQL_TYP_STAMP / SQL_TYP_NSTAMP | ||
n/a2 | 400/401 | SQL_TYP_CGSTR / SQL_TYP_NCGSTR | ||
BLOB | 404/405 | SQL_TYP_BLOB / SQL_TYP_NBLOB | ||
CLOB | 408/409 | SQL_TYP_CLOB / SQL_TYP_NCLOB | ||
DBCLOB | 412/413 | SQL_TYP_DBCLOB / SQL_TYP_NDBCLOB | ||
VARCHAR | 448/449 | SQL_TYP_VARCHAR / SQL_TYP_NVARCHAR | ||
CHAR | 452/453 | SQL_TYP_CHAR / SQL_TYP_NCHAR | ||
LONG VARCHAR | 456/457 | SQL_TYP_LONG / SQL_TYP_NLONG | ||
n/a3 | 460/461 | SQL_TYP_CSTR / SQL_TYP_NCSTR | ||
VARGRAPHIC | 464/465 | SQL_TYP_VARGRAPH / SQL_TYP_NVARGRAPH | ||
GRAPHIC | 468/469 | SQL_TYP_GRAPHIC / SQL_TYP_NGRAPHIC | ||
LONG VARGRAPHIC | 472/473 | SQL_TYP_LONGRAPH / SQL_TYP_NLONGRAPH | ||
FLOAT | 480/481 | SQL_TYP_FLOAT / SQL_TYP_NFLOAT | ||
REAL4 | 480/481 | SQL_TYP_FLOAT / SQL_TYP_NFLOAT | ||
DECIMAL5 | 484/485 | SQL_TYP_DECIMAL / SQL_TYP_DECIMAL | ||
INTEGER | 496/497 | SQL_TYP_INTEGER / SQL_TYP_NINTEGER | ||
SMALLINT | 500/501 | SQL_TYP_SMALL / SQL_TYP_NSMALL | ||
n/a | 804/805 | SQL_TYP_BLOB_FILE / SQL_TYPE_NBLOB_FILE | ||
n/a | 808/809 | SQL_TYP_CLOB_FILE / SQL_TYPE_NCLOB_FILE | ||
n/a | 812/813 | SQL_TYP_DBCLOB_FILE / SQL_TYPE_NDBCLOB_FILE | ||
n/a | 960/961 | SQL_TYP_BLOB_LOCATOR / SQL_TYP_NBLOB_LOCATOR | ||
n/a | 964/965 | SQL_TYP_CLOB_LOCATOR / SQL_TYP_NCLOB_LOCATOR | ||
n/a | 968/969 | SQL_TYP_DBCLOB_LOCATOR / SQL_TYP_NDBCLOB_LOCATOR | ||
|
動的 SQL を使用するアプリケーションを作成し、任意の SQL ステートメントを処理することができます。たとえば、アプリケーションがユーザーから SQL ステートメントを受け入れる場合、アプリケーションはステートメントについて事前にわかっていなくても、そのステートメントを実行できなければなりません。
PREPARE および DESCRIBE ステートメントを SQLDA 構造で使用することにより、アプリケーションは実行される SQL ステートメントのタイプを判別し、それに応じて処理することができます。
対話式 SQL ステートメントを処理するプログラムの例については、 例: ADHOC プログラムを参照してください。
SQL ステートメントを準備する場合、ステートメントのタイプに関する情報は SQLDA 構造を調べて判別することができます。この情報はステートメントの準備時に INTO 文節を指定して SQLDA 構造に入れるか、または事前に準備されたステートメントに対して DESCRIBE ステートメントを発行することによって、 SQLDA 構造に入れることができます。
いずれの場合でも、データベース・マネージャーは SQLDA 構造の SQLD フィールドに 1 つの値を入れ、 SQL ステートメントにより生成された結果表に列の数を示します。 SQLD フィールドにゼロ (0) が入っている場合、このステートメントは SELECT ステートメントではありません。ステートメントはすでに準備されているため、 EXECUTE ステートメントを使用してただちに実行することができます。
ステートメントにパラメーター・マーカーが含まれている場合、 USING 文節は SQL 解説書 に記述されている方法で指定する必要があります。 USING 文節は、ホスト変数のリストか SQLDA 構造のどちらかを指定することができます。
SQLD フィールドが 0 より大きい場合、ステートメントは SELECT ステートメントであるため、次の節での説明に従って処理しなければなりません。
可変リスト SELECT ステートメントとは、戻される列の数およびタイプがプリコンパイル時にはわからないステートメントのことです。この場合、アプリケーションには、結果表の行を保持するために宣言しなければならない正確なホスト変数がわかりません。
可変リスト SELECT ステートメントを処理するには、アプリケーションでは以下のようにすることができます。
これらのステップについては、以下の節で詳しく説明されています。
アプリケーションで任意の SQL ステートメントを保管できる場合、これらをデータ・タイプが VARCHAR、LONG VARCHAR、CLOB、VARGRAPHIC、 LONG VARGRAPHIC、または DBCLOB の列を持つ表に保管することができます。その VARGRAPHIC、LONG VARGRAPHIC、および DBCLOB データ・タイプは、 2 バイト文字サポート (DBCS) および拡張 UNIX コード (EUC) 環境でしか使用できないので注意してください。
ユーザーは、準備済みのバージョンの SQL ステートメントではなく、そのソースを保管しなければなりません。これは、表に保管されているバージョンを実行する前に、各ステートメントを検索して準備しなければならないことを意味します。つまり、アプリケーションは、文字ストリングから SQL ステートメントを準備し、このステートメントを動的に実行します。
この例では、対話式 SQL ステートメントを処理するための SQLDA の使用法を示します。
注: | この例 adhoc.sqc は、C 言語のみを対象としています。 |
EMB_SQL_CHECK マクロ / 関数は、プログラム外部にあるエラー検査ユーティリティーです。 DB2 API を呼び出す C プログラムの場合、 utilapi.c 内の sqlInfoPrint 関数は、 utilapi.h 内の API_SQL_CHECK として再定義されます。 C 組み込み SQL プログラムの場合、 utilemb.sqc 内の sqlInfoPrint 関数は、 utilemb.h 内の EMB_SQL_CHECK として再定義されます。このエラー検査ユーティリティーのソース・コードについては、 プログラム例での GET ERROR MESSAGE の使用を参照してください。
この例では、 utilemb.sqc ファイルにユーティリティーとして提供されているたくさんの追加のプロシージャーを使用することに注意してください。そのプロシージャーには、以下のものがあります。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sqlenv.h> #include <sqlcodes.h> #include <sqlda.h> (1) #include "utilemb.h" #ifdef DB268K /* Need to include ASLM for 68K applications */ #include <LibraryManager.h> #endif EXEC SQL INCLUDE SQLCA ; (2) #define SQLSTATE sqlca.sqlstate int process_statement( char * ) ; int main( int argc, char *argv[] ) { int rc ; char sqlInput[256] ; char st[1024] ; EXEC SQL BEGIN DECLARE SECTION ; (3) char userid[9] ; char passwd[19] ; EXEC SQL END DECLARE SECTION ; #ifdef DB268K /* Before making any API calls for 68K environment, need to initial the Library Manager */ InitLibraryManager(0,kCurrentZone,kNormalMemory) ; atexit(CleanupLibraryManager) ; #endif printf( "Sample C program : ADHOC interactive SQL\n" ) ; /* Initialize the connection to a database. */ if ( argc == 1 ) { EXEC SQL CONNECT TO sample ; EMB_SQL_CHECK( "CONNECT TO SAMPLE" ) ; } else if ( argc == 3 ) { strcpy( userid, argv[1] ) ; strcpy( passwd, argv[2] ) ; EXEC SQL CONNECT TO sample USER :userid USING :passwd ; (4) EMB_SQL_CHECK( "CONNECT TO SAMPLE" ) ; (5) } else { printf( "\nUSAGE: adhoc [userid passwd]\n\n" ) ; return( 1 ) ; } /* endif */ printf( "Connected to database SAMPLE\n" ) ; /* Enter the continuous command line loop. */ *sqlInput = '\0' ; while ( ( *sqlInput != 'q' ) && ( *sqlInput != 'Q' ) ) { (6) printf( "Enter an SQL statement or 'quit' to Quit :\n" ) ; gets( sqlInput ) ; if ( ( *sqlInput == 'q' ) || ( *sqlInput == 'Q' ) ) break ; if ( *sqlInput == '\0' ) { /* Don't process the statement */ printf( "No characters entered.\n" ) ; continue ; } strcpy( st, sqlInput ) ; while ( sqlInput[strlen( sqlInput ) - 1] == '\\' ) { st[strlen( st ) - 1] = '\0' ; gets( sqlInput ) ; strcat( st, sqlInput ) ; } /* Process the statement. */ rc = process_statement( st ) ; } printf( "Enter 'c' to COMMIT or Any Other key to ROLLBACK the transaction :\n" ) ; gets( sqlInput ) ; if ( ( *sqlInput == 'c' ) || ( *sqlInput == 'C' ) ) { printf( "COMMITING the transactions.\n" ) ; EXEC SQL COMMIT ; (7) EMB_SQL_CHECK( "COMMIT" ) ; } else { /* assume that the transaction is to be rolled back */ printf( "ROLLING BACK the transactions.\n" ) ; EXEC SQL ROLLBACK ; (8) EMB_SQL_CHECK( "ROLLBACK" ) ; } EXEC SQL CONNECT RESET ; (9) EMB_SQL_CHECK( "CONNECT RESET" ) ; return( 0 ) ; } /****************************************************************************** * FUNCTION : process_statement * This function processes the inputted statement and then prepares the * procedural SQL implementation to take place. ******************************************************************************/ int process_statement ( char * sqlInput ) { int counter = 0 ; struct sqlda * sqldaPointer ; short sqlda_d ; EXEC SQL BEGIN DECLARE SECTION ; (3) char st[1024] ; EXEC SQL END DECLARE SECTION ; strcpy( st, sqlInput ) ; (10) /* allocate an initial SQLDA temp pointer to obtain information about the inputted "st" */ init_da( &sqldaPointer, 1 ) ; (11) EXEC SQL PREPARE statement1 from :st ; /* EMB_SQL_CHECK( "PREPARE" ) ; */ EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer ; /* Expecting a return code of 0 or SQL_RC_W236, SQL_RC_W237, SQL_RC_W238, SQL_RC_W239 for cases where this statement is a SELECT statment. */ if ( SQLCODE != 0 && SQLCODE != SQL_RC_W236 && SQLCODE != SQL_RC_W237 && SQLCODE != SQL_RC_W238 && SQLCODE != SQL_RC_W239 ) { /* An unexpected warning/error has occurred. Check the SQLCA. */ EMB_SQL_CHECK( "DESCRIBE" ) ; } /* end if */ sqlda_d = sqldaPointer->sqld ; free( sqldaPointer ) ; if ( sqlda_d > 0 ) { (12) /* this is a SELECT statement, a number of columns are present in the SQLDA */ if ( SQLCODE == SQL_RC_W236 || SQLCODE == 0) /* this out only needs a SINGLE SQLDA */ init_da( &sqldaPointer, sqlda_d ) ; if ( SQLCODE == SQL_RC_W237 || SQLCODE == SQL_RC_W238 || SQLCODE == SQL_RC_W239 ) /* this output contains columns that need a DOUBLED SQLDA */ init_da( &sqldaPointer, sqlda_d * 2 ) ; /* need to reassign the SQLDA with the correct number of columns to the SQL statement */ EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer ; EMB_SQL_CHECK( "DESCRIBE" ) ; /* allocating the proper amount of memory space needed for the variables */ alloc_host_vars( sqldaPointer ) ; (14) /* Don't need to check the SQLCODE for declaration of cursors */ EXEC SQL DECLARE pcurs CURSOR FOR statement1 ; (15) EXEC SQL OPEN pcurs ; (15) EMB_SQL_CHECK( "OPEN" ) ; EXEC SQL FETCH pcurs USING DESCRIPTOR :*sqldaPointer; (16) EMB_SQL_CHECK( "FETCH" ) ; /* if the FETCH is successful, obtain data from SQLDA */ /* display the column titles */ display_col_titles( sqldaPointer ) ; (17) /* display the rows that are fetched */ while ( SQLCODE == 0 ) { counter++ ; display_da( sqldaPointer ) ; (18) EXEC SQL FETCH pcurs USING DESCRIPTOR :*sqldaPointer ; } /* endwhile */ EXEC SQL CLOSE pcurs ; (19) EMB_SQL_CHECK( "CLOSE CURSOR" ) ; printf( "\n %d record(s) selected\n\n", counter ) ; /* Free the memory allocated to this SQLDA. */ free_da( sqldaPointer ) ; } else { /* this is not a SELECT statement, execute SQL statement */ (13) EXEC SQL EXECUTE statement1 ; EMB_SQL_CHECK( "Executing the SQL statement" ) ; } /* end if */ return( 0 ) ; } /* end of program : ADHOC.SQC */