アプリケーション開発の手引き

PREPARE、DESCRIBE、FETCH、および SQLDA の使用

静的 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 プログラム

このサンプル・プログラムは、動的 SQL ステートメントに基づくカーソル処理を示しています。このプログラムは、名前列に STAFF という値を持つ表を除く SYSCAT.TABLES のすべての表をリストします。このサンプルは、以下のプログラム言語で入手可能です。

C
dynamic.sqc

Java
Dynamic.java

COBOL
dynamic.sqb

REXX
dynamic.cmd

DYNAMIC プログラムの動作の仕組み

  1. ホスト変数を宣言する。このセクションには次の 3 つのホスト変数宣言が含まれています。

    table_name
    FETCH ステートメントの実行中に戻されたデータを保持するために使用する。
    st
    テキスト形式で動的 SQL ステートメントを保持するために使用する。
    parm_var
    st のパラメーター・マーカーを置き換えるためにデータ値を与える。
  2. ステートメントを準備する。パラメーター・マーカー ('?') が 1 つ入っている SQL ステートメントは、ホスト変数に複写されます。このホスト変数は、妥当性検査を行うために PREPARE ステートメントに渡されます。 PREPARE ステートメントは SQL テキストを分析し、プリコンパイラーまたはバインダーと同じ方法でパッケージのアクセス・セクションを準備します。これは、プリコンパイル中ではなく、実行時にだけ行われます。
  3. カーソルを宣言する。 DECLARE ステートメントはカーソルを、動的に準備した SQL ステートメントに関連付けます。準備済み SQL ステートメントが SELECT ステートメントである場合、結果表から行を取り出すにはカーソルが必要です。
  4. カーソルをオープンする。 OPEN ステートメントは、結果表の第 1 行目より前を指すように、先に宣言しておいたカーソルを初期化します。 USING 文節は、準備済み SQL ステートメントのパラメーター・マーカーを置き換えるためのホスト変数を指定します。ホスト変数のデータ・タイプおよび長さは、関連する列のタイプおよび長さと互換性がなければなりません。
  5. データを取り出す。 FETCH ステートメントを使用して、 NAME 列を結果表から table_name ホスト変数に移動します。ホスト変数は、プログラムが別の行を取り出すためにループバックする前に印刷されます。
  6. カーソルをクローズする。 CLOSE ステートメントはカーソルをクローズし、そのカーソルに関連するリソースを解放します。

CHECKERR マクロ / 関数は、プログラム外部にあるエラー検査ユーティリティーです。エラー検査ユーティリティーの所在は、ご使用のプログラム言語により異なります。

C
DB2 API を呼び出す C プログラムの場合、 utilapi.c 内の sqlInfoPrint 関数は、 utilapi.h 内の API_SQL_CHECK として再定義されます。 C 組み込み SQL プログラムの場合、 utilemb.sqc 内の sqlInfoPrint 関数は、 utilemb.h 内の EMB_SQL_CHECK として再定義されます。

Java
SQL エラーは SQLException としてスローされ、アプリケーションの catch ブロックで処理されます。

COBOL
CHECKERRcheckerr.cbl という名前の外部プログラムです。

REXX
CHECKERR は現行プログラムの終わりにあるプロシージャーです。

このエラー検査ユーティリティーのソース・コードについては、 プログラム例での GET ERROR MESSAGE の使用を参照してください。

C の例: DYNAMIC.SQC

#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 */

Java の例: Dynamic.java

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

COBOL の例: DYNAMIC.SQB

       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

