CLI の手引きおよび解説書

記述子の使用

DB2 CLI は、結果セット内の列に関する情報 (データ・タイプ、サイズ、ポインターなど)、および SQL ステートメントのパラメーターを保管します。また、列およびパラメーターに対するアプリケーション・バッファーのバインドも、保管する必要があります。 記述子 は上記情報の論理視点であり、この情報を照会および更新するための 1 つの手段をアプリケーションに提供します。

多くの CLI 関数は記述子を利用しますが、アプリケーション自身は直接、記述子を操作する必要はありません。

たとえば、次のようにします。

データベース操作は記述子に対する直接アクセスを必要としてはいませんが、記述子による直接作業が、より効率的であったり、結果としてより簡単なコードとなる場合があります。たとえば、ある表から取り出される行を記述する記述子を使用すると、その後その表の中に挿入される行を記述することが可能になります。

記述子タイプ

記述子には次の 4 つのタイプがあります。

アプリケーション・パラメーター記述子 (APD)
アプリケーション・バッファー (ポインター、データ・タイプ、位取り、精度、長さ、最大バッファー長など) を記述します。これらのバッファーは、 SQL ステートメントのパラメーターにバインドされています。パラメーターが CALL ステートメントの一部の場合には、それは入力、出力、またはその両方の可能性があります。この情報は、アプリケーションの C データ・タイプを使用して記述されます。

アプリケーション行記述子 (ARD)
列にバインドするアプリケーション・バッファーを記述します。アプリケーションは、実装行記述子にあるバッファーからいろいろなデータ・タイプを指定して、列データのデータ変換を行うことができます。この記述子は、アプリケーションが指定する任意のデータ変換を反映します。

実装パラメーター記述子 (IPD)
SQL ステートメントにあるパラメーターを記述します (SQL タイプ、サイズ、精度など)。

実装行記述子 (IRD)
DB2 CLI がアプリケーションの C データ・タイプへの必要な変換を行う前の、結果セットからのデータの行を記述します。

上記に示す 4 つのタイプの記述子の唯一の違いは、それらがどのように使用されるかにあります。記述子の利点の 1 つは、単一の記述子が多くの目的に使用できることです。たとえば、あるステートメントにある行記述子は別のステートメントでパラメーター記述子として使用することができます。

記述子が存在するようになるとすぐに、それはアプリケーション記述子かまたは実装記述子かのどちらかになります。記述子がまだデータベース操作で使用されていない場合でも、こうしたケースがあります。記述子が SQLAllocHandle() を使用して、アプリケーションで割り当てられる場合、それはアプリケーション記述子となります。

記述子に保管される値

それぞれの記述子には、ヘッダー・フィールドとレコード・フィールドの両方があります。これらのフィールドが一緒になって、列またはパラメーターを完全に記述します。

ヘッダー・フィールド

それぞれのヘッダー・フィールドは各記述子の中で 1 回だけ出現します。このフィールドの 1 つを変更すると、すべての列またはパラメーターに影響します。

以下のヘッダー・フィールドの大部分は、ステートメント属性に対応しています。 SQLSetDescField() を使用して記述子のヘッダー・フィールドを設定することは、 SQLSetStmtAttr() を使用して対応するステートメント属性を設定するのと同じです。同じことが、SQLGetDescField() または SQLGetStmtAttr() を使用して情報を取り出す場合にもそのまま適用されます。アプリケーションに記述子ハンドルがすでに割り当てられているのでなければ、記述子ハンドルを割り当てて記述子呼び出しを使用するよりも、ステートメント属性呼び出しを使用する方が一層能率的です。


表 7. ヘッダー・フィールド
SQL_DESC_ALLOC_TYPE SQL_DESC_BIND_TYPEa
SQL_DESC_ARRAY_SIZEa SQL_DESC_COUNT
SQL_DESC_ARRAY_STATUS_PTRa SQL_DESC_ROWS_PROCESSED_PTRa
SQL_DESC_BIND_OFFSET_PTRa
注:

a
ステートメント属性に対応するヘッダー・フィールドです。

