SELECT INTO 文
1) Cygwinを起動します。

2) ソースを格納するディレクトリに移動
cd src でソースを格納するディレクトリに移動します。

3) ソース作成
カレントでviエディタを起動して test_sel.pgc を作成します。(Windowsのメモ帳などで作成してこのフォルダに保存してもよいです)内容は下です。
#include        <stdio.h>

static int check_sqlcode(long in_sqlcode);


int main(void)
{
        EXEC SQL BEGIN DECLARE SECTION;
        const char      * target = "mydb";
        const char      * user = "Owner";
        int             h_age;
        EXEC SQL END DECLARE SECTION;


        EXEC SQL CONNECT TO :target USER :user;

        if(sqlca.sqlcode != 0) {
                printf("error code = [%ld]\n", sqlca.sqlcode);
                return 1;
        }



        h_age = - 1;
        EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'chapp';
        printf("\n no.1  SELECT age INTO :h_age FROM neko WHERE name = 'chapp'\n");
        printf("age = [%d]\n", h_age);
        check_sqlcode(sqlca.sqlcode);


        h_age = - 1;
        EXEC SQL SELECT age INTO :h_age FROM neko;
        printf("\n no.2  SELECT age INTO :h_age FROM neko\n");
        printf("age = [%d]\n", h_age);
        check_sqlcode(sqlca.sqlcode);


        h_age = - 1;
        EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'yondaime';
        printf("\n no.3  SELECT age INTO :h_age FROM neko WHERE name = 'yondaime'\n");
        printf("age = [%d]\n", h_age);
        check_sqlcode(sqlca.sqlcode);


        h_age = - 1;
        EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'big';
        printf("\n no.4  SELECT age INTO :h_age FROM neko WHERE name = 'big'\n");
        printf("age = [%d]\n", h_age);
        check_sqlcode(sqlca.sqlcode);


        h_age = - 1;
        EXEC SQL SELECT COALESCE(age, 0) INTO :h_age FROM neko WHERE name = 'big';
        printf("\n no.5  SELECT COALESCE(age, 0) INTO :h_age FROM neko WHERE name = 'big'\n");
        printf("age = [%d]\n", h_age);
        check_sqlcode(sqlca.sqlcode);

        return 0;
}

static int check_sqlcode(long in_sqlcode)
{

        if(in_sqlcode == 0) {
                return 0;
        } else if(in_sqlcode == ECPG_TOO_MANY_MATCHES) {
                printf("ECPG_TOO_MANY_MATCHES error code = [%ld]\n", sqlca.sqlcode);
                return 0;
        } else if(in_sqlcode == ECPG_NOT_FOUND) {
                printf("ECPG_NOT_FOUND error code = [%ld]\n", sqlca.sqlcode);
                return 0;
        } else if(in_sqlcode == ECPG_MISSING_INDICATOR) {
                printf("ECPG_MISSING_INDICATOR error code = [%ld]\n", sqlca.sqlcode);
                return 1;
        } else {
                printf("error code = [%ld]\n", sqlca.sqlcode);
                return 1;
        }
}
4) ソースの説明
上のソースはSELECT INTO文のサンプルです。このプログラムを実行するとDBからデータを取得して画面に表示したりエラーメッセージを表示したりします。nekoというテーブルに少しづつ異なる方法でアクセスしてそれぞれの違いを理解します。予めテーブルとデータを用意しておきます。

static int check_sqlcode(long in_sqlcode);
はエラーをハンドリングするスタティック関数のプロトタイプ宣言です。関数本体はソースの最下部にあります。エラーの処理の仕方はいろいろです。これはあくまでも参考ですので、現場のサンプルソースをまねしてください。

EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION;
上の2行の間にホスト変数を定義します。この2行の他にも、EXEC SQL で始まるコードは全て埋め込みSQLです。これらのコードはC言語ではありません。このソースをgccでコンパイルするとエラーになります。これらのコードはまずプレコンパイラーを通してC言語に翻訳します。プリコンパイラーは「.pgc」ファイルから「.c」ファイルを生成します。ファイルの拡張子が「.pgc」であるということは、「ファイルはC言語でなく、C言語にするにはプリコンパイルが必要である」と言う事ことを意味します。

EXEC SQL CONNECT TO :target USER :user;
データベースに接続します。

if(sqlca.sqlcode != 0)
DB2が戻すエラーをハンドリングする。エラーがないと0が戻されます。他のエラーのハンドリングは関数check_sqlcodeにまとめました。

h_age = - 1;
ホスト変数を初期化します。どんな値で初期化してもかまいません。0で初期化することが多いようです。初期化しなくても問題ない場合が多いですが、そういうところを見る人がいるのでとりあえず初期化しておきます。

EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'chapp';
DB2にたいする問い合わせ SELECT age FROM neko WHERE name = 'chapp'; は結果として 4 を戻します。この結果の 4 をホスト変数h_age に格納します。

EXEC SQL SELECT age INTO :h_age FROM neko;
DB2にたいする問い合わせ SELECT age FROM nekoの結果は複数の行を戻します。SELECT INTO 文は結果が複数行の場合エラーになります。実際の開発でもこのエラーに対すハンドリングが必要になります。

EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'yondaime';
DB2にたいする問い合わせ SELECT age FROM neko WHERE name = 'yondaime';の結果は0行を戻します。SELECT INTO 文は結果が0行の場合エラーになります。実際の開発でもこのエラーに対すハンドリングが必要になります。

EXEC SQL SELECT age INTO :h_age FROM neko WHERE name = 'big';
DB2にたいする問い合わせ SELECT age FROM neko WHERE name = 'big';の結果は1行を戻しますが値はNULLです。SELECT INTO 文は結果の値がNULLの場合エラーになります。実際の開発ではNULLの値が戻されることが予想される場合は、NULL値を0等のほかの値に変換する関数を使った次の方法を使います。

EXEC SQL SELECT COALESCE(age, 0) INTO :h_age FROM neko WHERE name = 'big';
COALESCE(age, 0)はageがNULLの場合は0に置き換えます。select into 文はエラーとなりません。但しNULLと0は意味が違う場合が多いのでNULLを0に置き換えてしまっても問題ないか思慮が必要です。

5) プレコンパイル
ecpg コマンドでプレコンパイルします。ecpgはソースに埋め込まれたSQLをC言語に翻訳しc言語ファイルを生成します。
$ecpg -I/home/Owner/inc /home/Owner/src/test_sel.pgc
-Iはインクルードファイルのあるディレクトリを指示するオプションです。(この例では必要ありません)。

6) コンパイル
gccコマンドでコンパイル(&リンク)します。PostgreSQLを使う為のオプションが必要になります。
$ gcc -Wall -o /home/Owner/bin/test_sel /home/Owner/src/test_sel.c -lecpg -L/usr/local/pgsql/lib -I/home/Owner/inc
-lecpgはライブラリlibecpg.aをリンクすることを表します。
-L/usr/local/pgsql/lib はライブラリの場所をコンパイラ(リンカ)に知らせますが今の場合必要ないようです。
-I/home/Owner/incはユーザー定義のインクルードファイルの場所を指定します。

7) 実行
実行する前にcygserver、postgresを起動しておきます。



6節 埋め込みSQL   6節 埋め込みSQL