Libpq は Postgres へのプログラマインタフェイスです。Libpq はライブラリルーチンの集合で、Postgres のバックエンドに問い合わせを渡し、IPC チャネルからインスタンスを戻すことが可能です。
この文書のバージョンでは Cインタフェイスライブラリを説明します。この節の最後に Libpq を使ってどのようにプログラムを書くのかを示す3つの短いプログラムが含まれています。
Libpq アプリケーションのいくつかの例が次のディレクトリにあります。
../src/test/regress
../src/test/examples
../src/bin/psql
libpq を使うフロントエンドプログラムはヘッダファイル "libpq-fe.h" をインクルードして、 libpq ライブラリをリンクしなくてはなりません。
データベースの名前をアプリケーションプログラムにハードコーディングしなくてもよいように、次の環境変数でデフォルト値をセットアップできます:
次の環境変数は Postgres のセッションでのユーザレベルでのデフォルトの振舞いを指定するのに使うことができます:
次の環境変数はPostgres のセッションでの内部的なデフォルトの振舞いを指定するのに使うことができます:
これらの環境変数についての情報はset(l) マニュアルページを参照してください。
次のルーチンは Cプログラムからバックエンドへの接続を確立することを扱うものです。
PQsetdb
PGconn *PQsetdb(char *pghost,
char *pgport,
char *pgoptions,
char *pgtty,
char *dbName);
もし引数が NULL なら、関連する環境変数がチェックされます。もし環境変数もセットされていなければ、ハード的に実装されているデフォルトが使われます。
char *PQdb(PGconn *conn)
PQhost
接続のホスト名を返します。
char *PQhost(PGconn *conn)
PQoptions
接続に使われている実行時オプションを返します。
char *PQoptions(PGconn *conn)
PQport
接続のポートを返します。
char *PQport(PGconn *conn)
PQtty
接続のデバッグ出力先を返します。
char *PQtty(PGconn *conn)
PQstatus
接続ステータスを返します。ステータスは CONNECTION_OK もしくは CONNECTION_BAD になります。
ConnStatusType *PQstatus(PGconn *conn)
PQerrorMessage
接続に関連するエラーメッセージを返します。
char *PQerrorMessage(PGconn* conn);
PQfinish
void PQfinish(PGconn *conn)
void PQreset(PGconn *conn)
PQexec
PGresult *PQexec(PGconn *conn,
char *query);
バックエンドからの問い合わせ結果を PGresult 構造体にカプセル化します。Libpq プログラマは PGresult の取り扱いには注意するべきです。問い合わせの結果を得るには、下記のアクセサリ関数を使ってください。PGresult 構造体のフィールドを直接参照することは避けましょう。それらは将来的に変更されることを仮定しています。
PGRES_EMPTY_QUERY,
PGRES_COMMAND_OK, /* 問い合わせはコマンドでした */
PGRES_TUPLES_OK, /* 問い合わせは成功してタプルが返されました */
PGRES_COPY_OUT,
PGRES_COPY_IN,
PGRES_BAD_RESPONSE, /* 予期せぬ応答を受け取りました */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR
int PQntuples(PGresult *res);
char *PQcmdTuples(PGresult *res);
int PQnfields(PGresult *res);
char *PQfname(PGresult *res,
int field_index);
int PQfnumber(PGresult *res,
char* field_name);
Oid PQftype(PGresult *res,
int field_num);
int2 PQfsize(PGresult *res,
int field_index);
char* PQgetvalue(PGresult *res,
int tup_num,
int field_num);
int PQgetlength(PGresult *res,
int tup_num,
int field_num);
int PQgetisnull(PGresult *res,
int tup_num,
int field_num);
PQcmdStatus
char *PQcmdStatus(PGresult *res);
char* PQoidStatus(PGresult *res);
void PQprint(
FILE* fout, /* output stream */
PGresult* res, /* query results */
PQprintOpt *ps /* option structure */
);
PQprintOpt は下に定義される typedef された構造体です。.(Ctypedef struct _PQprintOpt { bool header; /* print table headings and row count */ bool align; /* fill align the fields */ bool standard; /* old brain dead format (needs align) */ bool html3; /* output html3+ tables */ bool expanded; /* expand tables */ bool pager; /* use pager if needed */ char *fieldSep; /* field separator */ char *caption; /* html table caption (or NULL) */ char **fieldName; /* null terminated array of field names (or NULL) */} PQprintOpt;
.LP PQclear
void PQclear(PQresult *res);
Postgres はバックエンドへの関数の呼び出しに "fast path" インタフェイスを提供します。これはシステム内部への蓋で、潜在的なセキュリティホールとなり得ます。通常ユーザはこの特色は使わない方がよいでしょう。
PGresult* PQfn(PGconn* conn,
int fnid,
int *result_buf,
int *result_len,
int result_is_int,
PQArgBlock *args,
int nargs);
fnid 引数は、実行される関数のオブジェクトID です。result_buf は戻り値を格納するバッファです。この関数を呼び出す人は、戻り値を格納する充分な空間をアロケートしておかなくてはなりません。戻り値の長さがresult_len に示される領域に戻ります。もし戻り値が整数なら、result_is_int を 1に、そうでないなら、 0にセットしてください。args とnargs で関数への引数を指定します。
typedef struct {
int len;
int isint;
union {
int *ptr;
int integer;
} u;
} PQArgBlock;
PQfn は常に有効な PGresult* を返します。その結果を使う前に resultStatus をチェックしてください。必要がなくなった時に、PGresult をPQclear で解放するのは呼び出した人の責任です。
Postgres はLISTEN とNOTIFY コマンドで非同期通知をサポートします。LISTEN コマンドでバックエンドは特定のリレーションへの興味を登録します。別のバックエンドでそのリレーション名で NOTIFY が実行されると、特定のリレーションにLISTENしているすべてのバックエンドは非同期に通知されます。他に通知者からリスナーへ送られる付加的な情報はありません。ですから、典型的には通信する必要のある実際のデータはリレーションを通して送られることになります。
Libpq アプリケーションは接続しているバックエンドが非同期通知を受け取ると、通知されます。しかし、バックエンドからフロントエンドへの通信は非同期ではありません。通知は他の問い合わせ結果の上に乗って来るのです。ですから、バックエンドの通知を受け取るためにはアプリケーションは空でもいいですから何か問い合わせを送らなくてはなりません。つまり、Libpq アプリケーションはバックエンドをポーリングして残っている通知情報がないかどうかを見なくてはなりません。問い合わせの後に、フロントエンドはPQNotifies を呼び出してバックエンドから通知データが来たかどうかを見ることができます。
PQNotifies
PGnotify* PQNotifies(PGconn *conn);
2番目のサンプルプログラムに非同期通知の例があります。
Postgres のcopy コマンドには Libpq に使われているネットワーク接続に対しての読み書きのオプションがあります。ですから、関数がこのネットワーク接続に直接アクセスすることが必要で、そうすることで、この特徴の長所をフルに生かすことができるのです。
PQgetline
../src/bin/psql/psql.c
のコードには COPY プロトコルの正しい処理を行うルーチンが含まれています。
int PQgetline(PGconn *conn,
char *string,
int length)
void PQputline(PGconn *conn,
char *string);
int PQendcopy(PGconn *conn);
例としては:
PQexec(conn, create table foo (a int4, b char16, d float8));
PQexec(conn, copy foo from stdin);
PQputline(conn, 3<TAB>hello world<TAB>4.5\n);
PQputline(conn,4<TAB>goodbye world<TAB>7.11\n);
...
PQputline(conn,\\.\n);
PQendcopy(conn);
PQtrace
void PQtrace(PGconn *conn
FILE *debug_port)
void PQuntrace(PGconn *conn)
もしユーザが適切な認証書を生成した(例えば、Kerberos チケットを入手した)なら、フロントエンド/バックエンドの認証プロセスは余計な仲介なしにPQexec で処理されます。conf(5) を参照してください) で決定されます。次のルーチンは何の意味もありませんので使わないでください。
fe_getauthname
char *fe_getauthname(char* errorMessage)
fe_setauthsvc
void fe_setauthsvc(char *name,
char* errorMessage)
認証要求からのエラーメッセージは errorMessage 引数に戻ります。
問い合わせバッファは 8192 バイトの長さですので、それ以上の長さの問い合わせは何も言わずに切られます。
/* * testlibpq.c * Postgres の C バージョンのフロントエンドライブラリ Libpq のテスト * * */ #include <stdio.h> #include libpq-fe.h
void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); }
main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j;
/* FILE *debug; */
PGconn* conn; PGresult* res;
/* まず、バックエンドとの接続のパラメータをセットします。 もしパラメータが NULL なら、システムは環境変数を参照して 適当なデフォルト値にしようとします。もしそれもできなければ、 ハードコーディングされた定数を使います */ pghost = NULL; /* バックエンドサーバのホスト名 */ pgport = NULL; /* バックエンドサーバのポート番号 */ pgoptions = NULL; /* バックエンドサーバのスタートアップオプション */ pgtty = NULL; /* バックエンドサーバのデバッグ出力先 */ dbName = template1;
/* データベースに接続します */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
/* バックエンドへの接続が成功したかどうかチェックします */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,データベース'%s'への接続に失敗しました。\\n, dbName); fprintf(stderr,%s,PQerrorMessage(conn)); exit_nicely(conn); }
/* debug = fopen(/tmp/trace.out,w); */ /* PQtrace(conn, debug); */
/* トランザクションブロックを開始します */ res = PQexec(conn,BEGIN); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,BEGINコマンドに失敗しました\\n); PQclear(res); exit_nicely(conn); } /* メモリリークを防ぐために、使わなくなった PGresult を PQclear しておきます */ PQclear(res);
/* データベースのシステムカタログ pg_database からインスタンスを取り出します */ res = PQexec(conn,DECLARE mycursor CURSOR FOR select * from pg_database); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,DECLARE CURSOR コマンドに失敗しました。\\n); PQclear(res); exit_nicely(conn); } PQclear(res);
res = PQexec(conn,FETCH ALL in mycursor); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,FETCH ALL コマンドがタプルを適切に返しませんでした。\\n); 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 mycursor); PQclear(res);
/* トランザクションをコミットします */ res = PQexec(conn, COMMIT); PQclear(res);
/* データベースへの接続を閉じてクリーンアップします */ PQfinish(conn);
/* fclose(debug); */ }
/* * testlibpq2.c * 非同期通知インタフェイスのテスト * 次のようにデータベースを作ってください:
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];
* そしてこのプログラムをスタートさせます * プログラムがスタートしたら次のようにします
INSERT INTO TBL1 values (10);
* * */ #include <stdio.h> #include libpq-fe.h
void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); }
main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j;
PGconn* conn; PGresult* res; PGnotify* notify;
/* まず、バックエンドとの接続のパラメータをセットします。 もしパラメータが NULL なら、システムは環境変数を参照して 適当なデフォルト値にしようとします。もしそれもできなければ、 ハードコーディングされた定数を使います */ pghost = NULL; /* バックエンドサーバのホスト名 */ pgport = NULL; /* バックエンドサーバのポート番号 */ pgoptions = NULL; /* バックエンドサーバのスタートアップオプション */ pgtty = NULL; /* バックエンドサーバのデバッグ出力先 */ dbName = getenv(USER); /* これをテストのデータベース名に変えてください */
/* データベースに接続します */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
/* バックエンドへの接続が成功したかどうかチェックします */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,データベース'%s'への接続に失敗しました。\\n, dbName); fprintf(stderr,%s,PQerrorMessage(conn)); exit_nicely(conn); }
res = PQexec(conn, LISTEN TBL2); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,LISTEN コマンドに失敗しました。\\n); PQclear(res); exit_nicely(conn); } /* メモリリークを防ぐために、使わなくなった PGresult を PQclear しておきます */ PQclear(res);
while (1) { /* 非同期通知は問い合わせの結果として戻るだけです */ /* 空の問い合わせを送ることもできます。*/ res = PQexec(conn, ); /* printf(res->status = %s\\n, pgresStatus[PQresultStatus(res)]); */ /* 非同期通知をチェックします */ notify = PQnotifies(conn); if (notify) { fprintf(stderr, '%s' への非同期通知をバックエンド(pid '%d')から受け取りました。\\n, notify->relname, notify->be_pid); free(notify); break; } PQclear(res); } /* データベースへの接続を閉じてクリーンアップします */ PQfinish(conn);
}
/* * testlibpq3.c * Postgres の C バージョンのフロントエンドライブラリ Libpq のテスト * バイナリカーソルのテスト * * * 次のようにデータベースを作ってください: CREATE TABLE test1 (i int4, d float4, p polygon);
INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
このような出力になるはずです:
tuple 0: got i = (4 bytes) 1, d = (4 bytes) 3.567000, p = (4 bytes) 2 points boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000) tuple 1: got i = (4 bytes) 2, d = (4 bytes) 89.050003, p = (4 bytes) 2 points boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
* */ #include <stdio.h> #include libpq-fe.h #include utils/geo-decls.h /* for the POLYGON type */
void exit_nicely(PGconn* conn) { PQfinish(conn); exit(1); }
main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int nFields; int i,j; int i_fnum, d_fnum, p_fnum;
PGconn* conn; PGresult* res;
/* まず、バックエンドとの接続のパラメータをセットします。 もしパラメータが NULL なら、システムは環境変数を参照して 適当なデフォルト値にしようとします。もしそれもできなければ、 ハードコーディングされた定数を使います */ pghost = NULL; /* バックエンドサーバのホスト名 */ pgport = NULL; /* バックエンドサーバのポート番号 */ pgoptions = NULL; /* バックエンドサーバのスタートアップオプション */ pgtty = NULL; /* バックエンドサーバのデバッグ出力先 */ dbName = getenv(USER); /* これをテストのデータベース名に変えてください */
/* データベースに接続します */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
/* バックエンドへの接続が成功したかどうかチェックします */ if (PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,データベース'%s'への接続に失敗しました。\\n, dbName); fprintf(stderr,%s,PQerrorMessage(conn)); exit_nicely(conn); }
/* トランザクションブロックを開始します */ res = PQexec(conn,BEGIN); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,BEGINコマンドに失敗しました\\n); PQclear(res); exit_nicely(conn); } /* メモリリークを防ぐために、使わなくなった PGresult を PQclear しておきます */ PQclear(res);
/* データベースのシステムカタログ pg_database からインスタンスを取り出します */ res = PQexec(conn,DECLARE mycursor BINARY CURSOR FOR select * from test1); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr,DECLARE CURSOR コマンドに失敗しました。\\n); PQclear(res); exit_nicely(conn); } PQclear(res);
res = PQexec(conn,FETCH ALL in mycursor); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,FETCH ALL コマンドがタプルを適切に返しませんでした。\\n); PQclear(res); exit_nicely(conn); } i_fnum = PQfnumber(res,i); d_fnum = PQfnumber(res,d); p_fnum = PQfnumber(res,p); for (i=0;i<3;i++) { printf(type[%d] = %d, size[%d] = %d\\n, i, PQftype(res,i), i, PQfsize(res,i)); } for (i=0; i < PQntuples(res); i++) { int *ival; float *dval; int plen; POLYGON* pval; /* これを知っている 3つのフィールドにします */ ival = (int*)PQgetvalue(res,i,i_fnum); dval = (float*)PQgetvalue(res,i,d_fnum); plen = PQgetlength(res,i,p_fnum);
/* plen にはlength フィールドの長さが含まれていないので、 VARHDSZ を足す必要があります */ pval = (POLYGON*) malloc(plen + VARHDRSZ); pval->size = plen; memmove((char*)&pval->npts, PQgetvalue(res,i,p_fnum), plen); printf(tuple %d: got\\n, i); printf( i = (%d bytes) %d,\\n, PQgetlength(res,i,i_fnum), *ival); printf( d = (%d bytes) %f,\\n, PQgetlength(res,i,d_fnum), *dval); printf( p = (%d bytes) %d points \\tboundbox = (hi=%f/%f, lo = %f,%f)\\n, PQgetlength(res,i,d_fnum), pval->npts, pval->boundbox.xh, pval->boundbox.yh, pval->boundbox.xl, pval->boundbox.yl); }
PQclear(res); /* カーソルを閉じます */ res = PQexec(conn, CLOSE mycursor); PQclear(res);
/* トランザクションをコミットします */ res = PQexec(conn, COMMIT); PQclear(res);
/* データベースへの接続を閉じてクリーンアップします */ PQfinish(conn);
}