上記フィールドのそれぞれの詳細については、 SQLSetDescField()『ヘッダー・フィールド』を参照してください。

記述子のヘッダー・フィールド SQL_DESC_COUNT は、情報が入っている最大番号の記述子レコードの、1 を基数とする指標です。 DB2 CLI は、列またはパラメーターがバインドおよびアンバインドされるときに、自動的にこのフィールド (および記述子の物理サイズ) を更新します。記述子が最初に割り当てられるとき、SQL_DESC_COUNT の初期値は 0 です。

記述子レコード

ゼロ個以上の記述子レコードが単一の記述子にあります。新しい列またはパラメーターがバインドされるとき、新しい記述子レコードがその記述子に追加されます。列またはパラメーターがアンバインドされるとき、その記述子レコードは除去されます。

表 8 には、記述子レコードにあるフィールドをリストしています。それぞれは 1 つの列またはパラメーターを記述し、各記述子レコード内で 1 回だけ出現します。


表 8. レコード・フィールド
SQL_DESC_AUTO_UNIQUE_VALUE SQL_DESC_LOCAL_TYPE_NAME
SQL_DESC_BASE_COLUMN_NAME SQL_DESC_NAME
SQL_DESC_BASE_TABLE_NAME SQL_DESC_NULLABLE
SQL_DESC_CASE_SENSITIVE SQL_DESC_OCTET_LENGTH
SQL_DESC_CATALOG_NAME SQL_DESC_OCTET_LENGTH_PTR
SQL_DESC_CONCISE_TYPE SQL_DESC_PARAMETER_TYPE
SQL_DESC_DATA_PTR SQL_DESC_PRECISION
SQL_DESC_DATETIME_INTERVAL_CODE SQL_DESC_SCALE
SQL_DESC_DATETIME_INTERVAL_PRECISION SQL_DESC_SCHEMA_NAME
SQL_DESC_DISPLAY_SIZE SQL_DESC_SEARCHABLE
SQL_DESC_FIXED_PREC_SCALE SQL_DESC_TABLE_NAME
SQL_DESC_INDICATOR_PTR SQL_DESC_TYPE
SQL_DESC_LABEL SQL_DESC_TYPE_NAME
SQL_DESC_LENGTH SQL_DESC_UNNAMED
SQL_DESC_LITERAL_PREFIX SQL_DESC_UNSIGNED
SQL_DESC_LITERAL_SUFFIX SQL_DESC_UPDATABLE

上記フィールドのそれぞれの詳細については、 SQLSetDescField()『レコード・フィールド』を参照してください。

据え置きフィールド

据え置きフィールドは、記述子ヘッダーまたは記述子レコード作成時に作成されます。定義される変数のアドレスは保管されますが、アプリケーションで使用されるのはもっと後です。これらの変数をフィールドに関連付けている時や、CLI がそれらを読み書きしている間は、アプリケーションはこれらの変数を割り当て解除または廃棄してはなりません。

以下の表には、据え置きフィールドとその意味、また NULL ポインターが適用できる箇所を示しています。


表 9. 据え置きフィールド
フィールド ヌル値の意味
SQL_DESC_DATA_PTR レコードがアンバインドされています。
SQL_DESC_INDICATOR_PTR (なし)
SQL_DESC_OCTET_LENGTH_PTR (ARD および APD 専用)
  • ARD: 列の長さ情報が返されない。
  • APD: パラメーターが文字ストリングの場合、ドライバーは文字列がヌル値であるとみなす。出力パラメーターの場合、このフィールドのヌル値はドライバーが長さ情報を返さないようにします。 (SQL_DESC_TYPE フィールドが文字ストリング・パラメーターを指定しない場合は、 SQL_DESC_OCTET_LENGTH_PTR フィールドは無視されます。)

SQL_DESC_ARRAY_STATUS_PTR (複数行取り出し専用) 複数行の取り出しで、行単位の診断情報の構成要素を返すのに失敗する。
SQL_DESC_ROWS_PROCESSED_PTR (複数行取り出し専用) (なし)

バインド済み記述子レコード

