Open-Source Wikis

/

PostgreSQL

/

Interfaces

/

libpq

postgres/postgres

libpq

libpq is the C client library for PostgreSQL — the official wire-protocol implementation. Almost every other PostgreSQL driver either wraps it via FFI or implements the same protocol independently. Source: src/interfaces/libpq/.

Source layout

src/interfaces/libpq/
├── exports.txt           # symbols exported from the shared library
├── fe-auth.c             # authentication: cleartext, MD5, SCRAM
├── fe-auth-oauth.c, fe-auth-oauth-curl.c  # OAUTHBEARER + curl backend
├── fe-auth-sasl.c        # SCRAM-SHA-256 implementation
├── fe-cancel.c           # PQcancelCreate / PQcancelStart (async cancel API)
├── fe-connect.c          # connection establishment, options parsing
├── fe-exec.c             # query API (PQexec, PQsendQuery, PQgetResult, ...)
├── fe-gssapi-common.c, fe-secure-gssapi.c  # GSSAPI/Kerberos
├── fe-lobj.c             # large objects API
├── fe-misc.c             # I/O helpers, message accumulation
├── fe-print.c            # PQprint* (legacy, retained for compatibility)
├── fe-protocol3.c        # protocol 3 message dispatch
├── fe-secure.c           # SSL framework
├── fe-secure-openssl.c   # OpenSSL backend
├── fe-trace.c            # PQtrace
├── libpq-fe.h            # public header
├── libpq-int.h           # internal header
└── pqexpbuffer.c         # buffer string management

Public API summary

The header libpq-fe.h declares everything user code uses. Major functional groups:

Connection management

PGconn *PQconnectdb(const char *conninfo);
PGconn *PQconnectdbParams(const char * const *keywords, const char * const *values, int expand_dbname);
PGconn *PQconnectStart(const char *conninfo);                       /* nonblocking */
PostgresPollingStatusType PQconnectPoll(PGconn *conn);
ConnStatusType PQstatus(const PGconn *conn);
void PQfinish(PGconn *conn);
void PQreset(PGconn *conn);

/* Connection options */
const char *PQhost(const PGconn *conn);
const char *PQdb(const PGconn *conn);
int PQbackendPID(const PGconn *conn);
int PQsocket(const PGconn *conn);

Connection strings accept either keyword=value form (host=db.example.com user=alice dbname=mydb) or URIs (postgres://alice@db.example.com/mydb?sslmode=require). The full option list is in the docs and in the PQconninfoOptions table inside fe-connect.c.

Synchronous query

PGresult *PQexec(PGconn *conn, const char *query);
PGresult *PQexecParams(PGconn *conn,
                       const char *command,
                       int nParams,
                       const Oid *paramTypes,
                       const char * const *paramValues,
                       const int *paramLengths,
                       const int *paramFormats,
                       int resultFormat);
PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);
PGresult *PQexecPrepared(PGconn *conn, const char *stmtName, ...);
ExecStatusType PQresultStatus(const PGresult *res);
char *PQerrorMessage(const PGconn *conn);
char *PQresultErrorField(const PGresult *res, int fieldcode);
int PQntuples(const PGresult *res);
int PQnfields(const PGresult *res);
char *PQgetvalue(const PGresult *res, int row, int col);
void PQclear(PGresult *res);

PQexec runs in simple query protocol — one round trip per ;-separated statement, results all returned at once. PQexecParams switches to extended query protocol with bind parameters; safer (no SQL injection) and supports binary parameters.

Asynchronous query

int PQsendQuery(PGconn *conn, const char *query);
int PQsendQueryParams(...);
int PQconsumeInput(PGconn *conn);
int PQisBusy(PGconn *conn);
PGresult *PQgetResult(PGconn *conn);
int PQflush(PGconn *conn);

The async API is the basis of every non-blocking driver. The pattern:

  1. PQsendQuery — buffers the query, returns immediately.
  2. Caller monitors PQsocket for read/write readiness.
  3. PQconsumeInput when the socket is readable.
  4. Loop calling PQgetResult until it returns NULL. Each call returns one result; multi-statement queries produce multiple results.

PQflush empties the send buffer when the socket becomes writable (on EAGAIN).

Single-row mode

PQsetSingleRowMode(conn);

After PQsendQuery, PQgetResult returns one result per row (status PGRES_SINGLE_TUPLE), then a final PGRES_TUPLES_OK summary. Avoids accumulating large result sets in memory.

Pipeline mode

Pipeline mode (added in 14) lets a client issue many queries before reading any results, similar to HTTP pipelining:

PQenterPipelineMode(conn);
PQsendQueryParams(conn, "INSERT ...", ...);
PQsendQueryParams(conn, "INSERT ...", ...);
PQpipelineSync(conn);
/* drain results */
while ((res = PQgetResult(conn)) != NULL) { ... }
PQexitPipelineMode(conn);

The server processes them serially but doesn't have to wait for the client between each. Significant latency reduction on round-trippy workloads.

COPY

/* Server → client */
PQputCopyData(conn, buf, len);
PQputCopyEnd(conn, errormsg);

/* Client → server */
PQgetCopyData(conn, &buf, async);

COPY is a sub-protocol: after the server replies CopyOutResponse or CopyInResponse, the connection is in COPY mode and exchanges raw CopyData messages until terminated.

Notifications

LISTEN channel; (server-side) plus the client polling:

PQconsumeInput(conn);
PGnotify *n;
while ((n = PQnotifies(conn)) != NULL)
{
    /* handle n->relname, n->extra, n->be_pid */
    PQfreemem(n);
}

Combined with PQsocket poll-readiness, this is how clients implement reactive features on top of NOTIFY.

Cancellation

PQcancelCreate (newer API) plus PQcancelStart / PQcancelPoll issue an out-of-band cancel request to the backend running on behalf of this connection. The legacy PQrequestCancel is still available but blocking.

Connection establishment

sequenceDiagram
    participant Client as PQconnectdb
    participant Net as TCP/Unix socket
    participant SSL as SSL handshake
    participant Auth as Auth handshake
    participant Server

    Client->>Net: connect()
    Net-->>Client: socket open
    opt sslmode != disable
        Client->>Server: SSLRequest
        Server-->>Client: 'S' or 'N'
        alt 'S'
            Client->>SSL: TLS handshake
            SSL-->>Client: secure stream
        end
    end
    Client->>Server: StartupMessage (proto=3, params)
    Server-->>Client: AuthenticationXxx (challenge)
    loop until AuthenticationOk
        Client->>Auth: build response
        Auth->>Server: PasswordMessage / SASL response
        Server-->>Client: AuthenticationXxx
    end
    Server-->>Client: ParameterStatus*, BackendKeyData
    Server-->>Client: ReadyForQuery

The state machine in fe-connect.c is driven by PQconnectPoll. When PQconnectdb is called synchronously, libpq spins on PQconnectPoll internally; when called via PQconnectStart, the application drives the loop.

Multiple host targets — host=a,b,c port=5432,5433,5432 — produce a list of candidates that libpq tries in order. target_session_attrs=read-write filters to a writable server.

SSL backends

fe-secure.c defines a tiny abstract interface:

struct PgSSL_Methods {
    int (*open_client)(PGconn *);
    int (*close)(PGconn *);
    ssize_t (*read)(PGconn *, void *, size_t);
    ssize_t (*write)(PGconn *, const void *, size_t);
    /* ... */
};

The OpenSSL backend (fe-secure-openssl.c) is the only one in tree. GSSAPI (fe-secure-gssapi.c) provides an analogous secure stream when the server uses gss encryption.

Authentication

fe-auth.c is the dispatcher. SCRAM-SHA-256 is implemented in fe-auth-sasl.c (the SASL exchange) plus fe-auth-scram.c. OAuth (SASL-OAUTHBEARER) is split into fe-auth-oauth.c (the SASL exchange) and fe-auth-oauth-curl.c (the device-code flow), with most of the actual flow loaded from libpq-oauth so the code is only linked when needed.

Threading

libpq is "thread-safe" in the sense that different connections from different threads work fine, but a single PGconn may be used by only one thread at a time. PQisthreadsafe() reports whether libpq was built with thread support — it almost always is.

Tracing

PQtrace(conn, FILE*) writes every protocol message in human-readable form to a file. Useful when chasing protocol-level bugs in a driver.

Tests

src/test/modules/libpq_pipeline/ carries a thorough pipeline-mode regression test. SSL, GSSAPI, SCRAM, and connection-string tests live in src/test/ssl/, src/test/kerberos/, and various TAP suites. The src/interfaces/libpq/test/ directory has a connection-string parser test.

Entry points for modification

  • New connection option: register in PQconninfoOptions in fe-connect.c. Add documentation in doc/src/sgml/libpq.sgml.
  • New auth method: hook into pg_fe_sendauth in fe-auth.c; corresponding backend support in src/backend/libpq/auth.c.
  • New protocol message: a wide-blast change touching fe-protocol3.c, fe-exec.c, the backend src/backend/tcop/postgres.c, and the protocol docs. Such patches are uncommon and require careful negotiation handling for older clients.

For other clients, see Interfaces.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

libpq – PostgreSQL wiki | Factory