MinGW(gcc)でFirebirdを使ってみる(2)
今回は、InterBase APIを使用してFirebirdデータベースに非問い合わせ型SQL文を実行します。
非問い合わせ型SQL文の実行
入力パラメータのない非問い合わせ型SQL文の実行
前回はisc_dsql_execute_immediate()を使用してSQL文を実行しましたが、 今回はステートメントを使用してSQL文を発行します。
まず、isc_dsql_allocate_statement()でステートメント領域を確保します。 次にisc_dsql_prepare()でSQL文を設定し、isc_dsql_execute()でSQL文を発行します。
if (isc_start_transaction(status, &hTrn, 1, &hDb, 0, NULL)) { cerr << "Failed to start transaction. status=" << isc_sqlcode(status) << endl; return 1; } isc_stmt_handle hStmt = NULL; char* sql = "INSERT INTO sample1 VALUES ( 1, 'item' )"; if (isc_dsql_allocate_statement(status, &hDb, &hStmt)) { cerr << "Failed to allocate statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_dsql_prepare(status, &hTrn, &hStmt, 0, sql, 1, NULL)) { cerr << "Failed to prepare statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_dsql_execute(status, &hTrn, &hStmt, 1, NULL)) { cerr << "Failed to execute statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_commit_transaction(status, &hTrn)) { cerr << "Failed to commit transaction. status=" << isc_sqlcode(status) << endl; return 1; }
ソースコードの例:sample2.cpp (エラーリターン時の後処理等はしていませんのでご注意願います。)
これだけなら、isc_dsql_execute_immediate()を使用した方がコードが少なくてすみます。 こんどは、プレースホルダを利用する方法を記述します。
入力パラメータのある非問い合わせ型SQL文の実行
上記のSQLは値をSQL文に直接記述していました。しかし、値はプログラムで作成した値を設定 したいかも知れません。この場合、SQL文をプログラムで直接編集することもできますが、下記のように 値の部分はSQL文には"?"と記述し、プログラムで動的に設定することができます。変更される"?"は プレースホルダと呼びます。
char* sql = "INSERT INTO sample1 VALUES ( ?, ? )";
プレースホルダに値を関係付けるには、XSQLDA構造体を定義し、isc_dsql_describe_bind()を呼び出し、 本構造体に値を設定します。 XSQLDA構造体の必要なサイズはXSQLDA_LENGTH()マクロでパラメータ数を引数に呼び出すことで取得できます。
if (isc_start_transaction(status, &hTrn, 1, &hDb, 0, NULL)) { cerr << "Failed to start transaction. status=" << isc_sqlcode(status) << endl; return 1; } char* sql = "INSERT INTO sample1 VALUES ( ?, ? )"; XSQLDA* inDa = (XSQLDA*)new char[XSQLDA_LENGTH(2)]; inDa->version = SQLDA_VERSION1; inDa->sqln = 2; if (isc_dsql_allocate_statement(status, &hDb, &hStmt)) { cerr << "Failed to allocate statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_dsql_prepare(status, &hTrn, &hStmt, 0, sql, 1, NULL)) { cerr << "Failed to prepare statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_dsql_describe_bind(status, &hStmt, 1, inDa)) { cerr << "Failed to describe bind. status=" << isc_sqlcode(status) << endl; return 1; }
上記のようにSQL文に含まれるプレースホルダの数が明示的にわかる場合はよいですが、 共通的な関数等を作成する場合、SQL文がわからない場合もあります。
その場合は、最初のXSQLDA構造体の確保は適当な数を指定し、isc_dsql_describe_bind()実行後、 XSQLDA構造体のsqldに必要数が格納されるので、メモリを確保しなおして再度、 isc_dsql_describe_bind()を発行します。
if (inDa->sqld > inDa->sqln) { short sqld = inDa->sqld; delete [] inDa; inDa = (XSQLDA*)new char[XSQLDA_LENGTH(sqld)]; inDa->sqln = sqld; inDa->version = SQLDA_VERSION1; if (isc_dsql_describe_bind(status, &hStmt, 1, inDa)) { cerr << "Failed to describe bind. status=" << isc_sqlcode(status) << endl; return 1; } }
次にXSQLDA構造体に含まれるsqlvarにパラメータを設定します。sqlvarはXSQLVAR構造体が パラメータ数分確保されています。次の例では汎用的にエリアを確保した後に、確保したエリア に値を代入していますが、今回のようにSQL文が明示的にわかる場合は、直接、値を格納したエリアを設定してもかまいません。
sqltypeはテーブルカラムの属性にマッピングされています。SQL_VARYINGは、VARCHARの場合で、 この場合、sqldataの先頭2バイトはデータの長さが設定されているとみなされます。sqltypeが 1との論理積が1の場合はNULLを許すカラムになりインディケータ領域を確保します。NULLを設定する場合はインディケータを1にします。
また、値の設定部分のコードは、明示的にカラムの属性や桁数がわかっている前提で記述しています。 共通関数等で汎用的に作成するのであればsqltypeを判断して適切な処理をすればよいでしょう。
XSQLVAR *var; int i; for (i = 0, var = inDa->sqlvar; i < inDa->sqld; i++, var++) { switch (var->sqltype & ~1) { case SQL_VARYING: var->sqldata = (char *)new char[var->sqllen + 2]; break; default: var->sqldata = (char *)new char[var->sqllen]; break; } if (var->sqltype & 1) { var->sqlind = (short *)new short; *(var->sqlind) = 0; } } var = inDa->sqlvar; *(short*)(var->sqldata) = 2; var = inDa->sqlvar + 1; memset(var->sqldata, ' ', var->sqllen); strncpy(var->sqldata, "val", 3);
後は、isc_dsql_execute()を発行して、コミットすれば完了です。
if (isc_dsql_execute(status, &hTrn, &hStmt, 1, inDa)) { cerr << "Failed to execute statement. status=" << isc_sqlcode(status) << endl; return 1; } if (isc_commit_transaction(status, &hTrn)) { cerr << "Failed to commit transaction. status=" << isc_sqlcode(status) << endl; return 1; }
ソースコードの例:sample3.cpp (エラーリターン時の後処理等はしていませんのでご注意願います。)