MinGW(gcc)でFirebirdを使ってみる(3)
今回は、InterBase APIを使用してFirebirdデータベースに問い合わせ型SQL文を実行します。
問い合わせ型SQL文の実行
入力パラメータのある問い合わせ型SQL文の実行
前回は非問い合わせ型SQL文の発行をとりあげましたが、今回は問い合わせ型SQL文について記載していきます。 入力パラメータのないものは、前回を参照していただければと思います。
入力パラメータのためのコードは非問い合わせ型SQL文の場合と同じです。 今度は問い合わせを行いますので、その結果セットを受け取れるようにします。結果セットも入力パラメータと同様に XSQLDA構造体を使用します。
まず、XSQLDA構造体をローカルで定義し、sqlnを1とにします。 次にisc_dsql_prepare()のパラメータにXSQLDA構造体を指定しています。 isc_dsql_prepare()はSQL文が何個のカラムを返すかをsqldに設定します。 次に、sqldを指定してXSQLDA構造体を確保し、sqlnに最初に取得したsqldを設定します。 確保したXSQLDA構造体を指定してisc_dsql_describe()を呼び出します。
if (isc_start_transaction(status, &hTrn, 1, &hDb, 0, NULL)) { cerr << "Failed to start transaction. status=" << isc_sqlcode(status) << endl; return 1; } char* sql = "SELECT * FROM sample1 WHERE ITEM1 = ?"; XSQLDA* inDa = (XSQLDA*)new char[XSQLDA_LENGTH(1)]; inDa->version = SQLDA_VERSION1; inDa->sqln = 1; XSQLDA paraDa; paraDa.version = SQLDA_VERSION1; paraDa.sqln = 1; 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, ¶Da)) { 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; } 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; } } 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; XSQLDA* outDa = (XSQLDA*)new char[XSQLDA_LENGTH(paraDa.sqld)]; outDa->version = SQLDA_VERSION1; outDa->sqln = paraDa.sqld; if (isc_dsql_describe(status, &hStmt, 1, outDa)) { cerr << "Failed to describe. status=" << isc_sqlcode(status) << endl; return 1; }
ここで、お気づきかと思いますが、入力パラメータのためのXSQLDA構造体は最初に適当に割り当てた後に、isc_dsql_describe_bind() を呼び出し、正確なsqldを指定して、XSQLDA構造体を再割り当てしisc_dsql_describe_bind()を再度呼び出しました。
出力用のXSQLDA構造体も同様にすることができます。 この場合はisc_dsql_prepare()には出力用のXSQLDA構造体を指定する必要はありません。 isc_dsql_describe()も呼び出されるとsqldを正確な値で再設定します。
(ここでは値の設定部分以外は、SQL文がわからなくても利用可能なコードとしていますが、 明示的に分かっている場合は、固定で設定できます)
値を格納するエリアを確保し、上記で確保したXSQLDA構造体に設定します。
for (i = 0, var = outDa->sqlvar; i < outDa->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; } }
isc_dsql_execute()でSQL文を実行します。
if (isc_dsql_execute(status, &hTrn, &hStmt, 1, inDa)) { cerr << "Failed to execute statement. status=" << isc_sqlcode(status) << endl; return 1; }
結果セットを取り出すため、isc_dsql_fetch()を呼び出します。isc_dsql_fetch()は問い合わせした結果を 1件づつ上記で確保した出力用のエリアに値を格納します。返す結果がなくなった時は100を返します。 後はタイプに従って値をとりだすことができます。例では標準出力に値をプリントしています。
int w_len; string w_str; int w_row = 0; long fetch_stat; while ((fetch_stat = isc_dsql_fetch(status, &hStmt, 1, outDa)) == 0) { cout << "== Row Index: " << w_row++ << "============" << endl; for (i = 0, var = outDa->sqlvar; i < outDa->sqld; i++, var++) { cout << " Column Index: " << i << endl; switch (var->sqltype & ~1) { case SQL_VARYING: cout << " type: SQL_VARYING:" << var->sqltype << endl; if ((var->sqltype & 1) && (*(var->sqlind) == -1)) { cout << " data: NULL" << endl; } else { w_len = *(short*)var->sqldata; w_str.assign(var->sqldata + 2, w_len); cout << " data: " << w_str << endl; } break; case SQL_TEXT: cout << " type: SQL_TEXT:" << var->sqltype << endl; if ((var->sqltype & 1) && (*(var->sqlind) == -1)) { cout << " data: NULL" << endl; } else { w_len = var->sqllen; w_str.assign(var->sqldata, w_len); cout << " data: " << w_str << endl; } break; case SQL_LONG: cout << " type: SQL_LONG:" << var->sqltype << endl; if ((var->sqltype & 1) && (*(var->sqlind) == -1)) { cout << " data: NULL" << endl; } else { cout << " data: " << *(long*)var->sqldata << endl; } break; case SQL_SHORT: cout << " type: SQL_SHORT:" << var->sqltype << endl; if ((var->sqltype & 1) && (*(var->sqlind) == -1)) { cout << " data: NULL" << endl; } else { cout << " data: " << *(short*)var->sqldata << endl; } break; default: cout << " type: " << var->sqltype << endl; cout << " data: Not implement." << endl; break; } } } if (fetch_stat != 100L) { cerr << "Failed to fetch. 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; }
ソースコードの例:sample4.cpp (エラーリターン時の後処理等はしていませんのでご注意願います。)
サンプルプログラムの実行
上記サンプルを実行した場合の出力結果が以下です。 ITME1が 2の行が2件あった時の出力例となります。
== Row Index: 0============ Column Index: 0 type: SQL_SHORT:501 data: 2 Column Index: 1 type: SQL_TEXT:453 data: val == Row Index: 1============ Column Index: 0 type: SQL_SHORT:501 data: 2 Column Index: 1 type: SQL_TEXT:453 data: val