シンプルな同期型のアプリケーションの場合, 問い合わせの実行は PQexec 関数で十分です. しかし,ここにはいくつか大きな問題があります.
PQexec は問い合わせが完了するまで待ち状態になります. この間,アプリケーションには他にするべき作業があるかもしれません (たとえばユーザインターフェースの調整など) このような場合は応答待ちでブロックされたくありません.
制御は PQexec 関数の中に埋め込まれてしまっています. したがって,処理中の問い合わせをキャンセルするタイミングを フロントエンド側で判断するのは困難です. (もっとも,シグナルハンドラからは可能ですが)
PQexec が返せる PGresult 構造体はひとつだけです. もし送信した問い合わせ文字列が複数の SQL コマンドを含んでいる場合, PQexec は最後のものだけを除いて,残りすべての PGresult を破棄してしまいます.
アプリケーションにとってこのような制限が望ましくない場合は, かわりに PQexec を構成する内部関数,PQsendQuery と PQgetResult を使ってください.
PQsendQuery 結果を待たずに問い合わせを Postgres へ送信します. 問い合わせは直ちに送信され,成功すれば TRUE を,失敗すれば FALSE を返します. (この場合,PQerrorMessage を使えば失敗した理由の詳細を知ることができます)
int PQsendQuery(PGconn *conn, const char *query);PQsendQuery 呼び出しが成功したら, PQgetResult をくり返し呼び出して問い合わせ結果を得ます. PQgetResult が NULL を返し,問い合わせが完了したことを示すまでは, 同じ接続で PQsendQuery を呼び出してはいけません.
PQgetResult 先に実行された PQsendQuery の結果を逐次待ち,その結果を返します. 問い合わせ実行が完了した場合 NULL を返します. NULL が返った場合,取り出す結果はそれ以上ありません.
PGresult *PQgetResult(PGconn *conn);問い合わせが完了したことを示す NULL が返るまで, PQgetResult を繰り返して呼び出さなければなりません. (問い合わせがなされていない状態で呼び出すと, PQgetResult はただちに NULL を返すだけです) PQgetResult から得られる(NULL でない)個々の結果は, 前に説明した PGresult のアクセッサ関数と同じものを使って処理してください. 処理が終ったら PQclear を使い, 個々の結果オブジェクトの領域を開放するのを忘れないように. なお問い合わせが実行中であり, かつ PQconsumeInput を使って必要な応答データを読み込んでいない時だけは, PQgetResult が処理をブロックしてしまうことに注意してください.
PQsendQuery と PQgetResult を使うことで PQexec の問題は一つ解決します. つまり,問い合わせが複数の SQL コマンドを含んでいる場合でも, これらのコマンドの結果を個々に得ることができるわけです. (これは多重処理をシンプルな形で実現します. 単一の文字列に含まれる複数の問い合わせのうち,後にくるものが処理中でも フロントエンドは先に完了した結果から扱うことができるからです) しかしバックエンドが次の SQL コマンドの処理に入ると, それが完了するまでやはり PQgetResult の呼び出しがフロントエンドをブロックしてしまいます. これを防ぐには,さらに三つの関数をうまく使うことです.
PQconsumeInput バックエンドからの入力が可能になった時点で,それを「吸い取り」ます.
int PQconsumeInput(PGconn *conn);PQconsumeInput は通常,「エラーなし」を示す 1 を返しますが, 何らかの障害があると 0 を返します.(この場合は,PQerrorMessage がセットされます) この結果は何らかの入力データが, 実際に収集されたかどうかを示しているのではないことに注意してください. PQconsumeInput の呼び出し後, アプリケーションは PQisBusy,または必要があれば PQnotifies を呼び出して 状態に変化がないか調べることができます. PQconsumeInput は,結果や通知を扱うようにまだ準備していないアプリケーション からでも呼び出すことができます. このルーチンは有効なデータを読み込んでバッファに保存し, 結果として select(2) による読み込み準備完了の通知をリセットします. したがってアプリケーションは PQconsumeInput を使うと select の検査条件をただちに満たすことができますから, あとはゆっくりと結果を調べてやればいいわけです.
PQisBusy この関数が TRUE を返したのであれば問い合わせは処理の最中で, PQgetResult も入力を待ったままブロック状態になってしまうでしょう. FALSE が返ったのであれば, PQgetResult を呼び出してもブロックされないことが保証されます.
int PQisBusy(PGconn *conn);PQisBusy 自身はバックエンドからデータを読み込む操作をしません. ですから,まずは最初に PQconsumeInput を呼び出さなければなりません. さもなければ,ビジー状態がいつまでも続くことになるでしょう.
PQsocket バックエンドとの接続ソケットに対するファイルディスクリプタ番号を得ます. 有効なディスクリプタなら値は 0 以上です. -1 の場合は,バックエンドとの接続がまだオープンされていない, ということを示します.
int PQsocket(PGconn *conn);select(2) を実行する際に使うバックエンドへのソケットディスクリプタを得るには, この PQsocket を使います. アプリケーションはこの関数によってバックエンドからの応答, あるいはその他の状態を待つことができるようになります. もし select(2) がバックエンドへのソケットからデータを読み込める状態に なったことを示したら,PQconsumeInput を呼び出してデータを読み込みます. それから PQisBusy, PQgetResult, あるいは必要なら PQnotifies を使って応答を処理します.
これらの関数を使う典型的なフロントエンドは select(2) を使ったメインループを備え, 応答しなければならない全ての状態を待ちます. やがてこの状態のうちひとつ(select 関数の点から見れば, PQsocket で確認したファイルディスクリプタから読み込めるデータ) がバックエンドから入力可能になります. メインループは入力が可能になったことを検出したら, PQconsumeInput を呼び出して入力を読み込みます. 続いて PQisBusy を呼び出し,PQisBusy が FALSE を返したらさらに PQgetResult を呼び出します. あるいは PQnotifies を呼び出して通知メッセージを検出する場合もあります (後出「非同期通知」セクションを参照)
PQsendQuery/PQgetResult を使うと,バックエンドで処理中の問い合わせを フロントエンドからキャンセルするよう要求することができます.
PQrequestCancel Postgres に現在の問い合わせの処理を中断するよう要求します
int PQrequestCancel(PGconn *conn);キャンセル要求の受け入れが成功すれば TRUE を,そうでなければ FALSE を返します. (FALSE の場合の理由は PQerrorMessage で知ることができます) しかし要求の受け入れが成功したとしても, その要求の効果が出ることは全く保証していません. したがってアプリケーションは PQrequestCancel の戻り値にかかわらず, PQgetResult を使った通常の結果読み込みシーケンスを継続しなければなりません. もしキャンセル操作が有効であれば現在の問い合わせは間もなく中断され, エラーが結果として返ります. キャンセル操作に失敗した場合 (つまりバックエンドが既に問い合わせ処理を完了していたため) 目に見える結果は何も出てこなくなります.
なお,処理中の問い合わせがトランザクションの一部だと, キャンセル処理によってトランザクション全体がアボートされます.
PQrequestCancel はシグナルハンドラから起動しても大丈夫です. また単に PQexec と組み合わせて使うこともできます. たとえば psql は PQexec を通して発行された問い合わせのキャンセル処理を, SIGINT シグナルハンドラから PQrequestCancel を起動することで インタラクティブに実行できるようになっています. なお,接続がオープンされていない, あるいはバックエンドが問い合わせの処理をしていない状態では, PQrequestCancel の呼び出しは何の効果もありません.