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がヌルポインタを返し、コマンドが完了したことを示すまでは、(同じ接続で)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プロトコルでは動作しません。
PQsendDescribePrepared
指定したプリペアド文に関する情報入手要求を送ります。 入手完了まで待機しません。
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
これはPQdescribePreparedの非同期版です。
要求の受付けが可能であれば1が返されます。不可能であれば0が返されます。
呼び出しに成功した後、PQgetResultを呼び出して結果を入手してください。
この関数のパラメータはPQdescribePreparedと同じように扱われます。
PQdescribePrepared同様、2.0プロトコル接続では動作しません。
PQsendDescribePortal
指定したポータルに関する情報入手要求を送信します。 完了まで待機しません。
int PQsendDescribePortal(PGconn *conn, const char *portalName);
これはPQdescribePortalの非同期版です。
要求の受付けが可能であれば1が返されます。不可能であれば0が返されます。
呼び出しに成功した後、PQgetResultを呼び出して結果を入手してください。
この関数のパラメータはPQdescribePortalと同じように扱われます。
PQdescribePortal同様、2.0プロトコル接続では動作しません。
PQgetResult
以前に呼び出したPQsendQuery、PQsendQueryParams、PQsendPrepare、PQsendQueryPrepared、PQsendDescribePrepared、PQsendDescribePortalから次の結果を待ち、その結果を返します。
コマンドが完了し、これ以上結果がない場合は、ヌルポインタが返されます。
PGresult *PQgetResult(PGconn *conn);
PQgetResultは、コマンドの完了を示すヌルポインタが返るまで、繰り返し呼び出さなければなりません。
(コマンド実行中以外での呼び出しでは、PQgetResultは単にヌルポインタを返します。)
PQgetResultの非ヌルの結果はそれぞれ前述と同じPGresultアクセス用関数を使用して処理されなければなりません。
各結果オブジェクトに対する処理が終わったら、そのオブジェクトをPQclearを使用して解放することを忘れないでください。
コマンドが活動中、かつ、必要な応答データがまだPQconsumeInputで読み込まれていない場合にのみ、PQgetResultがブロックすることに注意してください。
PQresultStatusが致命的なエラーを示した場合であっても、libpqがエラー情報を完全に処理できるようにヌルポインタが返されるまでPQgetResultを呼び出さなければなりません。
PQsendQueryとPQgetResultを使うことでPQexecの問題は1つ解決します。
つまり、コマンドが複数のSQLコマンドを含んでいる場合でも、これらのコマンドの結果を個々に得ることができるわけです
(これは多重処理を単純な形で実現します。
単一のコマンド文字列に含まれる複数の問い合わせの内、後ろのものが処理中でもフロントエンドは先に完了した結果から扱うことができるからです)。
PQsendQueryおよびPQgetResultで得られる、その他のよく望まれる機能は多くの問い合わせ結果を一度に1行受け取ることです。
これについては34.5で説明します。
サーバが次の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.8を参照)を検出することもできます。
また、PQsendQuery/PQgetResultを使用するクライアントは、サーバで処理中のコマンドに対してキャンセルを試行することができます。
34.6を参照してください。
しかし、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を返した後は、ソケットの読み込み準備が整うまで待ち、上述のように応答を読み取ってください。