C プログラムに SQL を埋め込む(プリプロセッサ ecpg を利用)

<--PostgreSQL メモへ戻る

SQL を C プログラムから呼び出すには,

 exec sql  xxx;
というように  "exec  sql"  というキーワードを用いる.

基本的にデータベースとのやりとり以外は通常の C プログラミングと何ら変わりはなく,
データベースとの

といった部分に上記の「exec  sql」を用いる. ただし,これらの命令を利用するため
 exec  sql  include sqlca ;
というヘッダの読み込み命令を書いておき,いったんプリプロセッサ ecpg を通す必要がある.
つまり,そのソースの拡張子を .pgc として
*.pgc ---(プリプロセッサecpg)---> *.c  ---(Cコンパイラ ) ---> バイナリ
という流れでアプリケーションを作成することになる.

データベースにデータを挿入する

データ挿入に際して ecpg ソースで利用する基本的な埋め込み命令の一覧を以下に示す.

exec sql connect to データベース名 user 所有者名 ;
 データベースに接続する
exec sql disconnect;
 データベースとの接続を切る
exec sql commit;
 データベースとのトランザクションを commit する
exec sql insert into 表の名前 (属性リスト) values (値リスト);
 指定された表にデータを挿入する
exec sql whenever sqlerror do 関数名;
 データベースとのやりとりでエラーが発生した
 場合に指定された関数を呼び出すよう設定する
 (エラーハンドラの設定)
exec sql whenever sqlerror continue;
 エラーハンドラを無効にする
exec sql rollback;
 トランザクションを rollback する

