ファイヤープロジェクト
接続と切断
2004-05-08T22:00+09:00   matsu
接続のための記述を通してecpgによるコーディングの基本について調査してみた.
ecpgはC/C++ソースコードに埋め込まれたSQLをC/C++コードに置き換えてくれる.ecpgは以下のような文をecpgへの指示文と解釈する.
EXEC SQL ... ;
ここではこれを便宜上EXEC SQL文と呼ぶ.「...」の部分にさまざまなものが記述され,セミコロンで終了する.EXEC SQL文は以下のように小文字でもよい.
exec sql ... ;
EXEC SQL文は一行で完結するものや,開始行と終了行を指定するものになっていて複数行のセクションを構成するものがある.以下は埋め込みSQLで使用する変数を宣言するセクションである.
EXEC SQL BEGIN DECLARE SECTION;
  int hoge;
  double fuga;
EXEC SQL END DECLARE SECTION;
変数hoge,fugaは埋め込みSQLの宣言セクション内にあり,別のEXEC SQL文で変数として使用できる.
DBに接続して切断する動作をecpgを使用して記述したサンプルを以下に示す(※).
これを実行ファイルにするにはまずecpgにかけ,出力されるファイルをgccにかける.Makefileは大体こんな感じになる.
ECPG=/usr/lib/postgresql/bin/ecpg
LIBS=-lecpg -lpq
INCLUDE=-I/usr/include/postgresql
CFLAGS=-DDEBUG_ON -Wall

connect: connect.c
	gcc -o connect $(INCLUDE) $(LIBS) $(CFLAGS) $?

connect.c: connect.pcg
	$(ECPG) $?

clean:
	rm connect connect.c
このようにgccにかける際は以下を付ける.
-lecpg -lpq -I/usr/include/postgresql
以下実行結果.
$> ./connect  -d tcp:postgresql://dbserver/firstdb -u matsu -p hogefuga
[10538]: ECPGdebug: set to 1
[10538]: ECPGconnect: opening database firstdb on dbserver port <DEFAULT> for user matsu
[10538]: ecpg_finish: Connection dbConnection closed.
さらにDBに接続できない状況での実行結果.
$> ./connect  -d tcp:postgresql://odin/firstdb -u matsu -p hogefuga
[10585]: ECPGdebug: set to 1
[10585]: ECPGconnect: opening database firstdb on odin port <DEFAULT> for user matsu
[10585]: ecpg_finish: Connection dbConnection closed.
[10585]: connect: could not open database firstdb on odin port <DEFAULT> for user matsu in line 39
        could not connect to server: Connection timed out
        Is the server running on host "odin" and accepting
        TCP/IP connections on port 5432?

[10585]: raising sqlcode -402 in line 39, 'Could not connect to database firstdb in line 39.'.
Could not connect to database firstdb in line 39.
failed to connect db
※ だいぶ後になって気づいたのだが,サフィックスは普通pgcらしい.この頁で間違えて,以降の頁でもずっとpcgと記述してしまっている...たぶん当時の私は何故かecpgの「cpg」の部分とpgcのどちらでもないものを選んでしまったのだろう...?
ecpgでは以下の関数呼び出しによって,ecpgが生成するコードの動きがデバッグ文として出力される.
void            ECPGdebug(int, FILE *);
第一引数に0以外を指定すると,第二引数のファイルストリームにデバッグ文が出力される.これはどういったSQL文が生成されたかとか,どういう順番で発行されたかを追跡するのに便利である.サンプルではコンパイル時の-DDEBUG_ONの指定の有無によってデバッグ出力有無を指定できるようにした.
/* デバッグ設定 */
#ifdef DEBUG_ON
#define DEBUG_FLAG 1
#else
#define DEBUG_FLAG 0
#endif
#define DEBUG_STREAM stderr

...省略...

  /* ECPGデバッグ文出力設定 */
  ECPGdebug(DEBUG_FLAG, DEBUG_STREAM);