各記述子レコードの SQL_DESC_DATA_PTR フィールドは、パラメーター値 (APD の場合) または列の値 (ARD の場合) を含む変数を指しています。これは、ヌルを省略時値とする据え置きフィールドです。列またはパラメーターが一度バインドされると、それはパラメーターまたは列の値を指します。この時点で、記述子レコードはバインドされたことになります。

アプリケーション・パラメーター記述子 (APD)
各バインド済みレコードはバインド済みパラメーターを構成します。アプリケーションはステートメントを実行する前に、 SQL ステートメントにあるそれぞれの入出力パラメーター・マーカーごとに 1 つのパラメーターをバインドする必要があります。

アプリケーション行記述子 (ARD)
各バインド済みレコードは、バインド済みの列に関連しています。

整合性検査

整合性検査は、アプリケーションが APD または ARD の SQL_DESC_DATA_PTR フィールドを設定するたびに、自動的に実行されます。検査では、種々のフィールドが互いに整合していること、および適切なデータ・タイプが指定されていることを確認します。

IPD フィールドの整合性検査を強制させるには、アプリケーションは IPD の SQL_DESC_DATA_PTR フィールドを設定します。この設定は整合性検査を強制する場合にのみ使用されます。値は保管されません。それで、 SQLGetDescField() または SQLGetDescRec() への呼び出しで取り出すことはできません。

整合性検査は、IRD では実行できません。

整合性検査に関する詳細については、 SQLSetDescRec()『整合性検査』を参照してください。

記述子の割り当ておよび解放

記述子は次の 2 つの方法のどちらかで割り当てられます。

暗黙的に割り当てられる記述子
ステートメント・ハンドルが割り当てられると、一連の 4 つの記述子が暗黙的に割り当てられます。ステートメント・ハンドルが解放されると、暗黙的に割り当てられた記述子ハンドルすべてが同様に解放されます。

暗黙的に割り当てられる記述子に対するハンドルを得るには、アプリケーションは、ステートメント・ハンドルおよび次に示す属性 値を渡して、 SQLGetStmtAttr() を呼び出します。

明示的に割り当てられる記述子
アプリケーションは、明示的にアプリケーション記述子を割り当てることができます。しかし、実装記述子を割り当てることはできません。

接続でのアプリケーション記述子は、データベースに接続されるどんな時にでも明示的に割り当てることができます。このことは、ステートメント・ハンドルおよび次に示す属性 値を渡して、 SQLSetStmtAttr() を呼び出すことにより行われます。

この場合は、暗黙的に割り当てられた記述子よりも、明示的に指定され割り当てられる記述子が使用されます。

明示的に割り当てられる記述子は、複数のステートメントに関連付けることが可能です。

フィールドの初期設定

アプリケーションの行記述子が割り当てられると、そのフィールドは SQLSetDescField() のセクション 『記述子フィールドの初期設定』に示されている初期値を受け取ります。 SQL_DESC_TYPE フィールドは SQL_DEFAULT にセットされます。それは、アプリケーションへの表示のためのデータベース・データの標準的な取り扱いを提供します。アプリケーションは、記述子レコードのフィールドを設定することにより、データの異なる取り扱いを指定することができます。

SQL_DESC_ARRAY_SIZE ヘッダー・フィールドの初期値は 1 です。複数行の取り出しを可能にするには、アプリケーションはこの値を ARD にセットし、行セット内の行数を指定します。スクロール可能カーソル内の行セットに関する詳細は、 スクロール可能カーソルを参照してください。

IRD のフィールドについては省略時値がありません。これらのフィールドはステートメントの準備または実行時に設定されます。

IPD 内の以下のフィールドは、 SQLPrepare() への呼び出しで自動的に移植されるまでは未定義のままです。

IPD の自動移植

