PostgreSQL フロントエンド/バックエンドプロトコル

PostgreSQLのバイナリ型のデータって、PostgreSQLのフロントエンド/バックエンドプロトコル上では、どういう形式になっているのか、ちょっと調べてみた。

結論からいえば、少なくともlibpqを使った場合には、バイナリ型のデータであっても、テキスト形式としてサーバ/クライアント間では通信されているように思える。

検証

調査のために使ったのはstoneというTCP/UDPリピータ。これにはダンプのオプションがあって、それを使って中を見てみることにする。

とりあえずstoneをこんな感じで起動しておく。

$ stone -ppp localhost:5434 5435

で、psqlで5435ポートのほうに接続して、バイナリ型を含むテーブルにSELECT文をほいっと発行してみる。

$ psql test -h localhost -p 5435
psql (9.1.4)
Type "help" for help.

test=# \d bin_test
   Table "public.bin_test"
 Column |  Type   | Modifiers
--------+---------+-----------
 id     | integer |
 t_data | text    |
 b_data | bytea   |

test=# SELECT * FROM bin_test;

 id | t_data | b_data
----+--------+--------
  1 | abc001 | \x0101
(1 row)

すると、stoneを起動したターミナルにどわーっとダンプが出力される。
とりあえず最初はすっとばして、SELECT文の発行から結果返却までを抜き出すと・・・

Jun  7 11:14:22.421401 47663467702496 3 5>6 51 00 00 00 1c 53 45 4c  Q....SEL
Jun  7 11:14:22.421427 47663467702496 3 5>6 45 43 54 20 2a 20 46 52  ECT * FR
Jun  7 11:14:22.421439 47663467702496 3 5>6 4f 4d 20 62 69 6e 5f 74  OM bin_t
Jun  7 11:14:22.421463 47663467702496 3 5>6 65 73 74 3b 00           est;.
Jun  7 11:14:22.423138 47663467702496 3 5<6 54 00 00 00 4d 00 03 69  T...M..i
Jun  7 11:14:22.423152 47663467702496 3 5<6 64 00 00 00 4a 59 00 01  d...JY..
Jun  7 11:14:22.423162 47663467702496 3 5<6 00 00 00 17 00 04 ff ff  ........
Jun  7 11:14:22.423173 47663467702496 3 5<6 ff ff 00 00 74 5f 64 61  ....t_da
Jun  7 11:14:22.423183 47663467702496 3 5<6 74 61 00 00 00 4a 59 00  ta...JY.
Jun  7 11:14:22.423193 47663467702496 3 5<6 02 00 00 00 19 ff ff ff  ........
Jun  7 11:14:22.423211 47663467702496 3 5<6 ff ff ff 00 00 62 5f 64  .....b_d
Jun  7 11:14:22.423221 47663467702496 3 5<6 61 74 61 00 00 00 4a 59  ata...JY
Jun  7 11:14:22.423231 47663467702496 3 5<6 00 03 00 00 00 11 ff ff  ........
Jun  7 11:14:22.423253 47663467702496 3 5<6 ff ff ff ff 00 00 44 00  ......D.
Jun  7 11:14:22.423265 47663467702496 3 5<6 00 00 1f 00 03 00 00 00  ........
Jun  7 11:14:22.423274 47663467702496 3 5<6 01 31 00 00 00 06 61 62  .1....ab
Jun  7 11:14:22.423284 47663467702496 3 5<6 63 30 30 31 00 00 00 06  c001....
Jun  7 11:14:22.423294 47663467702496 3 5<6 5c 78 30 31 30 31 43 00  \x0101C.
Jun  7 11:14:22.423343 47663467702496 3 5<6 00 00 0d 53 45 4c 45 43  ...SELEC
Jun  7 11:14:22.423355 47663467702496 3 5<6 54 20 31 00 5a 00 00 00  T 1.Z...
Jun  7 11:14:22.423364 47663467702496 3 5<6 05 49                    .I

こんなダンプが出てくる。
で、この中をPostgreSQL文書の53.7. メッセージの書式を参考にしながら読んでみる。

