Modules

Postgres

moduleSource
macOSLinuxWindows

since 0.27.0

#import "Extensions/Postgres";

A PostgreSQL client over Io fibers. A jaic extension, not official Jai: other Jai compilers have no such module, and it may change between jaic releases. See docs/extensions/postgres.md.

Example
Io :: #import "Extensions/Io";
Pg :: #import "Extensions/Postgres";

main :: () {
    Io.io_run(() {
        config, error := Pg.parse_url("postgres://app@localhost/app_dev");
        db:, error = Pg.connect(config);
        if error.kind != .NONE { print("%\n", Pg.error_text(error)); return; }
        defer Pg.close(db);

        result:, error = Pg.query_dynamic(db, "SELECT id, name FROM users WHERE id > $1", 10);
        defer Pg.free_result(*result);
        for row: 0..result.row_count - 1 {
            id := Pg.get(*result, row, 0, s64);
            name := Pg.get(*result, row, 1, string);
        }
    });
}

This one is only checked to compile, not run.

Show moreShow less

Every call that talks to the server runs on a fiber inside Io.io_run and blocks only that fiber. Io.with_deadline bounds any of them.

TLS needs Extensions/Tls (mbedTLS). Programs that only connect over Unix sockets or cleartext TCP can leave it out with #import "Extensions/Postgres"(TLS = false).

The other parameters say where the compile-time side finds the project's files, relative to the project root (docs/postgres/typed-queries.md, "Module parameters"):

Example
Pg :: #import "Extensions/Postgres"(MIGRATIONS = "sql/migrations", SCHEMA = "sql/schema.jai", QUERY_CACHE = "sql/cache", MIN_SERVER_VERSION = 15);

Doesn't run in the browser (skipped on wasm).

MIN_SERVER_VERSION is a major version; a server older than that fails every describe.

Procedures#

Error#

error_textsince 0.27.0
(error: Pg_Error) -> string

One line describing the error, in temporary storage.

sqlstate_textsince 0.27.0
(code: Sqlstate) -> string

The five characters of a code, in temporary storage.

sqlstate_classsince 0.27.0
(code: Sqlstate) -> Sqlstate

The code with only its class kept: sqlstate_class(.UNIQUE_VIOLATION) == .INTEGRITY_CONSTRAINT_VIOLATION.

Config#

parse_urlsince 0.27.0
(url: string) -> (Config, Pg_Error)

postgres://user:password@host:port/database?sslmode=verify-full&application_name=api. postgresql:// works too. A Unix socket directory goes in host, percent-encoded or as a parameter: postgres:///app?host=/var/run/postgresql. It is parse_conninfo for a URI.

parse_conninfosince 0.27.0
(text: string, use_environment := false) -> (Config, Pg_Error)

libpq's connection strings: a URI (see parse_url) or host=localhost port=5432 dbname=app, where a value with spaces is quoted, 'a value', and \ escapes the next character (\' and \\ inside quotes). Parameters (keywords): host, hostaddr, port, user, password, passfile, dbname, sslmode, sslnegotiation (direct), sslrootcert (a PEM file, or system), sslcert, sslkey, sslpassword, sslcertmode, sslcrl, sslcrldir, sslsni, sslkeylogfile, ssl_min_protocol_version, ssl_max_protocol_version, channel_binding, application_name, fallback_application_name, options, connect_timeout (seconds), client_encoding, replication and require_auth. libpq's other keywords are accepted and ignored. Errors read like libpq's.

(info: Conninfo, use_environment := false) -> (Config, Pg_Error)

The Config for the options of a connection string, as parse_conninfo makes it. Programs that combine several sources (a command line and a connection string) set the options themselves with conninfo_set first.

copy_configsince 0.27.0
(config: Config) -> Config

A config that owns copies of its strings, for keeping beyond the original (free_config).

free_configsince 0.27.0
(config: *Config)

Frees the strings of a config from parse_conninfo, parse_url or copy_config; does nothing for one the program built.

Conninfo#

(text: string) -> (Conninfo, Pg_Error)

