Skip to content

Postgre Frontend Backend Protocol简介

flabby edited this page Jun 21, 2017 · 1 revision

1. Intro

  • 基于消息的应用层协议
  • 可以使用TCP socket或者unix domain socket
  • 文档很完善,都有覆盖;

1.1 2 Pharse

  • Startup Pharse
    • 环境变量交互:user、db、encoding、etc
    • auth:passwd、SSL
    • 由server驱动,除了最开始的startup消息;
  • Normal Pharse
    • 客户端query,服务端响应;
    • 服务端也有一些主动发送的消息;
    • 两种子协议:
      • Simple Query: client发送查询文本,服务端立马执行,响应;
      • Extended Query:分为多个步骤:
        • Prepare、parse、bind、describe、execute、sync等;
        • jdbc、pdo都是extended
    • 其他特殊指令:COPY等

1.2 Extended Query Overview

  • Prepared statement
    • results of parse、analysis and plan
    • 缺少实参;
    • Prepared statement 分为named,unamed;
  • portal :
    • 有实参;

1.3 Message Format

[ header ] [ body ]

  • 两种版本的header
  • v3: [ message_type (1 byte) | length (4 bytes) ]
  • v2: [ length (4 bytes) | message_type (4 bytes) ]
  • length是网络字节序的int32;表示的是到消息剩余长度(包括自身4个字节);

2. Message Flow

2.1 Start up

step 1:Startup message

  • 建立连接后, client发送startup message;

    • case PKT_STARTUP: 包括username, dbname; 服务端根据配置文件确认是否需要额外验证;
    • case PKT_SSLREQ: SSL请求的包,服务端做出响应;
  • StartUp message:

    • PKT_STARTUP_V2 0x20000
    • PKT_STARTUP 0x30000
  • 可能的响应包:

    • ErrorResponse 'E'
    • AuthenticationOk 'R' body = 0;
    • AuthenticationMD5Password 'R' body = 4 bytes salt;
    • AuthenticationCryptPassword 'R'
    • ...

step 2: server send Welcome messages

  • client收到AuthOk后,server发送一些ParameterStatus、BackendKeyData,最终发送ReadyForQuery

  • ParameterStatus message: 'S'

    • 包括encoding、datestyle、等
    • jdbc需要响应对应的client_encoding、session_authorization
  • BackendKeyData: 'K'

    • client用来cancel的cancel key;
    • 不需要响应这个消息;
  • ReadyForQuery: 'Z' body = 'I';

    • start up结束,client可以请求;
  • ErrorResponse 'E' body = "SERROR", "C42601", 'M', msg, ""

  • NoticeResponse 'N' body = "SNOTICE", "C00000", 'M', msg, ""

2.2 Normal

2.2.1 Simple Query

  • SimpleQuery 'Q'
    • one packet
  • 可能的响应:
    • CommandComplete 'C'
    • ErrorResponse 'E'
    • CopyInResponse 'G'
    • etc

2.2.2 Extended Query

  • 分为多个阶段 (parsed statement可以多次使用提高效率)
    • parse
    • bind
    • execute
    • sync
Parse 'P'
  • 包括sql或者变量占位符,name;
  • 分为named unamed;
    • named 可以一直存在,直到显示deallocate
    • unamed一直存在到下一次的parse
  • 响应 ParseComplete '1'
Bind 'B'
  • Bind message 'B'
    • 可以包括parsed statement name,portal name;
  • 响应 BindComplete '2'
  • bind成功,会生一个portal对象;
Describe 'D'
  • 响应
    • NoData 'n'
    • RowDescription 'T'
Execute 'E'
  • 包括portal name,最大result-row count
  • 响应 CommandComplete
Sync 'S'
  • 结束当前transcation,返回结果;
Other message
  • Close
  • Flush

3 example

通过抓包分析一个jdbc,通过user1连接数据库test,执行一个insert语句的过程;

  • s1. client 发送Startup message
    • header: length = 0x67, type=0x030000;
    • body: user user1 database test client_encoding UTF8 DateStyle ISO ...
