31.6. ホスト変数の使用

項31.4では、埋め込みSQLプログラムでどのようにSQL文を実行するのかについて説明しました。 この文の中には固定値しか使用しないものや、ユーザが指定する値を文の中に挿入する手段を提供しないもの、問い合わせが返す値をプログラムで処理する手段を提供しないものがありました。 この種の文は実際のアプリケーションでは役に立ちません。 本節では、ホスト変数という単純な機構を使用した、Cプログラムと埋め込みSQL文との間でデータをやり取りする方法を詳細に説明します。 埋め込みSQLプログラムでは、SQL文をホスト言語となるCプログラムコードにおけるゲストguestsとみなします。 したがって、Cプログラムの変数はホスト変数と呼ばれます。

31.6.1. 概要

埋め込みSQLにおけるCプログラムとSQL文との間でのデータのやり取りは特に単純です。 値に適切な引用符を付与するといった、様々な複雑な処理を伴う、プログラムにデータを文中に貼り付けさせるという方法はなく、単にSQL文の中に、先頭にコロンを付けたC変数名を書くだけです。 以下に例を示します。

EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);

この文は、v1v2という2つのC変数を参照し、また、通常のSQL文字列リテラルも使用しています。 これは、使用できるデータの種類は1つだけという制限がないことを表しています。

SQL文内にCの変数を挿入するこの様式は、SQL文で値式が想定されている所であればどこでも動作します。

31.6.2. 宣言セクション

例えば問い合わせ内のパラメータとして、プログラムからデータベースへデータを渡す、もしくは、データベースからプログラムへデータを渡すためには、このようなデータを格納させる予定のC変数を、埋め込みSQLプリプロセッサが管理できるように、特殊な印のついたセクションで宣言する必要があります。

このセクションは以下で始まります。

EXEC SQL BEGIN DECLARE SECTION;

そして、以下で終わります。

EXEC SQL END DECLARE SECTION;

この行の間は、以下のような通常のC変数宣言でなければなりません。

int   x = 4;
char  foo[16], bar[16];

見て判るとおり、省略可能ですが、変数に初期値を代入することができます。 変数のスコープはプログラム内の宣言セクションの場所により決まります。 また、以下のような暗黙的に宣言セクションを生成する構文を使って変数を宣言することもできます。

EXEC SQL int i = 4;

プログラム内に複数の宣言セクションを持たせることができます。

また、宣言は普通のC変数としてそのまま出力ファイルに出力されます。 ですので、これらを再度宣言する必要はありません。 通常、SQLコマンドで使用する予定がない変数はこの特別なセクションの外側で宣言されます。

構造体やunionの定義もまた、DECLAREセクションの内側で表す必要があります。 さもないと、プリプロセッサはその定義が不明であるために、これらの型を扱うことができません。

31.6.3. 様々なホスト変数の型

ホスト変数として配列、typedef、構造体、ポインタを使用することができます。 さらに、ECPGでのみ存在する特別なホスト変数の型が存在します。

ホスト変数の例を以下に示します。

配列

配列宣言でもっともよく使われるものの1つは、文字配列の割当てです。

EXEC SQL BEGIN DECLARE SECTION;
    char str[50];
EXEC SQL END DECLARE SECTION;

配列長には開発者が注意しなければならないことに注意してください。 このホスト変数を49文字より長い文字列を返す問い合わせの対象変数に使用した場合、バッファオーバーフローが発生します。

Typedefs

typedefキーワードを使用して、新しい型を既存の型に割り当てることができます。

EXEC SQL BEGIN DECLARE SECTION;
    typedef char mychartype[40];
    typedef long serial_t;
EXEC SQL END DECLARE SECTION;

以下も使用できることに注意してください。

EXEC SQL TYPE serial_t IS long;

この宣言は宣言セクションの一部である必要はありません。

ポインタ

よく使用される型へのポインタを宣言することができます。 しかし、自動割当てなしで対象変数としてポインタを使用することはできない点に注意してください。 自動割当てに関する情報は項31.10を参照してください。

EXEC SQL BEGIN DECLARE SECTION;
    int   *intp;
    char **charp;
EXEC SQL END DECLARE SECTION;
Special types of variables

ECPGには、SQLサーバからのデータを簡単に取り扱うための特別な型がいくつか含まれています。 例えば、varcharnumericdatetimestampinterval型に対するサポートが実装されています。 項31.8 には、これらの型を扱う基本関数があります。 これらを使用すれば、例えば時間間隔をタイムスタンプに加算するためだけにSQLサーバに問い合わせを送信する必要がなくなります。

特別なVARCHAR型は各変数を名前付きのstructに変換します。

VARCHAR var[180];

という宣言は、以下のように変換されます。

struct varchar_var { int len; char arr[180]; } var;

この構造体は、varchar型のSQLデータとのインタフェースに適しています。

31.6.4. SELECT INTOFETCH INTO

ここまでで、プログラムで生成したデータをSQLコマンドに渡すことができるようになりました。 しかし、どのように問い合わせの結果を取り出すのでしょうか? この目的のために、埋め込みSQLでは、通常のSELECTFETCHを派生した、特殊なコマンドを提供しています。 これらのコマンドは特別なINTO句を持ち、ここで返された値をどのホスト変数に格納すればよいかを指定します。

以下にサンプルを示します。

/*
 * 以下のテーブルを前提としています。
 * CREATE TABLE test1 (a int, b varchar(50));
 */

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;

INTO句が選択リストとFROM句の間に現れます。 選択リスト内の要素数とINTO直後のリスト(目的リストとも呼ばれます)の要素数は等しくなければなりません。

以下にFETCHコマンドの使用例を示します。

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;

 ...

do {
    ...
    EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
    ...
} while (...);

ここでは、INTO句が通常の全ての句の後ろに現れています。

これらの手法は両方とも、一度に1行のみを取り出すことができます。 複数の行を含む可能性がある結果集合を処理する必要がある場合は、2番目の例で示したようにカーソルを使用する必要があります。

31.6.5. 指示子

上の例ではNUJLL値を扱いません。 実際、取り出し例では、もしデータベースからNULL値が取り出された場合にはエラーが発生します。 データベースへNULL値を渡す、または、データベースからNULL値を取り出すためには、第二のホスト変数仕様をデータを格納するホスト変数それぞれに追加しなければなりません。 第二のホスト変数は指示子と呼ばれ、データがNULLかどうかを表すフラグが含まれます。 NULLの場合、実際のホスト変数の値は無視されます。 以下に、NULL値の取り出しを正しく扱う例を示します。

EXEC SQL BEGIN DECLARE SECTION;
VARCHAR val;
int val_ind;
EXEC SQL END DECLARE SECTION:

 ...

EXEC SQL SELECT b INTO :val :val_ind FROM test1;

値がNULLでなければ、指示子変数val_indは0となります。 値がNULLならば、負の値となります。

指示子は他の機能を持ちます。 指示子の値が正ならば、値がNULLではありませんが、ホスト変数に格納する際に一部削除されたことを示します。