惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
L
LangChain Blog
美团技术团队
N
Netflix TechBlog - Medium
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog
博客园 - 司徒正美
爱范儿
爱范儿
D
DataBreaches.Net
月光博客
月光博客
U
Unit 42
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
Apple Machine Learning Research
Apple Machine Learning Research
Jina AI
Jina AI
MongoDB | Blog
MongoDB | Blog
腾讯CDC

EDB

FOSS4G NA Enterprise Automation Resilience: Red Hat AAP on EDB Postgres AI EDB heads to PGConf.Brasil 2026, this is what we’ll be talking about! Powering Invisible Commerce at World Cup Speed By the Time Your Data Warehouse Answers, the Opportunity Is Gone Building a Sovereign, Intelligent Data Foundation with EDB Postgres® AI on IBM LinuxONE 5 Deep Dive Into EDB Postgres AI's Agentic Database Capabilities Jumping the gun: looking ahead at PostgreSQL 19 Meeting in Montreal: Developer U plan(ner) patches KubeCon + CloudNativeCon NA EDB Summer Academy Your Database Goes Down. What Does That Cost Your Business? The Oracle Renewal Is Coming. This Time, There’s a Way Out. One Dashboard to Rule Them All — and Finally Get Your Fridays Back Your Database Should Be Working While You Sleep Inside the Agentic Database: How EDB Turned Postgres Into a Self-Managing System The Architecture IS the Security: Building Sovereign AI Ops on Postgres with EDB Agent Factory EDB Named a Leader in Multimodel Data Platforms Evaluation PGDay Hyderabad The Role of AI in Data Analytics: Moving From Hype to High-Octane Utility Iga Januszek Mike Olifirowicz Meeting EU Data Sovereignty Requirements While Speeding-Up Innovation Inside EDB’s New Principles for Responsible AI: Sovereign, Governed, Trusted and Beneficial Built From the Data Up: A Trusted Foundation for the Agentic Era | EDB Postgres® AI Q2-2026 Release EDB Launches Agentic Database, Converged Analytics, and Governance, Bringing Sovereign AI Where Enterprise Data Already Lives Stop Spending Hours on What Should Take Minutes: A DBA's Guide to EDB Postgres AI’s Agentic Database Capabilities Making Agentic AI Smarter at the Architecture Level Charly Batista Buildfarm Query API
Documenting the PostgreSQL protocol with pg_protoexport
Xavier Fischer · 2026-07-14 · via EDB

TL;DR: pg_protoexport is a PostgreSQL protocol documentation tool. It is opensource, multiplatform and publicly available here https://github.com/xfischer/pg_protoexport

Every conversation between a PostgreSQL client and server is just bytes on a wire. The wire protocol is well documented, but the docs are abstract: a `Parse`, a `Bind`, an `Execute`, a stream of `DataRow`s. When you open a real capture in Wireshark, you see the packets — but not the story they tell.

I was doing research around the protocol, and I wanted to share some findings with my team, including protocol diagrams.

ASCII art could be used to represent packets, but it’s quite painful to do this by hand. How can I draw one of those? Are there any obvious tools around that do this?

The database used in this example is the famous pagila sample database updated for PostgreSQL by EDB colleague Devrim Gunduz and Robert Treat (AWS).

Visualizing a simple query conversation

Let’s dig into a basic example: a simple query returning one data row:

Here is the query run with psql:

pagila=# SELECT * FROM actor LIMIT 1;
 actor_id | first_name | last_name |     last_update
----------+------------+-----------+---------------------
        2 | NICK       | WAHLBERG  | 2006-02-15 09:34:33
(1 row)

Now, if we were to explain what is sent and received on the wire, we could use plain english:

Messages are "Type Length Value". The first byte of a message identifies the message type, and the next four bytes specify the length of the rest of the message (this length count includes itself, but not the message-type byte). The remaining contents of the message are determined by the message type. 

A Query message is sent with the query text as the payload. The server responds with a RowDescription, DataRow, CommandComplete and ReadyForQuery messages.

