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