Skip to content

PostgreSQL Wire Protocol

ParticleDB implements the PostgreSQL wire protocol (v3), making it compatible with every PostgreSQL client, driver, and ORM. This is the primary interface for all SQL operations.

postgresql://[user[:password]@]host[:port][/database][?params]

Examples:

Terminal window
# Default (no auth)
postgresql://localhost:5432/main
# With authentication
postgresql://admin:secret@localhost:5432/main
# With SSL
postgresql://admin:secret@localhost:5432/main?sslmode=require
# Unix socket (lowest latency)
postgresql:///main?host=/tmp
Terminal window
# TCP connection
psql -h localhost -p 5432 -d main
# Unix socket
psql -h /tmp -d main
# With user/password
psql -h localhost -p 5432 -U admin -d main
SettingDefault
Listen address0.0.0.0:5432
Unix socketDisabled (enable with --pg-unix-socket)
Auth methodtrust (no password)
Max connections100
Idle timeout300 seconds
FeatureStatus
Simple querySupported
Extended query (Parse / Bind / Execute)Supported
Prepared statements (named and unnamed)Supported
Parameter binding ($1, $2, …)Supported
Portal suspension (max_rows)Supported
COPY FROM STDIN (tab-separated)Supported
LISTEN / NOTIFYNot yet supported
FormatStatusNotes
Text formatSupportedDefault for most clients
Binary formatSupported2-4x faster for numeric types
FeatureStatus
BEGIN / COMMIT / ROLLBACKSupported
SAVEPOINT / ROLLBACK TOSupported
Transaction isolation (SSI)Supported
SET TRANSACTIONSupported
PostgreSQL TypeParticleDB Mapping
BIGINT / INT864-bit signed integer
INTEGER / INT432-bit signed integer
DOUBLE PRECISION / FLOAT864-bit IEEE float
REAL / FLOAT432-bit IEEE float
VARCHAR / TEXTUTF-8 string
BOOLEANBoolean
TIMESTAMPMicrosecond-precision timestamp
DATECalendar date
JSON / JSONBJSON document
BYTEABinary data
VECTOR(n)n-dimensional float vector

Pipeline mode sends multiple queries in a single network round-trip, dramatically reducing latency for high-throughput workloads. ParticleDB processes pipelined queries under a single lock acquisition for maximum efficiency.

In pipeline mode, the client sends multiple Bind + Execute message pairs before waiting for results. ParticleDB buffers the responses and flushes them all on Sync.

Client ParticleDB
|--- Parse ------------------>|
|--- Bind (val=1) ----------->|
|--- Execute ----------------->|
|--- Bind (val=2) ----------->|
|--- Execute ----------------->|
|--- Bind (val=3) ----------->|
|--- Execute ----------------->|
|--- Sync ------------------->|
|<-- ParseComplete -----------|
|<-- BindComplete ------------|
|<-- DataRow (result 1) -----|
|<-- CommandComplete ---------|
|<-- BindComplete ------------|
|<-- DataRow (result 2) -----|
|<-- CommandComplete ---------|
|<-- BindComplete ------------|
|<-- DataRow (result 3) -----|
|<-- CommandComplete ---------|
|<-- ReadyForQuery ----------|

Higher pipeline depth amortizes per-query overhead:

Pipeline DepthThroughput (single connection)
1 (no pipeline)~107K ops/sec
32~2.4M ops/sec
256~5.7M ops/sec
1024~6.4M ops/sec
PQenterPipelineMode(conn);
PQsendQueryParams(conn, "SELECT * FROM t WHERE id=$1", 1, NULL, vals1, NULL, NULL, 0);
PQsendQueryParams(conn, "SELECT * FROM t WHERE id=$1", 1, NULL, vals2, NULL, NULL, 0);
PQpipelineSync(conn);
// Read results...
PQexitPipelineMode(conn);

Binary format eliminates text parsing overhead for numeric types. An INT8 is transferred as 8 raw bytes instead of its ASCII representation.

Most drivers enable binary format through a connection option or per-query flag:

