Hey all,
I’ve been working on Postgres.jl for a while now, a PostgreSQL client written in Julia! Version 2.0 is available, and I’d love to have people try it out.
Postgres.jl implements PostgreSQL’s wire protocol directly, using Reseau.jl for networking. It doesn’t wrap libpq. The public API uses DBInterface for connections and queries, and query results support both Tables.jl and StructUtils.jl, so results can be handled in nice tabular or domain-model friendly ways, respectively.
Highlights include:
- Parameterized queries and prepared statements. Bind Julia values through
DBInterface.execute, with connection-level statement caching. - Transactions and connection pools. Transaction blocks handle commit and rollback, nested transactions use savepoints, and pools give concurrent tasks separate connections.
- Streaming and bulk transfer. Cursors fetch rows in batches. PostgreSQL’s
COPYprotocol supports bulk input and output, including binary payloads. - Typed results. Read rows through
Tables.jl, or ask for a Julia struct or named tuple through the StructUtils integration. Custom enums, composites, and ranges can be registered for decoding. - Shared timestamp and decimal types. Timestamps use
Durations.Timestamp{Dates.Microsecond}, preserving PostgreSQL’s six fractional digits. Numeric values useDataDecimals.DecimalValue{DataDecimals.Int256}, preserving the value and scale. Explicit decimal targets require an exact conversion. - PostgreSQL connection features. URI and keyword connection strings, environment defaults, SCRAM authentication, TLS, query cancellation, and
LISTEN/NOTIFY.
A small example
On Julia 1.10 or later:
import Pkg
Pkg.add("Postgres)
With a local PostgreSQL server, adjust the connection details and run:
using Postgres
params = Postgres.ConnectionParams(
host="127.0.0.1", port=5432,
user="postgres", password="postgres", dbname="postgres",
sslmode="disable", # local example; use verify-full for authenticated TLS
)
DBInterface.connect(Postgres.Connection, params) do conn
row = only(DBInterface.execute(conn, raw"""
SELECT $1::int AS id,
$2::text AS name,
'2026-09-16 12:34:56.123456'::timestamp AS observed_at,
12.3400::numeric AS amount
""", (1, "Julia")))
@show row.id row.name
@show row.observed_at # all six fractional digits
@show row.amount # 12.3400
end
using Postgres re-exports DBInterface; package-specific APIs use the Postgres. namespace. The manual has examples for transactions, pools, typed queries, cursors, and COPY.
A few details worth knowing
timestamptz results are returned in UTC. Explicit DateTime fields are available when millisecond precision is sufficient; those fields truncate finer fractions. Timestamp parameters with submicrosecond precision raise an error.
CI tests PostgreSQL 14 through 18, including real database round trips and certificate tests on Linux. The suite also includes seeded fuzz tests for protocol framing, connection strings, arrays, and composite values. Connections currently use TCP; Unix-domain sockets are not supported. Client-certificate TLS uses TLS 1.2. The support policy covers the other limits, including transaction-mode poolers.
Please give it a try! I’d especially appreciate reports from real applications, managed PostgreSQL services, and PgBouncer setups. If something doesn’t work, open an issue with the Julia/Postgres.jl/PostgreSQL versions and a small example, with credentials removed. Questions and suggestions here are welcome too.
-Jacob Quinn