このプロトコルでは、接続開始と通常操作で段階が分かれています。 接続開始段階で、フロントエンドはサーバへの接続を開き、サーバの義務を履行できるよう自身を証明します。 (これは使用する認証方法に応じて、単一のメッセージになったり、複数のメッセージになったりします。) すべてうまく行けば、サーバはフロントエンドに状態情報を送信し、最終的に通常操作段階に入ります。 初期の開始要求メッセージを除いて、プロトコルのこの部分はサーバによって駆動されます。
通常操作の間、フロントエンドは問い合わせやその他のコマンドをバックエンドに送信し、バックエンドは問い合わせ結果やその他の応答を返送します。
(NOTIFY
のように)バックエンドから依頼されずにメッセージが送信されるまれな場合がありますが、セッションのこの部分のほとんどはフロントエンドの要求によって駆動されます。
セッションの終了は通常フロントエンドが選択することですが、特定の場合はバックエンドによって強制される可能性があります。 どちらの場合でも、バックエンドが接続を閉ざす時、終了前に実行中の(未完の)トランザクションをすべてロールバックします。
通常操作中は、SQLコマンドを2つのサブプロトコルのうちのいずれかによって実行することができます。 「簡易問い合わせ」プロトコルでは、フロントエンドはテキストで問い合わせ文字列を単に送信し、バックエンドによって解析され、即実行されます。 「拡張問い合わせ」プロトコルでは、問い合わせの処理は、解析、パラメータ値の結び付け、そして実行という複数の段階に分離されます。 これは複雑性が加わりますが、柔軟性と性能という点で利点が生まれます。
通常操作には、さらに、COPY
のような特殊な操作向けのサブプロトコルがあります。
すべての通信はメッセージストリームを介します。 メッセージの先頭バイトはメッセージ種類を識別するもの、次の4バイトはメッセージの残りの長さを指定するものです (この長さにはメッセージ種類バイトは含まれませんが、自身を含んで数えられます)。 残りのメッセージの内容は、メッセージ種類で決まります。 歴史的な理由のため、一番初めにクライアントから送信されるメッセージ(開始メッセージ)にはメッセージ種類バイトはありません。
メッセージストリームの同期ずれを防ぐために、サーバとクライアントの両方は、通常、メッセージの内容を処理し始める前に、(バイト数を使用して)メッセージ全体をバッファ内に読み込みます。 これにより、その内容を処理する時にエラーが検出された場合に、簡単に復旧することができます。 (メッセージをバッファするために十分なメモリがない場合のような)極限状況では、受信側はメッセージの読み取りを再開する前にどれだけの量の入力を飛ばすかどうかを決定するために、バイト数を活用することができます。
反対に、サーバとクライアントの両方は、不完全なメッセージを決して送信しないように注意しなければなりません。 これは通常、送信する前にバッファ内のメッセージ全体を整列させることで行われます。 メッセージの送受信の途中で通信エラーが発生した場合、メッセージ境界の同期を復旧できる望みはほとんどありませんので、実用的な唯一の応答は通信を中断することです。
拡張問い合わせプロトコルでは、SQLコマンドの実行は複数の段階に分割されます。
段階間で保持される状態は、プリペアド文とポータルの2種類のオブジェクトで表現されます。
プリペアド文は、テキスト問い合わせ文字列の解析、意味解析を表現します。
プリペアド文は実行準備が整ったことを示すものではありません。
パラメータの特定の値が欠落しているかもしれないからです。
ポータルは、すべてのパラメータ値が設定され、実行準備が整った、あるいは、既に一部実行された文を表現します。
(SELECT
文では、ポータルは開いているカーソルと等価です。
しかし、カーソルはSELECT
以外の文を扱いませんので、ここでは異なる用語を使用するよう選択しました。)
実行サイクル全体は、テキストの問い合わせ文字列からプリペアド文を生成するparse段階、プリペアド文と必要なパラメータ値によりポータルを作成するbind段階、ポータルの問い合わせを実行するexecute段階からなります。
行を返す問い合わせ(SELECT
、SHOW
など)の場合、操作を完了させるために複数の実行段階が必要とすることができるように、実行段階に限定された行数のみを取り出すよう指示することができます。
バックエンドは複数のプリペアド文とポータルの経過を追うことができます (しかし、1つのセッション内でのみ存在可能です。複数のセッションで共有することはできません)。 存在するプリペアド文とポータルは、作成時に割り当てられた名前で参照されます。 さらに、「無名の」プリペアド文とポータルも存在します。 これらは名前付きのオブジェクトとほとんど同じ動きをしますが、問い合わせを一回だけ実行し、その後に破棄する場合に備えて、これらに対する操作は最適化されています。 一方、名前付きオブジェクトの操作は複数回の使用を想定して最適化されています。
特定のデータ型のデータはいくつかの異なる書式で転送することができます。 PostgreSQL 7.4の時点でサポートしている書式は「テキスト」と「バイナリ」のみですが、プロトコルは将来の拡張に備えて準備をしています。 任意の値の必要な書式は書式コードで指定されます。 クライアントは、転送されるパラメータ値それぞれに書式コードを指定することも、問い合わせ結果の列それぞれに書式コードを指定することもできます。 テキストは書式コード0、バイナリは書式コード1です。 他の書式コードは将来の定義用に予約されています。
値のテキスト表現は、特定のデータ型の入出力変換関数で生成され、受け付けられる何らかの文字列です。 転送時の表現では、ヌル終端文字はありません。 フロントエンドでC言語文字列として処理したい場合は、必ず受信した値にヌル終端文字を追加しなければなりません。 (ついでですが、テキスト書式ではヌルを埋め込むことはできません。)
整数用のバイナリ表現はネットワークバイト順(先頭に最上位バイト)を使用します。 他のデータ型のバイナリ表現については、文書もしくはソースコードを参照してください。 複雑なデータ型のバイナリ表現はサーバのバージョンによって異なる可能性があることに注意してください。 通常、テキスト書式がより移植性が高い選択肢です。