pqi

Driver-agnostic interface to the PostgreSQL libpq API

pqi

Hackage Continuous Haddock

A driver-agnostic interface to the PostgreSQL libpq API.

Ecosystem

PackageDescription
pqi (this)The interface: IsConnection class, shared types, and connection-independent helpers
pqi-ffiFFI adapter backed by postgresql-libpq and the C libpq library. Battle-tested, production-safe
pqi-nativePure-Haskell adapter speaking the PostgreSQL wire protocol directly. No C dependency. Experimental
pqi-conformanceReusable hspec conformance suite that differentially tests any adapter against postgresql-libpq

Motivation

Every major Haskell PostgreSQL driver today depends on postgresql-libpq, a binding to the C libpq library. This means every user of every driver needs libpq installed — on their development machine, in CI, in production containers, on cross-compilation targets. There is no way to opt out.

pqi solves this by separating the interface from the implementation. It defines a driver-agnostic type class (IsConnection) that mirrors the libpq API surface, then ships two adapters:

  • pqi-ffi — a thin wrapper around postgresql-libpq. Battle-tested, production-safe. The default choice.
  • pqi-native — a pure-Haskell implementation of the PostgreSQL wire protocol, generated with LLM assistance. Experimental. It produces byte-identical output to postgresql-libpq for all protocol-derived values (verified by differential testing), but it has not yet been exercised in production at scale. If you adopt it, we want to hear from you.

A driver built against pqi gives its users transport choice without any changes to the driver itself. The user picks an adapter at connection time:

-- C-backed (safe, requires libpq)
connection <- connect (Proxy @Pqi.Ffi.Connection) settings

-- Pure Haskell (experimental, no C dependency)
connection <- connect (Proxy @Pqi.Native.Connection) settings

Testing model

pqi comes accompanied by a comprehensive conformance suite isolated into an implementation-agnostic pqi-conformance package that covers various edge-cases and error conditions and covers most operations with a precondition that they must behave in exactly the same way that postgresql-libpq does.

Interface

pqi reproduces the API surface of the postgresql-libpq package, but reifies the connection — and the results it produces — as a type class instead of a single concrete type. Code written against this interface runs unchanged on any adapter:

  • pqi-ffi — a thin adapter backed by the C libpq library via postgresql-libpq.
  • pqi-native — a pure-Haskell adapter that speaks the PostgreSQL wire protocol directly.

The interface mirrors libpq in semantics, not just shape: every compliant adapter must produce byte-identical output to libpq for all protocol-derived values. This contract is enforced by pqi-conformance, which runs every operation differentially against postgresql-libpq and asserts equality.

This package ships only the interface: the IsConnection class, the associated ResultOf result type family, the shared type vocabulary (statuses, field codes, formats, OIDs), and the connection-independent helpers.

Relationship to postgresql-libpq

The function names, argument order, and semantics mirror Database.PostgreSQL.LibPQ. The deliberate departures are:

  • Connection and Result become the class parameter c and the associated type family ResultOf c.
  • OIDs are a plain Word32 and row/column/parameter indices are a plain Int32, instead of the C-specific newtypes of the original.
  • There's no invalidOid constant. It's just 0.
  • Ambiguous, rarely-useful helpers (e.g. resStatus) are omitted.