Queryメッセージ

最初はQueryメッセージ

51 00 00 00 1c 53 45 4c 45 43 54 20 2a 20 46 52 65 73 74 3b 00
  • 先頭が'Q'なので簡易問い合わせ(Query)である。
  • 0000001c(28)の長さのメッセージ
  • 以降はSQL文そのもの。(SELECT * FROM bin_test)

RowDescriptionメッセージ

次はRowDescriptionメッセージ

65 73 74 3b 00・・・
  • 先頭が'T'なのでメッセージ行の記述である(RowDescription)。
  • 00 00 00 4d 長さ=77のメッセージであることを示す。
  • 00 03 は行のフィールド数=3を示す。
  • 69 64 00 は"id"(null-terminate)
  • 00 00 4a 59 はテーブルのオブジェクトID(19033)
  • オブジェクトIDの確認
test=# select oid, relname from pg_class where relname = 'bin_test';
  oid  | relname
-------+----------
 19033 | bin_test
(1 row)
  • 00 01 は"id"列の属性番号(1)。
  • 00 00 00 17 は"id"列のデータ型のオブジェクトID(23, int4)
  • 00 04 はデータ型の大きさ(4)
  • ff ff ff ff は型修飾子(-1)。-1は特に設定なしを示す。
  • 00 00 フィールドに使用される書式コード(0)。0なのでテキスト書式を示す。

で、以下2つのカラムについての情報が展開される。

  • 74 5f 64 61 74 61 00 は列名(t_data)
  • 00 00 4a 59 はテーブルのオブジェクトID(19033)
  • 00 02 は"t_data"列の属性番号(2)。
  • 00 03 はデータ型(text)
  • 00 00 00 19 は"t_data"列のデータ型のオブジェクトID(25, text)
  • ff ff はデータ型の大きさ(-1, 可変長)
  • ff ff ff ff は型修飾子(-1)。-1は特に設定なしを示す。
  • 00 00 フィールドに使用される書式コード(0)。0なのでテキストを示す。
  • 62 5f 64 61 74 61 00 は列名(b_data)
  • 00 00 4a 59 はテーブルのオブジェクトID(19033)
  • 00 03 は"b_data"列の属性番号(3)。
  • 00 00 00 11 は"b_data"列のデータ型のオブジェクトID(17, bytea)
  • ff ff はデータ型の大きさ(-1, 可変長)
  • ff ff ff ff は型修飾子(-1)。-1は特に設定なしを示す。
  • 00 00 フィールドに使用される書式コード(0)。0なのでテキストを示す。
    • ★ つまり、bytea型でもテキスト書式で送信?

DataRow

次はDataRowメッセージ。

44 00 00 1f 00 03 00 00 00 ・・・
  • 先頭が'D'なのでデータ行(DataRow)の記述である。
  • 00 00 00 1f は自身を含む、メッセージ内容の長さ(31バイト)。
  • 00 03 は後に続く列値の数(3)
  • 00 00 00 01 は列1のデータ長(1)
  • 31 は列1の値"1"
  • 00 00 00 06 は列2のデータ長(6)
  • 61 62 63 30 30 31 は列2のデータ"abc012"
  • 00 00 00 06 は列3のデータ長(6)
  • 5c 78 30 31 30 31 は列3のデータ"\x0101C"

CommandComplete

その後はCommandCompleteメッセージ

  • 先頭が'C'(0x43)なのでCommandCompleteの記述である。
  • 00 00 00 0d は自身を含む、メッセージ内容の長さ(13バイト)
  • 53 45 4c 45 43 54 20 31 00 はコマンドタグを示す(SELECT 1)。"1"は取り込んだ行数を示す。

ReadyForQuery

  • 先頭が'Z'なのでデータ行(ReadyForQuery)の記述である。
  • 00 00 00 05 は自身を含む、メッセージ内容の長さ(5バイト)。
  • 49 はトランザクション状態(待機状態(トランザクションブロックにない状態)を示す。