PQexec
関数は普通の同期処理のアプリケーションにおけるコマンドの送信に適したものです。
しかし、一部のユーザにとって重要な問題となり得る、数個の問題があります。
PQexec
はコマンドが完了するまで待機します。
アプリケーションによっては(例えばユーザインタフェースの調整処理など)他に行うべき作業があります。
この場合は応答待ちでブロックさせたくはありません。
クライアントアプリケーションの実行が結果を待っている間停止されるため、アプリケーションで送信したコマンドをキャンセルさせる指示を行うことは困難です。 (シグナルハンドラを使って達成することができますが、他の方法はありません。)
PQexec
が返すことができるPGresult
構造体は1つだけです。
もし送信した問い合わせ文字列が複数のSQLコマンドを含んでいる場合、PQexec
は最後のものだけを除いて、残りすべてのPGresult
を破棄してしまいます。
PQexec
は常にコマンドの結果全体を収集し、1つのPGresult
内に保管します。
アプリケーションにおけるエラー処理を簡単にしますが、多くの行になる結果では非現実的になるかもしれません。
アプリケーションにとってこのような制限が望ましくない場合は、代わりにPQexec
を構成する関数PQsendQuery
とPQgetResult
を使用してください。
また、PQsendQueryParams
とPQsendPrepare
、PQsendQueryPrepared
、PQsendDescribePrepared
、PQsendDescribePortal
もあり、PQgetResult
を使用して、それぞれPQexecParams
とPQprepare
、PQexecPrepared
、PQdescribePrepared
、PQdescribePortal
と同等の機能を行うことができます。
PQsendQuery
結果を待つことなく、サーバにコマンドを発行します。
コマンドの登録に成功した場合1が、失敗した場合0が返されます。
(後者の場合、PQerrorMessage
を使用して失敗についてのより多くの情報を取り出してください。)
int PQsendQuery(PGconn *conn, const char *command);
PQsendQuery
呼び出しが成功したら、PQgetResult
を繰り返し呼び出して、実行結果を取得します。
PQgetResult
がNULLポインタを返し、コマンドが完了したことを示すまでは、(同じ接続で)PQsendQuery
を再度呼び出すことはできません。
パイプラインモードでは、複数のSQLコマンドを含むコマンド文字列は許可されません。
PQsendQueryParams
結果を待つことなく、サーバにコマンドとパラメータとを分けて発行します。
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);
これは、問い合わせのパラメータが問い合わせ文字列と分けて指定できる点を除き、PQsendQuery
と同じです。
この関数のパラメータはPQexecParams
と同様に扱われます。
PQexecParams
同様、問い合わせ文字列には1つのコマンドしか指定できません。
PQsendPrepare
指定パラメータを持つプリペアド文の作成要求を送信します。 その完了を待ちません。
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);
これはPQprepare
の非同期版です。
要求の登録に成功した場合1が、失敗した場合0が返されます。
呼び出しの成功の後、サーバがプリペアド文の生成に成功したかを確認するためにはPQgetResult
を呼び出してください。
この関数のパラメータはPQprepare
と同様に扱われます。
PQsendQueryPrepared
結果を待つことなく、指定したパラメータでプリペアド文の実行要求を送信します。
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);
これはPQsendQueryParams
と似ていますが、実行されるコマンドは問い合わせ文字列ではなく、事前に準備された文の名前で指定されます。
この関数のパラメータはPQexecPrepared
と同様に扱われます。
PQsendDescribePrepared
指定したプリペアド文に関する情報入手要求を送ります。 入手完了まで待機しません。
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
これはPQdescribePrepared
の非同期版です。
要求の登録に成功した場合1が、失敗した場合0が返されます。
呼び出しに成功した後、PQgetResult
を呼び出して結果を入手してください。
この関数のパラメータはPQdescribePrepared
と同じように扱われます。
PQsendDescribePortal
指定したポータルに関する情報入手要求を送信します。 完了まで待機しません。
int PQsendDescribePortal(PGconn *conn, const char *portalName);
これはPQdescribePortal
の非同期版です。
要求の登録に成功した場合1が、失敗した場合0が返されます。
呼び出しに成功した後、PQgetResult
を呼び出して結果を入手してください。
この関数のパラメータはPQdescribePortal
と同じように扱われます。
PQgetResult
以前に呼び出したPQsendQuery
、PQsendQueryParams
、PQsendPrepare
、PQsendQueryPrepared
、PQsendDescribePrepared
、PQsendDescribePortal
、PQpipelineSync
から次の結果を待ち、その結果を返します。
コマンドが完了し、これ以上結果がない場合は、NULLポインタが返されます。
PGresult *PQgetResult(PGconn *conn);
PQgetResult
は、コマンドの完了を示すNULLポインタが返るまで、繰り返し呼び出さなければなりません。
(コマンド実行中以外での呼び出しでは、PQgetResult
は単にNULLポインタを返します。)
PQgetResult
の非NULLの結果はそれぞれ前述と同じPGresult
アクセス用関数を使用して処理されなければなりません。
各結果オブジェクトに対する処理が終わったら、そのオブジェクトをPQclear
を使用して解放することを忘れないでください。
コマンドが活動中、かつ、必要な応答データがまだPQconsumeInput
で読み込まれていない場合にのみ、PQgetResult
がブロックすることに注意してください。
パイプラインモードでは、PQgetResult
はエラーが発生しない限り通常通りに戻ります。
エラーを引き起こした問い合わせの後、次の同期点まで(そして次の同期点を除外して)送られた問い合わせに対しては、PGRES_PIPELINE_ABORTED
型の特殊な結果が返され、その後にNULLポインタが返されます。
パイプライン同期点に到達すると、PGRES_PIPELINE_SYNC
型の結果が返されます。
同期点の後の次の問い合わせの結果はすぐに続きます(つまり、同期点の後にNULLポインタは返されません)。
PQresultStatus
が致命的なエラーを示した場合であっても、libpqがエラー情報を完全に処理できるようにNULLポインタが返されるまでPQgetResult
を呼び出さなければなりません。
PQsendQuery
とPQgetResult
を使うことでPQexec
の問題は1つ解決します。
つまり、コマンドが複数のSQLコマンドを含んでいる場合でも、これらのコマンドの結果を個々に得ることができるわけです
(これは多重処理を単純な形で実現します。
単一のコマンド文字列に含まれる複数の問い合わせの内、後ろのものが処理中でもフロントエンドは先に完了した結果から扱うことができるからです)。
PQsendQuery
およびPQgetResult
で得られる、その他のよく望まれる機能は多くの問い合わせ結果を一度に1行受け取ることです。
これについては34.6で説明します。
サーバが次のSQLコマンドの処理に入ると、それが完了するまでやはりPQgetResult
の呼び出しがフロントエンドをブロックしてしまいます。
さらに以下の2つの関数をうまく使用してこれを防ぐことができます。
PQconsumeInput
サーバからの入力が可能になった場合、それを吸い取ります。
int PQconsumeInput(PGconn *conn);
PQconsumeInput
は通常、「エラーなし」を示す1を返しますが、何らかの障害があると0を返します(この場合は、PQerrorMessage
を参考にしてください)。
この結果は、何らかの入力データが実際に収集されたかどうかを示しているのではないことに注意してください。
PQconsumeInput
の呼び出し後、アプリケーションはPQisBusy
、または必要があればPQnotifies
を呼び出して状態に変化がないか調べることができます。
PQconsumeInput
は、結果や通知を扱うようにまだ準備していないアプリケーションからでも呼び出すことができます。
この関数は有効なデータを読み込んでバッファに保存し、結果としてselect
による読み込み準備完了の通知をリセットします。
従ってアプリケーションはPQconsumeInput
を使うとselect()
の検査条件をただちに満たすことができますから、あとはゆっくりと結果を調べてやればいいわけです。
PQisBusy
この関数が1を返したのであれば、問い合わせは処理の最中で、PQgetResult
も入力を待ったままブロック状態になってしまうでしょう。
0が返ったのであれば、PQgetResult
を呼び出してもブロックされないことが保証されます。
int PQisBusy(PGconn *conn);
PQisBusy
自身はサーバからデータを読み込む操作をしません。
ですから、まず最初にPQconsumeInput
を呼び出す必要があります。
そうしないとビジー状態がいつまでも続きます。
これら3関数を使用するアプリケーションは通常、select()
もしくはpoll()
を使用するメインループを持ち、対応しなければならないすべての状態を待機しています。
その内の1つの条件は、サーバからの利用可能な入力となるでしょう。
これは、select()
の見地からは、PQsocket
で識別されるファイル記述子上で読み込み可能なデータがあることを意味します。
メインループが入力準備完了を検出すると、その入力を読み込むためにPQconsumeInput
を呼び出さなければなりません。
そして、PQisBusy
を、更にPQisBusy
が偽(0)を返す場合にPQgetResult
も呼び出すことができます。
また、PQnotifies
を呼び出して、NOTIFY
メッセージ(34.9を参照)を検出することもできます。
また、PQsendQuery
/PQgetResult
を使用するクライアントは、サーバで処理中のコマンドに対してキャンセルを試行することができます。
34.7を参照してください。
しかし、PQcancel
の戻り値と関係なく、アプリケーションはPQgetResult
を使用した通常の結果読み取り手順を続けなければなりません。
キャンセル手続きの成功は単に、そのコマンドを通常よりも早めに終わらせるだけです。
上述の関数を使用して、データベースサーバからの入力待ちのためのブロックを行わずに済みます。
しかしまだ、サーバへの出力送信を待つためにアプリケーションはブロックする可能性があります。
これは比較的あまり発生しませんが、非常に長いSQLコマンドやデータ値が送信される場合に発生することがあります。
(しかし、アプリケーションがCOPY IN
経由でデータを送信する場合よく発生します。)
この発生を防ぎ、完全な非ブロックのデータベース操作を行うためには、さらに以下の関数を使用してください。
PQsetnonblocking
接続の非ブロック状態を設定します。
int PQsetnonblocking(PGconn *conn, int arg);
arg
が1の場合、接続状態を非ブロックに設定します。
arg
が0の場合はブロックに設定します。
問題がなければ0が、エラー時は-1が返ります。
非ブロック状態ではPQsendQuery
、PQputline
、PQputnbytes
、PQputCopyData
およびPQendcopy
の呼び出しはブロックされませんが、再度呼び出さなければならない場合、エラーが返ります。
PQexec
は非ブロックモードにはしたがわないことに注意してください。
この関数の呼び出しは、必ずブロック方式で動作します。
PQisnonblocking
データベース接続のブロック状態を返します。
int PQisnonblocking(const PGconn *conn);
接続が非ブロック状態の場合は1が、ブロック状態の場合は0が返ります。
PQflush
キューに蓄えられたサーバへの出力データの吐き出しを行います。 成功時(および送信キューが空の場合)は0が返ります。 何らかの原因で失敗した場合は-1が、送信キュー内のデータをすべて送信できなかった場合は1が返ります。 (これは接続が非ブロックの場合にのみ発生します。)
int PQflush(PGconn *conn);
非ブロック接続時にはコマンドやデータを送信した後に、PQflush
を呼び出してください。
1が返った場合、ソケットの読み込みまたは書き込み準備ができるまで待ってください。
書き込み準備ができたら、PQflush
を再度呼び出してください。
読み込み準備ができたら、PQconsumeInput
を呼び出してから、PQflush
を再度呼び出してください。
これをPQflush
が0を返すまで繰り返してください。
(例えばNOTICEメッセージのように、こちらがそのデータを読むまで、サーバがデータを送ろうとするのを妨げ、こちらのデータを読もうとしないことがありますので、読み込み準備ができたことを確認してPQconsumeInput
で入力をすべて抜き取ることが必要です。)
PQflush
が0を返した後は、ソケットの読み込み準備が整うまで待ち、上述のように応答を読み取ってください。