通常のC/C++部分で使用する変数を埋め込みSQL部分で使用するには,その変数をEXEC SQLで宣言する必要がある.
EXEC SQL BEGIN DECLARE SECTION;
char *db;
char *user;
char *passwd;
EXEC SQL END DECLARE SECTION;
セクション内は通常のC/C++の宣言と同様で,ecpgによって処理されたあともC/C++における意味はそのままであるが,ecpgによって埋め込みSQLで使用される変数として認識される.このセクションで宣言した変数は,埋め込みSQLでは
:変数名
とすることで指定できる.具体例は次節で出て来る.
サンプルでは以下で接続を行っている.
EXEC SQL CONNECT TO :db AS dbConnection user :user USING :passwd;
:dbと:userは前節で触れた宣言セクションで宣言した変数である.接続のEXEC SQL文の書式を以下に示す.
EXEC SQL CONNECT TO target [AS connection-name] [USER user-name];
各項目の説明を以下に示す.
target
接続先.以下のような書式をもつ文字列リテラルか文字列変数への参照である(サンプルは後者).
  • dbname[@hostname][:port]
  • tcp:postgresql://hostname[:port][/dbname][?options]
  • unix:postgresql://hostname[:port][/dbname][?options]
  • DEFAULT
connection-name
接続名.同時に複数接続する際にそれぞれを区別するために使用する.
user-name
ユーザ名.以下のような書式をとる.
  • username
  • username/password
  • username IDENTIFIED BY password
  • username USING password
サンプルでは以下で切断を行っている.
EXEC SQL DISCONNECT dbConnection;
EXEC SQL DISCONNECTの書式を以下に示す.
EXEC SQL DISCONNECT [connection];
connectionは接続名で省略可能である.省略した場合は現在の接続(最後に接続したもの?)を切断する.connectonには接続名の他に以下を取る.
  • DEFAULT
  • CURRENT
  • ALL
...DEFAULTとCURRENTはよくわからないが,個人的にはALLを指定するのでなければ,どの接続を切断するかは常に明示すべきだと思う.
ecpgが埋め込みSQLをC/C++コードに変換してくれるといっても,SQL実行後のエラーコードチェックは必要である.ecpgが生成するコードにはsqlca.hへのinclude文がある.
$> head connect.c 
/* Processed by ecpg (3.1.1) */
/* These include files are added by the preprocessor */
#include <ecpgtype.h>
#include <ecpglib.h>
#include <ecpgerrno.h>
#include <sqlca.h>
#line 1 "connect.pcg"
/* End of automatic include section */
...省略...
sqlca.hによりsqlcaというものを参照できるようになる.sqlcaにはsqlcodeとsqlstateという二つのフィールドがあり(※),sqlca.sqlcodeとかsqlca.sqlstateなどと書くことによってSQL実行後のエラーコードチェックができる.
long            sqlcode;
char            sqlstate[5];
ただし,sqlcodeはもはや標準ではなく,sqlstateの使用が推奨されているらしいが,サンプルではsqlcodeを使用してしまった.
if (sqlca.sqlcode != 0) {
  fprintf (stderr, "%s\n", sqlca.sqlerrm.sqlerrmc);
  fprintf (stderr, "failed to connect db\n");
  exit(EXIT_FAILURE);
}
sqlca.sqlcodeは正常時は0,負なら致命的なエラー,正なら検索結果などが0件の場合である.また,上のように,エラーが発生した場合,エラーメッセージを以下で参照できる.
sqlca.sqlerrm.sqlerrmc
先のサンプルの実行結果の
Could not connect to database firstdb in line 37.
はsqlca.sqlerrm.sqlerrmcの値を出力したものである.sqlstateは文字列だが,構造を持っている.
  1. クラス
  2. クラス
  3. クラス内の特定条件
  4. クラス内の特定条件
  5. クラス内の特定条件
クラスが00ならば正常である.PostgreSQL エラーコード
※ sqlcaと書くことによりsqlca_t構造体の実体が取得できる.で,sqlca_t構造体にsqlcodeとsqlstateという二つのフィールドがある.
struct sqlca_t *ECPGget_sqlca(void);

#ifndef POSTGRES_ECPG_INTERNAL
#define sqlca (*ECPGget_sqlca())
#endif
matsu(C)
Since 2002
Mail to matsu