LIBPQ INTRO 03/12/94 日本語PostgreSQL 日本語PostgreSQL

説明

Libpq は Postgres へのプログラマインタフェイスです。Libpq はライブラリルーチンの集合で、Postgres のバックエンドに問い合わせを渡し、IPC チャネルからインスタンスを戻すことが可能です。

この文書のバージョンでは Cインタフェイスライブラリを説明します。この節の最後に Libpq を使ってどのようにプログラムを書くのかを示す3つの短いプログラムが含まれています。

Libpq アプリケーションのいくつかの例が次のディレクトリにあります。

../src/test/regress
../src/test/examples
../src/bin/psql
    

libpq を使うフロントエンドプログラムはヘッダファイル "libpq-fe.h" をインクルードして、 libpq ライブラリをリンクしなくてはなりません。

制御と初期化

データベースの名前をアプリケーションプログラムにハードコーディングしなくてもよいように、次の環境変数でデフォルト値をセットアップできます:

データベース接続関数

次のルーチンは Cプログラムからバックエンドへの接続を確立することを扱うものです。

PQsetdb

バックエンドへの新らしい接続を確立します。
PGconn *PQsetdb(char *pghost,
                char *pgport,
                char *pgoptions,
                char *pgtty,
                char *dbName); 
    
もし引数が NULL なら、関連する環境変数がチェックされます。もし環境変数もセットされていなければ、ハード的に実装されているデフォルトが使われます。

PQsetdb は常に有効な PGconn ポインタを返します。その接続で問い合わせが送られる前に、PQstatus (下記を参照してください)コマンドで接続が適切に行われたかどうかを確認してください。Libpq プログラマは PGconn の取り扱いには注意するべきです。PGconn の内容を取り出すには下のアクセサリ関数を使ってください。直接 PGconn 構造体のフィールドを参照することはさけましょう。それらは将来的に変更されることを仮定しています。

PQdb 接続のデータベース名を返します。
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

バックエンドへの接続を閉じます。また、PGconn 構造体に使われていたメモリを解放します。PQfinish が呼ばれた後に PGconn ポインタは使わないでください。
void PQfinish(PGconn *conn)
    

PQreset

バックエンドとの通信ポートをリセットします。この関数はバックエンドへの IPC ソケット接続を閉じて、同じバックエンドへの新しい接続を再度確立しようとします。
void PQreset(PGconn *conn)
    

問い合わせ実行関数

PQexec

問い合わせを Postgres に提出します。問い合わせが成功すると PGresult へのポインタを、そうでなければ NULL を返します。もし NULL が戻れば、PQerrorMessage でエラーの情報を得ることができます。
PGresult *PQexec(PGconn *conn,
                 char *query);
    
バックエンドからの問い合わせ結果を PGresult 構造体にカプセル化します。Libpq プログラマは PGresult の取り扱いには注意するべきです。問い合わせの結果を得るには、下記のアクセサリ関数を使ってください。PGresult 構造体のフィールドを直接参照することは避けましょう。それらは将来的に変更されることを仮定しています。

PQresultStatus

問い合わせの結果ステータスを返します。PQresultStatus は次の値のどれかを返します:
PGRES_EMPTY_QUERY,
PGRES_COMMAND_OK,  /* 問い合わせはコマンドでした */
PGRES_TUPLES_OK,  /* 問い合わせは成功してタプルが返されました */
PGRES_COPY_OUT, 
PGRES_COPY_IN,
PGRES_BAD_RESPONSE, /* 予期せぬ応答を受け取りました */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR
    

もし結果のステータスが PGRES_TUPLES_OK なら、問い合わせから戻ったタプルを次のルーチンで取り出すことができます。


PQntuples 問い合わせ結果のタプル(インスタンス)数を返します。
int PQntuples(PGresult *res);
    

PQcmdTuples INSERT、UPDATE、DELETE の問い合わせで影響されたタプル(インスタンス)の数を返します。
char *PQcmdTuples(PGresult *res);
    

PQnfields 問い合わせ結果のフィールド(属性)数を返します。
int PQnfields(PGresult *res);
    

PQfname 与えられたフィールド番号に関連するフィールド(属性)名を返します。フィールド番号は 0から始まります。
char *PQfname(PGresult *res,
             int field_index);
    

