PostgreSQLに対して、ECPGで以下のSQL実行を行い、SQL実行エラー時のみそのSQL実行のデバッグログを出力する簡単なプログラム例を示します。
- ローカルで稼働中のPostgreSQLに対して、DBユーザtestuserでデータベースtestdbに接続する。
- (もし未作成であれば) テーブルtesttblを作成する。テーブルtesttblは、主キーであるinteger型の1カラムnumだけを持つ。
- プログラムの引数で指定された整数値をテーブルtesttblにINSERTする。
- SQL実行がエラーになった場合は、そのSQL実行のデバッグログを標準エラー出力に出力する。
これらを実現するECPGプログラムの簡単な例は下記です。
debug.pgc
#include <stdio.h>
#include <stdlib.h>
/*
* SQL実行でエラーが発生したら、デバッグログを出力のための関数として
* output_debug()を呼び出すようにグローバルに宣言する。
*/
static void output_debug(void);
EXEC SQL WHENEVER SQLERROR CALL output_debug();
/*
* デバッグログを一時保存するメモリストリーム関連の変数を宣言する。
*/
static char *debug_buf;
static size_t debug_bufsize;
static FILE *debug_fp;
/*
* デバッグログを一時保存するメモリストリームをクローズする関数を宣言する。
*/
static void free_debug_buf(void);
/*
* ECPGの各内部関数を実行する前に、それまでにメモリストリームに
* 一時保存されていたデバッグログをクリアする。これにより、
* メモリストリームには、これから実行する各内部関数で生成された
* デバッグログのみが一時保存される。
*
* 今回のコード例ではECPGconnect()、ECPGdo()、ECPGprepare()、
* ECPGtrans()、ECPGdisconnect()のみに 対応しており、必要に応じて
* 他の内部関数についても対応しなければならない可能性がある。
*/
#define ECPGconnect(a, b, c, d, e, f, g) \
do { \
rewind(debug_fp); \
ECPGconnect(a, b, c, d, e, f, g); \
} while (0)
#define ECPGdo(a, b, c, d, e, f, g, ...) \
do { \
rewind(debug_fp); \
ECPGdo(a, b, c, d, e, f, g, __VA_ARGS__); \
} while (0)
#define ECPGprepare(a, b, c, d, e) \
do { \
rewind(debug_fp); \
ECPGprepare(a, b, c, d, e); \
} while (0)
#define ECPGtrans(a, b, c) \
do { \
rewind(debug_fp); \
ECPGtrans(a, b, c); \
} while (0)
#define ECPGdisconnect(a, b) \
do { \
rewind(debug_fp); \
ECPGdisconnect(a, b); \
} while (0)
/*
* このプログラムのメイン関数。
* テーブルtesttblにINSERTするレコードの整数値を第1引数で指定できる。
*/
int
main(int argc, char *argv[])
{
/*
* ECPGで使うローカル変数を宣言する。
*/
EXEC SQL BEGIN DECLARE SECTION;
char *sql = "INSERT INTO testtbl VALUES (?)";
int num = 0;
EXEC SQL END DECLARE SECTION;
/*
* 第1引数が指定された場合は、引数の整数値をINSERTする。
*/
if (argc > 1)
num = atoi(argv[1]);
/*
* デバッグログを一時保存するメモリストリームをオープンする。
*/
debug_fp = open_memstream(&debug_buf, &debug_bufsize);
if (!debug_fp)
{
fprintf(stderr, "could not open dynamic memory buffer stream\n");
exit(1);
}
/*
* デバッグログの生成を有効化する。
*/
ECPGdebug(1, debug_fp);
/*
* ローカル内で稼働中のPostgreSQLに対して、DBユーザtestuserで
* データベースtestdbに接続する。
*/
EXEC SQL CONNECT TO testdb@localhost:5432 USER testuser;
/*
* もし未作成であれば、テーブルtesttblを作成する。
*/
EXEC SQL CREATE TABLE IF NOT EXISTS testtbl (num integer primary key);
/*
* 動的SQLを使って、テーブルtesttblにレコードを1件INSERTする。
*/
EXEC SQL PREPARE teststmt FROM :sql;
EXEC SQL EXECUTE teststmt USING :num;
/*
* トランザクションを完了する。
*/
EXEC SQL COMMIT;
/*
* PostgreSQLへの接続をクローズする。
*/
EXEC SQL DISCONNECT;
/*
* デバッグログを一時保存するメモリストリームをクローズする。
*/
free_debug_buf();
return 0;
}
/*
* SQL実行でエラーが発生したときに呼び出される、
* デバッグログを出力するための関数。
*/
static void
output_debug(void)
{
/*
* メモリストリームに一時保存されているデバッグログを標準エラー出力に
* 出力する。
*/
fflush(debug_fp);
fprintf(stderr, "%s\n", debug_buf);
/*
* デバッグログを一時保存するメモリストリームをクローズする。
*/
free_debug_buf();
exit(1);
}
/*
* デバッグログを一時保存するメモリストリームをクローズする関数。
*/
static void
free_debug_buf(void)
{
fclose(debug_fp);
free(debug_buf);
}
このECPGプログラムは以下のとおりコンパイルします。以下では、PostgreSQLのインストール先を/opt/pgsqlディレクトリとします。
$ ecpg debug.pgc
$ gcc -o debug debug.c -I/opt/pgsql/include -L/opt/pgsql/lib -lecpg
プログラムの実行例は以下のとおりです。
$ ./debug 1
$ ./debug 2
$ ./debug 2
[67125]: ecpg_execute on line 114: query: INSERT INTO testtbl VALUES ($1); with 1 parameter(s) on connection testdb
[67125]: ecpg_execute on line 114: using PQexecPrepared for "INSERT INTO testtbl VALUES ($1)"
[67125]: ecpg_free_params on line 114: parameter 1 = 2
[67125]: ecpg_check_PQresult on line 114: bad response - ERROR: duplicate key value violates unique constraint "testtbl_pkey"
DETAIL: Key (num)=(2) already exists.
CONTEXT: unnamed portal with parameters: $1 = '2'
[67125]: raising sqlstate 23505 (sqlcode -403): duplicate key value violates unique constraint "testtbl_pkey" on line 114