31.8. COPYコマンド関連関数

PostgreSQLCOPYコマンドでは、libpqが使っているネットワーク接続に対して読み込み、あるいは書き込みを選ぶことができるようになっています。 本節で説明する関数により、アプリケーションはコピーするデータの提供やコピーされるデータの使用が可能になるという利点を持ちます。

全体的な処理として、アプリケーションはまずPQexecもしくは同等な関数経由でCOPY SQLコマンドを発行します。 (コマンドでエラーが発生しなければ)この応答は、(指定したコピーの方向に応じて)PGRES_COPY_OUTもしくはPGRES_COPY_INという状態コードを持ったPGresultになります。 その後、アプリケーションは本節の関数を使用して、行データを受信、もしくは、送信しなければなりません。 データの転送が完了した時、転送に成功したか失敗したかを示す別のPGresultオブジェクトが返されます。 その状態は、成功時にはPGRES_COMMAND_OKになり、何らかの問題が起きていた時には PGRES_FATAL_ERRORになります。 この時点で、別のSQLコマンドをPQexec経由で発行することができます。 (COPY操作の実行中は、同じ接続を使用して他のSQLコマンドを実行することはできません。)

COPYコマンドが、他にもコマンドを含んだ文字列としてPQexec経由で発行された場合、アプリケーションはCOPY処理を終えた後に、PQgetResult経由で結果の取り出しを続けなければなりません。 PQexecコマンド文字列が完了し、その後のコマンドが安全に発行できることが確実になるのは、PQgetResultNULLを返す時のみです。

本節の関数は、PQexecもしくはPQgetResultからPGRES_COPY_OUTもしくはPGRES_COPY_INという結果状態を得た後のみに実行されなければなりません。

これらの状態値の一つを持つPGresultオブジェクトは、開始したCOPY操作に関する追加データを持ちます。 この追加データは、以下の問い合わせ結果を持つ接続で使用される関数を使用して利用することができます。

PQnfields

コピーされる列(フィールド)数を返します。

PQbinaryTuples

0は、コピー全体の書式がテキスト(改行で区切られた行、区切り文字で区切られた列など)であることを示します。 1は、コピー全体の書式がバイナリであることを示します。 詳細はCOPYを参照してください。

PQfformat

コピー操作対象の列それぞれに関した書式コード(テキストでは0、バイナリでは1)を返します。 コピー全体の書式がテキストの場合は、列単位の書式コードは常にゼロです。 しかし、バイナリ書式はテキスト列もバイナリ列もサポートすることができます。 (しかし、現在のCOPY実装では、バイナリコピーでのみバイナリ列が発生します。 そのため、今の所列単位の書式は常に全体の書式と一致します。)

注意: これらの追加データ値はプロトコル3.0を使用した場合にのみ利用可能です。 プロトコル2.0を使用する場合は、これらの関数はすべて0を返します。

31.8.1. COPYデータ送信用関数

これらの関数は、COPY FROM STDIN期間にデータを送信するために使用されます。 接続がCOPY_IN状態でない時に呼び出された場合、これらは失敗します。

PQputCopyData

COPY_IN状態の間、サーバにデータを送信します。

int PQputCopyData(PGconn *conn,
                  const char *buffer,
                  int nbytes);

指定したbufferにあるCOPYデータをnbytes長分、サーバに送信します。 データが送信された場合、この結果は1になります。 送信試行がブロックされたために送信できなかった場合はゼロになります。 (これは、接続が非ブロックモードの場合にのみ起こります。) エラーが発生した場合は-1になります。 (戻り値が-1の場合、詳細を取り出すためにはPQerrorMessageを使用してください。 戻り値がゼロの場合は書き込み準備が整うまで待ち、再実行してください。)

アプリケーションはCOPYデータストリームを使いやすい大きさのバッファに分けて読み込むことができます。 送信時の読み込みバッファの境界には意味的な重要性はありません。 データストリームの内容は、COPYコマンドで想定しているデータ書式に一致している必要があります。 詳細はCOPYを参照してください。

PQputCopyEnd

COPY_IN状態の間に、サーバにデータ終了指示を送信します。

int PQputCopyEnd(PGconn *conn,
                 const char *errormsg);