アプリケーションが準備済み SQL ステートメントのパラメーターに関する情報を知りたい場合もあります。ある ad-hoc 照会が準備された場合の適切な例を考えてみましょう。アプリケーションは事前に、パラメーターに関することは何もわかりません。アプリケーションが SQL_ATTR_ENABLE_AUTO_IPD ステートメント属性を SQL_TRUE に (SQLSetStmtAttr() を使用して) 設定することで、 IPD の自動移植を可能にすると、 IPD のフィールドはパラメーターを記述するため自動的に移植されます。これには、データ・タイプ、精度、位取りなど
(SQLDescribeParam() が返すのと同じ情報) が含まれます。アプリケーションはこの情報を使って、データ変換が必要かどうか、およびどのアプリケーション・バッファーがパラメーターをバインドするのに最も適切かを判別します。

IPD の自動移植には、いくらかのオーバーヘッドを必要とします。この情報が CLI ドライバーにより自動的に集められる必要がない場合は、 SQL_ATTR_ENABLE_AUTO_IPD ステートメント属性は SQL_FALSE に設定してください。これは省略時の設定であり、アプリケーションで必要がなくなったときに、この値を返すべきです。

IPD の自動移植が活動中のとき、SQLPrepare() への各呼び出しを使用すると、 IPD のフィールドが更新されることになります。その結果の記述子情報は、次の関数を呼び出すことにより取り出せます。

記述子の解放

明示的に割り当てられる記述子
明示的に割り当てられる記述子が解放されると、その解放された記述子が適用されていたすべてのステートメント・ハンドルは、暗黙的に割り当てられていた元の記述子に自動的に戻ります。

明示的に割り当てられている記述子は、次の 2 つの方法のどちらかにより解放されます。

暗黙的に割り当てられる記述子
暗黙的に割り当てられている記述子は、次の 2 つの方法のどちらかにより解放されます。

暗黙的に割り当てられた記述子は、 SQL_HANDLE_DESC の HandleType を指定して SQLFreeHandle() を呼び出すことでは解放できません。

記述子フィールドの入手、設定、およびコピー

以降のセクションでは、記述子ハンドルを使用した記述子の操作について説明します。最終セクションであるハンドルを使用しない記述子のアクセスでは、記述子ハンドルを使わない CLI 関数を呼び出すことにより記述子の値をどのように操作するかを説明します。

明示的に割り当てられる記述子のハンドルは、その記述子を割り当てるためにアプリケーションが SQLAllocHandle() を呼び出す時点で、 OutputHandlePtr 引き数に返されます。

暗黙的に割り当てられる記述子のハンドルは、 SQL_ATTR_IMP_PARAM_DESC または SQL_ATTR_IMP_ROW_DESC のどちらかを指定して SQLGetStmtAttr() を呼び出すことで得られます。

記述子フィールドの値の取り出し

記述子レコードの単一フィールドを得る方法の詳細については、 SQLGetDescField - 記述子レコードの単一フィールド設定を入手するを参照してください。

データ・タイプおよび列またはパラメーター・データの記憶域に影響する複数の記述子フィールドの設定を入手する方法については、 SQLGetDescRec - 記述子レコードの複数フィールド設定を入手するを参照してください。

記述子フィールドの値の設定

このセクションでは記述子ハンドルを使用して記述子フィールドの値を設定する方法を扱っています。また、記述子ハンドルを使用しないでこれらのフィールドの多くを設定することもできます。詳細については、ハンドルを使用しない記述子のアクセスを参照してください。

次の 2 つの方式を使用して、記述子フィールドを設定することができます。それは、一度に 1 つのフィールド、または一度に複数のフィールドの 2 つの方式です。

個々に記述子フィールドを設定する

中には、読み取り専用の記述子フィールドもありますが、その他のフィールドは関数 SQLSetDescField() を使用して設定できます。以下のものを設定する各フィールドに関する特有の情報については、以下のセクションを参照してください。

レコードおよびヘッダー・フィールドは、 SQLSetDescField() を使用してそれぞれに設定されます。

ヘッダー・フィールド
SQLSetDescField() への呼び出しでは、設定するヘッダー・フィールドと、レコード番号 0 を渡します。記述子につき 1 つのヘッダー・フィールドしかないので、レコード番号は無視されます。この場合、レコード番号 0 はブックマーク・フィールドを示していません。

