非同期コマンドの処理

PostgreSQL 8.0.0 文書
前のページ | 巻戻し | 第 27章libpq - C ライブラリ | 早送り | 次のページ

# 27.4. 非同期コマンドの処理

`PQexec`関数は普通の同期処理のアプリケーションにおけるコマンドの送信に適したものです。 しかし、一部のユーザにとって重要な問題となり得る、2つの問題があります。

- `PQexec` はコマンドが完了するまで待機します。 アプリケーションによっては(例えばユーザインタフェースの調整処理など)他に行うべき作業があります。 この場合は応答待ちでブロックさせたくはありません。

- クライアントアプリケーションの実行が結果を待っている間停止されるため、アプリケーションで送信したコマンドをキャンセルさせる指示を行うことは困難です。 (シグナルハンドラを使って達成することができますが、他の方法はありません。)

- `PQexec`が返すことができる`PGresult`構造体は1つだけです。 もし送信した問い合わせ文字列が複数のSQLコマンドを含んでいる場合、`PQexec`は最後のものだけを除いて、残り全ての`PGresult`を破棄してしまいます。

アプリケーションにとってこのような制限が望ましくない場合は、代わりに`PQexec`を構成する関数`PQsendQuery`と`PQgetResult`を使用してください。 また、`PQsendQueryParams`と`PQsendPrepare`、`PQsendQueryPrepared`もあり、`PQgetResult`を使用して、それぞれ`PQexecParams`と`PQprepare`、`PQexecPrepared`と同等の機能を行うことができます。

`PQsendQuery`

  結果を待つことなく、サーバにコマンドを発行します。 コマンドの登録に成功した場合1が、失敗した場合0が返されます。 (後者の場合、`PQerrorMessage` を使用して失敗についてのより多くの情報を取り出してください。)

      int PQsendQuery(PGconn *conn, const char *command);

  `PQsendQuery`呼び出しが成功したら、`PQgetResult`を繰り返し呼び出して、実行結果を取得します。 `PQgetResult`がヌルポインタを返し、コマンドが完了したことを示すまでは、(同じ接続で)`PQsendQuery`を再度呼んではいけません。

`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`同様、これは2.0プロトコルでは動作しませんし、問い合わせ文字列には1つのコマンドしか指定できません。

`PQsendPrepare`

  指定パラメータを持つ準備された文の作成要求を送信します。 その完了を待ちません。

      int PQsendPrepare(PGconn *conn,
                        const char *stmtName,
                        const char *query,
                        int nParams,
                        const Oid *paramTypes);

  これは`PQprepare`の非同期版です。 要求の登録に成功した場合1が、失敗した場合0が返されます。 呼び出しの成功の後、サーバが準備された文の生成に成功したかを確認するためには`PQgetResult`を呼び出してください。 この関数のパラメータは`PQprepare`と同様に扱われます。 `PQprepare`同様、これは2.0プロトコルの接続では動作しません。

`PQsendQueryPrepared`

  結果を待つことなく、指定したパラメータで準備された文の実行要求を送信します。

      int PQsendQueryPrepared(PGconn *conn,
                              const char *stmtName,
                              int nParams,
                              const char * const *paramValues,
                              const int *paramLengths,
                              const int *paramFormats,
                              int resultFormat);

  これは`PQsendQueryParams`と似ていますが、実行されるコマンドは、問い合わせ文字列ではなく、事前に準備された文の名前で指定されます。 この関数のパラメータは`PQexecPrepared`と同様に扱われます。 `PQexecPrepared`同様、これは2.0プロトコルでは動作しません。