errormsgNULLの場合は、COPY_IN操作の終了に成功しました。 errormsgNULLでない場合は、COPYは強制的に失敗されました。 errormsgが指し示す文字列はエラーメッセージとして使用されます。 (しかし、このエラーメッセージが正しくサーバから返ったものであるとは仮定すべきではありません。 サーバは既に別の原因でCOPYに失敗していた可能性があります。 また、この強制的な失敗は3.0より前のプロトコルの接続を使用している場合は動作しません。)

終端データが送信された場合は結果は1になります。 送信試行がブロックされたため送信できなかった場合はゼロになります。 (これは、接続が非ブロックモードの場合にのみ起こります。) エラーが発生した場合は-1になります。 (戻り値が-1の場合、詳細を取り出すためにはPQerrorMessageを使用してください。 戻り値がゼロの場合は書き込み準備が整うまで待ち、再実行してください。)

PQputCopyEndの呼び出しに成功した後、PQgetResultを呼び出してCOPYコマンドの最終的な結果状態を取り出してください。 通常の方法でこの結果が使用できるようになるまで待機しても構いません。 そして、通常の操作に戻ってください。

31.8.2. COPYデータ受信用関数

これらの関数はCOPY TO STDOUT時にデータを受信するために使用されます。 COPY_OUT状態以外の接続で呼び出すと、失敗します。

PQgetCopyData

COPY_OUT状態時にサーバからデータを受信します。

int PQgetCopyData(PGconn *conn,
                  char **buffer,
                  int async);

COPY期間中、サーバから別の行データの入手を試みます。 常に1度に1つの行データが返されます。 部分的な行のみが利用可能な場合は返されません。 行データの取得に成功することは、そのデータを保持するためのメモリチャンクの割り当てを意味します。 bufferパラメータは非NULLでなければなりません。 *bufferは割り当てられたメモリへのポインタに、バッファが返されなかった場合はNULLに設定されます。 非NULLの結果バッファは、不要になったらPQfreememを使用して解放しなければなりません。

行の取り込みに成功した時、戻り値は行内のデータのバイト数になります。 (これは常に0より大きくなります。) 返された文字列は常にヌル終端ですが、おそらくテキストCOPYでのみ有用になるでしょう。 ゼロという結果は、COPYが進行中で、行がまだ利用できない状態であることを示します。 (asyncが真の場合にのみ発生することがあります。) -1という結果は、COPYが完了したことを示します。 -2という結果はエラーが発生したことを示します。 (その理由についてはPQerrorMessageを参照してください。)

asyncが真(非0)の場合、PQgetCopyDataは入力待ちのためのブロックを行いません。 COPY実行中で完全な行を取り出せない場合PQgetCopyDataは0を返します。 (この場合、再試行の前に読み込み準備が整うまで待機してください。 PQconsumeInputを呼び出したかどうかは関係ありません。) asyncが偽(0)の場合、PQgetCopyDataはデータが利用できるようになるまで、もしくは、操作が完了するまでブロックします。

PQgetCopyDataが-1を返した後、PQgetResultを呼び出して、COPYコマンドの最終結果状態を取り出してください。 通常の方法で結果が利用できるようになるまで待機しても構いません。 そして、通常の操作に戻ってください。

31.8.3. 廃れたCOPY用関数

以下の関数はCOPYを取扱う、古めの手法を行います。 これらはまだ動作しますが、エラーの取扱いが貧弱であることやデータの終端を検知する方法が不便であることより使用を奨めません。

PQgetline

改行で終端する文字列(サーバから送信されたもの)を長さlengthのバッファ用文字列に読み込みます。

int PQgetline(PGconn *conn,
              char *buffer,
              int length);

この関数はバッファにlength-1個までの文字をコピーし、終端の改行を1バイトのゼロに置き換えます。 PQgetlineは、入力の終端ではEOFを、行全体が読み込まれれば0を返します。 そしてまだ終端の改行が読み込まれていないうちにバッファがいっぱいになってしまった場合は1を返します。

アプリケーションは新しく読み込んだ行が、\.という2文字であるかどうか確認しなければいけません。 この2文字は、COPYコマンドの結果をサーバが送信し終えたことを示すものです。 アプリケーションには、仮にlength-1文字より長い行を受け取るようなことがあっても、間違いなく\.行を認識するような配慮が必要です (また例えば長いデータの行の終端を、最終行と取り違えないようにもしてください)。

PQgetlineAsync

COPYデータ行(サーバから送信されたもの)を、ブロッキングなしでバッファに読み込みます。

int PQgetlineAsync(PGconn *conn,
                   char *buffer,
                   int bufsize);