レコード・フィールド
SQLSetDescField() への呼び出しでは、設定するレコード・フィールドと、レコード番号 1 またはそれ以上の数値を渡します。あるいは、ブックマーク・フィールドを示す 0 を渡します。

記述子の個々のフィールドを設定するときは、 『記述子フィールドの設定順序』で定義されているステップに従ってください。いくつかのフィールドを設定すれば、 DB2 CLI は自動的にその他のフィールドを設定できます。整合性検査は、アプリケーションが指定のステップに従った後に行われます。これは、記述子フィールドの値が整合していることを確認します。詳細については、整合性検査を参照してください。

記述子を設定するはずの関数呼び出しが失敗した場合、関数呼び出しが失敗した後は、その記述子フィールドの内容は未定義のままです。

一度に複数の記述子フィールドを設定する

事前に定義された記述子フィールドのセットを、個々のフィールドを 1 つずつ設定するのではなく、 1 回の呼び出しでまとめて設定することができます。 SQLSetDescRec() は、単一の列またはパラメーターについて、以下のフィールドを設定します。

(SQL_DESC_DATETIME_INTERVAL_CODE も ODBC で定義されていますが、 DB2 CLI ではサポートされていません。)

詳細については、SQLSetDescRec - 列またはパラメーター・データに複数の記述子フィールドを設定するを参照してください。

記述子のコピー

記述子の 1 つの利点は、単一の記述子が多目的に使用できるという点にあります。たとえば、あるステートメント・ハンドルでの ARD を、別のステートメント・ハンドルでの APD として使用できます。

他の例を挙げてみましょう。ここで、アプリケーションが元の記述子のコピーを作ろうとします。そしてあるフィールドを変更します。この場合には、 SQLCopyDesc() を使用して別の記述子からの値によって既存の記述子のフィールドを上書きします。複写元記述子および複写先記述子の両方で定義されているフィールドだけが、コピーされます (変更できない SQL_DESC_ALLOC_TYPE フィールドは例外) 。

フィールドはどんなタイプの記述子からもコピーできますが、アプリケーション記述子 (APD や ARD) または IPD に対してだけコピーできます。 IRD にコピーすることはできません。記述子の割り当てタイプは、コピー手順によって変更されることはありません (SQL_DESC_ALLOC_TYPE フィールドは変更できません)。

記述子のコピーについての詳細な説明は、SQLCopyDesc - ハンドル間で記述子情報をコピーするを参照してください。

ハンドルを使用しない記述子のアクセス

記述子に関してこのセクションの初めに述べたように、多くの CLI 関数は、記述子を使用していますが、アプリケーション自身は直接、記述子を操作する必要はありません。その代わりに、アプリケーションは他の関数を実行する場合と同じように、 1 つまたは複数の記述子フィールドを設定または取り出す別個の関数を使用できます。このカテゴリーの CLI 関数は、コンサイス 関数と呼ばれています。 SQLBindCol() は、記述子フィールドを操作するコンサイス関数の一例です。

複数のフィールドを操作することに加えて、コンサイス関数は記述子ハンドルを明示的に指定しないで呼び出されます。それで、アプリケーションは、コンサイス関数を使用するために記述子ハンドルを取り出す必要さえもありません。

以下のタイプのコンサイス関数があります。

記述子のサンプル

/* ... */
    SQLCHAR * sqlstmt =
       "SELECT deptname, location from org where division = ? " ;
