[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

Starred!

really appreciate you, so many best-in-class foundational packages (HTTP, CSV, JSON), and now this! if we only had x10 Quinnj the Julia lang and ecosystem would fly

Agreed! Only a year ago I was worried if investing in Julia would be a mistake for production systems, especially for smaller teams that cannot afford to write high quality foundational packages themselves. That concern was entirely erased in 2026. Big thank you to Jacob and everyone else involved in these efforts, it’s significantly expanded the horizons for Julia in terms of what is possible and painless in the language now.

Having a modern / native Postgres client is already making me consider Julia for future database applications, can’t wait to try it out!

I have been using LibPQ.jl for several years and found it both performant and easy to use. Could you briefly comment on what the advantage of Postgresql.jl is over LibPQ.jl? Should I switch?

Many thanks.

This is excellent work. there’s Octo.jl v0.3.0 release. it’s about using Postgres.jl as its backend. Thanks to this, the update process was smooth and easy.

Thanks, Roger, the main reasons to consider switching are the additional conveniences and Julia integration, I asked AI to help come up with a few useful bullet points highlighting the differences:

  • Less connection and transaction code. Postgres.jl includes connection pools, transaction blocks that handle commit/rollback, nested transactions through savepoints, and cursors that fetch results in batches. You can build these workflows with LibPQ, but Postgres provides the helpers directly.

  • Query directly into your own types. The StructUtils integration lets a query return a Julia struct or Vector{YourType}, including mapping SQL column names to Julia field names. This can remove a fair amount of result-conversion code. These features have examples in the manual.

  • Microsecond timestamps by default. Postgres preserves PostgreSQL’s six fractional timestamp digits. LibPQ’s default timestamp mappings retain milliseconds, although it supports custom conversions. This is a concrete benefit if timestamp precision matters to your application. (Postgres types, LibPQ timestamp parsing)

  • The client implementation is Julia. Postgres implements the wire protocol directly, without depending on libpq. That makes the driver easier to inspect and extend using Julia tools. It does not, by itself, mean every query will be faster.

In addition, I think Postgres.jl is currently in a more maintainable state; LibPQ.jl has long lacked active maintainership and it’s been hard to get updates merged & released.

Give it a whirl and let me know how it goes!

Thanks! Sounds good, but I have a lot of code that uses LibPQ.jl. Nevertheless, I will give it a try, I have to do a lot of boxing into Julia types and that sounds easier or even unnecessary with Postgres.jl

I switched from LibPQ.jl to Postres.jl with the benefit that my program will now precompile as mentioned in Interface FunSQL with LibPQ using DBInterface - #11 by Jake . For details on the interface see Using FunSQL.jl with Postgres.jl .