0040         00 00 00 67 00 03 00 00 75 73 65 72 00 75  .....g....user.u
0050   73 65 72 31 00 64 61 74 61 62 61 73 65 00 74 65  ser1.database.te
0060   73 74 00 63 6c 69 65 6e 74 5f 65 6e 63 6f 64 69  st.client_encodi
0070   6e 67 00 55 54 46 38 00 44 61 74 65 53 74 79 6c  ng.UTF8.DateStyl
0080   65 00 49 53 4f 00 54 69 6d 65 5a 6f 6e 65 00 50  e.ISO.TimeZone.P
0090   52 43 00 65 78 74 72 61 5f 66 6c 6f 61 74 5f 64  RC.extra_float_d
00a0   69 67 69 74 73 00 32 00 00                       igits.2..
  • s2. server 响应AuthOk, 发送ParameterStatus和ReadyForQuery
    • AuthOk 'R': header - length = 0x52
    • 一堆ParameterStatus 'S';
0040         52 00 00 00 08 00 00 00 00 53 00 00 00 16    R........S....
0050   61 70 70 6c 69 63 61 74 69 6f 6e 5f 6e 61 6d 65  application_name
0060   00 00 53 00 00 00 19 63 6c 69 65 6e 74 5f 65 6e  ..S....client_en
0070   63 6f 64 69 6e 67 00 55 54 46 38 00 53 00 00 00  coding.UTF8.S...
0080   17 44 61 74 65 53 74 79 6c 65 00 49 53 4f 2c 20  .DateStyle.ISO, 
0090   4d 44 59 00 53 00 00 00 19 69 6e 74 65 67 65 72  MDY.S....integer
00a0   5f 64 61 74 65 74 69 6d 65 73 00 6f 6e 00 53 00  _datetimes.on.S.
00b0   00 00 1b 49 6e 74 65 72 76 61 6c 53 74 79 6c 65  ...IntervalStyle
00c0   00 70 6f 73 74 67 72 65 73 00 53 00 00 00 14 69  .postgres.S....i
00d0   73 5f 73 75 70 65 72 75 73 65 72 00 6f 6e 00 53  s_superuser.on.S
00e0   00 00 00 19 73 65 72 76 65 72 5f 65 6e 63 6f 64  ....server_encod
00f0   69 6e 67 00 55 54 46 38 00 53 00 00 00 1a 73 65  ing.UTF8.S....se
0100   72 76 65 72 5f 76 65 72 73 69 6f 6e 00 38 2e 33  rver_version.8.3
0110   2e 32 33 00 53 00 00 00 20 73 65 73 73 69 6f 6e  .23.S... session
0120   5f 61 75 74 68 6f 72 69 7a 61 74 69 6f 6e 00 75  _authorization.u
0130   73 65 72 31 00 53 00 00 00 23 73 74 61 6e 64 61  ser1.S...#standa
0140   72 64 5f 63 6f 6e 66 6f 72 6d 69 6e 67 5f 73 74  rd_conforming_st
0150   72 69 6e 67 73 00 6f 6e 00 53 00 00 00 11 54 69  rings.on.S....Ti
0160   6d 65 5a 6f 6e 65 00 50 52 43 00 4b 00 00 00 0c  meZone.PRC.K....
0170   00 00 f3 0b 47 c8 22 c4 5a 00 00 00 05 49        ....G.".Z....I
  • s3. client采用Extended Query,依次发送Parse Bind Describe execute 和sync
    • Parse 'P': header - length=0x3b
    • Bind 'B': header - length=0x0c
    • Describe 'D': header - length=0x06
    • Execute 'E': header - length=0x09
    • Sync 'S': header - length=0x04
0040         50 00 00 00 3b 00 69 6e 73 65 72 74 20 69    P...;.insert i
0050   6e 74 6f 20 74 61 62 6c 65 31 20 76 61 6c 75 65  nto table1 value
0060   73 20 28 31 2c 20 31 31 20 2c 20 31 29 2c 20 20  s (1, 11 , 1),  
0070   28 32 2c 20 20 32 32 2c 20 32 29 00 00 00 42 00  (2,  22, 2)...B.
0080   00 00 0c 00 00 00 00 00 00 00 00 44 00 00 00 06  ...........D....
0090   50 00 45 00 00 00 09 00 00 00 00 01 53 00 00 00  P.E.........S...
00a0   04                                               .
  • s4. server依次响应ParseComplete,BindComplete,NoData,CommandComplete, 然后加一个ReadyForQuery;
    • ParseComplete '1';
    • BindComplete '2';
    • NoData 'n';
    • CommandComplete 'C';
    • ReadyForQuery 'Z';
0040         31 00 00 00 04 32 00 00 00 04 6e 00 00 00    1....2....n...
0050   04 43 00 00 00 0f 49 4e 53 45 52 54 20 30 20 32  .C....INSERT 0 2
0060   00 5a 00 00 00 05 49                             .Z....I