PQgetlineと似ていますが、COPYのデータを非同期的に、つまりブロッキングなしで読み出さなければならないアプリケーションで使用することができます。 COPYコマンドを発行し、そしてPGRES_COPY_OUT応答を受け取ったら、アプリケーションはデータ終了の合図を受け取るまでPQconsumeInputPQgetlineAsyncを呼び出します。

PQgetlineと違い、この関数はデータ終了の検出に対して責任を持ちます。

PQgetlineAsyncの個々の呼び出しでは、libpqの入力バッファ内で完全な行データが利用できる場合にデータを返します。 さもなければ、行の残りが届くまでデータは返されません。 この関数は、コピーデータの終端を示す符号を認識すると-1を、また何もデータがなければ0を、そしてデータを返す場合はそのバイト数を正の値で返します。 もし-1が返されたら、呼び出し側は次にPQendcopyを呼び出さなければいけません。 それから通常の処理に戻ります。

返されるデータは行データの境界を越えて拡張されることはありません。 可能であれば行全体を一度に返します。 しかし呼び出し側が準備したバッファが少なすぎ、サーバから送られてくる行を保持しておくことができない場合には、分割された行データを返します。 テキストデータでは、これは最後の1バイトが\nかどうかを確認すれば検出できます。 (バイナリCOPYの場合に同様の検出を行うためには、実際にCOPYデータの書式を解析しなければなりません。) なお、返される文字列はヌル終端ではありません。 (ヌル終端を後から付け加えるのであれば、実際に確保するバッファサイズ-1をbufsizeとして渡すようにしてください。)

PQputline

サーバにヌル終端の文字列を送信します。 問題なければ0を返します。 文字列の送信ができなかった場合はEOFを返します。

int PQputline(PGconn *conn,
              const char *string);

PQputlineの呼び出しによって送信されるCOPYデータストリームは、PQgetlineAsyncで返される書式と同じ書式を持ちます。 ただし、アプリケーションは、PQputline毎に正確に1つのデータ行を送信するように強制されていません。 呼び出し毎に行の一部や複数の行を送信しても問題ありません。

注意: PostgreSQLプロトコル3.0より前では、アプリケーションは、サーバに対してCOPYデータの送信を完了したことを通知するために、最終の行として\.という2文字を明示的に送信する必要がありました。 これはまだ動作します。 しかし、これは廃れたものとして、\.の特殊な意味は将来のリリースで無くなることが予想されます。 実際のデータの送信完了後にPQendcopyを呼び出すことが重要です。

PQputnbytes

ヌル終端ではない文字列をサーバに送信します。 問題なければ0を返します。 文字列の送信ができなかった場合はEOFを返します。

int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);

これはまさにPQputlineと同様です。 ただし、直接送信バイト数を指定するため、ヌル終端である必要がありません。 バイナリデータを送信する時はこのプロシージャを使用してください。

PQendcopy

サーバと同期します。

int PQendcopy(PGconn *conn);

この関数はサーバがコピーを完了するのを待ちます。 この関数は、PQputlineを使ったサーバへの文字列送信が完了した時点、あるいはPGgetlineを使ったサーバからの文字列受信が完了した時点のいずれでも呼び出さなければなりません。 これを発行しないと、サーバはクライアントとの"同期がずれた"状態になってしまいます。 この関数から戻った時点で、サーバは次のSQLコマンドを受ける準備が整います。 正常に終了した場合、返り値は0です。 さもなくば、非ゼロです。 (戻り値が非ゼロの場合、PQerrorMessageを使用して詳細を取り出してください。)

PQgetResultを使う場合、アプリケーションはPQgetlineを繰り返し呼び出してPGRES_COPY_OUTに応答し、終端行を見つけたら続いてPQendcopyを呼び出さなければなりません。 それから、PQgetResultがヌルポインタを返すまで、PQgetResultのループに戻らなければなりません。 同じように PGRES_COPY_INは連続したPQputlineで処理し、それからPQendcopyで締めくくった後にPQgetResultのループに戻ります。 このようにすることで、一連のSQLコマンド群に含めたCOPYコマンドを確実に、また正しく実行できるはずです。

比較的古いアプリケーションでは、COPYPQexecで実行し、PQendcopyの実行でトランザクションは完了する、と想定していることがよくあります。 これはコマンド文字列中のSQLCOPYだけであった時にのみ正しく動作します。