PostgreSQL の COPY コマンドでは、libpq が使っているネットワーク接続に対して読み込み、あるいは書き込みを選ぶことができるようになっています。 そこでアプリケーションがこの機能によって恩恵を受けることができるように、このネットワーク接続に直接アクセスするための関数が必要になります。
これらの関数は、PQexec または PQgetResult から、PGRES_COPY_OUT または PGRES_COPY_IN 結果オブジェクトを受け取った後にのみ、実行すべきです。
PQgetline 改行で終端する文字列 (バックエンドから送信されたもの) を長さ length のバッファ string に読み込みます。
int PQgetline(PGconn *conn, char *string, int length)
fgets と同様、このルーチンはバッファに最大 (length-1) 個の文字をコピーしますが、終端の改行がゼロバイトに置き換えられる点では、gets に似ています。PQgetline は、入力の終端では EOF を、行全体が読み込まれれば 0 を返します。 そしてまだ終端の改行が読み込まれていないうちにバッファがいっぱいになってしまった場合は1を返します。
アプリケーションは新しく読み込んだ行が、\. という2文字であるかどうか確認しなければいけません。 この2文字は、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 の個々の呼び出しでは、次の場合にデータを返します。 1つはデータ行を、改行終端の完全な形で libpq の入力バッファから取り出すことができる場合、もう1つは、呼び出し側が用意したバッファでは受信データが長すぎて、収まりきらない場合です。 一方、行の残り部分が届かない限りは、何も返しません。
このルーチンは、コピーされるデータの終端を示すマークを認識すると -1 を、また何もデータがなければ 0 を、そしてデータを返す場合はそのバイト数を正の値で返します。 もし -1 が返されたら、呼び出し側は次に PQendcopy を呼び出さなければいけません。 それから通常の処理に戻ります。 返されるデータは改行文字までです。 可能であれば行全体を一度に返します。 しかし呼び出し側が準備したバッファが少なすぎ、バックエンドから送られてくる行を保持しておくことができない場合には、分割されたデータを返します。 これは最後の 1 バイトが \n かどうかを確認すれば検出できます。 なお、返される文字列は NULL 終端にはなっていません (終端NULLをあとから付け加えるのであれば、(実際のバッファサイズ- 1)を bufsize として渡すようにしてください)。
PQputline NULL終端の文字列をバックエンドサーバに送信します。 正常終了なら 0 を返し、文字列を送信できなかった場合は EOF を返します。
int PQputline(PGconn *conn, const char *string);
アプリケーションはデータ送信の完了をバックエンドに示すために、最終行で \. の2文字を明示的に送信しなければならないことに注意して下さい。
PQputnbytes NULL 終端されていない文字列をバックエンドサーバに送信します。 正常終了なら 0 を返し、文字列を送信できなかった場合は EOF を返します。
int PQputnbytes(PGconn *conn, const char *buffer, int nbytes);
送信するバイト数を直接指定するので、データバッファが NULL 終端である必要がない点を除けば、これは PQputline とまったく同様のものです。
PQendcopy バックエンドと同期します。 この関数はバックエンドがコピーを完了するのを待ちます。 この関数は、PQputline を使ったバックエンドへの文字列送信が完了した時点、あるいは PGgetline を使ったバックエンドからの文字列受信が完了した時点のいずれでも呼び出すべきです。これを発行しないと、バックエンドはフロントエンドとの "同期がずれた" 状態になってしまいます。 この関数から戻った時点で、バックエンドの次の SQL コマンドを受ける準備が整います。正常に終了した場合、返り値は 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 コマンド群に含めた copy in/copy out コマンドを確実に、また正しく実行できるはずです。
比較的古いアプリケーションでは、copy in /copy out をPQexec で実行し、PQendcopy の実行でトランザクションは完了する、と想定していることがよくあります。 これはコマンド文字列中の SQL が copy in/copy out だけであったときにのみ正しく動作します。