Parses a URI (postgresql:// or postgres://) or a key=value list. The strings in the result belong to it: free_conninfo.

is_conninfosince 0.27.0
(text: string) -> bool

Whether text is something a program should hand to parse_conninfo: a URI or a list with an = in it. psql and libpq use this to tell a connection string from a database name.

conninfo_getsince 0.27.0
(info: Conninfo, keyword: string) -> (string, bool)

The value of keyword and whether it was set.

conninfo_setsince 0.27.0
(info: *Conninfo, keyword: string, value: string) -> bool

Sets keyword to a copy of value, replacing what was there. False for a keyword libpq does not know.

Saslprep#

saslprepsince 0.27.0
(password: string) -> (string, bool)

The prepared password and true, or password and false when it cannot be prepared. ASCII passwords come back unchanged. The result is in temporary storage unless it is password.

Auth#

passfile_passwordsince 0.27.0
(config: Config, host: string, port: u16, database: string, user: string) -> string

The password for a login from the password file, or an empty string. host is the name connected to; a socket directory or no name searches for localhost.

Connection#

connectsince 0.27.0
(config: Config) -> (*Connection, Pg_Error)

Opens a connection and authenticates. On failure the connection is null and nothing needs closing. Must run on a fiber (Io.io_run); config.connect_timeout bounds each attempt.

closesince 0.27.0
(c: *Connection, now := false)

Ends the session (Terminate) and frees the connection. now skips the goodbye.

parametersince 0.27.0
(c: *Connection, name: string) -> string

A ParameterStatus value, or "" if the server did not report it.

Query#

query_dynamicsince 0.27.0
(c: *Connection, sql: string, args: ..Any) -> (Result, Pg_Error)

Runs one statement with parameters $1, $2, ... taken from args, and returns its rows in text format. Arguments may be integers, floats, bools, strings, [] u8 (bytea), Uuid, Timestamp, Local_Timestamp, Date, Time, Interval, Numeric, Nullable of any of those, and one-dimensional arrays of them.

query_binarysince 0.27.0
(c: *Connection, sql: string, args: ..Any) -> (Result, Pg_Error)

query_dynamic with binary results: smaller and faster to decode, but every column's type must be one get can decode in binary (see Oid).

execute_scriptsince 0.27.0
(c: *Connection, sql: string) -> Pg_Error

Runs SQL that may hold several statements, without parameters (the simple query protocol), and discards any rows. For migrations and scripts. Without an explicit BEGIN the whole string runs as one implicit transaction, so the first error stops it and undoes the statements before it.

run_scriptsince 0.27.0
(c: *Connection, sql: string, hooks: *Script_Hooks) -> Pg_Error

Runs SQL that may hold several statements (the simple query protocol) and reports each statement's outcome to hooks. The returned error is the one that stopped the script, after the results of the statements before it were reported. Without an explicit BEGIN the whole string runs as one implicit transaction.

get2 overloadssince 0.27.0
(r: *Result, row: s64, column: s64, $T: Type) -> (T, Pg_Error)

The value at row and column as a T. NULL is an error unless T is a Nullable.

is_nullsince 0.27.0
(r: *Result, row: s64, column: s64) -> bool

Whether the value is NULL. False when the row or column does not exist.

column_indexsince 0.27.0
(r: *Result, name: string) -> s64

The first column with this name, or -1.

raw_valuesince 0.31.0
(r: *Result, row: s64, column: s64) -> (bytes: [] u8, is_null: bool)

The undecoded bytes of a value, in the column's format; is_null for NULL.

clear_rowssince 0.27.0
(r: *Result)

Drops the rows of a result and what was decoded from them, keeping its columns.

free_resultsince 0.27.0
(r: *Result)

Frees a result and everything decoded from it. It is zeroed.

Statements#

query_cachedsince 0.27.0
(c: *Connection, sql: string, args: ..Any, plan := Plan.AUTO, binary := false) -> (Result, Pg_Error)

Runs one statement like query_dynamic, prepared under a name and kept in the connection's cache (unless plan is .CUSTOM or the cache is off): the first run parses it, later runs with the same text and parameter types only bind. Typed queries do this on their own.

statement_namesince 0.27.0
(sql: string, parameter_types: [] u32 = .[]) -> string

The server name of the statement a query with this text and these parameter types is prepared as (j and 16 hex digits), for looking it up in pg_prepared_statements.

Copy#

copy_in2 overloadssince 0.27.0
(c: *Connection, sql: string, rows: [] $Row, columns: [] string = .[]) -> (s64, Pg_Error)

Sends rows through sql, a copy <table> (<columns>) from stdin (format binary) statement, and returns how many rows the server stored. Each row sends the members named in columns, in that order, or every member when columns is empty. The columns' types must match the members' binary forms exactly (an s32 for int4, an s64 for int8): COPY casts nothing. A Json(T) member goes as jsonb unless it has the @pg_json note, which sends it as json.

stored, error := Pg.copy_in(db, "copy users (email, score) from stdin (format binary)", users, .["email", "score"]);

Not run: the example is marked ignore.

copy_out4 overloadssince 0.27.0
(c: *Connection, sql: string, proc: Copy_Out_Proc, user_data: *void = null) -> (s64, Pg_Error)

Runs a COPY <table or (query)> TO STDOUT statement and calls proc with each piece of its data, as it arrives, so a table of any size streams through. Returns how many rows the server sent. Any format works: text, CSV or binary.

count, error := Pg.copy_out(db, "copy (select email from users) to stdout (format csv)", write_chunk, file);

Not run: the example is marked ignore.

free_copy_rowssince 0.27.0
(copied: *Copy_Rows($Row))

Frees the rows of a binary COPY and everything they point to.

Types#

or_elsesince 0.27.0
(n: Nullable($T), default: T) -> T

The value, or default when it is NULL.

somesince 0.27.0
(value: $T) -> Nullable(T)

A non-null Nullable holding value.

jsonsince 0.27.0
(value: $T) -> Json(T)

value as a json/jsonb parameter.

nowsince 0.27.0
() -> Timestamp

The current time from the system clock.

date_from_civilsince 0.27.0
(year: s64, month: s64, day: s64) -> Date

The date of a proleptic Gregorian calendar day. Year 0 is 1 BC, as in ISO 8601.

civil_from_datesince 0.27.0
(date: Date) -> (year: s64, month: s64, day: s64)

The year, month and day of a date.

(year: s64, month: s64, day: s64, hour := 0, minute := 0, second := 0, microsecond := 0) -> Timestamp

The instant at that wall-clock reading, taken as UTC.

dayssince 0.27.0
(count: s32) -> Interval

Spans of time as an Interval, for arithmetic on timestamps: Pg.now() - Pg.days(7). A day is a calendar day, not 24 hours, as in Postgres; the rest are exact.

hourssince 0.27.0
(count: s64) -> Interval

An interval of exact hours.

minutessince 0.27.0
(count: s64) -> Interval

An interval of exact minutes.

secondssince 0.27.0
(count: s64) -> Interval

An interval of exact seconds.

parse_uuidsince 0.27.0
(text: string) -> (Uuid, bool)

Uuid from "0f8fad5b-d9cb-469f-a165-70867728950e" (dashes optional, any case).

uuid_textsince 0.27.0
(u: Uuid) -> string

"0f8fad5b-d9cb-469f-a165-70867728950e", in temporary storage.

numeric_to_s64since 0.27.0
(n: Numeric) -> (s64, bool)

The integer part of a numeric; false for NaN, the infinities, or one out of range. A fraction is truncated toward zero.

numeric_to_float64since 0.27.0
(n: Numeric) -> (float64, bool)

The numeric as a float, and false if its text does not read as a number.

Pool#

create_poolsince 0.27.0
(config: Config, settings := Pool_Config.{}) -> (*Pool, Pg_Error)

The config is copied. Nothing connects until the first acquire, or min_idle asks for it.

close_poolsince 0.27.0
(pool: *Pool)

Closes the idle connections and frees the pool once every borrowed one is back.

acquiresince 0.27.0
(pool: *Pool) -> (*Connection, Pg_Error)

A connection for this fiber alone, until release. Waits for one when max_size are in use, up to acquire_timeout (and any Io.with_deadline around the call).

releasesince 0.27.0
(pool: *Pool, c: *Connection)

Gives a connection back. One left inside a transaction is rolled back first; a broken one, or one past max_lifetime, is closed instead.

pingsince 0.27.0

A round trip that runs nothing: an empty Sync. Fails if the connection is dead.

transaction4 overloadssince 0.27.0
(pool: *Pool, data: $T, body: (tx: *Connection, data: T) -> Pg_Error, isolation := Isolation.DEFAULT, retries := 0, read_only := false) -> Pg_Error

Runs body in a transaction on a connection from the pool. The transaction commits when body returns no error, and rolls back when it returns one (which comes out of transaction) or a statement in it failed. A serialization failure or deadlock (40001, 40P01) runs body again, up to retries more times, so body must be safe to repeat. data is passed through, as Jai procedures capture nothing.

Transfer :: struct { from: s64; to: s64; amount: s64; }
error := Pg.transaction(pool, Transfer.{ 1, 2, 10 }, (tx: *Pg.Connection, t: Transfer) -> Pg.Pg_Error {
    _, error := Pg.query_dynamic(tx, "update accounts set balance = balance - $1 where id = $2", t.amount, t.from);
    if error.kind != .NONE return error;
    _, error = Pg.query_dynamic(tx, "update accounts set balance = balance + $1 where id = $2", t.amount, t.to);
    return error;
}, .SERIALIZABLE, retries = 3);

Not run: the example is marked ignore.

Pipeline#

takesince 0.27.0
(p: *Pipeline, pending: Pending($R)) -> (R, Pg_Error)

The answer to a statement queued with queue or queue_execute: its rows, or the count of rows it changed. The statements queued so far get a sync point first if they have none, so the batch ends. Pendings may be taken in any order; results read on the way are kept for their own take.

pipeline_querysince 0.27.0
(p: *Pipeline, sql: string, args: ..Any) -> Pg_Error

Queues a statement with parameters $1, $2, ... from args (see query_dynamic for the types), results in text format.

(p: *Pipeline, sql: string, types: [] u32, values: [] string, result_format := Format.TEXT) -> Pg_Error

Queues a statement whose parameters are already text. types are the parameter OIDs to declare (0, or fewer than values, leaves the rest to the server).

pipeline_preparesince 0.27.0
(p: *Pipeline, name: string, sql: string, types: [] u32 = .[]) -> Pg_Error

Prepares a named statement. types are the parameter OIDs to declare.

Asks the server to send the answers so far without ending the batch, and sends everything queued. Without a sync point, a batch that fails is not rolled back until the next one.

(p: *Pipeline, count: s64) -> bool

Makes the next statement to be read deliver its rows count at a time (.ROWS; the last piece may have fewer), then end with a .RESULT that has no rows but the command tag, so a large result never has to fit in memory. It only works before the statement's first result is read. Reverts at the statement's end.

Reads the result of the oldest queued statement or sync point, sending what is still unsent first. Blocks the fiber until the answer is in, within any Io.with_deadline: after a deadline the pipeline keeps what it had read, and the call can be repeated. The result's kind is NONE when nothing is queued. The returned error is for failures of the connection; the server's errors come as .ERROR results. Free the result with free_pipeline_result.

pipeline_abandonsince 0.27.0
(p: *Pipeline)

Gives up on everything queued: asks the server to cancel what runs, reads and drops all answers up to the last sync point (queuing one if needed), and leaves pipeline mode. The connection breaks if the server does not finish within cancel_timeout.

Notify#

next_notificationsince 0.27.0
(c: *Connection) -> (n: Notification, found: bool)

The oldest queued notification, without waiting. found is false when none is queued. Free it with free_notification.

The oldest queued notification, or the next one to arrive: blocks the fiber until then, within any Io.with_deadline or Io.cancel (the error is .TIMED_OUT or .CANCELED, and the connection stays usable). Free the notification with free_notification. Nothing else may use the connection while a fiber waits on it. With on_notification set, notifications never queue, so there is nothing to wait for.

listen2 overloadssince 0.27.0
(c: *Connection, channel: string) -> Pg_Error

LISTEN on a channel. The name is quoted, so any text is a channel.

notifysince 0.27.0
(c: *Connection, channel: string, payload := "") -> Pg_Error

NOTIFY a channel, with a payload of up to 7999 bytes. Delivered to the listeners when the transaction commits; inside none, at once. A connection also hears its own notifications.

quote_identifiersince 0.27.0
(name: string) -> string

The text as an SQL identifier: in double quotes, with embedded quotes doubled.

Listener#

receivesince 0.27.0
(listener: *Listener) -> (Notification, Pg_Error)

The next notification, in order. Blocks the fiber until one arrives, within any Io.with_deadline or Io.cancel (.TIMED_OUT or .CANCELED); .CLOSED once the listener is closed and what it held is taken. Free the notification with free_notification.

close_listenersince 0.27.0
(listener: *Listener)

Stops listening: the connection goes back to the pool without its subscriptions, and the listener is freed. Notifications not taken yet are dropped.

Trace#

set_tracesince 0.27.0
(c: *Connection, proc: Trace_Proc, data: *void = null, regress := true)

Starts tracing: proc gets a line per message from now on (null stops). With regress, the output is libpq's PQTRACE_SUPPRESS_TIMESTAMPS | PQTRACE_REGRESS_MODE: OIDs, process ids and the server's source locations are replaced by fixed text, and an error's length by NN, so a trace is the same from one server run to the next. Without it they show as they are.

Migrate#

load_migrationssince 0.27.0
(folder: string, location := #caller_location) -> [] Migration

The migrations in folder, sorted by version, for embedding: #run load_migrations(...). A relative folder starts at the calling file's folder. A badly named file or a repeated version stops the compile.

read_migrationssince 0.27.0
(folder: string, relative_to := "") -> ([] Migration, problem: string)

load_migrations for tools: the migrations, or a message saying what is wrong with the folder. relative_to is a file whose folder relative paths start at; empty for the working directory.

schema_hashsince 0.27.0
(migrations: [] Migration) -> string

The schema hash: SHA-256 chained over each migration's name and digest, in order, so it changes when any file is renamed, edited, added or removed. Hex.

prefix_hashessince 0.27.0
(migrations: [] Migration) -> [] string

The schema hash of each prefix of migrations: entry i covers migrations 0 to i.

migratesince 0.27.0
(c: *Connection, migrations: [] Migration, options := Migrate_Options.{}) -> (applied: s64, Pg_Error)

Applies the pending migrations in order, each in its own transaction, and returns how many it applied. An advisory lock keeps two processes from migrating at once. It refuses (applying nothing) when an applied migration changed or disappeared, or a pending one is numbered below the newest applied one, unless options allows it.

split_statementssince 0.27.0
(sql: string) -> [] string

Splits a script into its statements, skipping quoted text, dollar quotes and comments, so a ; inside them does not end a statement. Empty statements are dropped. The pieces point into sql.

(text: string, position: s64) -> (line: s64, column: s64)

The line and column of a 1-based character position in text, or 0 and 0 for no position.

(text: string, from: s64, to: s64) -> s64

Characters between two byte offsets of text.

migration_failuresince 0.27.0
(name: string, index: s64, whole: string, statement: string, error: Pg_Error) -> Migration_Failure

Where error stopped in the file whole of migration name. statement is the text the server was sent: the whole file, or one statement inside it (a slice of whole) for a migration without a transaction.

(c: *Connection, name: string, index: s64, whole: string, message: string, failure: Migration_Failure) -> Migration_Failure

For an error that came without a position: the first statement of the migration whole that fails the same way, as the failure's line. Runs inside the caller's open transaction, in the state the migration started from, and leaves it in that state (a savepoint is rolled back to). Gives failure back unchanged when no single statement reproduces the error, which happens when the file's ; splitting is not how the server saw it.

count_linessince 0.27.0
(text: string) -> s64

How many newlines text has.

JSON options#

pack_json_optionssince 0.27.0
(options: Json_Options) -> u32

A Json_Columns entry's three options as the second parameter of Json(T, OPTIONS): two bits each, in the order of the enums.

Wire#

begin_messagesince 0.27.0
(c: *Connection, type: u8)

Starts a message in the output buffer, with its length left to fill in.

put_parsesince 0.27.0
(c: *Connection, name: string, sql: string, types: [] u32)

Adds a Parse message to the output.

put_bindsince 0.27.0
(c: *Connection, portal: string, statement: string, params: [] Param, result_format: Format)

Parameters go as text unless one needs another format; the format codes are left out when every parameter is text.

put_closesince 0.27.0
(c: *Connection, kind: u8, name: string)

Adds a Close message to the output.

put_executesince 0.27.0
(c: *Connection, portal: string, limit: u32)

Adds an Execute message to the output.

put_u8since 0.27.0
(out: *[..] u8, value: u8)
put_u16since 0.27.0
(out: *[..] u8, value: u16)
put_u32since 0.27.0
(out: *[..] u8, value: u32)
put_u64since 0.27.0
(out: *[..] u8, value: u64)
reserve_forsince 0.30.0
(array: *[..] $T, total: s64)

Room for total items, growing by doubling so a long run of small appends copies little.

put_bytes2 overloadssince 0.27.0
(out: *[..] u8, bytes: [] u8)

Appends bytes.

put_cstringsince 0.27.0
(out: *[..] u8, text: string)

A C string: the text and a zero. Text holding a zero would end early, so callers check first.

write_u32since 0.27.0
(at: *u8, value: u32)
read_u16since 0.27.0
(at: *u8) -> u16

Reads a big-endian number.

read_u32since 0.27.0
(at: *u8) -> u32

Reads a big-endian number.

read_u64since 0.27.0
(at: *u8) -> u64

Reads a big-endian number.

restsince 0.27.0
(r: *Cursor) -> [] u8

The bytes left in the cursor's data.

Startup#

opensince 0.27.0
(c: *Connection, config: Config) -> Pg_Error

Connects c to the server: transport, TLS, the startup message and authentication.

append_key_logsince 0.27.0
(line: string, data: *void)

Appends a line to the connection's key log file, which only its owner may read. A file that cannot be opened or written is reported, as libpq does, and the connection goes on.

is_sslv3_alertsince 0.27.0
(alert: u8) -> bool

The alerts OpenSSL words as "ssl/tls alert ..." (those of SSL 3.0); the others it calls TLS 1.0's.

looks_like_addresssince 0.27.0
(host: string) -> bool

Whether host is written as an address (only digits and dots, or with a colon), which libpq never sends as SNI.

Asks the server to stop what c is running: a CancelRequest on a connection of its own, over TLS when c uses it. The server answers nothing and closes that connection once it has acted. Whether a statement was stopped shows on c (QUERY_CANCELED), not here.

authenticatesince 0.27.0
(c: *Connection, config: Config, user: string) -> Pg_Error

Reads the server's replies to the startup message until ReadyForQuery, answering authentication requests.

contains_zerosince 0.27.0
(text: string) -> bool

Whether text has a zero byte, which a startup parameter cannot hold.

cancel_querysince 0.27.0

Asks the server to cancel the statement c is running, as libpq's PQcancel does. Meant for another fiber than the one waiting for the statement (a signal handler's, say): the waiting call then returns the server's "canceling statement due to user request". Does nothing useful when no statement is running.

TLS name#

host_addresssince 0.27.0
(host: string) -> (Io.Address, bool)

The address host stands for and true, or false when host is a name.

is_hex_lettersince 0.27.0
(c: u8) -> bool

Whether c is a to f in either case.

address_matchessince 0.27.0
(raw: string, address: Io.Address) -> bool

Whether the certificate entry is address, byte for byte.

address_textsince 0.27.0
(raw: string) -> string

The text of a raw address, as libpq shows it in its messages.

name_matchessince 0.27.0
(certificate_name: string, host: string, host_is_address: bool) -> bool

A name from the certificate against the host: equal without regard to case, or a wildcard *.rest for a host with exactly one more label in front of .rest.

SCRAM#

salted_passwordsince 0.27.0
(password: string, salt: string, iterations: s64) -> [32] u8

PBKDF2 of the password, taken from the cache when it was computed before.

prepare_passwordsince 0.27.0
(password: string) -> string

The password as SCRAM uses it: SASLprep (saslprep.jai), or its own bytes when that cannot be done, like libpq.

(stream: *Tls.Tls_Stream) -> string

The hash of the server's certificate for tls-server-end-point: the certificate's own signature hash, with MD5 and SHA-1 raised to SHA-256. Empty when the signature uses no hash (Ed25519) or one this client cannot compute.

scram_secretsince 0.27.0
(password: string, iterations := 4096) -> string

The SCRAM-SHA-256 secret the server stores for a password, to put in ALTER ROLE ... PASSWORD without the server seeing the password (libpq's PQencryptPasswordConn does the same). The password is prepared with SASLprep. iterations is what the server reports as scram_iterations, 4096 by default.

scram_client_proofsince 0.27.0
(password: string, client_first_bare: string, binding: string, server_first: string, server_nonce: string, salt: string, iterations: s64) -> (final: string, server_signature: [32] u8)

The client-final-message for a server-first-message, and the signature the server must prove it knows in its final message. binding is the GS2 header (plus the certificate hash for -PLUS), salt the decoded salt. Exported so the RFC 7677 example can be checked.

scram_pbkdf2_runssince 0.27.0
() -> s64

How many times a SCRAM exchange ran PBKDF2 in this process: cache hits do not count.

md5_secretsince 0.27.0
(password: string, user: string) -> string

The MD5 secret for a password and a role name: md5 and the hash of the two joined.

SQL scan#

(source: string) -> Rewrite

Rewrites % to $1, $2, ... in order and %% to %. A $n of Postgres' own in the source is an error: one style only. Text inside quotes and comments is copied as it is.

source_offset_ofsince 0.27.0
(rewrite: *Rewrite, rewritten_offset: s64) -> s64

The offset in the source of a byte of the rewritten statement. Inside a replaced % it is that %.

byte_of_charactersince 0.27.0
(text: string, character: s64) -> s64

The byte offset in text of a 1-based character position (what an ErrorResponse's P field counts, in characters, not bytes). Past the end, the end.

statement_idsince 0.27.0
(rewritten_sql: string) -> u64

A 64-bit statement id: the first eight bytes of SHA-256 of the rewritten statement.

(text: string) -> string

The full SHA-256 of a text, as lower-case hex.

id_hexsince 0.27.0
(id: u64) -> string

The 16 lower-case hex digits of a statement id.

scan_hex_textsince 0.27.0
(data: *u8, count: s64) -> string

Lower-case hex of count bytes at data, twice as long.

Entry#

Describe#

describe_answersince 0.27.0
(db: *Connection, request: Describe_Request) -> string

Describes request.sql on db and returns the answer as JSON text: {"kind":"ok","entry":{...}} the cache entry (see entry_to_json) {"kind":"sql_error","error":{...}} the server refused the statement {"kind":"error","message":"..."} anything else (the connection died, say)

Schema databases#

(hash: string) -> string

The name of the database for a schema hash.

schema_targetsince 0.27.0
(state: *Schema_Databases, db: *Connection, config: Config, folder: string) -> (Schema_Target, problem: string)

Picks (and builds when it is time) the schema database for the migrations in folder. db is connected to the server's own database and config is how it was reached.

(db: *Connection)

Drops schema databases beyond the newest SCHEMA_DATABASES_KEPT. One still in use (by another editor, say) is left for next time.

Resolve#

migrations_pathsince 0.27.0
(root: string) -> string

The project's migrations folder, schema file and query cache, from the module parameters MIGRATIONS, SCHEMA and QUERY_CACHE (paths relative to the project root).

schema_pathsince 0.27.0
(root: string) -> string

The path of the schema file.

query_cache_pathsince 0.27.0
(root: string) -> string

The path of the query cache folder.

min_server_versionsince 0.27.0
() -> s64

MIN_SERVER_VERSION: the oldest server major version the project supports; 0 accepts any.

find_project_rootsince 0.27.0
(file: string) -> string

The folder that holds the project's files: from the file with the query, the nearest folder up the tree with a jacket.toml or the first folder of MIGRATIONS (db, by default); if there is none, that file's own folder. JACKET_PG_ROOT overrides it.

database_urlsince 0.27.0
(root: string) -> string

The compile-time database's URL: JACKET_DATABASE_URL, else DATABASE_URL, from the environment; when it has neither, the same two names in the project's .env file. Empty when there is none.

migrations_ofsince 0.27.0
(root: string) -> (folder: string, hash: string)

The project's migrations folder and their schema hash (see schema_hash), or empty strings when there are none.

cache_filesince 0.27.0
(root: string, id: u64) -> string

The path of the cache file for a statement.

read_cache_entrysince 0.27.0
(path: string, sql: string) -> (Query_Entry, bool)

The entry in a cache file and true, or false when there is none, it cannot be read, or it is for another statement.

write_cache_entrysince 0.27.0
(root: string, id: u64, entry: Query_Entry)

Writes a statement's cache entry, leaving the file alone if it would not change.

Dotenv#

dotenv_valuesince 0.27.0
(text: string, key: string) -> (string, bool)

The value key gets in the text of a .env file, and whether it is assigned at all. The last assignment wins. The result lives in temporary storage.

(root: string) -> string

The database URL the project's .env file gives: JACKET_DATABASE_URL, else DATABASE_URL. Empty when there is no file or neither is assigned.

ORM#

selectsince 0.27.0
(db: $D, $T: Type, $where: Code = EVERY_ROW, $order_by: Code = NO_ORDER, $desc := false, $limit := -1, $offset := 0, $from_sql := "", $fields := "", $call := #caller_code) -> (Rows(#insert #run orm_select_row(fields)), Pg_Error)

The rows of T's table that pass where, in order_by order (desc for descending), at most limit of them (-1: all) after skipping offset. from_sql replaces the table with a query of your own that returns its columns, with %name / %(expression) parameters: from_sql = "select * from users_visible_to(%viewer)". fields = "id, email" selects only those columns, and the rows are then Partial(T, "id, email").

updatesince 0.27.0
(db: $D, $T: Type, $set: Code, $where: Code, $call := #caller_code) -> (s64, Pg_Error)

Sets fields of the rows that pass where: set = #code it.score = score + 1 (or a block of such assignments). Returns the number of rows changed.

deletesince 0.27.0
(db: $D, $T: Type, $where: Code, $call := #caller_code) -> (s64, Pg_Error)

Deletes the rows that pass where and returns how many there were. where is required: pass #code true to delete every row.

insertsince 0.27.0
(db: $D, value: $T, $call := #caller_code) -> (T, Pg_Error)

Inserts value and returns the row the table now holds (with its defaults and generated columns). Fields with a database default (@pg_default) or a generated value (@pg_generated) are left to the database.

insert_manysince 0.27.0
(db: $D, values: [] $T, $call := #caller_code) -> ([] T, Pg_Error)

Inserts each value in turn (one statement each), stopping at the first error. Returns the rows inserted so far, from context.allocator.

eachsince 0.27.0
(db: $D, $T: Type, error: *Pg_Error, $where: Code = EVERY_ROW, $order_by: Code = NO_ORDER, $desc := false, $limit := -1, $offset := 0, $from_sql := "", $strategy := Orm_Strategy.PRELOAD, $call := #caller_code) -> Orm_Each(D, T, where, order_by, desc, limit, offset, from_sql, strategy, call)

A select whose columns come from the loop that reads it: only the fields of T the body reads, plus the primary key, are fetched; the other fields of it stay zero. A json document read only for some top-level fields comes back with just those keys, and it.author.email (a to-one relation) is fetched in the same statement, making it an Orm_Item that usings a T. When it is used whole (passed to a procedure, stored, *it), every column is fetched. The query runs when the loop starts, and a failure lands in error (which is cleared first). Strings in it read from the result, which is freed when the loop ends: copy what you keep.

error: Pg.Pg_Error;
for Pg.each(db, User, *error, where = #code it.score > min) print("%\n", it.email);   // select "id", "email" ...

Not run: the example is marked ignore.

for_expansion3 overloadssince 0.27.0
(q: *Orm_Each($D, $T, $where, $order_by, $desc, $limit, $offset, $from_sql, $strategy, $call), body: Code, flags: For_Flags)

Makes Pg.each loopable.

copy_manysince 0.27.0
(db: $D, values: [] $T) -> (s64, Pg_Error)

Stores values with one binary COPY and returns how many rows the server stored. Much faster than insert_many for many rows, but it reads nothing back: fields marked @pg_default or @pg_generated are left to the database, as in insert, and their values are not returned.

load_manysince 0.27.0
(db: $D, parents: [] $T, $relation: string, $call := #caller_code) -> (Loaded_Many(#insert,scope(call) #run orm_relation_target(T, relation, .HAS_MANY)), Pg_Error)

Loads a has-many relation for several rows; see Loaded_Many.

load_onesince 0.27.0
(db: $D, parents: [] $T, $relation: string, $call := #caller_code) -> (Loaded_One(#insert,scope(call) #run orm_relation_target(T, relation, .BELONGS_TO)), Pg_Error)

Loads a to-one relation for several rows; see Loaded_One.

orm_load_many_assince 0.27.0
(db: $D, parents: [] $T, $relation: string, $Target: Type, $project_file: string) -> (Loaded_Many(Target), Pg_Error)

load_many with the related struct given, for Pg.each, whose loop knows it already.

likesince 0.27.0
(value: string, pattern: string) -> bool

like: Pg.like(it.email, "%@example.com").

ilikesince 0.27.0
(value: string, pattern: string) -> bool

ilike: case-insensitive like.

insince 0.27.0
(value: $T, values: [] T) -> bool

= any(values): Pg.in(it.id, ids).

lowersince 0.27.0
(value: string) -> string

lower(value).

uppersince 0.27.0
(value: string) -> string

upper(value).

if_somesince 0.27.0
(value: $T, condition: Code) -> bool

(value is null or condition): an optional filter, Pg.if_some(team, #code it.team_id == team) with team: Nullable(s64), where team stands for its value inside the condition.

has_keysince 0.27.0
(document: $T, key: string) -> bool

?: the jsonb document has the top-level key: Pg.has_key(it.settings, "beta").

json_pathsince 0.27.0
(document: $T, path: string) -> bool

@?: the jsonpath matches: Pg.json_path(it.settings, "$.tags[*] ? (@ == \"x\")").

Typed#

fetch_allsince 0.27.0
(rows: *Rows($T), allocator := context.allocator) -> [] T

Every row decoded and copied, with the strings, arrays and documents it points to, into one block from allocator. The rows stay valid after free_rows; array_free(items) frees all of it (with temp, nothing needs freeing). Check rows.error afterwards.

users := Pg.fetch_all(*rows);   // or Pg.fetch_all(*rows, temp)
Pg.free_rows(*rows);

Not run: the example is marked ignore.

free_rowssince 0.27.0
(rows: *Rows($T))

Frees the result data of rows.

querysince 0.27.0
(db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $call := #caller_code) -> (Rows(#run row_type(sql, call, 1, #insert,scope(call) #run row_table_type(sql, call, 1, #insert,scope(call) #run row_table_exists(sql, call, 1)), Row(#run row_shape(sql, call, 1)))), Pg_Error)

Runs the statement and returns its rows. Rows(Row(...)) has one field per column; when the columns are exactly one table's, it is Rows(Table) with that table's struct from db/schema.jai (row type identity, see row_type).

query_onesince 0.27.0
(db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $call := #caller_code) -> (#run row_type(sql, call, 1, #insert,scope(call) #run row_table_type(sql, call, 1, #insert,scope(call) #run row_table_exists(sql, call, 1)), Row(#run row_shape(sql, call, 1))), Pg_Error)

The only row, or a .NOT_FOUND error when there is none. More rows are a .USAGE error. The row lives in temporary storage (until reset_temporary_storage).

query_optsince 0.27.0
(db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $call := #caller_code) -> (#run row_type(sql, call, 1, #insert,scope(call) #run row_table_type(sql, call, 1, #insert,scope(call) #run row_table_exists(sql, call, 1)), Row(#run row_shape(sql, call, 1))), bool, Pg_Error)

The first row and whether there was one.

executesince 0.27.0
(db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $project_file := "", $call := #caller_code) -> (s64, Pg_Error)

Runs a statement and returns the number of rows it affected.

queuesince 0.27.0
(p: *Pipeline, $sql: string, args: ..Any, $call := #caller_code) -> (Pending(Rows(#run row_type(sql, call, 1, #insert,scope(call) #run row_table_type(sql, call, 1, #insert,scope(call) #run row_table_exists(sql, call, 1)), Row(#run row_shape(sql, call, 1))))), Pg_Error)

query on a pipeline: the statement is queued, checked like query, and its rows come later from take. Queue several, then take them: they share one round trip.

p := Pg.pipeline_enter(c);
users := Pg.queue(p, "select id, name from users where id = $1", id);
count := Pg.queue_execute(p, "update users set seen = now() where id = $1", id);
rows, error := Pg.take(p, users);
changed, error = Pg.take(p, count);
Pg.pipeline_exit(p);

Not run: the example is marked ignore.

queue_executesince 0.27.0
(p: *Pipeline, $sql: string, args: ..Any, $call := #caller_code) -> (Pending(s64), Pg_Error)

execute on a pipeline: take gives the count of rows the statement changed.

streamsince 0.27.0
(c: *Connection, error: *Pg_Error, $sql: string, args: ..Any, batch := 1000, $call := #caller_code) -> Row_Stream(#run row_type(sql, call, 2, #insert,scope(call) #run row_table_type(sql, call, 2, #insert,scope(call) #run row_table_exists(sql, call, 2)), Row(#run row_shape(sql, call, 2))))

query for results too big to hold: the rows arrive batch at a time and for decodes them one by one, so at most one batch is in memory. The statement is sent here, and the connection is busy until the loop ends; leaving the loop early cancels the rest. A failure, before or during the loop, lands in error (cleared first). Strings in it live until the next batch: copy what you keep.

error: Pg.Pg_Error;
for Pg.stream(c, *error, "select id, body from events where day = %", day, batch = 5000) {
    total += it.body.count;
}
if error.kind != .NONE ...

Not run: the example is marked ignore.

query_scalarsince 0.27.0
(db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $call := #caller_code) -> (type_of(Scalar(#run scalar_type(sql, call, 1)).value), Pg_Error)

The first column of the only row.

query_assince 0.27.0
($T: Type, db: $D, $sql: string, args: ..Any, plan := Plan.AUTO, json := Json_Options.{}, $project_file := "", $call := #caller_code) -> (Rows(T), Pg_Error)

query into your own struct. The columns are matched to fields by name, and checked: a column that can be NULL needs a Nullable field, the types must be assignable, and a Json(T) field says which type a json column holds. project_file is as for execute.

Schema#

json_column_typesince 0.27.0
(column: string) -> Type

The Jai type a json or jsonb column holds, from the Json_Columns visible where this is called (a macro, so the generated file sees the user's list), else Json_Raw. A mapping can change without generating the schema again.

(column: string) -> u32

The options of the Json_Columns entry for column, packed as the second parameter of Json(T, OPTIONS); 0 when it has none.

file_exists_besidesince 0.27.0
(file: string, name: string) -> bool

Does a file called name sit in the folder of file? For the generated file to load json_columns.jai when there is one.

check_schemasince 0.27.0
(file: string, location := #caller_location)

Stops the compile when file (the generated schema) was made from other migrations than the ones in the project now. Needs no server: it compares hashes of db/migrations/*.sql.

schema_problemsince 0.27.0
(file: string) -> string

Why file no longer matches the migrations, or an empty string when it does.

(text: string) -> string

The hash a generated file records in its header, or an empty string.

check_json_columnssince 0.27.0
(mappings: [] Json_Column, known: [] string, location := #caller_location)

Stops the compile when a Json_Columns entry names something that is not a json or jsonb column. known is every json column of the schema, as the generated file lists them.

enum_member_namesince 0.27.0
(label: string) -> string

How a Postgres enum label is spelled as a Jai enum member, and back. A label that is a plain name is used as it is, so 'pending' is .pending. Any other (spaces, dashes, a leading digit, a Jai keyword, a double underscore) becomes __ followed by its letters and digits, with every other byte as _ and two hex digits: 'in progress' is __in_20progress.

enum_labelsince 0.27.0
(name: string) -> string

The label an enum member name stands for: the inverse of enum_member_name.

Generate#

generate_schemasince 0.27.0
(config: Config, migrations_folder: string) -> (source: string, problem: string)

Reads the catalog of the migrations' schema database (see schema_databases.jai) on the server config names, building it first when there is none, and returns the text of db/schema.jai; or what went wrong. Runs on a fiber, like every call that talks to the server.

migrated_catalogsince 0.27.0
(config: Config, migrations_folder: string) -> (Catalog, hash: string, problem: string)

The catalog of the migrations in migrations_folder and their schema hash, read from the schema database on the server config names (built first when there is none); or what went wrong. Runs on a fiber, like every call that talks to the server.

schema_sourcesince 0.27.0
(catalog: Catalog, hash: string) -> (source: string, problem: string)

The text of db/schema.jai for a catalog made from migrations with this schema hash, or a problem such as a column that cannot be a field name. Laid out as the formatter would.

assign_namessince 0.27.0
(catalog: Catalog) -> (Names, problem: string)

The Jai names of the catalog's types. One whose preferred name is taken falls back to a longer one, so two tables never collapse into one struct.

Diff#

diff_schemasince 0.27.0
(config: Config, migrations_folder: string, schema_file: string) -> (Migration_Proposal, problem: string)

Reads the catalog of the migrations' schema database (see schema_databases.jai), compares it with the structs in schema_file, and returns the proposed SQL; or what went wrong. An empty sql means nothing differs. Runs on a fiber, like every call that talks to the server.

parse_schema_textsince 0.27.0
(text: string) -> (Schema_Description, problem: string)

The structs and enums of db/schema.jai, which must have the shape generate writes: one declaration per line of the form Name :: struct @note(...) {, then name: Type; @note per field line, then a } in the first column. Structs without @pg_table are not tables and are skipped. Anything else in a table struct or an enum is a problem naming the line.

sql_type_of_textsince 0.27.0
(text: string, refs: [] Enum_Ref) -> string

The Jai type text of a field as the Postgres type that would hold it, or an empty string for a type that has no mapping. refs resolves the names of enums.

Describe service#

mainsince 0.27.0
()
answersince 0.27.0
(request_text: string) -> (Service.Status, string)

The service's answer to one request.

managed_urlsince 0.27.0
(version: string) -> (url: string, failure: string)

The URL of the managed server, starting it if needed, or why it cannot.

slot_forsince 0.27.0
(url: string, database: string) -> *Held

The slot holding the connection for this pair, added if there is none.

drop_connectionsince 0.27.0
(url: string, database: string)

Closes the held connection for this pair, if any.

connection_forsince 0.27.0
(url: string, database: string) -> (*Pg.Connection, string)

The held connection for this pair, connecting when there is none, or why it cannot. Failures are remembered for a while.

Types#

Sqlstateenumsince 0.27.0
:: enum

The five-character SQLSTATE, packed into a number so that it compares and switches like an enum: if error.code == .UNIQUE_VIOLATION. Codes not listed here still compare and print (sqlstate_text). Its class is the first two characters: sqlstate_class(code).

Pg_Errorstructsince 0.27.0
:: struct

What a call returns. It is small on purpose (the hot calls hand it back by value): the kind, the SQLSTATE and the primary message. The rest of an ErrorResponse is in error_fields(error).

Error_Fieldsstructsince 0.30.0
:: struct

The other fields of an ErrorResponse or NoticeResponse. Empty strings and zeros for the ones the server did not send, and for errors that did not come from the server.

Target_Session_Attrsenumsince 0.27.0
:: enum

Which of several servers connect accepts, as libpq's target_session_attrs.

Load_Balance_Hostsenumsince 0.27.0
:: enum

The order connect tries the hosts in, as libpq's load_balance_hosts.

Ssl_Protocol_Versionenumsince 0.27.0
:: enum

The TLS versions libpq names in ssl_min_protocol_version and ssl_max_protocol_version. The TLS library behind this client speaks 1.2 and 1.3 only, so a range that ends below 1.2 cannot be used, and a minimum below it is raised to 1.2.

Configstructsince 0.27.0
:: struct
Conninfostructsince 0.27.0
:: struct

The options a connection string set. A keyword that was not given has no value; one given empty has an empty value, which is not the same.

Auth_Policystructsince 0.27.0
:: struct

The methods require_auth allows. restricted is false when it is empty: anything goes.

Protocol_Versionenumsince 0.27.0
:: enum

A minor version of protocol 3, for Config.min_protocol_version and max_protocol_version. Version 3.2 (PostgreSQL 18 and later) allows cancel keys of up to 256 bytes, where 3.0 has four.

Notice_Proctypesince 0.27.0
:: #type (connection: *Connection, notice: *Pg_Error)

Called for each NoticeResponse (warnings, RAISE NOTICE). The notice is freed after the call.

Notification_Proctypesince 0.27.0
:: #type (connection: *Connection, notification: *Notification)

Called for each NotificationResponse (a NOTIFY on a channel the connection LISTENs to), in the middle of whatever request is reading. The notification is freed after the call. Without one, notifications queue on the connection (next_notification, wait_for_notification).

Columnstructsince 0.27.0
:: struct
Resultstructsince 0.27.0
:: struct

The rows of one statement. Strings and byte slices that get returns point into the result, and arrays and numerics it builds belong to it: all stay valid until free_result.

Statement_Infostructsince 0.27.0
:: struct

What the server makes of a statement without running it: the types it infers for $1, $2, ... and the columns it would return (none for a statement that returns no rows).

Script_Hooksstructsince 0.27.0
:: struct

What run_script does with each statement's outcome. Every hook is optional.

Copy_Infostructsince 0.27.0
:: struct

The shape of a COPY the server has started.

Statement_Cache_Modeenumsince 0.27.0
:: enum

Whether a connection keeps prepared statements (Config.statement_cache).

Planenumsince 0.27.0
:: enum

How the server plans a typed query (plan = on query, execute, ...).

Copy_Out_Proctypesince 0.27.0
:: #type (data: [] u8, user_data: *void)

Gets the data of a COPY ... TO STDOUT in the pieces the server sends it in. A piece is one row in the text and CSV formats, and any slice of the stream in binary; it is valid during the call.

Copy_Rowsstructsince 0.27.0
:: struct

The rows of a binary COPY. Strings, byte slices and what arrays and numerics hold belong to result: free them all with free_copy_rows.

Oidenumsince 0.27.0
:: enum

The type OIDs this client knows by number. Other types (enums, domains, composites) get their OIDs when they are created, so they are only known by name.

Formatenumsince 0.27.0
:: enum
Nullablestructsince 0.27.0
:: struct

A value that may be SQL NULL. null is true for NULL, and value is then zero.

Jsonstructsince 0.27.0
:: struct

A json or jsonb document holding a T. get decodes it with the Json module's decoder for T, under the result's json_options and the query's json (json_options.jai); as a parameter it is encoded with write_json. OPTIONS is a Json_Columns entry's options (packed by pack_json_options), which the generated schema puts in the type; 0 says nothing. write lets a parameter passed as Any find its encoder, so leave it as it is. Json(Json_Raw) keeps the document undecoded: it is what a generated schema gives a json column nobody mapped.

Json_Rawstructsince 0.27.0
:: struct

A json or jsonb document, undecoded: its text, which points into the result like any string. decode_json turns it into a T later. Also a parameter, sent as it is.

Uuidstructsince 0.27.0
:: struct

uuid: the 16 bytes in network order.

Timestampstructsince 0.27.0
:: struct

timestamptz: an instant, in microseconds since 2000-01-01 00:00:00 UTC (Postgres' own epoch). TIMESTAMP_INFINITY and TIMESTAMP_MINUS_INFINITY are infinity and -infinity.

Local_Timestampstructsince 0.27.0
:: struct

timestamp (without time zone): a wall-clock reading, in microseconds since 2000-01-01 00:00:00.

Datestructsince 0.27.0
:: struct

date: days since 2000-01-01. DATE_INFINITY and DATE_MINUS_INFINITY are infinity and -infinity.

Timestructsince 0.27.0
:: struct

time (without time zone): microseconds since midnight.

Intervalstructsince 0.27.0
:: struct

interval: months, days and microseconds are kept apart, as Postgres keeps them, because a month has no fixed number of days and a day has no fixed number of microseconds across a daylight-saving change. From PostgreSQL 17 infinity is every field at S32_MAX / S64_MAX and -infinity every field at the minimum.

Numericstructsince 0.27.0
:: struct

numeric: an exact decimal, as its text: "-12.3400", "0", "NaN", "Infinity", "-Infinity". numeric_to_s64 and numeric_to_float64 convert it.

Poolstructsince 0.27.0
:: struct

Belongs to one scheduler: create it, and use it, on the fibers of one io_run (with io_run_threads, one pool per thread). No locks are involved.

Pipeline_Resultstructsince 0.27.0
:: struct

What one queued statement or sync point turned into.

Pipelinestructsince 0.27.0
:: struct

The pipeline of a connection (pipeline_enter). While it is active, the connection answers no other request.

Pendingstructsince 0.27.0
:: struct

A queued typed statement whose answer is still to come: take it after queuing the rest. R is what take returns.

Listenerstructsince 0.27.0
:: struct

A dedicated, self-healing LISTEN on a pool's database. listen(pool, "jobs") takes a connection out of the pool for as long as the listener lives, LISTENs on it, and reads notifications on a fiber of its own into a queue. If the connection fails (the server restarts, the backend is terminated, the network drops) the listener returns it to the pool, takes another, LISTENs again and carries on. Postgres keeps no notifications for a session that is not connected, so those sent while the listener is reconnecting are lost; code that must not miss any should read its table again after reconnects changes.

listener, error := Pg.listen(pool, "jobs", "mail");
while true {
    n, error := Pg.receive(listener);
    if error.kind != .NONE break;
    defer Pg.free_notification(*n);
    print("% on %\n", n.payload, n.channel);
}
Pg.close_listener(listener);

Not run: the example is marked ignore.

Trace_Proctypesince 0.27.0
:: #type (connection: *Connection, line: string)

Called with each traced line, without its newline. The line is in temporary storage.

Migration_Failurestructsince 0.27.0
:: struct

Where a migration stopped, for tools that point at the file.

Migration_Stateenumsince 0.27.0
:: enum

Where one migration stands against a database.

Cursorstructsince 0.27.0
:: struct

Reading inside a message body. A read past the end sets bad and returns zeros, so a parser checks bad once at the end.

Scramstructsince 0.27.0
:: struct
Rewrite_Editstructsince 0.27.0
:: struct

One place where the rewritten text differs from the source: a % that became $n (hole is n), or a %% that became % (hole is 0).

Rewritestructsince 0.27.0
:: struct
Schema_Targetstructsince 0.27.0
:: struct

Where to describe: database on the same server (empty: the URL's own database) after running setup inside the transaction. built names a database this call created, so the caller can let go of connections to older ones and then prune_schema_databases.

Setup_Markstructsince 0.27.0
:: struct

Where one migration's SQL lies in setup, so a failure in the transaction can be traced to its file.

Partialstructsince 0.27.0
:: struct

Some fields of a table struct, with their types and notes: the rows of select(..., fields = ...). fields is a comma-separated list of T's field names.

Contact :: Pg.Partial(User, "id, email");

Doesn't run in the browser (skipped on wasm).

Orm_Strategyenumsince 0.27.0
:: enum

How Pg.each loads a has-many relation its loop body reads (for post: it.posts).

Loaded_Manystructsince 0.27.0
:: struct

The rows parents have through the has-many relation relation (a @pg_has_many note of T), in one query: items[i] belongs to parents[i]. Free it with free_loaded; the items read from its result.

posts, error := Pg.load_many(db, users, "posts");
for users print("% wrote % posts\n", it.email, posts.items[it_index].count);

Not run: the example is marked ignore.

Loaded_Onestructsince 0.27.0
:: struct

The row each of parents points to through the to-one relation relation (a @pg_belongs_to note of T), in one query: items[i] is null when parents[i] points nowhere.

Null_Markerstructsince 0.27.0
:: struct

A typed SQL NULL for any parameter: Pg.query(db, "update t set x = %", Pg.NULL).

Rowstructsince 0.27.0
:: struct

The row of a described statement: one field per column, named by the column. shape is the struct body the describe step wrote (doc comments included), so equal columns give one type.

Rowsstructsince 0.27.0
:: struct

The result of query: it owns the received data, and for rows hands out one row at a time. Strings and byte slices in a row point into that data and stay valid until free_rows.

Scalarstructsince 0.27.0
:: struct

A one-field struct whose field has the type of a column: for query_scalar's return type.

Json_Columnstructsince 0.27.0
:: struct

One line of the hand-written db/json_columns.jai:

Json_Columns :: Pg.Json_Column.[ .{ "users.settings", Settings, .{ unknown_fields = .DENY } } ];

Doesn't run in the browser (skipped on wasm).

Catalog_Relationstructsince 0.27.0
:: struct

A table, view, materialized view or foreign table.

Catalogstructsince 0.27.0
:: struct
Namesstructsince 0.27.0
:: struct
Schema_Fieldstructsince 0.27.0
:: struct

One field of a struct in the schema file.

Schema_Structstructsince 0.27.0
:: struct

A struct with an @pg_table note.

Schema_Enumstructsince 0.27.0
:: struct

An enum of the schema file: a Postgres enum.

Schema_Descriptionstructsince 0.27.0
:: struct

What parse_schema_text reads from the file: the table structs and the enums.

Enum_Refstructsince 0.27.0
:: struct

How an enum of the file is called in Postgres.

Heldstructsince 0.27.0
:: struct

A connection kept between requests, and the backoff for reaching it again after a failure. database empty is the URL's own database; otherwise a schema database on the same server.

Managed_Statestructsince 0.27.0
:: struct

The managed server this service process owns (managed.jai). It is started by the first request that needs it and then reused; start_managed takes a lock, so two services (two compiles, or a compile and the editor) never start the same data folder at once. The server keeps running when the service exits, so the next build finds it ready.

Constants#

CONNINFO_KEYWORDSsince 0.27.0
:: string.[ "service", "user", "password", "passfile", "channel_binding", "connect_timeout", "dbname", "host", "hostaddr", "port", "client_encoding", "options", "a ...

Every keyword libpq accepts, in the order it lists them.

SASLPREP_CODESsince 0.27.0
:: u32.[ 0xA0, 0xA8, 0xAA, 0xAF, 0xB2, 0xB3, 0xB4, 0xB5, 0xB8, 0xB9, 0xBA, 0xBC, 0xBD, 0xBE, 0x132, 0x133, 0x13F, 0x140, 0x149, 0x17F, 0x1C4, 0x1C5, 0x1C6, 0x1C7, ...
SASLPREP_STARTSsince 0.27.0
:: u32.[ 0x0, 0x1, 0x3, 0x4, 0x6, 0x7, 0x8, 0xA, 0xB, 0xD, 0xE, 0xF, 0x12, 0x15, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x31 ...
SASLPREP_CHARSsince 0.27.0
:: u32.[ 0x20, 0x20, 0x308, 0x61, 0x20, 0x304, 0x32, 0x33, 0x20, 0x301, 0x3BC, 0x20, 0x327, 0x31, 0x6F, 0x31, 0x2044, 0x34, 0x31, 0x2044, 0x32, 0x33, 0x2044, 0x34, ...
TIMESTAMP_INFINITYsince 0.27.0
:: S64_MAX

infinity and -infinity of timestamps and dates, as the server stores them.

LISTENER_QUEUEsince 0.27.0
:: 256

notifications held before the reader waits for the program

MAX_MESSAGE_BYTESsince 0.27.0
:: 1 << 30

Messages the server may send are never larger than this; a bigger length means the stream is out of step or hostile.

HEADER_BYTESsince 0.27.0
:: 5

A message starts with its type byte and its four-byte length.

PROTOCOL_3_2since 0.27.0
:: 196610

PostgreSQL 18 and later; older servers answer with version 3.0

AUTH_OKsince 0.27.0
:: 0

AuthenticationRequest codes.

SALTED_CACHE_SIZEsince 0.27.0
:: 16

SaltedPassword (the PBKDF2 of the password) for the last few (password, salt, iterations) triples of the process, so a pool that reconnects, or opens several connections at once, runs the iterations once. The key is a hash of the triple: the cache holds no password text. The salted value is as good as the password for logging in with SCRAM, and lives as long as the process, as the password does in the connection's Config.

ENTRY_FORMATsince 0.27.0
:: 1

The version of what an entry holds and how its statement is rewritten. Raise it when the description changes shape, or when the placeholder rewrite, the alias syntax or anything else that decides what a statement means does: every entry written before is then a miss, described again when a database is there (or by the pg tool's prepare), and an error offline.

:: 2_000

How long a set of migrations must stay unchanged before its database is built; JACKET_PG_BUILD_DELAY_MS overrides it.

EVERY_ROWsince 0.27.0
:: #code true

Defaults for select: no filter, no order.

NULLsince 0.27.0
:: Nullable(Null_Marker).{ null = true }