(例)データベース bookmark 上の表 Name_list にデータを挿入する ecpg プログラム(name_list_db.pgc

 1: /*******************************************************************************
 2: * 名前リストデータベース作成プログラム [2000/01/25 H.Aman]                    *
 3: *******************************************************************************/
 4: #include <stdio.h>
 5:
 6: /* ecpg ライブラリを使用するためのヘッダ */
 7: exec sql include sqlca;
 8:
 9: /* エラーハンドラ */
10: void error_exit(void); 
11:
12: int main(int argc, char* argv[]){
13:
14:    /* ホスト変数として ID と NAME を宣言 */
15:   exec sql begin declare section;
16:    int ID;
17:    char NAME[256];
18:    exec sql end declare section;
19:
20:    /*******  データベースに接続 *******/
21:    /* 何らかのエラーが発生した場合は関数 error_exit() をコールする */
22:    exec sql whenever sqlerror do error_exit();
23:    /* データベース bookmark へユーザ dbuser として接続する */
24:    exec sql connect to bookmark user dbuser;
25:
26:    /* コマンドライン引数としてデータベースへ登録するための *
27:     * 情報(id, name)を受け取る                                */
28:    if ( argc != 3 ){
29:       fprintf(stderr,"\n ID \n");
30:       return 0;
31:    }
32:    ID = atoi(argv[1]);
33:    strcpy(NAME,argv[2]);
34:
35:    /******** データベースへ情報の挿入 ***********/
36:    exec sql insert into Name_list (id, name) values (:ID,:NAME);
37:
38:
39:    /********  データベースへの接続を切り離す ********/
40:    exec sql commit;
41:    exec sql disconnect;
42:
43:    return 0;
44: }
45:
46: /* データベースとの通信でエラーが発生した場合の処理関数 */
47: void error_exit(void){
48:    fprintf(stderr, "PostgreSQL error : %d[%.*s]\n",
49:            sqlca.sqlcode, sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
50:
51:    exec sql whenever sqlerror continue;
52:    exec sql rollback;
53:    exec sql disconnect;
54:
55:    exit(1);
56: }

この例ではコマンドライン引数として登録情報を受け取り,それを表 Name_list へ挿入している.
ここでホスト変数の概念が登場する.
SQL を利用する側の言語(この場合は C 言語)をホスト言語といい,ホスト言語と SQL との間でデータを授受するための変数がホスト変数である.
この例の場合データベースへ id (整数)と name (文字列)を登録するため,ホスト変数として IDNAME を宣言している(15-18 行目).
これらはホスト言語側では通常の変数と何ら変わりなく利用できる(32,33行目).
SQL との間で用いる場合はコロン(:)を変数名の前に付ける(37行目).

これを次のようにしてコンパイルする.

$ ecpg name_list_db.pgc -I/usr/include/pgsql
$ gcc -o name_list_db name_list_db.c -I/usr/include/pgsql -lecpg

(見づらいでしょうが,/usr/include/pgsql の前についているのは i の大文字,
ecpg の前についているのは L の小文字です)

出来上がったバイナリ name_list_db を使ってデータベースにデータを挿入してみる.

$ ./name_list_db 96342601 Hirohisa_Aman
$ ./name_list_db 95102021 Shuichi_Ueda

psql を使って確認すると次のようになる.

$ psql -U dbuser bookmark

(途中略)

bookmark=> select * from name_list;

      id|name 
--------+-------------
96342601|Hirohisa_Aman
95102021|Shuichi_Ueda
(2 rows)

データベースからデータを取り出す

(対話形式の)psqlでは select 文によってデータの抽出が行なわれる. そこでは条件に適合しているすべてのデータが表のかたちで表示される.
しかしプログラムではホスト変数を介してデータを受け取らなくてはならず, いきなり表のかたちで出力されても処理できない.
したがって select 文で得られた表を保持しつつ,表中のデータを 1 行ずつ取り出すという手段をとる.

select 文によって得られた表は カーソル というポインタによって指定される. (表をファイルに見立てると, カーソルはそのファイルポインタのようなものと考えればよい.)

カーソルの宣言は

exec  sql  declare  カーソル名  cursor  for  select 文;
という書式でおこなう. そして
exec  sql  open  カーソル名;
としてカーソルをオープンする.

またカーソル内のデータを 1 行ずつ取り出すには fetch 文 を用いる.

exec  sql  fetch  in  カーソル名  into  ホスト変数リスト ;
fetch は,すべての行を読み終わると "not found" という例外を発生する仕組みになっている. したがって, この例外が発生した時のアクションを前もって指定する必要がある. (次の例の 31 行目参照).

(例)name_list_db で作成した名前リストを標準出力へ出力する ecpg プログラム (lookup_name_list.pgc

     1  /****************************************************
     2   * 名前リスト表示プログラム [2000/04/04 H.Aman] *
     3   ****************************************************/
     4  #include 
     5
     6  /* ecpg ライブラリを使用するためのヘッダ */
     7  exec sql include sqlca;
     8
     9  /* データベースとの通信でエラーが発生した場合の処理関数 */
    10  void error_exit(void); 
    11
    12  int main(int argc, char* argv[]){
    13    /* ホスト変数として ID と NAME を宣言 */
    14    exec sql begin declare section;
    15    int ID;
    16    char NAME[256];
    17    exec sql end declare section;
    18
    19    /******* カーソル nlcursor の宣言 *******/
    20    exec sql declare nlcursor cursor 
    21          for select id, name from Name_list;
    22
    23    /******* データベースに接続 *******/
    24    /* 何らかのエラーが発生した場合は関数 error_exit() をコールする */
    25    exec sql whenever sqlerror do error_exit();
    26    /* データベース bookmark へユーザ dbuser として接続する */
    27    exec sql connect to bookmark user dbuser;
    28
    29    /* すべての行を読み終わると not found になるので,*
    30     * while ループを break するよう指定する            */
    31    exec sql whenever not found do break;
    32
    33    /******* カーソルのオープン *******/
    34    exec sql open nlcursor;
    35
    36    while (1){
    37      /******* データベースからの情報の取り出し *******/
    38      exec sql fetch in nlcursor into :ID, :NAME;
    39      printf("id = %d : name = %s\n", ID, NAME);
    40    }
    41
    42    /******* カーソルのクローズ *******/
    43    exec sql close nlcursor;
    44
    45    /******** データベースへの接続を切り離す ********/
    46    exec sql commit;
    47    exec sql disconnect;
    48
    49    return 0;
    50  }
    51
    52
    53  /* データベースとの通信でエラーが発生した場合の処理関数 */
    54  void error_exit(void){
    55    fprintf(stderr, "PostgreSQL error : %d[%.*s]\n", 
    56            sqlca.sqlcode, sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    57
    58    exec sql whenever sqlerror continue;
    59    exec sql rollback;
    60    exec sql disconnect;
    61
    62    exit(1);
    63  }


(C) 2000 - 2004 Hirohisa AMAN <aman@cs.ehime-u.ac.jp>