# asyncpg uses binary format by default
import asyncpg
conn = await asyncpg.connect("postgresql://localhost:5432/main")
# psycopg2: use server-side cursors with binary
cur = conn.cursor("my_cursor")
cur.execute("SELECT id, price FROM products")
// pgx uses binary format by default for known types
conn, _ := pgx.Connect(ctx, "postgresql://localhost:5432/main")

Enable TLS on the server:

Terminal window
particledb start \
--tls-cert /path/to/server.crt \
--tls-key /path/to/server.key

Client connection with TLS:

Terminal window
psql "postgresql://localhost:5432/main?sslmode=require"

See TLS / Security for full certificate configuration.

ParticleDB supports three authentication methods for the PG wire protocol:

MethodFlagDescription
trust--auth-method trustNo password required (default)
password--auth-method passwordCleartext password (use with TLS)
md5--auth-method md5MD5-hashed password
Terminal window
# Start with password auth
particledb start --auth-method password --pg-password mysecret
# Connect with password
psql "postgresql://admin:mysecret@localhost:5432/main"

ParticleDB has been tested with the following PostgreSQL clients:

ClientLanguageStatus
psqlCLITested
psycopg2PythonTested
asyncpgPythonTested
pg (node-postgres)TypeScript/JSTested
pgxGoTested
tokio-postgresRustTested
PostgreSQL JDBCJavaTested
NpgsqlC# / .NETTested
libpqCTested
DBeaverGUITested
DataGripGUITested
pgAdminGUITested

ParticleDB supports the PostgreSQL COPY ... FROM STDIN protocol for bulk data loading over the wire. Data is sent in tab-separated format with one row per line.

Terminal window
# Load data from a TSV file via psql
psql -h localhost -d main -c "COPY products FROM STDIN" < products.tsv
# Or interactively in psql
psql -h localhost -d main
main=# COPY products (id, name, price) FROM STDIN;
1 Widget 9.99
2 Gadget 19.99
\.

The server reads CopyData messages from the client until it receives a CopyDone message, then bulk-inserts the parsed rows. This is significantly faster than individual INSERT statements for large data loads.

Two environment variables enable detailed protocol-level debugging:

Set PDB_WIRE_TRACE=1 to log every PG wire message (Parse, Bind, Execute, Describe, Sync, Close) with per-message timing. Useful for understanding query sequencing and identifying round-trip overhead.

Terminal window
PDB_WIRE_TRACE=1 particledb start --pg-addr 0.0.0.0:5432

Example output:

[WIRE] Parse stmt="" query="SELECT $1::int8"
[WIRE] Bind stmt="" portal="" params=1
[WIRE] Execute portal="" max_rows=0
[WIRE] Sync
[WIRE] total=42us

Set PDB_WIRE_DUMP=1 for byte-level message framing output. This dumps the raw wire bytes (message type tag, length, payload) and is intended for debugging protocol interoperability issues with specific drivers.

Terminal window
PDB_WIRE_DUMP=1 particledb start

ParticleDB correctly parses the format code field in Bind messages, which specifies whether parameter values are in text (0) or binary (1) format. A fix was applied to handle the JDBC driver’s convention of sending a single format code that applies to all parameters (as opposed to one format code per parameter). This ensures correct binary parameter decoding for PostgreSQL JDBC and other drivers that use the compact format.

Some PostgreSQL clients (notably older JDBC versions and certain ORMs) send protocol messages that deviate slightly from the v3 specification. ParticleDB includes a ClientCompat detection layer that identifies the client during the startup handshake and adjusts behavior accordingly:

  • JDBC: Accepts single-format-code Bind messages; returns INT4 OIDs for ParameterDescription when the client advertises JDBC in the application_name startup parameter.
  • psql: Sends RowDescription fields with the display format the client expects (text by default, even when the server prefers binary).
  • Default: Strict v3 compliance for all other clients.

ClientCompat is automatic and requires no configuration.

For the lowest possible latency when client and server are on the same machine, use a Unix domain socket:

Terminal window
# Start with Unix socket
particledb start --pg-unix-socket /tmp/.s.PGSQL.5432
# Connect via Unix socket
psql -h /tmp -p 5432 -d main

The default socket path is /tmp/.s.particledb.5432.