Module jape

A PostgreSQL client for D.

Open a Connection, run a statement with its values, and read the answer the way that suits it: one value, all the rows, or one row at a time — as rows, or straight into your own structs. Values are sent to the server separately from the SQL text, as bound parameters, so they can never be mistaken for SQL: injection is not possible.

Example

struct User { int id; string name; int age; }

auto db = Connection("host=localhost dbname=app user=app");

db.exec("insert into users(name, age) values($1, $2)", "Ada", 36);
auto adults = db.scalar!long("select count(*) from users where age >= $1", 18);

foreach (u; db.stream!User("select id, name, age from users order by id"))
    writeln(u.name);

Where to start:

  • Connection — exec, scalar and stream, the three ways to run a statement;
  • Query — for when the values come from different places: db.sql(...).bind(...);
  • Transaction and Connection.transact — all or nothing, and retried when the server asks;
  • CopyIn — loading many rows fast;
  • Numeric — money and other exact decimals;
  • PgException — everything the server said when something went wrong.

jape is a thin layer over libpq, which it reads through ImportC: no hand-written bindings, and import jape_pq; reaches all of libpq when you need something this module does not wrap.

See Also

the README for a guided tour, llms-full.txt for the whole API in one file.

Functions

NameDescription
isRetryable(sqlstate) The two the server raises to mean "nothing happened, try again".

Classes

NameDescription
PgException What the server said, taken apart.

Structs

NameDescription
Column UDA mapping a member onto a differently named column: @Column("user_id") int id;
Connection A connection to the server, closed by its destructor.
CopyIn A bulk load in progress.
CopyOut Rows coming out of a COPY, one line at a time.
Field One cell. A copyable value that keeps its PGresult alive for as long as it exists.
Numeric An exact decimal, the way Postgres numeric is exact.
PreparedStatement The plan is computed once and reused for the rest of the session.
Query A builder: it accumulates bindings at runtime and only hands everything to libpq on exec(). Values are always sent separately from the SQL text, as bound parameters, so they can never be mistaken for SQL.
Result Owns the PGresult via a reference count, allowing safe use with lazy ranges.
Row One row. Also a copyable view, and it too keeps its PGresult alive.
Rows A copyable view over a result's rows: a full RandomAccessRange.
RowStream A lazy InputRange. Copyable (map/filter need that), but every copy shares one cursor — advancing any of them advances all — and the draining happens exactly once, at the last reference: breaking out of a foreach early still leaves the connection clean.
Savepoint RAII, like Transaction: if you never call release(), the destructor rolls back to it.
Transaction

Enums

NameDescription
Access A read-only transaction is refused any write, and says so if one is attempted.
Isolation How much the transaction is allowed to see of what others are doing.

Manifest constants

NameTypeDescription
defaultChunkSize Rows per chunk when stream is called without an explicit size: single-row mode.
hasChunkedRows True when the libpq headers this was compiled against provide PQsetChunkedRowsMode and PGRES_TUPLES_CHUNK, i.e. libpq 17 or newer.