/* ... */
    /* macro to initalize server, uid and pwd */
    INIT_UID_PWD ;
    /* allocate an environment handle */
    rc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv ) ;
    if ( rc != SQL_SUCCESS ) return( terminate( henv, rc ) ) ;
    /* allocate a connect handle, and connect */
    rc = DBconnect( henv, &hdbc ) ;
    if ( rc != SQL_SUCCESS ) return( terminate( henv, rc ) ) ;
    rc = SQLAllocHandle( SQL_HANDLE_STMT, hdbc, &hstmt ) ;
    CHECK_HANDLE( SQL_HANDLE_DBC, hdbc, rc ) ;
    /* Use SQLGetStmtAttr() to get implicit parameter descriptor handle */
    rc = SQLGetStmtAttr ( hstmt,
                          SQL_ATTR_IMP_PARAM_DESC,
                          &hIPDdesc,
                          SQL_IS_POINTER,
                          NULL);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* Use SQLGetStmtAttr() to get implicit row descriptor handle */
    rc = SQLGetStmtAttr ( hstmt,
                          SQL_ATTR_IMP_ROW_DESC,
                          &hIRDdesc,
                          SQL_IS_POINTER,
                          NULL);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* Call SQLGetDescField() to see how the header field */
    /* SQL_DESC_ALLOC_TYPE is set. */
    rc = SQLGetDescField( hIPDdesc,
                          0, /* ignored for header fields */
                          SQL_DESC_ALLOC_TYPE,
                          &desc_smallint, /* The result */
                          SQL_IS_SMALLINT,
                          NULL ); /* ignored */
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* Print the descriptor information */
    printf("The IPD header descriptor field SQL_DESC_ALLOC_TYPE is %s\n",
            ALLOCTYPES[desc_smallint]);
    /* prepare statement for multiple use */
    rc = SQLPrepare(hstmt, sqlstmt, SQL_NTS);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* bind division to parameter marker in sqlstmt */
    rc = SQLBindParameter( hstmt,
                           1,
                           SQL_PARAM_INPUT,
                           SQL_C_CHAR,
                           SQL_CHAR,
                           10,
                           0,
                           division.s,
                           11,
                           NULL
                         ) ;
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* bind deptname to first column in the result set */
    rc = SQLBindCol(hstmt, 1, SQL_C_CHAR, (SQLPOINTER) deptname.s, 15,
                    &deptname.ind);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    rc = SQLBindCol(hstmt, 2, SQL_C_CHAR, (SQLPOINTER) location.s, 14,
                    &location.ind);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* Call SQLGetDescField() to see how the descriptor record */
    /* field SQL_DESC_PARAMETER_TYPE is set */
    rc = SQLGetDescField( hIPDdesc,
                          1, /* Look at the parameter */
                          SQL_DESC_PARAMETER_TYPE,
                          &desc_smallint, /* The result */
                          SQL_IS_SMALLINT,
                          NULL ); /* ignored */
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    printf("The IPD record descriptor field SQL_DESC_PARAMETER_TYPE is %s\n",
            PARAMTYPE[desc_smallint]);
    strcpy( division.s,  "Eastern");
    rc = SQLExecute(hstmt);
    CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    printf("\nDepartments in %s Division:\n", division.s);
    printf("Department       Location\n");
    printf("-------------- -------------\n");
    while ( ( rc = SQLFetch( hstmt ) ) == SQL_SUCCESS )
       CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
       printf( "%-14.14s %-13.13s \n", deptname.s, location.s ) ;
    if ( rc != SQL_NO_DATA_FOUND )
       CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
    /* Print out some implementation row descriptor fields */
    /* from the last SQLFetch() above */
    for (colCount = 1; colCount <=2; colCount++) {
       printf("\nInformation for column %i\n",colCount);
       /* Call SQLGetDescField() to see how the descriptor record */
       /* field SQL_DESC_TYPE_NAME is set */
       rc = SQLGetDescField( hIRDdesc,
                   colCount,
                   SQL_DESC_TYPE_NAME, /* record field */
                   desc_char, /* The result */
                   25,
                   NULL ); /* ignored */
       CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
       printf("  - IRD record descriptor field SQL_DESC_TYPE_NAME is %s\n",
            desc_char);
       /* Call SQLGetDescField() to see how the descriptor record */
       /* field SQL_DESC_LABEL is set */
       rc = SQLGetDescField( hIRDdesc,
                   colCount,
                   SQL_DESC_LABEL, /* record field */
                   desc_char, /* The result */
                   25,
                   NULL ); /* ignored */
       CHECK_HANDLE( SQL_HANDLE_STMT, hstmt, rc ) ;
       printf("  - IRD record descriptor field SQL_DESC_LABEL is %s\n",
            desc_char);
    } /* End of the for statement */
 


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