COPY
コマンド関連関数
PostgreSQLのCOPY
コマンドでは、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
コマンド文字列が完了し、その後のコマンドが安全に発行できることが確実になるのは、PQgetResult
がNULL
を返す時のみです。
本節の関数は、PQexec
もしくはPQgetResult
からPGRES_COPY_OUT
もしくはPGRES_COPY_IN
という結果状態を得た後のみに実行されなければなりません。
これらの状態値の一つを持つPGresult
オブジェクトは、開始したCOPY
操作に関する追加データを持ちます。
この追加データは、以下の問い合わせ結果を持つ接続で使用される関数を使用して利用することができます。
PQnfields
コピーされる列(フィールド)数を返します。
PQbinaryTuples
0は、コピー全体の書式がテキスト(改行で区切られた行、区切り文字で区切られた列など)であることを示します。 1は、コピー全体の書式がバイナリであることを示します。 詳細はCOPYを参照してください。
PQfformat
コピー操作対象の列それぞれに関した書式コード(テキストでは0、バイナリでは1)を返します。
コピー全体の書式がテキストの場合は、列単位の書式コードは常にゼロです。
しかし、バイナリ書式はテキスト列もバイナリ列もサポートすることができます。
(しかし、現在のCOPY
実装では、バイナリコピーでのみバイナリ列が発生します。
そのため、今の所列単位の書式は常に全体の書式と一致します。)
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);
errormsg
がNULL
の場合は、COPY_IN
操作の終了に成功しました。
errormsg
がNULL
でない場合は、COPY
は強制的に失敗させられました。
errormsg
が指し示す文字列はエラーメッセージとして使用されます。
(しかし、このエラーメッセージが正しくサーバから返ったものであるとは仮定すべきではありません。
サーバは既に別の原因でCOPY
に失敗していた可能性があります。)
終端メッセージが送信された場合は結果は1になります。
非ブロックモードでは、終端メッセージがキューに入れられたことしか意味しないかもしれません。
(非ブロックモードでデータが送信されたことを確認するには、次に書き込み準備ができるまで待ち、PQflush
を呼ぶことを、それが0を返すまでくり返します。)
バッファが一杯で終端メッセージがキューに入れられなかった場合はゼロになります。
これは、接続が非ブロックモードの場合にのみ起こります。
(この場合、書き込み準備ができるまで待ち、再度PQputCopyEnd
を呼び出してみてください。)
ハードエラーが発生した場合は-1になります。
このとき、詳細を取得するためにPQerrorMessage
を使用できます。
PQputCopyEnd
の呼び出しに成功した後、PQgetResult
を呼び出してCOPY
コマンドの最終的な結果状態を取り出してください。
通常の方法でこの結果が使用できるようになるまで待機しても構いません。
そして、通常の操作に戻ってください。
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より大きくなります。)
返された文字列は常にNULL終端ですが、おそらくテキストCOPY
でのみ有用になるでしょう。
ゼロという結果は、COPY
が進行中で、行がまだ利用できない状態であることを示します。
(async
が真の場合にのみ発生することがあります。)
-1という結果は、COPY
が完了したことを示します。
-2という結果はエラーが発生したことを示します。
(その理由についてはPQerrorMessage
を参照してください。)
async
が真(非0)の場合、PQgetCopyData
は入力待ちのためのブロックを行いません。
COPY
実行中で完全な行を取り出せない場合PQgetCopyData
は0を返します。
(この場合、再試行の前に読み込み準備が整うまで待機してください。
PQconsumeInput
を呼び出したかどうかは関係ありません。)
async
が偽(0)の場合、PQgetCopyData
はデータが利用できるようになるまで、もしくは、操作が完了するまでブロックします。
PQgetCopyData
が-1を返した後、PQgetResult
を呼び出して、COPY
コマンドの最終結果状態を取り出してください。
通常の方法で結果が利用できるようになるまで待機しても構いません。
そして、通常の操作に戻ってください。
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
応答を受け取ったら、アプリケーションはデータ終了の合図を受け取るまでPQconsumeInput
とPQgetlineAsync
を呼び出します。
PQgetline
と違い、この関数はデータ終了の検出に対して責任を持ちます。
PQgetlineAsync
の個々の呼び出しでは、libpqの入力バッファ内で完全な行データが利用できる場合にデータを返します。
さもなければ、行の残りが届くまでデータは返されません。
この関数は、コピーデータの終端を示す符号を認識すると-1を、また何もデータがなければ0を、そしてデータを返す場合はそのバイト数を正の値で返します。
もし-1が返されたら、呼び出し側は次にPQendcopy
を呼び出さなければいけません。
それから通常の処理に戻ります。
返されるデータは行データの境界を越えて拡張されることはありません。
可能であれば行全体を一度に返します。
しかし呼び出し側が準備したバッファが少なすぎ、サーバから送られてくる行を保持しておくことができない場合には、分割された行データを返します。
テキストデータでは、これは最後の1バイトが\n
かどうかを確認すれば検出できます。
(バイナリCOPY
の場合に同様の検出を行うためには、実際にCOPY
データの書式を解析しなければなりません。)
なお、返される文字列はNULL終端ではありません。
(NULL終端を後から付け加えるのであれば、実際に確保するバッファサイズ-1をbufsize
として渡すようにしてください。)
PQputline
サーバにNULL終端の文字列を送信します。
問題なければ0を返します。
文字列の送信ができなかった場合はEOF
を返します。
int PQputline(PGconn *conn, const char *string);
PQputline
の呼び出しによって送信されるCOPY
データストリームは、PQgetlineAsync
で返される書式と同じ書式を持ちます。
ただし、アプリケーションは、PQputline
毎に正確に1つのデータ行を送信するように強制されていません。
呼び出し毎に行の一部や複数の行を送信しても問題ありません。
PostgreSQLプロトコル3.0より前では、アプリケーションは、サーバに対してCOPY
データの送信を完了したことを通知するために、最終の行として\.
という2文字を明示的に送信する必要がありました。
これはまだ動作します。
しかし、これは廃れたものとして、\.
の特殊な意味は将来のリリースで無くなることが予想されます。
実際のデータの送信完了後にPQendcopy
を呼び出すことが重要です。
PQputnbytes
NULL終端ではない文字列をサーバに送信します。
問題なければ0を返します。
文字列の送信ができなかった場合はEOF
を返します。
int PQputnbytes(PGconn *conn, const char *buffer, int nbytes);
これはまさにPQputline
と同様です。
ただし、直接送信バイト数を指定するため、NULL終端である必要がありません。
バイナリデータを送信する時はこのプロシージャを使用してください。
PQendcopy
サーバと同期します。
int PQendcopy(PGconn *conn);
この関数はサーバがコピーを完了するのを待ちます。
この関数は、PQputline
を使ったサーバへの文字列送信が完了した時点、あるいはPQgetline
を使ったサーバからの文字列受信が完了した時点のいずれでも呼び出さなければなりません。
これを発行しないと、サーバはクライアントとの「同期がずれた」状態になってしまいます。
この関数から戻った時点で、サーバは次のSQLコマンドを受ける準備が整います。
正常に終了した場合、返り値は0です。 さもなくば、非ゼロです。
(戻り値が非ゼロの場合、PQerrorMessage
を使用して詳細を取り出してください。)
PQgetResult
を使う場合、アプリケーションはPQgetline
を繰り返し呼び出してPGRES_COPY_OUT
に応答し、終端行を見つけたら続いてPQendcopy
を呼び出さなければなりません。
それから、PQgetResult
がNULLポインタを返すまで、PQgetResult
のループに戻らなければなりません。
同じように PGRES_COPY_IN
は連続したPQputline
で処理し、それからPQendcopy
で締めくくった後にPQgetResult
のループに戻ります。
このようにすることで、一連のSQLコマンド群に含めたCOPY
コマンドを確実に、また正しく実行できるはずです。
比較的古いアプリケーションでは、COPY
をPQexec
で実行し、PQendcopy
の実行でトランザクションは完了する、と想定していることがよくあります。
これはコマンド文字列中のSQLがCOPY
だけであった時にのみ正しく動作します。