Ref: PostgreSQL official documentation (Simple Query and Query message format

PQtrace

Another common way of communicating is the PQtrace format. It's the format used by libpq when message logging is activated. This can only be done via C code. (timestamps omitted for readability)

F	33	Query	 "SELECT * FROM actor LIMIT 1;"
B	120	RowDescription	 4 "actor_id" 1469051 1 23 4 -1 0 "first_name" 1469051 2 1043 65535 49 0 "last_name" 1469051 3 1043 65535 49 0 "last_update" 1469051 4 1114 8 -1 0
B	54	DataRow	 4 1 '2' 4 'NICK' 8 'WAHLBERG' 19 '2006-02-15 09:34:33'
B	13	CommandComplete	 "SELECT 1"
B	5	ReadyForQuery	 I

Here we can see that the frontend (F) sends a Query message, and the backend (B) sends 4 messages back.

This format is pretty compact (and has been improved since PG14). 

We can assume that RowDescription gives the following information :

  • Query syntax is OK

  • There are incoming DataRow messages

  • What columns to expect

While very useful, if your application is not using libpq, this approach is not an option.

ASCII Art

Another common way of communicating is via ASCII art: we represent the packet as a datagram using text.

There are two ways of communicating what's going on. First a sequence diagram where frontend and backend are on opposite sides and we see a compact view of messages sent, with timings:

Client (::1:51885)                 Server (::1:5434)
|                                                  |
|         16:36:55.580514 (capture start)          |
|--------------------- Query --------------------->|
|                                                  |
|           Δ +3094 µs (total +3094 µs)            |
|<-- RowDescription / DataRow / CommandComplete ---|
|                                                  |
|<---------------- ReadyForQuery ------------------|

Another way is when giving full details in a graphic way:

16:36:55.580514 (capture start)
[F->B] Query (34 bytes)
0      1        5                               34
+------+--------+--------------------------------+
| code | length |             query              |
| 'Q'  |   33   | "SELECT * FROM actor LIMIT 1;" |
+------+--------+--------------------------------+

Δ +3094 µs (total +3094 µs)
[B->F] RowDescription (121 bytes)
0      1        5            7
+------+--------+------------+
| code | length | fieldCount |
| 'T'  |  120   |     4      |
+------+--------+------------+
    7             16          20             22         26              28              32       34
    +-------------+-----------+--------------+----------+---------------+---------------+---------+
    | columnName0 | tableOid0 | columnIndex0 | typeOid0 | columnLength0 | typeModifier0 | format0 |
    | "actor_id"  |  1469051  |      1       |    23    |       4       |      -1       |    0    |
    +-------------+-----------+--------------+----------+---------------+---------------+---------+
    34             45          49             51         55              57              61       63
    +--------------+-----------+--------------+----------+---------------+---------------+---------+
    | columnName1  | tableOid1 | columnIndex1 | typeOid1 | columnLength1 | typeModifier1 | format1 |
    | "first_name" |  1469051  |      2       |   1043   |      -1       |      49       |    0    |
    +--------------+-----------+--------------+----------+---------------+---------------+---------+
    63            73          77             79         83              85              89       91
    +-------------+-----------+--------------+----------+---------------+---------------+---------+
    | columnName2 | tableOid2 | columnIndex2 | typeOid2 | columnLength2 | typeModifier2 | format2 |
    | "last_name" |  1469051  |      3       |   1043   |      -1       |      49       |    0    |
    +-------------+-----------+--------------+----------+---------------+---------------+---------+
    91              103         107            109        113             115             119     121
    +---------------+-----------+--------------+----------+---------------+---------------+---------+
    |  columnName3  | tableOid3 | columnIndex3 | typeOid3 | columnLength3 | typeModifier3 | format3 |
    | "last_update" |  1469051  |      4       |   1114   |       8       |      -1       |    0    |
    +---------------+-----------+--------------+----------+---------------+---------------+---------+

[B->F] DataRow (55 bytes)
0      1        5            7
+------+--------+------------+
| code | length | fieldCount |
| 'D'  |   54   |     4      |
+------+--------+------------+
    7               11            12
    +---------------+--------------+
    | columnLength0 | columnValue0 |
    |       1       |      2       |
    +---------------+--------------+
    12              16            20
    +---------------+--------------+
    | columnLength1 | columnValue1 |
    |       4       |    "NICK"    |
    +---------------+--------------+
    20              24            32
    +---------------+--------------+
    | columnLength2 | columnValue2 |
    |       8       |  "WAHLBERG"  |
    +---------------+--------------+
    32              36                     55
    +---------------+-----------------------+
    | columnLength3 |     columnValue3      |
    |      19       | "2006-02-15 09:34:33" |
    +---------------+-----------------------+

[B->F] CommandComplete (14 bytes)
0      1        5           14
+------+--------+------------+
| code | length |  message   |
| 'C'  |   13   | "SELECT 1" |
+------+--------+------------+

[B->F] ReadyForQuery (6 bytes)
0      1        5            6
+------+--------+------------+
| code | length | statusCode |
| 'Z'  |   5    |    'I'     |
+------+--------+------------+

Graphical representations

Text is very portable. But there are tools around that handle the graphical representation better than text. I chose PlantUML and Mermaid as they are the ones I am familiar with.

PlantUML is pretty handy, I used to work a lot with it. As it's not rendered easily without plugins, the final product is usually an image. 

Mermaid is the (relatively) new guy in town, and is natively supported on GitHub and other platforms. Great for sharing diagrams!

This diagram can be viewed live on the pg_protoexport repository. The full packet detailed view is here. (however, not that readable)

pg_protoexport Mermaid sequence diagram

LaTeX is my preferred ❤️ rendered, and the first exporter I wrote. With the bytefield package, it renders nice and clean diagrams:

pg_protoexport Latex datagram

With this LaTex rendering you have a good level of control over the design. The main caveat is that the final product is usually an image (I generate it with ImageMagick) or a PDF.

A new tool is born: pg_protoexport

This idea of documenting protocol conversations became code. I did the LaTeX implementation by hand and then used Claude Code to refactor, handle CI/CD and add more exporters. Here are the features implemented:

  • Nearly all message formats are supported
  • Six output formats included: ASCII, LaTeX, PQTrace-style, Mermaid, PlantUML, and HTML
  • Live capture: a capture command records a .pcapng straight from a NIC, so you no longer need to reach for tcpdump or Wireshark by hand.
  • Auto port detection, batch export over a directory of captures, and an interactive demo tour for newcomers.
  • Cross-platform (Windows, macOS, Linux)
  • C# distribution via NuGet packages (If you want to build upon the tool)

The tool is distributed through two channels:

  • Self contained binaries on GitHub releases page
  • As a .NET CLI Tool if you have .NET SDK installed: 

    dotnet tool install --global pg_protoexport

And it also launched my speaker journey, as I gave several talks about the protocol in 2026 (PGDay ArmeniaPGConf.BE and Swiss PG Day), presenting the tool.

I will give this “Deep dive into the PostgreSQL frontend/backend protocol” next at PG Summit US 2026 and will be happy to do a demo of pg_protoexport at the EDB booth.

All message diagrams is my slides were generated by pg_protoexport.

Source code is available here, and contributions are welcome!