PQfnumber 与えられたフィールド名に関連するフィールド(属性)番号を返します。
int PQfnumber(PGresult *res,
             char* field_name);
    

PQftype 与えられたフィールド番号に関連したフィールドの型を返します。戻り値の整数はその型の内部コーディングになります。フィールド番号は 0から始まります。
Oid PQftype(PGresult *res,
            int field_num);
    

PQfsize 与えられたフィールド番号に関連するフィールドのサイズをバイト単位で返します。もし返ったサイズが -1 なら、そのフィールドは可変長フィールドです。フィールド番号は 0から始まります。
int2 PQfsize(PGresult *res,
             int field_index);
    

PQgetvalue フィールド(属性)の値を返します。ほとんどの問い合わせでPQgetvalue から戻る値は、その属性値のヌル終端の ASCII 文字列表現です。もし問い合わせが バイナリ カーソルの結果なら、PQgetvalue から戻る値は、バックエンドサーバの内部フォーマットの型のバイナリ表現となります。そのデータを正しい C の型にキャストや変換をするのはプログラマの責任となります。PQgetvalue の返す値はPGresult 構造体の一部の記憶領域を指すポインタです。PGresult 構造体の寿命よりも長く値を使おうとするなら、その値を明示的に他の記憶領域にコピーしてください。
char* PQgetvalue(PGresult *res,
                 int tup_num,
                 int field_num);
    

PQgetlength フィールド(属性)の長さをバイト単位で返します。もしフィールドがstruct varlena なら、ここで戻る長さはvarlena のサイズフィールドは含まれて いません。 つまり、4バイト足りません。
int PQgetlength(PGresult *res,
                int tup_num,
                int field_num);
    

PQgetisnull フィールドの NULL ステータスを返します。
int PQgetisnull(PGresult *res,
                int tup_num,
                int field_num);
    

PQcmdStatus

最後の問い合わせコマンドに関連するコマンドステータスを返します。
char *PQcmdStatus(PGresult *res);
    

PQoidStatus

もし最後の問い合わせが INSERT コマンドなら、挿入されたタプルのオブジェクトID の文字列が返ります。
char* PQoidStatus(PGresult *res);
    

PQprint

+ インテリジェントな方法ですべてのタプルを表示します。
psql プログラムがこの関数を出力に使っています。
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

PGresult に関連した記憶領域を解放します。問い合わせの結果は使わなくなった時に適切に解放されるべきです。これを行わないと、フロントエンドアプリケーションでメモリリークを起こすことがあります。渡される PQresult* は PQexec() から返った値にしてください。PQresult ポインタに対して PQclear() を呼ぶと、簡単にコアダンプしてしまいます。
void PQclear(PQresult *res);
    

Fast Path

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にセットしてください。argsnargs で関数への引数を指定します。

typedef struct {
    int len;
    int isint;
    union {
        int *ptr;	
	int integer;
    } u;
} PQArgBlock;
    

PQfn は常に有効な PGresult* を返します。その結果を使う前に resultStatus をチェックしてください。必要がなくなった時に、PGresult をPQclear で解放するのは呼び出した人の責任です。

非同期通知

Postgres はLISTENNOTIFY コマンドで非同期通知をサポートします。LISTEN コマンドでバックエンドは特定のリレーションへの興味を登録します。別のバックエンドでそのリレーション名で NOTIFY が実行されると、特定のリレーションにLISTENしているすべてのバックエンドは非同期に通知されます。他に通知者からリスナーへ送られる付加的な情報はありません。ですから、典型的には通信する必要のある実際のデータはリレーションを通して送られることになります。

Libpq アプリケーションは接続しているバックエンドが非同期通知を受け取ると、通知されます。しかし、バックエンドからフロントエンドへの通信は非同期ではありません。通知は他の問い合わせ結果の上に乗って来るのです。ですから、バックエンドの通知を受け取るためにはアプリケーションは空でもいいですから何か問い合わせを送らなくてはなりません。つまり、Libpq アプリケーションはバックエンドをポーリングして残っている通知情報がないかどうかを見なくてはなりません。問い合わせの後に、フロントエンドはPQNotifies を呼び出してバックエンドから通知データが来たかどうかを見ることができます。

