シンプルな同期型のアプリケーションの場合。問い合わせの実行は PQexec 関数で十分です。しかし、大きな問題をいくつ か抱えています。
PQexec は問い合わせが完了するまで待ち状態になりま す。この間、アプリケーションには他にするべき作業があるかもしれません (たとえばユーザインターフェースの調整など)このような場合は応答待ちでブ ロックされたくありません。
制御は PQexec 関数の中に埋め込まれてしまっていま す。したがって、処理中の問い合わせをキャンセルするタイミングをフロント エンド側で判断するのは困難です。(もっとも、シグナルハンドラからは可能 ですが)
PQexecが返せる PGresult 構造体は一つだけです。も し送信した送信した問い合わせ文字列が複数の SQL コマ ンドを含んでいる場合、PQexec は最後のものだけを除 いて、残りすべての PGresult を破棄してしまいます。
アプリケーションにとってこのような制限が望ましくない場合は、代わりに PQexec を構成する関数 PQsendQuery と PQgetResultを 使用してください。
PQputline や PQputnbytes と 同様に、この機能性を使っている古いプログラムは、バックエンドへのデータ 送信待ちをブロックしてしまう可能性がありました。この問題に対応するため に、PQsetnonblocking が追加されました。
古めのアプリケーションでは、PQsetnonblocking が使 われていなかったり、昔の潜在的にブロックする挙動を得ていなかったりしま した。 新しいプログラムでは、PQsetnonblocking を使うこと で、バックエンドと完全にブロックされない接続をすることが出来るようにな りました。
PQsetnonblocking 接続のステータスを設定します。
int PQsetnonblocking(PGconn *conn, int arg)引数が TRUE の場合は、接続が非ブロック状態になり、FALSE の場合はブ ロック状態になります。 成功した場合は 0 を、失敗した場合は -1 を返します。
非ブロック状態においては、PQputline や PQputnbytes、 PQsendQuery、そして PQendcopy の呼び出しはブロックされませんが、 その代わり、エラーを返した場合にはそれらの関数をもう一度呼び出さ なければなりません。
データベース接続が非ブロックモードになっていて、 PQexec を呼び出したときには、 PQexec が完了するまで、一時的に接続をブロック 状態にします。
libpq の多くのものについても、近い将来 PQsetnonblockingが安全に機能するようになると 思われます。
PQisnonblocking データベース接続がブロックされているかどうかを返します。
int PQisnonblocking(const PGconn *conn)接続が非ブロックモードになっている場合は TRUE を返し、ブロック モードのときは FALSE を返します。
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 この関数が 1 を返したのであれば問い合わせは処理の最中で、 PQgetResultも入力を待ったままブロック状態になって しまうでしょう。 0 が返ったのであれば、PQgetResultを呼び出してもブ ロックされないことが保証されます。
int PQisBusy(PGconn *conn);PQisBusy自身はバックエンドからデータを読み込む操 作をしません。ですから、まずは最初に PQconsumeInputを呼び出さなければなりません。さも なければ、ビジー状態がいつまでも続くことになるでしょう。
PQflush 送信待ちのデータをバックエンドへ送信します。 データの送信に成功するか送信待ちのデータがなければ、0 を返します。 なんらかの理由で失敗した場合は、EOF を返します。
int PQflush(PGconn *conn);PQflushは、応答があったかどうかを決めるために、 selectを呼び出す前に、非ブロック接続時に呼び出さ なければなりません。 0 が返ってきた場合、バックエンドへ送信されずにいるデータはありません。 PQsetnonblockingを使用しているアプリケーションの みでこの関数が必要になります。
PQsocket バックエンドとの接続ソケットに対するファイルディスクリプタ番 号を得ます。有効なディスクリプタなら値は 0 以上です。-1 の場 合は、バックエンドとの接続がまだオープンされていない、という ことを示します。
int PQsocket(const PGconn *conn);select(2)を実行する際に使うバックエンドへのソケッ トディスクリプタを得るには、この PQsocket を使い ます。アプリケーションはこの関数によってバックエンドからの応答、あるい はその他の状態を待つことができるようになります。もし select(2)がバックエンドへのソケットからデータを読 み込める状態に なったことを示したら、 PQconsumeInputを呼び出してデータを読み込みます。 それからPQisBusyやPQgetResult、 あるいは必要ならPQnotifiesを使って応答を処理しま す。
PQflushが 0 を返して、バックエンドへの送信を待っ ているバッファリングされたデータがないことを示すまで、 PQsetnonblockingを使った非ブロック接続は、 selectを使用すべきではありません。
これらの関数を使用するフロントエンドは、select(2) を使ったメインループを備え、応答しなければならないすべての状態を待ちま す。 やがてこの状態のうちひとつ(select関数の点から見れ ば、PQsocketで確認したファイルディスクリプタから 読みこめるデータ)がバックエンドから入力可能になります。 メインループは入力が可能になったことを検出したら、 PQconsumeInputを呼び出して入力を読み込みます。 続いてPQisBusyを呼び出し、 PQisBusyがFALSE(0)を返したら、更に PQgetResultを呼び出します。 あるいはPQnotifiesを呼び出して、通知メッセージを 検出する場合もあります。(後出の「非同期通知」セクションを参照)
PQsendQuery/PQgetResultを使 うと、バックエンドで処理中の問い合わせをフロントエンドからキャンセルす るよう要求することができます。
PQrequestCancel Postgresに現在の問い合わせの処理 を中断するよう要求します。
int PQrequestCancel(PGconn *conn);キャンセル要求の受け入れが成功すれば、1 を、そうでなければ 0 を返しま す。(失敗した場合は、PQerrorMessageで原因を知る ことができます。) しかし要求の受け入れが成功したとしても、その要求の効果が出ることは全く 保証していません。したがって、アプリケーションは PQrequestCancelの戻り値にかかわらず、 PQgetResultを使った通常の結果読み込みシーケンスを 継続しなければなりません。 もしキャンセル操作が有効であれば、現在の問い合わせは間もなく中断され、 エラーが結果として返ります。キャンセル操作に失敗した場合(つまりバック エンドが既に問い合わせ処理を終了していたため)、目に見える結果は何も出 てこなくなります。
なお、処理中の問い合わせがトランザクションの一部だと、キャンセル処理に よってトランザクション全体がアボートされます。
PQrequestCancelはシグナルハンドラから起動しても大 丈夫です。また、単にPQexecと組み合わせて使うこと ができます。たとえばpsqlは PQexecを通して発行された問い合わせのキャンセル処 理を、SIGINTシグナルハンドラからPQrequestCancelを 起動することで、インタラクティブに実行できるようになっています。 なお、接続がオープンされていない、あるいはバックエンドが問い合わせの処 理をしていない状態では、PQrequestCancelの呼び出し は何の効果もありません。