PostgresのCOPYコマンドでは、 libpqが使っているネットワーク接続に対して読み込み、 あるいは書き込みを選ぶことができるようになっています。 そこでこのネットワーク接続に直接アクセスするための関数が必要になります。 もちろんアプリケーションもこの機能によって恩恵を受けるでしょう。
これらの関数は、PQexecまたは PQgetResultから、PGRES_COPY_OUT ないしPGRES_COPY_IN結果オブジェクトを受け取った後に のみ、実行すべきです。
PQgetline 改行で終端する文字列(バックエンドから送信されたもの)を長さ length のバッファ string に読み込みます。
int PQgetline(PGconn *conn, char *string, int length)fgets(3)と同様、このルーチンはバッファに最大 (length-1)個の文字をコピーしますが、終端の改行がNULL文字に置き換えられ る点では、gets(3)に似ています。 PQgetlineは、ファイルの終端では EOFを、行全体が読み込まれれば 0 を返します。 そしてまだ終端の改行が読み込まれていないうちにバッファがいっぱいになっ てしまった場合は 1 を返します。
アプリケーションは新しく読み込んだ行が、"\."という二文字であるかどうか 確認しなければなりません。この二文字は、COPY コマンドの結果をバックエ ンドサーバが送信し終えたことを示すものです。 アプリケーションには、仮に (length-1) 文字より長い行を受け取るようなこ とがあっても、間違いなく最終行("\.")を認識するような配慮が必要です (またたとえば長いデータの行の切れっぱしを、最終行と取りちがえないよう にもしてください)。 プログラム src/bin/psql/copy.c が、COPY プロトコルを正しく処理するルーチンを持っています。
PQgetlineAsync 改行で終端する行(バックエンドから送信されたもの)を、ブロッキ ングなしでバッファに読み込みます。
int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)PQgetlineと似ていますが、このルーチンは COPY コマ ンドのデータを非同期的に、つまりブロッキングなしで読み出さなければなら ないアプリケーションで使います。 COPY コマンドを発行し、そしてPGRES_COPY_OUTを受け取っ たら、アプリケーションはデータ終了の合図を受け取るまで PQconsumeInputと PQgetlineAsyncを呼び出し続けます。 PQgetlineと違い、このルーチンはデータ終了の検出に 対して責任を持ちます。 PQgetlineAsync が個々の呼び出しでデータを返すのは 次の場合です。ひとつはデータ行を、改行終端の完全な形で libpq の入力バッ ファから取り出すことができる場合。もう一つは、呼び出し側が用意したバッ ファでは受信データが長すぎて、収まりきらない場合です。 一方、このような長い行の残り部分が届かない限りは、何も返しません。
このルーチンは、コピーされるデータの終端を示すマークを認識すると -1 を、 また何もデータがなければ 0 を、そしてデータを返す場合はそのバイト数を 正の値で返します。 もし -1 が返されたら、呼び出し側は次にPQendcopyを を呼び出さなければなりません。それから通常の処理に戻ります。 返されるデータは改行文字までで、可能であれば行全体を一度に返します。 しかし呼び出し側が準備したバッファが少なすぎ、バックエンドから送られて くる行を保持しておくことができない場合には、部分ごとのデータを返します。 これは最後の1バイトが "\n" かどうかを確認すれば検 出できます。 なお、返される文字列は NULL 終端にはなっていません。 (終端 NULL をあとから付け加えるのであれば、(実際のバッファサイズ - 1) を bufsize として渡すようにしてください)
PQputline NULL 終端の文字列をバックエンドサーバに送信します。正常終了なら 0 を返 し、文字列を送信できなかった場合は EOF を返します。
int PQputline(PGconn *conn, const char *string);アプリケーションはデータ送信の完了をバックエンドに示すために、最終行で "\."の二文字を明示的に送信しなければなりません。
PQputnbytes NULL 終端されていない文字列をバックエンドサーバに送信します。正常終了 なら 0 を返し、文字列を送信できなかった場合は EOF を返します。
int PQputnbytes(PGconn *conn, const char *buffer, int nbytes);送信するバイト数を直接指定するので、データバッファが NULL 終端である必 要がない点を除けば、これは PQputline とまったく同 様のものです。
PQendcopy バックエンドと同期します。この関数はバックエンドがコピーを完了するの を待ちます。この関数は、PQputlineを使ったバック エンドへの文字列送信が完了した時点、あるいはPGgetline を使ったバックエンドからの文字列受信が完了した時点のいずれでも呼び出 すべきです。さもないと、バックエンドがフロントエンドとの「同期ずれ」 を起こしてしまうかもしれません。この関数から戻った時点で、バックエン ドの次の問い合わせを受ける準備が整います。同期が成功すれば 0 を、そう でなければ 0 以外の値を返します。
int PQendcopy(PGconn *conn);
一例です:
PQexec(conn, "create table foo (a int4, b char(16), d double precision)"); PQexec(conn, "copy foo from stdin"); PQputline(conn, "3\thello world\t4.5\n"); PQputline(conn,"4\tgoodbye world\t7.11\n"); ... PQputline(conn,"\\.\n"); PQendcopy(conn);
PQgetResultを使う場合、アプリケーションは PQgetlineを繰り返し呼び出して PGRES_COPY_OUTに応答し、最終行を見つけたら続いて PQendcopyを呼び出します。 それから、PQgetResultが NULL を返すまで、 PQgetResultのループに戻っておくべきです。 同じようにPGRES_COPY_INは連続した PQputlineで処理し、それから PQendcopyでしめくくった後に PQgetResultのループに戻ります。このようにすること で、ひとつづきのSQLコマンド群に含めたコピーイン/ア ウトコマンドを確実に、また正しく実行できるはずです。
比較的古いアプリケーションでは、コピーイン/アウトを PQexecで実行し、PQendcopyの 実行でトランザクションは完了する、と想定していることがよくあります。 これは文字列中のSQLが唯一コピーイン/アウトであっ たときにのみ正しく動作します。