PQNotifies

バックエンドからのまだ処理していない通知のリストを返します。もしバックエンドからの通知がなければ、NULL を返します。PQNotifies の動作はスタックのポップのようになります。PQNotifies から通知が戻ると、その通知は処理されたと考えられて、通知リストから除かれます。
PGnotify* PQNotifies(PGconn *conn);
    

2番目のサンプルプログラムに非同期通知の例があります。

COPY コマンドに関連した関数

Postgres のcopy コマンドには Libpq に使われているネットワーク接続に対しての読み書きのオプションがあります。ですから、関数がこのネットワーク接続に直接アクセスすることが必要で、そうすることで、この特徴の長所をフルに生かすことができるのです。

PQgetline

(バックエンドサーバから送られた) 改行で終端する文字列一行をlength サイズのバッファstring に読み込みます。fgets(3) のように、このルーチンはlength -1 文字までstring にコピーします。しかし、gets(3) のように、終端の改行をヌル文字に変換します。

PQgetline は最後に EOFを、一行全部読んだ時に 0を、バッファがいっぱいになってしまってもまだ終端の改行を読んでいないときに 1を返します。

アプリケーションは新しい行がバックエンドサーバがcopy コマンドの結果を送り終えたことを示す "\\." という文字になっているかどうかをチェックしなくてはなりません。そして、もしアプリケーションがlength -1 文字以上の行を読み込むことがあるようなら、アプリケーションはPQgetline の戻り値を注意深くチェックしなくてはなりません。

../src/bin/psql/psql.c
    
のコードには COPY プロトコルの正しい処理を行うルーチンが含まれています。
int PQgetline(PGconn *conn,
              char *string,
              int length)
    

PQputline

ヌル終端文字列string をバックエンドサーバに送ります。

アプリケーションはデータを送り終えたときには、 "\\." という文字列を明示的にバックエンドに送らなくてはなりません。
void PQputline(PGconn *conn,
               char *string);
    

PQendcopy

バックエンドと同期をとります。この関数はバックエンドが COPY を終えるまで待ちます。これはPQputline で最後の文字列をバックエンドに送った時と、PQgetline で最後の文字列をバックエンドから受け取った時の両方とも発行すべきです。発行しなければ、"out of sync" をバックエンドから受け取ることがあります。この関数から戻ると、バックエンドは次の問い合わせを受け取る準備ができています。

正常終了すると 0を、それ以外には 0以外が返ります。
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);
    

LIBPQ トレース関数

PQtrace

デバッグファイルストリームへのフロントエンド/バックエンドの通信のトレースを有効にします。
void PQtrace(PGconn *conn
             FILE *debug_port)
    

PQuntrace

PQtrace で始めたトレースを無効にします。
void PQuntrace(PGconn *conn)
    

ユーザ認証関数

もしユーザが適切な認証書を生成した(例えば、Kerberos チケットを入手した)なら、フロントエンド/バックエンドの認証プロセスは余計な仲介なしにPQexec で処理されます。conf(5) を参照してください) で決定されます。次のルーチンは何の意味もありませんので使わないでください。

fe_getauthname

ユーザが認証された名前を含むスタティックな空間へのポインタを返します。アプリケーションがこのルーチンをgetenv(3)getpwuid(3) の代わりに使うことが強く推奨されます。認証されたユーザ名が環境変数
USER の値や/etc/passwd のユーザエントリと違うことは大いにあるからです。
char *fe_getauthname(char* errorMessage)
    

fe_setauthsvc

Libpq に認証サービスとして、コンパイルされたデフォルトではなくname を使うように指定します。この値は典型的にはコマンドラインスイッチとして与えられます。
void fe_setauthsvc(char *name,
                   char* errorMessage)
    
認証要求からのエラーメッセージは errorMessage 引数に戻ります。

バグ

問い合わせバッファは 8192 バイトの長さですので、それ以上の長さの問い合わせは何も言わずに切られます。

サンプルプログラム


サンプルプログラム 1

/*
 * 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); */ }

サンプルプログラム 2

/*
 * 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);
}

サンプルプログラム 3

/*
 * 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);
}