以下を含むサンプルプログラムが、ソースコード配布物内のsrc/test/examplesディレクトリにあります。
例34.1 libpq サンプルプログラム 1
/*
* src/test/examples/testlibpq.c
*
*
* testlibpq.c
*
* C言語PostgreSQLフロントエンドライブラリlibpqの試験
*/
#include <stdio.h>
#include <stdlib.h>
#include "libpq-fe.h"
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
int nFields;
int i,
j;
/*
* ユーザがコマンドラインでパラメータを提供した場合、conninfo文字列として使用する。
* 提供されない場合はデフォルトでdbname=postgresを使用する。
* その他の接続パラメータについては環境変数やデフォルトを使用する。
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* データベースとの接続を確立する */
conn = PQconnectdb(conninfo);
/* バックエンドとの接続確立に成功したかを確認する */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* 悪意のユーザによる乗っ取りを防ぐように常に安全なサーチパスを設定 */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* メモリリークを避けるため、必要なくなったときにはいつでもPGresultを
* PQclearすべき
*/
PQclear(res);
/*
* この試験ケースではカーソルを使用する。
* そのため、トランザクションブロック内で実行する必要がある。
* すべてを単一の"select * from pg_database"というPQexec()で行うこと
* も可能だが、例としては簡単過ぎる。
*/
/* トランザクションブロックを開始する。 */
res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/*
* データベースのシステムカタログpg_databaseから行を取り出す。
*/
res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
res = PQexec(conn, "FETCH ALL in myportal");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/* まず属性名を表示する。 */
nFields = PQnfields(res);
for (i = 0; i < nFields; i++)
printf("%-15s", PQfname(res, i));
printf("\n\n");
/* そして行を表示する。 */
for (i = 0; i < PQntuples(res); i++)
{
for (j = 0; j < nFields; j++)
printf("%-15s", PQgetvalue(res, i, j));
printf("\n");
}
PQclear(res);
/* ポータルを閉ざす。ここではエラーチェックは省略した… */
res = PQexec(conn, "CLOSE myportal");
PQclear(res);
/* トランザクションを終了する */
res = PQexec(conn, "END");
PQclear(res);
/* データベースとの接続を閉じ、後始末を行う。 */
PQfinish(conn);
return 0;
}
例34.2 libpq サンプルプログラム 2
/*
* src/test/examples/testlibpq2.c
*
*
* testlibpq2.c
* 非同期通知インタフェースの試験
*
* このプログラムを起動し、別ウィンドウからpsqlを使用して以下を実行してください。
* NOTIFY TBL2;
* 4回繰り返すとこのプログラムは終了します。
*
* もう少し凝りたければ、以下を実施してください。
* 以下のコマンド(src/test/examples/testlibpq2.sqlで提供)でデータベースを作成します。
*
* CREATE SCHEMA TESTLIBPQ2;
* SET search_path = TESTLIBPQ2;
* CREATE TABLE TBL1 (i int4);
* CREATE TABLE TBL2 (i int4);
* CREATE RULE r1 AS ON INSERT TO TBL1 DO
* (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2);
*
* Start this program, then from psql do this four times:
*
* INSERT INTO TESTLIBPQ2.TBL1 VALUES (10);
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "libpq-fe.h"
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
PGnotify *notify;
int nnotifies;
/*
* ユーザがコマンドラインでパラメータを提供した場合、conninfo文字列として使用する。
* 提供されない場合はデフォルトでdbname=postgresを使用する。
* その他の接続パラメータについては環境変数やデフォルトを使用する。
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* データベースとの接続を確立する。 */
conn = PQconnectdb(conninfo);
/* バックエンドとの接続確立に成功したかを確認する */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* 悪意のユーザによる乗っ取りを防ぐように常に安全なサーチパスを設定 */
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
/*
* Should PQclear PGresult whenever it is no longer needed to avoid memory
* leaks
*/
PQclear(res);
/*
* LISTENコマンドを発行して、INSERTルールによる通知を有効にする。
*/
res = PQexec(conn, "LISTEN TBL2");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/* 4回通知を受けたら終了する。 */
nnotifies = 0;
while (nnotifies < 4)
{
/*
* その接続で何かが起こるまで待機する。ここでは入力待ちのために
* select(2)を使用する。poll()や類似機能を使用することも可能
* である。
*/
int sock;
fd_set input_mask;
sock = PQsocket(conn);
if (sock < 0)
break; /* 発生してはならない。 */
FD_ZERO(&input_mask);
FD_SET(sock, &input_mask);
if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0)
{
fprintf(stderr, "select() failed: %s\n", strerror(errno));
exit_nicely(conn);
}
/* ここで入力を確認する。 */
PQconsumeInput(conn);
while ((notify = PQnotifies(conn)) != NULL)
{
fprintf(stderr,
"ASYNC NOTIFY of '%s' received from backend PID %d\n",
notify->relname, notify->be_pid);
PQfreemem(notify);
nnotifies++;
PQconsumeInput(conn);
}
}
fprintf(stderr, "Done.\n");
/* データベースとの接続を閉じ、後始末を行う。 */
PQfinish(conn);
return 0;
}
例34.3 libpq サンプルプログラム 3
/*
* src/test/examples/testlibpq3.c
*
*
* testlibpq3.c
* 行以外のパラメータとバイナリI/Oの試験。
*
* 実行前に、以下のコマンド(src/test/examples/testlibpq3.sqlで提供)を使用して
* データベースを作成してください。
*
* CREATE SCHEMA testlibpq3;
* SET search_path = testlibpq3;
* CREATE TABLE test1 (i int4, t text, b bytea);
* INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004');
* INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000');
*
* 以下の出力が想定されます。
*
* tuple 0: got
* i = (4 bytes) 1
* t = (11 bytes) 'joe's place'
* b = (5 bytes) \000\001\002\003\004
*
* tuple 0: got
* i = (4 bytes) 2
* t = (8 bytes) 'ho there'
* b = (5 bytes) \004\003\002\001\000
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include "libpq-fe.h"
/* ntohl/htonl用 */
#include <netinet/in.h>
#include <arpa/inet.h>
static void
exit_nicely(PGconn *conn)
{
PQfinish(conn);
exit(1);
}
/*
* この関数は上のコメントで定義したテーブルからバイナリフォーマットでフェッチした
* クエリ結果を表示します。
* main() 関数が2度使うので、結果を分割します。
*/
static void
show_binary_results(PGresult *res)
{
int i,
j;
int i_fnum,
t_fnum,
b_fnum;
/* 結果中の列オーダーの仮定を嫌うので PQfnumber を利用する */
/* PQfnumber */
i_fnum = PQfnumber(res, "i");
t_fnum = PQfnumber(res, "t");
b_fnum = PQfnumber(res, "b");
for (i = 0; i < PQntuples(res); i++)
{
char *iptr;
char *tptr;
char *bptr;
int blen;
int ival;
/* 列の値を取得(NULLを出来る限り無視) */
iptr = PQgetvalue(res, i, i_fnum);
tptr = PQgetvalue(res, i, t_fnum);
bptr = PQgetvalue(res, i, b_fnum);
/*
* INT4のバイナリ表現はネットワークバイトオーダーによる。
* よって、ローカルバイトオーダーに合わせた方が良い。
*/
ival = ntohl(*((uint32_t *) iptr));
/*
* TEXT型のバイナリ表現も同様にテキスト。
* 更にlibpqはその最後にゼロバイトを付与するので、
* C言語の文字列として単純に扱うことができる。
*
* BYTEA のバイト表現はバイトの集まりである。
* null 埋め込みを含むのでフィールド長に注意を払わなければいけない。
*/
blen = PQgetlength(res, i, b_fnum);
printf("tuple %d: got\n", i);
printf(" i = (%d bytes) %d\n",
PQgetlength(res, i, i_fnum), ival);
printf(" t = (%d bytes) '%s'\n",
PQgetlength(res, i, t_fnum), tptr);
printf(" b = (%d bytes) ", blen);
for (j = 0; j < blen; j++)
printf("\\%03o", bptr[j]);
printf("\n\n");
}
}
int
main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;
const char *paramValues[1];
int paramLengths[1];
int paramFormats[1];
uint32_t binaryIntVal;
/*
* ユーザがコマンドラインでパラメータを提供した場合、conninfo文字列として使用する。
* 提供されない場合はデフォルトでdbname=postgresを使用する。
* その他の接続パラメータについては環境変数やデフォルトを使用する。
*/
if (argc > 1)
conninfo = argv[1];
else
conninfo = "dbname = postgres";
/* データベースとの接続を確立する */
conn = PQconnectdb(conninfo);
/* バックエンドとの接続確立に成功したかを確認する */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s",
PQerrorMessage(conn));
exit_nicely(conn);
}
/* 悪意のユーザによる乗っ取りを防ぐように常に安全なサーチパスを設定 */
res = PQexec(conn, "SET search_path = testlibpq3");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
PQclear(res);
/*
* このプログラムのポイントは、行外パラメータを持つPQexecParams()の使用方法、
* および、データのバイナリ転送を示すことである。
*
* この最初の例はパラメータをテキストとして渡す。
* しかし結果はバイナリフォーマットで受ける。
* 行外パラメータを使うことで、データがテキストであっても引用符付けや
* エスケープ処理といった多くの長たらしいゴミをなくすことができる。
* パラメータ値内部の引用符に対して特殊な処理を行う必要がないことに注目して
* ほしい。
*/
/* 以下が行外パラメータの値である。 */
paramValues[0] = "joe's place";
res = PQexecParams(conn,
"SELECT * FROM test1 WHERE t = $1",
1, /* パラメータは1つ。 */
NULL, /* バックエンドにパラメータの型を推測させる。 */
paramValues,
NULL, /* テキストのため、パラメータ長は不要。 */
NULL, /* デフォルトですべてのパラメータはテキスト。 */
1); /* バイナリ結果を要求。 */
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
show_binary_results(res);
PQclear(res);
/*
* 2つ目の例は、バイナリフォームの中で整数値パラメータを渡す。
* そして再びバイナリフォームで結果を受け取る。
*
* バックエンドにパラメータタイプを推測させていると PQexecParams に伝えるが、
* クエリテキストの中にパラメータシンボルを入れることによって 強制的に決定する。
* これはバイナリパラメータに送るときに安全で良い大きさである。
*/
/* 整数値 "2" をネットワークバイトオーダーに変換 */
binaryIntVal = htonl((uint32_t) 2);
/* PQexecParams 用にパラメータ配列をセットする */
paramValues[0] = (char *) &binaryIntVal;
paramLengths[0] = sizeof(binaryIntVal);
paramFormats[0] = 1; /* バイナリ */
res = PQexecParams(conn,
"SELECT * FROM test1 WHERE i = $1::int4",
1, /* パラメータは1つ */
NULL, /* バックエンドにパラメータの型を推測させる。 */
paramValues,
paramLengths,
paramFormats,
1); /* バイナリ結果を要求。 */
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn));
PQclear(res);
exit_nicely(conn);
}
show_binary_results(res);
PQclear(res);
/* データベースとの接続を閉じ、後始末を行う。 */
PQfinish(conn);
return 0;
}