[ANN] Postgres.jl: a Julia-native PostgreSQL client

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 COPY protocol 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 use DataDecimals.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

6 Likes