Skip to content

PostgreSQL libpq dependency review

This note records the dependency review for adding PostgreSQL support through libpq, the PostgreSQL project-owned C client library.

Decision

libpq is accepted as the PostgreSQL client dependency for Merovingian. It is wrapped inside the database module and must not leak PGconn, PGresult, or other libpq-specific types into homeserver services.

Why it is needed

PostgreSQL is the intended production database backend. Speaking the PostgreSQL wire protocol directly would be high-risk, unauditable project code. libpq is the native PostgreSQL client API, is packaged by supported Linux and BSD distributions, and gives the project a stable interface for connection management, parameterized execution, and PostgreSQL error reporting.

Security boundary

  • Connection strings are accepted only in explicit PostgreSQL URI or libpq key/value form.
  • Connection-opening diagnostics use a fixed redacted identity and generic parser/connection-status refusal reasons, not a reconstructed option summary or forwarded library error text. Host/database values are suppressed too. Actual DEBUG-console and returned-diagnostic confidentiality checks cover malformed quoted input and policy-admitted private missing-socket failures; they do not establish general SQL-result error or live authentication/TLS confidentiality. See ADR-0139.
  • PGconn ownership is RAII-managed with PQfinish.
  • PGresult ownership is RAII-managed with PQclear.
  • Statement execution uses PQexecParams so parameter values stay separate from SQL command text.
  • Existing project statement-name, SQL-shape, and placeholder validation still runs before libpq execution.
  • Runtime startup uses PostgreSQL only when a URI file is explicitly present; otherwise existing in-memory test flows remain isolated from ambient databases.
  • Runtime startup requires database.role=runtime; offline migration planning requires database.role=migration.

Maintenance and platform posture

libpq is resolved from the operating-system PostgreSQL client package and linked dynamically. The top-level Meson dependency sets allow_fallback: false, so clean builds must provide libpq.pc through the host package manager even when the wrapper scripts use Meson's forcefallback mode for other dependencies. There is no subprojects/libpq.wrap (ADR-0218).

Supported development paths install the OS development package explicitly: Debian-family hosts use libpq-dev, Fedora-family hosts use libpq-devel, FreeBSD uses postgresql17-client, OpenBSD uses postgresql-client, and NetBSD/pkgsrc uses postgresql17-client.

License

PostgreSQL libpq is released under the PostgreSQL license (SPDX: PostgreSQL). It is compatible with Merovingian's GPL-3.0-or-later distribution and is resolved from the operating-system PostgreSQL client package.

Current limitations

  • Live integration tests against a temporary PostgreSQL server are gated by MEROVINGIAN_TEST_POSTGRESQL_URI.
  • Separate PostgreSQL users/grants for runtime and migration roles are enforced at connection time: open_postgresql_persistent_store() (src/database/postgresql_store.cpp) runs SET ROLE to the configured migration role before applying any DDL and to the runtime role before serving traffic, refusing to open the store if either role cannot be assumed. This depends on the two roles existing and owning the schema objects — provision them with packaging/postgresql/provision-roles.sql before pointing database.migration_role/database.runtime_role at a deployment.

Source references

  • PostgreSQL libpq connection control documentation.
  • PostgreSQL libpq command execution documentation.