`PQgetResult`

  以前に呼び出した`PQsendQuery`、`PQsendQueryParams`、`PQsendPrepare`、`PQsendQueryPrepared`から次の結果を待ち、その結果を返します。 コマンドが完了し、これ以上結果がない場合は、ヌルポインタが返されます。

      PGresult *PQgetResult(PGconn *conn);

  `PQgetResult`は、コマンドの完了を示すヌルポインタが返るまで、繰り返し呼び出さなければなりません。 (コマンド実行中以外での呼び出しでは、`PQgetResult`は単にヌルポインタを返します。) `PQgetResult`の非ヌルの結果はそれぞれ前述と同じ`PGresult`アクセッサ関数を使用して処理されなければなりません。 各結果オブジェクトに対する処理が終わったら、そのオブジェクトを`PQclear`を使用して解放することを忘れないでください。 コマンドが活動中、かつ、必要な応答データがまだ`PQconsumeInput`で読み込まれていない場合にのみ、`PQgetResult`がブロックすることに注意してください。

`PQsendQuery`と`PQgetResult`を使うことで`PQexec`の問題は1つ解決します。 つまり、コマンドが複数のSQLコマンドを含んでいる場合でも、これらのコマンドの結果を個々に得ることができるわけです （これは多重処理をシンプルな形で実現します。 単一のコマンド文字列に含まれる複数の問い合わせの内、後ろのものが処理中でもフロントエンドは先に完了した結果から扱うことができるからです）。 しかしサーバが次の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`メッセージ( 項27.7を参照)を検出することもできます。

また、`PQsendQuery`/`PQgetResult`を使用するクライアントは、サーバで処理中のコマンドに対してキャンセルを試行することができます。 項27.5を参照してください。 しかし、`PQcancel`の戻り値と関係なく、アプリケーションは`PQgetResult`を使用した通常の結果読み取り手順を続けなければなりません。 キャンセル手続きの成功は単に、そのコマンドを通常よりも早めに終わらせるだけです。

上述の関数を使用して、データベースサーバからの入力待ちのためのブロックを行わずに済みます。 しかし、まだ、サーバへの出力送信を待つためにアプリケーションはブロックする可能性があります。 これは、比較的あまり発生しませんが、非常に長いSQLコマンドやデータ値が送信される場合に発生することがあります。 (しかしアプリケーションが`COPY IN`経由でデータを送信する場合よく発生します。) この発生を防ぎ、完全な非ブロックのデータベース操作を行うためには、更に以下の関数を使用してください。

`PQsetnonblocking`

  接続の非ブロック状態を設定します。

      int PQsetnonblocking(PGconn *conn, int arg);

  `arg`が1の場合、接続状態を非ブロックに設定します。 `arg`が0の場合はブロックに設定します。 問題がなければ0が、エラー時は-1が返ります。

  非ブロック状態では、`PQsendQuery`、`PQputline`、`PQputnbytes`、`PQendcopy`の呼び出しはブロックされず、これらを再度呼び出す必要がある場合はエラーが返ります。

  `PQexec`は非ブロックモードには従いません。 この関数の呼び出しは、必ずブロック方式で動作します。

`PQisnonblocking`

  データベース接続のブロック状態を返します。

      int PQisnonblocking(const PGconn *conn);

  接続が非ブロック状態の場合は1が、ブロック状態の場合は0が返ります。

`PQflush`

  キューに蓄えられたサーバへの出力データの吐き出しを行ないます。 成功時(および送信キューが空の場合)は0が返ります。 何らかの原因で失敗した場合は-1が、送信キュー内のデータを全て送信できなかった場合は1が返ります。 (これは接続が非ブロックの場合にのみ発生します。)

      int PQflush(PGconn *conn);

非ブロック接続時にはコマンドやデータを送信した後に、`PQflush`を呼び出してください。 1が返った場合、ソケットの書き込み準備ができるまで待ち、再度呼び出してください。 これを0が返るまで繰り返してください。 `PQflush`が0を返した後は、ソケットの読み込み準備が整うまで待ち、上述のように応答を読み取ってください。

前のページ | ホーム | 次のページ
コマンド実行関数 | 上に戻る | 処理中の問い合わせのキャンセル