DB2 CLI は、結果セット内の列に関する情報 (データ・タイプ、サイズ、ポインターなど)、および SQL ステートメントのパラメーターを保管します。また、列およびパラメーターに対するアプリケーション・バッファーのバインドも、保管する必要があります。 記述子 は上記情報の論理視点であり、この情報を照会および更新するための 1 つの手段をアプリケーションに提供します。
多くの CLI 関数は記述子を利用しますが、アプリケーション自身は直接、記述子を操作する必要はありません。
たとえば、次のようにします。
データベース操作は記述子に対する直接アクセスを必要としてはいませんが、記述子による直接作業が、より効率的であったり、結果としてより簡単なコードとなる場合があります。たとえば、ある表から取り出される行を記述する記述子を使用すると、その後その表の中に挿入される行を記述することが可能になります。
記述子には次の 4 つのタイプがあります。
上記に示す 4 つのタイプの記述子の唯一の違いは、それらがどのように使用されるかにあります。記述子の利点の 1 つは、単一の記述子が多くの目的に使用できることです。たとえば、あるステートメントにある行記述子は別のステートメントでパラメーター記述子として使用することができます。
記述子が存在するようになるとすぐに、それはアプリケーション記述子かまたは実装記述子かのどちらかになります。記述子がまだデータベース操作で使用されていない場合でも、こうしたケースがあります。記述子が SQLAllocHandle() を使用して、アプリケーションで割り当てられる場合、それはアプリケーション記述子となります。
それぞれの記述子には、ヘッダー・フィールドとレコード・フィールドの両方があります。これらのフィールドが一緒になって、列またはパラメーターを完全に記述します。
それぞれのヘッダー・フィールドは各記述子の中で 1 回だけ出現します。このフィールドの 1 つを変更すると、すべての列またはパラメーターに影響します。
以下のヘッダー・フィールドの大部分は、ステートメント属性に対応しています。 SQLSetDescField() を使用して記述子のヘッダー・フィールドを設定することは、 SQLSetStmtAttr() を使用して対応するステートメント属性を設定するのと同じです。同じことが、SQLGetDescField() または SQLGetStmtAttr() を使用して情報を取り出す場合にもそのまま適用されます。アプリケーションに記述子ハンドルがすでに割り当てられているのでなければ、記述子ハンドルを割り当てて記述子呼び出しを使用するよりも、ステートメント属性呼び出しを使用する方が一層能率的です。
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 |
| ||
|
上記フィールドのそれぞれの詳細については、 SQLSetDescField() の 『ヘッダー・フィールド』を参照してください。
記述子のヘッダー・フィールド SQL_DESC_COUNT は、情報が入っている最大番号の記述子レコードの、1 を基数とする指標です。 DB2 CLI は、列またはパラメーターがバインドおよびアンバインドされるときに、自動的にこのフィールド (および記述子の物理サイズ) を更新します。記述子が最初に割り当てられるとき、SQL_DESC_COUNT の初期値は 0 です。
ゼロ個以上の記述子レコードが単一の記述子にあります。新しい列またはパラメーターがバインドされるとき、新しい記述子レコードがその記述子に追加されます。列またはパラメーターがアンバインドされるとき、その記述子レコードは除去されます。
表 8 には、記述子レコードにあるフィールドをリストしています。それぞれは 1 つの列またはパラメーターを記述し、各記述子レコード内で 1 回だけ出現します。
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 ポインターが適用できる箇所を示しています。
フィールド | ヌル値の意味 |
---|---|
SQL_DESC_DATA_PTR | レコードがアンバインドされています。 |
SQL_DESC_INDICATOR_PTR | (なし) |
SQL_DESC_OCTET_LENGTH_PTR (ARD および APD 専用) |
|
SQL_DESC_ARRAY_STATUS_PTR (複数行取り出し専用) | 複数行の取り出しで、行単位の診断情報の構成要素を返すのに失敗する。 |
SQL_DESC_ROWS_PROCESSED_PTR (複数行取り出し専用) | (なし) |
各記述子レコードの SQL_DESC_DATA_PTR フィールドは、パラメーター値 (APD の場合) または列の値 (ARD の場合) を含む変数を指しています。これは、ヌルを省略時値とする据え置きフィールドです。列またはパラメーターが一度バインドされると、それはパラメーターまたは列の値を指します。この時点で、記述子レコードはバインドされたことになります。
整合性検査は、アプリケーションが APD または ARD の SQL_DESC_DATA_PTR フィールドを設定するたびに、自動的に実行されます。検査では、種々のフィールドが互いに整合していること、および適切なデータ・タイプが指定されていることを確認します。
IPD フィールドの整合性検査を強制させるには、アプリケーションは IPD の SQL_DESC_DATA_PTR フィールドを設定します。この設定は整合性検査を強制する場合にのみ使用されます。値は保管されません。それで、 SQLGetDescField() または SQLGetDescRec() への呼び出しで取り出すことはできません。
整合性検査は、IRD では実行できません。
整合性検査に関する詳細については、 SQLSetDescRec() の 『整合性検査』を参照してください。
記述子は次の 2 つの方法のどちらかで割り当てられます。
暗黙的に割り当てられる記述子に対するハンドルを得るには、アプリケーションは、ステートメント・ハンドルおよび次に示す属性 値を渡して、 SQLGetStmtAttr() を呼び出します。
接続でのアプリケーション記述子は、データベースに接続されるどんな時にでも明示的に割り当てることができます。このことは、ステートメント・ハンドルおよび次に示す属性 値を渡して、 SQLSetStmtAttr() を呼び出すことにより行われます。
この場合は、暗黙的に割り当てられた記述子よりも、明示的に指定され割り当てられる記述子が使用されます。
明示的に割り当てられる記述子は、複数のステートメントに関連付けることが可能です。
アプリケーションの行記述子が割り当てられると、そのフィールドは SQLSetDescField() のセクション 『記述子フィールドの初期設定』に示されている初期値を受け取ります。 SQL_DESC_TYPE フィールドは SQL_DEFAULT にセットされます。それは、アプリケーションへの表示のためのデータベース・データの標準的な取り扱いを提供します。アプリケーションは、記述子レコードのフィールドを設定することにより、データの異なる取り扱いを指定することができます。
SQL_DESC_ARRAY_SIZE ヘッダー・フィールドの初期値は 1 です。複数行の取り出しを可能にするには、アプリケーションはこの値を ARD にセットし、行セット内の行数を指定します。スクロール可能カーソル内の行セットに関する詳細は、 スクロール可能カーソルを参照してください。
IRD のフィールドについては省略時値がありません。これらのフィールドはステートメントの準備または実行時に設定されます。
IPD 内の以下のフィールドは、 SQLPrepare() への呼び出しで自動的に移植されるまでは未定義のままです。
アプリケーションが準備済み 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 つの方法のどちらかにより解放されます。
暗黙的に割り当てられた記述子は、 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() を使用してそれぞれに設定されます。
記述子の個々のフィールドを設定するときは、 『記述子フィールドの設定順序』で定義されているステップに従ってください。いくつかのフィールドを設定すれば、 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() は、記述子フィールドを操作するコンサイス関数の一例です。
複数のフィールドを操作することに加えて、コンサイス関数は記述子ハンドルを明示的に指定しないで呼び出されます。それで、アプリケーションは、コンサイス関数を使用するために記述子ハンドルを取り出す必要さえもありません。
以下のタイプのコンサイス関数があります。
また、必要であれば、アプリケーションは記述子呼び出しを使用して、バインドの個々の細目を直接変更することができます。この場合、記述子ハンドルを取り出し、バインドを変更するために関数 SQLSetDescField() または SQLSetDescRec() を呼び出す必要があります。
/* ... */ 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 */