/* 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 の宣言

SQLDA には不定数の SQLVAR 項目が入っており、 図 2 に示されているように、各 SQLVAR 項目は 1 データ行に 1 つの列を記述するフィールドの集まりで構成されます。 SQLVAR 項目には、基本 SQLVAR と 2 次 SQLVAR という 2 つのタイプがあります。その 2 つのタイプについては、 SQL 解説書 を参照してください。

図 2. SQL 記述域 (SQLDA)


SQL 記述域 (SQLDA)

必要とされる SQLVAR 項目の数は結果表の列の数によって決まるため、アプリケーションは必要に応じて適切な数の SQLVAR 要素を割り振ることができなければなりません。これは、以下の 2 とおりの方法で行うことができます。言及されている SQLDA のフィールドについては、 SQL 解説書 を参照してください。

上記の方式の場合、最初にいくつの SQLVAR 項目を割り当てればよいかという疑問が生じます。各 SQLVAR 要素は、最高 44 バイトの記憶域を使用します (SQLDATA および SQLIND フィールドに割り振られる記憶域はカウントしていません)。メモリーに十分余裕があれば、 SQLDA の最大サイズを割り振るという最初の方法を実行するのは簡単です。

より小さい SQLDA を割り当てるという 2 番目の方法は、メモリーの動的割り振りをサポートする C および C++ のようなプログラミング言語にしか適用できません。メモリーの動的割り振りをサポートしない COBOL や FORTRAN のような言語の場合、最初の方法を使用することが必要です。

最小の SQLDA 構造を用いたステートメントの準備

アプリケーションが、 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 は以下の場合に設定されます。

以下の場合には、SQLDA 内の SQLVAR は設定されません (追加スペースを割り振り、DESCRIBE をもう 1 度指定するよう要求されます)。

BIND コマンドの SQLWARN オプションは、 DESCRIBE (または PREPARE...INTO) が以下の警告を戻すかどうかを制御します。

アプリケーション・コードでは、これらの SQLCODE が戻される可能性のあることを常に考慮に入れておいてください。警告 SQLCODE +238 (SQLSTATE 01005) は、選択リストに LOB 列があり SQLDA に不適当な SQLVAR がある場合に、必ず戻されます。これは、結果セット内に LOB 列があるために SQLVAR の数が 2 倍になっていなければならないということをアプリケーションが認識できる唯一の方法です。

十分な数の SQLVAR 項目を指定した SQLDA の割り振り

結果表の列数が決まったら、記憶域を 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 マクロを使用することにより、自分の計算をしないようにし、バージョン固有の従属関係をすべて回避することができます。

SELECT ステートメントの記述

fulsqlda に十分なスペースを割り振るためには、アプリケーションに以下のステップを含める必要があります。

  1. fulsqlda の SQLN フィールドに値 20 を保管する。
  2. 2 番目の SQLDA 構造 fulsqlda を用いて SELECT ステートメントに関する情報を入手する。これには、次の 2 とおりの方法があります。

DESCRIBE ステートメントを使用するとステートメントを 2 回準備するコストが省けるため、この方法のほうが好んで使用されます。 DESCRIBE ステートメントは、準備操作中に入手した前の情報を再度使用するだけで、その情報を新規の SQLDA 構造に入れます。次のステートメントを発行することができます。

     EXEC SQL DESCRIBE STMT INTO :fulsqlda

このステートメントを実行すると、それぞれの SQLVAR 要素には結果表の 1 つの列の記述が含まれます。

行を保持するための記憶域の獲得

SQLDA 構造を用いて結果表の任意の行を取り出すには、アプリケーションは事前に以下の処理を行っておく必要があります。

  1. それぞれの SQLVAR 記述を分析して、その列の値に必要なスペースの量を判別する。

    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 の詳細については、オブジェクト関連機能の使用を参照してください。

  2. その列の値に記憶域を割り振る。
  3. 割り振った記憶域のアドレスを SQLDA 構造の SQLDATA フィールドに保管する。

これらのステップは、各列の記述を分析し、それぞれの SQLDATA フィールドの内容をその列の値を保持するだけの大きさをもつ記憶域と置き換えることによって行われます。長さ属性は、LOB タイプでないデータ項目に対する各 SQLVAR 項目の SQLLEN フィールドから判別されます。タイプが BLOB、CLOB、または DBCLOB の項目の場合、長さ属性は 2 番目の SQLVAR 項目の SQLLONGLEN フィールドから判別されます。

さらに、指定した列にヌルを使用できる場合、アプリケーションは SQLIND フィールドの内容を列の標識変数のアドレスと置き換えなければなりません。

カーソルの処理

SQLDA 構造の割り振りが適切に行われると、 SELECT ステートメントに関連するカーソルをオープンし、 FETCH ステートメントの USING DESCRIPTOR 文節を指定することによって行を取り出すことができます。

これが終了したら、カーソルをクローズし、動的に割り振ったメモリーを解放してください。

SQLDA 構造の割り振り

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 を使用してデータを受け渡すほうが、より高い柔軟性が得られます。たとえば、SQLDA を用いて、固有のホスト言語に対応するものをもたないデータ (C 言語の DECIMAL データなど) を転送することができます。 ADHOC と呼ばれるサンプル・プログラムは、この技法を用いた例です。 (例: ADHOC プログラムを参照。) 数値と記号名がどのように関連付けられているかを示す便利な相互参照リストについては、 表 8 を参照してください。

表 8. DB2 V2 SQLDA SQL タイプ
数値および対応する記号名
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
Note:これらの定義タイプは sql.h インクルード・ファイルにあり、インクルード・ファイル自体は、 sqllib ディレクトリーの include サブディレクトリーにあります。 (たとえば、C プログラミング言語の場合は sqllib/include/sql.h となります。)

  1. COBOL プログラミング言語の場合、SQLTYPE には下線 (_) を使用しませんが、その代わりにハイフン (-) を使用します。
  2. これは NULL 終了グラフィック・ストリングです。
  3. これは NULL 終了文字ストリングです。
  4. SQLDA での REAL と DOUBLE の違いは長さの値です (4 または 8)。
  5. 精度は最初のバイトにあります。位取りは 2 番目のバイトにあります。

対話式 SQL ステートメントの処理

動的 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 ステートメントとは、戻される列の数およびタイプがプリコンパイル時にはわからないステートメントのことです。この場合、アプリケーションには、結果表の行を保持するために宣言しなければならない正確なホスト変数がわかりません。

可変リスト SELECT ステートメントを処理するには、アプリケーションでは以下のようにすることができます。

  1. SQLDA を宣言する。可変リスト SELECT ステートメントを処理するには、 SQLDA 構造を必ず使用します。
  2. INTO 文節を使用してステートメントを PREPARE (準備) する。アプリケーションは、宣言した SQLDA 構造に十分な SQLVAR 要素があるかどうかを判別します。十分な要素がない場合、アプリケーションは必要な数の SQLVAR 要素を持つ別の SQLDA 構造を割り振り、新規の SQLDA を用いて追加の DESCRIBE ステートメントを発行します。
  3. SQLVAR 要素を割り振る。各 SQLVAR に必要なホスト変数および標識に、記憶域を割り振ります。このステップでは、それぞれの SQLVAR 要素にデータの割り振りアドレスおよび標識変数を入れます。
  4. SELECT ステートメントを処理する。カーソルは準備済みステートメントに関連付けられ、オープンされます。行は適切に割り振られた SQLDA 構造を用いて取り出されます。

これらのステップについては、以下の節で詳しく説明されています。

エンド・ユーザーからの SQL 要求の保管

アプリケーションで任意の SQL ステートメントを保管できる場合、これらをデータ・タイプが VARCHAR、LONG VARCHAR、CLOB、VARGRAPHIC、 LONG VARGRAPHIC、または DBCLOB の列を持つ表に保管することができます。その VARGRAPHIC、LONG VARGRAPHIC、および DBCLOB データ・タイプは、 2 バイト文字サポート (DBCS) および拡張 UNIX コード (EUC) 環境でしか使用できないので注意してください。

ユーザーは、準備済みのバージョンの SQL ステートメントではなく、そのソースを保管しなければなりません。これは、表に保管されているバージョンを実行する前に、各ステートメントを検索して準備しなければならないことを意味します。つまり、アプリケーションは、文字ストリングから SQL ステートメントを準備し、このステートメントを動的に実行します。

例: ADHOC プログラム

この例では、対話式 SQL ステートメントを処理するための SQLDA の使用法を示します。
注:この例 adhoc.sqc は、C 言語のみを対象としています。

ADHOC プログラムの動作の仕組み

  1. SQLDA 構造を定義する。 INCLUDE SQLDA ステートメントは、データベース・マネージャーとプログラムとの間でデータを受け渡しする際に用いられる SQLDA 構造を定義および宣言します。
  2. SQLCA 構造を定義する。 INCLUDE SQLCA ステートメントは SQLCA 構造を定義し、その構造内の要素として SQLCODE を定義します。 SQLCA 構造の SQLCODE フィールドは、SQL ステートメントの実行後に、データベース・マネージャーにより診断情報を用いて更新されます。
  3. ホスト変数を宣言する。 BEGIN DECLARE SECTION および END DECLARE SECTION ステートメントは、ホスト変数宣言を区切ります。ホスト変数には、SQL ステートメントで参照される際に、接頭部としてコロン (:) が付けられます。
  4. データベースに接続する。プログラムはユーザー指定のデータベースに接続し、そのデータベースへの共用アクセスを要求します。 (START DATABASE MANAGER API 呼び出しまたは db2start コマンドが発行されていることを前提としています。) 共用アクセス・モードで同じデータベースに接続しようとする他のプログラムにもアクセスが許可されます。
  5. 完了をチェックする。 SQLCA 構造は、CONNECT TO ステートメントが正常に終了したかどうかをチェックします。 SQLCODE 値が 0 のときは、接続が成功したことを示します。
  6. 対話式プロンプト。 SQL ステートメントはプロンプトによって入力され、その後の処理のために process_statement 関数に送られます。
  7. トランザクションの終了 - COMMIT。作業単位が終了したことをユーザーが確認したならば、COMMIT で終了します。最後の COMMIT 以後、入力された SQL ステートメントによって要求されたすべての変更がデータベースに保管されます。
  8. トランザクションの終了 - ROLLBACK。作業単位が終了したことをユーザーが確認したならば、ROLLBACK で終了します。最後の COMMIT またはプログラムの開始以後、入力された SQL ステートメントによって要求されたすべての変更は取り消されます。
  9. データベースから切断する。プログラムは CONNECT RESET ステートメントを実行して、データベースから切断します。実行時には、SQLCA が正常に終了したかどうかがチェックされます。
  10. SQL ステートメント・テキストをホスト変数に複写する。ステートメント・テキストは、ホスト変数 st で指定したデータ域に複写されます。
  11. 処理のために SQLDA を準備する。初期 SQLDA 構造が宣言され、メモリーが init_da プロシージャーによって割り振られ、 SQL ステートメントによって生成される出力タイプが決められます。 PREPARE ステートメントから戻された SQLDA は、 SQL ステートメントから戻される列の数を報告します。
  12. SQLDA が存在している出力列を報告する。 SQL ステートメントは SELECT ステートメントです。 SQLDA は init_da プロシージャーによって初期化され、準備済み SQL ステートメントが置かれるメモリー・スペースを割り振ります。
  13. SQLDA は出力列がないことを報告する。戻される列がありません。 SQL ステートメントは EXECUTE ステートメントを使用して動的に 実行されます。
  14. SQLDA 用のメモリー・スペースを準備する。メモリーは、SQLDA 内の列構造を反映して割り振られます。メモリーの所要量は、SQLDA 内の列構造の SQLTYPE と SQLLEN によって選択されます。
  15. カーソルを宣言およびオープンする。 DECLARE ステートメントはカーソル pcurssqlStatement 内にあり動的に準備された SQL ステートメントに関連付け、その後カーソルはオープンされます。
  16. 行を取り出す。 FETCH ステートメントは、カーソルを次の行に位置付け、その行の内容を SQLDA に移動します。
  17. 列タイトルを表示する。取り出された最初の行が、列タイトル情報です。
  18. 行情報を表示する。連続して実行される FETCH によって収集された行情報が表示されます。
  19. カーソルをクローズする。 CLOSE ステートメントはカーソルをクローズし、カーソルに関連付けられていた資源を解放します。

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 ファイルにユーティリティーとして提供されているたくさんの追加のプロシージャーを使用することに注意してください。そのプロシージャーには、以下のものがあります。

init_da
準備済み SQL ステートメントにメモリーを割り振ります。内部記述関数 SQLDASIZE を使用して、適正な記憶容量が計算されます。
alloc_host_vars
SQLDA ポインターからのデータ用にメモリーを割り振ります。
free_da
SQLDA データ構造を使用するために割り振られていたメモリーを解放します。
print_var
SQLDA SQLVAR 変数を印刷します。このプロシージャーは、まずデータ・タイプを判別してから、データの印刷に必要なサブルーチンを呼び出します。
display_da
渡されているポインターの出力を表示します。出力データの構造に関係のあるすべての情報は、プロシージャー print_var で検査されたものとして、このポインターから利用できます。

C の例: ADHOC.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 */


[ ページのトップ | 前ページ | 次ページ | 目次 | 索引 ]