PostgreSQLのフロントエンド/バックエンドプロトコル(続き)

前回、調査したときにはバイナリデータもテキスト形式として扱われている、と書いたのだけど、よくよくマニュアルを見ると、libpqにはバイナリ書式を扱うためのオプションがあるようだ。
なので、前回調査した結果というのは「psqlを使ったから」というのが正しいのだろう。

実験

ということで、自分でバイナリデータ取得するをlibpqのアプリケーションを作って再確認することにした。といっても0から作るのはかったるいので、PostgreSQLが用意しているサンプル(postgresql-9.1.4/src/test/examples/testlibpq3.c)をベースにする。
実行結果はこんな感じ。プリペアSELECTを文を投げて、int, text, binary型のカラムを持つレコードを検索して表示する。

i format = 1
t format = 1
b format = 1
tuple 0: got
 i = (size 4 bytes) (length 4 bytes) 1
 t = (size -1 bytes) (length 4 bytes) 'ABCD'
 b = (size -1 bytes) (length 4 bytes) 0x00010203

で、前回と同様にstoneを使ってダンプを取得してみた。

結論

結論からいうと、単純問い合わせでない場合には、バイナリ型はきちんとバイナリとしてバックエンドからフロントエンドに送信されているっぽい。
フロントエンド/バックエンドプロトコルのシーケンスとしては

  • Parse(F→B)
  • Bind(F→B)
  • Describe (F→B)
  • Execute (F→B)
  • Sync (F→B)
  • ParseComplete (B→F)
  • BindComplete (B→F)
  • RowDescription (B→F)
  • DataRow (B→F)

の順序で発行されている。
で、最後のDataRowのダンプを見ると

               44 00 00
00 1e 00 03 00 00 00 04
00 00 00 01 00 00 00 04
41 42 43 44 00 00 00 04
00 01 02 03

のようになっている。この部分を頭から見ていくと・・・

  • 先頭は'D'(0x44)なので、メッセージがデータ行の記述であることを識別します。
  • 自身を含む、メッセージ内容の長さ(バイト単位)。0x0000001e = 30
  • 後に続く列値の数。 0x0003 = 3
  • 列(1)値のバイト単位の長さ。0x00000004 = 4
  • 関連する書式コードで示される書式における列(1)の値。 0x00000001 = 1
  • 列(2)値のバイト単位の長さ。0x00000004 = 4
  • 関連する書式コードで示される書式における列(2)の値。 0x41424344 = 'ABCD'
  • 列(3)値のバイト単位の長さ。0x00000004 = 4
  • 関連する書式コードで示される書式における列(3)の値。 0x00010203 ★ ここはバイナリになっている。