Modules

Postgres

moduleextensionexperimentalSource

Available on macOS, Linux, Windows·since 0.27.0

#import "Extensions/Postgres";

A PostgreSQL client over Io fibers.

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.

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.

Set the module parameters when importing:

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).

Module parameters#

TLS := true
Connects over TLS, which needs Extensions/Tls (mbedTLS). Set false to leave it out of programs that only use Unix sockets or cleartext TCP.
MIGRATIONS := "db/migrations"
The migrations folder, relative to the project root.
SCHEMA := "db/schema.jai"
The schema file, relative to the project root.
QUERY_CACHE := "db/queries"
The folder of the typed-query cache, relative to the project root.
MIN_SERVER_VERSION := 0
The oldest server major version accepted; an older server fails every describe.

Procedures#

Error#

error_textexperimentalsince 0.27.0Source
(error: Pg_Error) -> string

One line describing the error, in temporary storage.

sqlstate_classexperimentalsince 0.27.0Source
(code: Sqlstate) -> Sqlstate

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

Config#

parse_urlexperimentalsince 0.27.0Source
(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_conninfoexperimentalsince 0.27.0Source
(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.

config_from_conninfoexperimentalsince 0.27.0Source
(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_configexperimentalsince 0.27.0Source
(config: Config) -> Config

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

free_configexperimentalsince 0.27.0Source
(config: *Config)

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

Conninfo#

is_conninfoexperimentalsince 0.27.0Source
(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_getexperimentalsince 0.27.0Source
(info: Conninfo, keyword: string) -> (string, bool)

The value of keyword and whether it was set.

conninfo_setexperimentalsince 0.27.0Source
(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#

saslprepexperimentalsince 0.27.0Source
(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_passwordexperimentalsince 0.27.0Source
(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#

connectexperimentalsince 0.27.0Source
(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.

closeexperimentalsince 0.27.0Source
(c: *Connection, now := false)

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

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

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

Query#

query_dynamicexperimentalsince 0.27.0Source
(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_binaryexperimentalsince 0.27.0Source
(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_scriptexperimentalsince 0.27.0Source
(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_scriptexperimentalsince 0.27.0Source
(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.

getexperimental2 overloadssince 0.27.0Source
(r: *Result, row: s64, column: s64, $T: Type) -> (T, Pg_Error)
(r: *Result, row: s64, column_name: string, $T: Type) -> (T, Pg_Error)

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

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

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

raw_valueexperimentalsince 0.31.0Source
(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_rowsexperimentalsince 0.27.0Source
(r: *Result)

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

free_resultexperimentalsince 0.27.0Source
(r: *Result)

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

Statements#

query_cachedexperimentalsince 0.27.0Source
(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_nameexperimentalsince 0.27.0Source
(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_inexperimental2 overloadssince 0.27.0Source
(c: *Connection, sql: string, rows: [] $Row, columns: [] string = .[]) -> (s64, Pg_Error)
(pool: *Pool, 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_outexperimental4 overloadssince 0.27.0Source
(c: *Connection, sql: string, proc: Copy_Out_Proc, user_data: *void = null) -> (s64, Pg_Error)
(pool: *Pool, sql: string, proc: Copy_Out_Proc, user_data: *void = null) -> (s64, Pg_Error)
(c: *Connection, sql: string, $Row: Type, columns: [] string = .[]) -> (Copy_Rows(Row), Pg_Error)
(pool: *Pool, sql: string, $Row: Type, columns: [] string = .[]) -> (Copy_Rows(Row), 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.

Types#

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

The value, or default when it is NULL.

someexperimentalsince 0.27.0Source
(value: $T) -> Nullable(T)

A non-null Nullable holding value.

jsonexperimentalsince 0.27.0Source
(value: $T) -> Json(T)

value as a json/jsonb parameter.

nowexperimentalsince 0.27.0Source
() -> Timestamp

The current time from the system clock.

date_from_civilexperimentalsince 0.27.0Source
(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_dateexperimentalsince 0.27.0Source
(date: Date) -> (year: s64, month: s64, day: s64)

The year, month and day of a date.

timestamp_from_civilexperimentalsince 0.27.0Source
(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.

daysexperimentalsince 0.27.0Source
(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.

parse_uuidexperimentalsince 0.27.0Source
(text: string) -> (Uuid, bool)

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

uuid_textexperimentalsince 0.27.0Source
(u: Uuid) -> string

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

numeric_to_s64experimentalsince 0.27.0Source
(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_float64experimentalsince 0.27.0Source
(n: Numeric) -> (float64, bool)

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

Pool#

create_poolexperimentalsince 0.27.0Source
(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_poolexperimentalsince 0.27.0Source
(pool: *Pool)

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

acquireexperimentalsince 0.27.0Source
(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).

releaseexperimentalsince 0.27.0Source
(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.

pingexperimentalsince 0.27.0Source

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

transactionexperimental4 overloadssince 0.27.0Source
(pool: *Pool, data: $T, body: (tx: *Connection, data: T) -> Pg_Error, isolation := Isolation.DEFAULT, retries := 0, read_only := false) -> Pg_Error
(pool: *Pool, body: (tx: *Connection) -> Pg_Error, isolation := Isolation.DEFAULT, retries := 0, read_only := false) -> Pg_Error
(c: *Connection, data: $T, body: (tx: *Connection, data: T) -> Pg_Error, isolation := Isolation.DEFAULT, retries := 0, read_only := false) -> Pg_Error
(c: *Connection, body: (tx: *Connection) -> 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#

takeexperimentalsince 0.27.0Source
(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_queryexperimentalsince 0.27.0Source
(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.

pipeline_query_textexperimentalsince 0.27.0Source
(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_prepareexperimentalsince 0.27.0Source
(p: *Pipeline, name: string, sql: string, types: [] u32 = .[]) -> Pg_Error

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

pipeline_flush_requestexperimentalsince 0.27.0Source

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.

pipeline_chunked_rowsexperimentalsince 0.27.0Source
(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.

pipeline_next_resultexperimentalsince 0.27.0Source

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_abandonexperimentalsince 0.27.0Source
(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#

wait_for_notificationexperimentalsince 0.27.0Source

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.

notifyexperimentalsince 0.27.0Source
(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_identifierexperimentalsince 0.27.0Source
(name: string) -> string

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

Listener#

receiveexperimentalsince 0.27.0Source
(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_listenerexperimentalsince 0.27.0Source
(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_traceexperimentalsince 0.27.0Source
(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_migrationsexperimentalsince 0.27.0Source
(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_migrationsexperimentalsince 0.27.0Source
(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_hashexperimentalsince 0.27.0Source
(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_hashesexperimentalsince 0.27.0Source
(migrations: [] Migration) -> [] string

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

migrateexperimentalsince 0.27.0Source
(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_statementsexperimentalsince 0.27.0Source
(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.

line_column_of_positionexperimentalsince 0.27.0Source
(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.

migration_failureexperimentalsince 0.27.0Source
(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.

locate_failing_statementexperimentalsince 0.27.0Source
(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_linesexperimentalsince 0.27.0Source
(text: string) -> s64

How many newlines text has.

Managed#

project_managed_settingsexperimentalsince 0.31.0Source
(root: string) -> (managed: s64, version: string)

The settings the compile-time describe service uses: the defaults, with JACKET_PG_MANAGED_VERSION (a pinned release such as "16.15.1") and JACKET_PG_MANAGED_CACHE (the cache folder) applied. Managed mode as the project's jacket.toml sets it, under [postgres]: managed = true turns it on (-1 when the file or key is missing) and managed_version = "16.11.1" picks a release. JACKET_PG_MANAGED and JACKET_PG_MANAGED_VERSION win over both.

managed_triple_forexperimentalsince 0.31.0Source
(os: Managed_Os, arm64: bool, musl: bool) -> string

The release's name for a platform, or "" when it publishes nothing for it. musl only matters on Linux.

managed_tripleexperimentalsince 0.31.0Source
() -> string

The triple for the machine this runs on. "" when there is none (an unsupported OS or CPU).

managed_checksumexperimentalsince 0.31.0Source
(version: string, triple: string) -> string

The pinned SHA-256 for a release and triple, or "".

managed_cache_dir_forexperimentalsince 0.31.0Source
(os: Managed_Os, home: string, xdg_cache_home: string, local_app_data: string) -> string

The per-user folder for downloaded binaries, from the platform's conventions. The arguments are the environment variables it reads, so the choice can be checked without them.

ensure_binariesexperimentalsince 0.31.0Source
(settings: Managed_Settings) -> (bin_dir: string, error: string)

Downloads the release for this machine into the cache unless it is there, and returns its bin folder. Safe to run in several processes at once: each unpacks into its own folder and the first to rename it into place wins.

start_managedexperimentalsince 0.31.0Source
(settings: Managed_Settings) -> (Managed_Server, error: string)

Makes sure the binaries are there, creates the cluster if needed, and starts a server on it, or reuses one already running on the same data folder (found through postmaster.pid). The returned config connects as postgres.

prepare_managedexperimentalsince 0.31.0Source
(settings: Managed_Settings) -> error: string

Everything start_managed does before starting the server: the download, and the cluster. These are the slow parts (seconds to tens of seconds, the first time only), so code with a time limit on its requests calls this first, somewhere without one, and start_managed is then quick. Safe to call again and from several processes; it returns an error text.

stop_managedexperimentalsince 0.31.0Source
(server: *Managed_Server, force := false) -> string

Stops a server start_managed started. A server it found already running is left alone unless force is set. Returns an error text, empty when it stopped (or was not asked to).

JSON options#

pack_json_optionsexperimentalsince 0.27.0Source
(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#

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

Adds a Parse message to the output.

put_bindexperimentalsince 0.27.0Source
(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_u8experimentalsince 0.27.0Source
(out: *[..] u8, value: u8)
put_u16experimentalsince 0.27.0Source
(out: *[..] u8, value: u16)
put_u32experimentalsince 0.27.0Source
(out: *[..] u8, value: u32)
put_u64experimentalsince 0.27.0Source
(out: *[..] u8, value: u64)
reserve_forexperimentalsince 0.30.0Source
(array: *[..] $T, total: s64)

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

put_bytesexperimental2 overloadssince 0.27.0Source
(out: *[..] u8, bytes: [] u8)
(out: *[..] u8, text: string)

Appends bytes.

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

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

read_u16experimentalsince 0.27.0Source
(at: *u8) -> u16

Reads a big-endian number.

read_u32experimentalsince 0.27.0Source
(at: *u8) -> u32

Reads a big-endian number.

read_u64experimentalsince 0.27.0Source
(at: *u8) -> u64

Reads a big-endian number.

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

The bytes left in the cursor's data.

Startup#

append_key_logexperimentalsince 0.27.0Source
(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_alertexperimentalsince 0.27.0Source
(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_addressexperimentalsince 0.27.0Source
(host: string) -> bool

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

send_cancel_requestexperimentalsince 0.27.0Source

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.

contains_zeroexperimentalsince 0.27.0Source
(text: string) -> bool

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

cancel_queryexperimentalsince 0.27.0Source

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_addressexperimentalsince 0.27.0Source
(host: string) -> (Io.Address, bool)

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

is_hex_letterexperimentalsince 0.27.0Source
(c: u8) -> bool

Whether c is a to f in either case.

address_textexperimentalsince 0.27.0Source
(raw: string) -> string

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

name_matchesexperimentalsince 0.27.0Source
(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_passwordexperimentalsince 0.27.0Source
(password: string, salt: string, iterations: s64) -> [32] u8

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

prepare_passwordexperimentalsince 0.27.0Source
(password: string) -> string

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

tls_server_end_pointexperimentalsince 0.27.0Source
(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_secretexperimentalsince 0.27.0Source
(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_proofexperimentalsince 0.27.0Source
(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_runsexperimentalsince 0.27.0Source
() -> s64

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

md5_secretexperimentalsince 0.27.0Source
(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#

rewrite_placeholdersexperimentalsince 0.27.0Source
(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_ofexperimentalsince 0.27.0Source
(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_characterexperimentalsince 0.27.0Source
(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_idexperimentalsince 0.27.0Source
(rewritten_sql: string) -> u64

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

id_hexexperimentalsince 0.27.0Source
(id: u64) -> string

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

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

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

Entry#

Describe#

describe_answerexperimentalsince 0.27.0Source
(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#

schema_targetexperimentalsince 0.27.0Source
(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.

prune_schema_databasesexperimentalsince 0.27.0Source
(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_pathexperimentalsince 0.27.0Source
(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_pathexperimentalsince 0.27.0Source
(root: string) -> string

The path of the schema file.

min_server_versionexperimentalsince 0.27.0Source
() -> s64

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

find_project_rootexperimentalsince 0.27.0Source
(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_urlexperimentalsince 0.27.0Source
(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_ofexperimentalsince 0.27.0Source
(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_fileexperimentalsince 0.27.0Source
(root: string, id: u64) -> string

The path of the cache file for a statement.

read_cache_entryexperimentalsince 0.27.0Source
(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_entryexperimentalsince 0.27.0Source
(root: string, id: u64, entry: Query_Entry)

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

Dotenv#

dotenv_valueexperimentalsince 0.27.0Source
(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.

dotenv_database_urlexperimentalsince 0.27.0Source
(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#

selectexperimentalsince 0.27.0Source
(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").

updateexperimentalsince 0.27.0Source
(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.

deleteexperimentalsince 0.27.0Source
(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.

insertexperimentalsince 0.27.0Source
(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_manyexperimentalsince 0.27.0Source
(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.

eachexperimentalsince 0.27.0Source
(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_expansionexperimental3 overloadssince 0.27.0Source
(q: *Orm_Each($D, $T, $where, $order_by, $desc, $limit, $offset, $from_sql, $strategy, $call), body: Code, flags: For_Flags)
(rows: *Rows($T), body: Code, flags: For_Flags)
(stream: *Row_Stream($T), body: Code, flags: For_Flags)

Makes Pg.each loopable.

copy_manyexperimentalsince 0.27.0Source
(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_manyexperimentalsince 0.27.0Source
(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_oneexperimentalsince 0.27.0Source
(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_asexperimentalsince 0.27.0Source
(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.

likeexperimentalsince 0.27.0Source
(value: string, pattern: string) -> bool

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

ilikeexperimentalsince 0.27.0Source
(value: string, pattern: string) -> bool

ilike: case-insensitive like.

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

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

lowerexperimentalsince 0.27.0Source
(value: string) -> string

lower(value).

upperexperimentalsince 0.27.0Source
(value: string) -> string

upper(value).

if_someexperimentalsince 0.27.0Source
(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_keyexperimentalsince 0.27.0Source
(document: $T, key: string) -> bool

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

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

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

Typed#

fetch_allexperimentalsince 0.27.0Source
(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.

queryexperimentalsince 0.27.0Source
(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_oneexperimentalsince 0.27.0Source
(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_optexperimentalsince 0.27.0Source
(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.

executeexperimentalsince 0.27.0Source
(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.

queueexperimentalsince 0.27.0Source
(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_executeexperimentalsince 0.27.0Source
(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.

streamexperimentalsince 0.27.0Source
(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_scalarexperimentalsince 0.27.0Source
(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_asexperimentalsince 0.27.0Source
($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_typeexperimentalsince 0.27.0Source
(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.

json_column_optionsexperimentalsince 0.27.0Source
(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_besideexperimentalsince 0.27.0Source
(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_schemaexperimentalsince 0.27.0Source
(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_problemexperimentalsince 0.27.0Source
(file: string) -> string

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

recorded_schema_hashexperimentalsince 0.27.0Source
(text: string) -> string

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

check_json_columnsexperimentalsince 0.27.0Source
(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_nameexperimentalsince 0.27.0Source
(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_labelexperimentalsince 0.27.0Source
(name: string) -> string

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

Generate#

generate_schemaexperimentalsince 0.27.0Source
(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_catalogexperimentalsince 0.27.0Source
(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_sourceexperimentalsince 0.27.0Source
(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_namesexperimentalsince 0.27.0Source
(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_schemaexperimentalsince 0.27.0Source
(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_textexperimentalsince 0.27.0Source
(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_textexperimentalsince 0.27.0Source
(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.

Main#

mainexperimental2 overloadssince 0.27.0Source
()
()
from_shellexperimentalsince 0.31.0Source
(path: string) -> string

jaic runs the tool from the tool's own folder, so a relative path is taken from the folder the shell was in (PWD), when the shell says.

database_configexperimentalsince 0.31.0Source
() -> (Pg.Config, bool)

The compile-time database's settings from the environment, or a message on problem.

managed_allowedexperimentalsince 0.31.0Source
() -> bool

Commands that only read a schema built from the migrations, so a managed server can stand in for DATABASE_URL. migrate and status work on a real database and need its URL.

managed_configexperimentalsince 0.31.0Source
() -> (Pg.Config, bool)

The managed server (docs/postgres/managed.md), downloaded and started on first use and left running for the next build, as the describe service does.

refreshexperimentalsince 0.31.0Source
(db: *Pg.Connection, path: string, id: string, schema_hash: string, setup: string, check_only: bool) -> bool

Describes one entry again. True when it differs (and, unless checking, was rewritten).

propose_diffexperimentalsince 0.31.0Source
()

Writes the migration that would bring the migrated schema to the structs of db/schema.jai, unless nothing differs.

next_migration_pathexperimentalsince 0.31.0Source
(label: string) -> (string, bool)

The path of db/migrations/NNNN_name.sql, numbered after the newest migration and as wide as it; or a problem reported.

slugexperimentalsince 0.31.0Source
(text: string) -> string

"Add users table" as "add_users_table".

Describe service#

managed_urlexperimentalsince 0.27.0Source
(version: string) -> (url: string, failure: string)

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

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

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

drop_connectionexperimentalsince 0.27.0Source
(url: string, database: string)

Closes the held connection for this pair, if any.

connection_forexperimentalsince 0.27.0Source
(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.

Foreign#

Types#

Error_Kindenumexperimentalsince 0.27.0Source
:: enum

What sort of failure a Pg_Error is. SERVER errors also carry the server's fields.

NONE
CONFIG
a bad URL or setting, or a call made outside Io.io_run
IO
the connection failed; io says how
TIMED_OUT
an Io.with_deadline limit passed
CANCELED
the fiber was canceled
CLOSED
the connection was closed, or broken by an earlier failure
TLS
the TLS handshake or a TLS record failed
AUTH
authentication was refused or could not be done safely
PROTOCOL
the server sent something this client does not understand
SERVER
an ErrorResponse: code, message and the rest are set
DECODE
a value did not decode into the type asked for
USAGE
the call does not fit the result (a missing column, more rows than one, say)
NOT_FOUND
query_one matched no row
HOST_REJECTED
a server answered, but is not the kind target_session_attrs asks for
ABORTED
a pipelined statement was skipped because an earlier one of its batch failed
Sqlstateenumexperimentalsince 0.27.0Source
:: 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).

NONE
SUCCESSFUL_COMPLETION
00000
WARNING
01000
NO_DATA
02000
CONNECTION_EXCEPTION
08000
CONNECTION_DOES_NOT_EXIST
08003
CONNECTION_FAILURE
08006
PROTOCOL_VIOLATION
08P01
FEATURE_NOT_SUPPORTED
0A000
CARDINALITY_VIOLATION
21000
DATA_EXCEPTION
22000
STRING_DATA_RIGHT_TRUNCATION
22001
NUMERIC_VALUE_OUT_OF_RANGE
22003
NULL_VALUE_NOT_ALLOWED
22004
INVALID_DATETIME_FORMAT
22007
DATETIME_FIELD_OVERFLOW
22008
DIVISION_BY_ZERO
22012
CHARACTER_NOT_IN_REPERTOIRE
22021
INVALID_PARAMETER_VALUE
22023
INVALID_TEXT_REPRESENTATION
22P02
INVALID_BINARY_REPRESENTATION
22P03
INVALID_JSON_TEXT
22P05
INTEGRITY_CONSTRAINT_VIOLATION
23000
RESTRICT_VIOLATION
23001
NOT_NULL_VIOLATION
23502
FOREIGN_KEY_VIOLATION
23503
UNIQUE_VIOLATION
23505
CHECK_VIOLATION
23514
EXCLUSION_VIOLATION
23P01
INVALID_CURSOR_STATE
24000
INVALID_TRANSACTION_STATE
25000
ACTIVE_SQL_TRANSACTION
25001
READ_ONLY_SQL_TRANSACTION
25006
IN_FAILED_SQL_TRANSACTION
25P02
IDLE_IN_TRANSACTION_SESSION_TIMEOUT
25P03
INVALID_SQL_STATEMENT_NAME
26000
INVALID_AUTHORIZATION_SPECIFICATION
28000
INVALID_PASSWORD
28P01
TRANSACTION_ROLLBACK
40000
SERIALIZATION_FAILURE
40001
STATEMENT_COMPLETION_UNKNOWN
40003
DEADLOCK_DETECTED
40P01
SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION
42000
SYNTAX_ERROR
42601
INSUFFICIENT_PRIVILEGE
42501
CANNOT_COERCE
42846
GROUPING_ERROR
42803
DATATYPE_MISMATCH
42804
WRONG_OBJECT_TYPE
42809
UNDEFINED_COLUMN
42703
UNDEFINED_FUNCTION
42883
UNDEFINED_TABLE
42P01
UNDEFINED_PARAMETER
42P02
UNDEFINED_OBJECT
42704
DUPLICATE_COLUMN
42701
DUPLICATE_DATABASE
42P04
DUPLICATE_FUNCTION
42723
DUPLICATE_PREPARED_STATEMENT
42P05
DUPLICATE_SCHEMA
42P06
DUPLICATE_TABLE
42P07
DUPLICATE_OBJECT
42710
AMBIGUOUS_COLUMN
42702
AMBIGUOUS_FUNCTION
42725
INDETERMINATE_DATATYPE
42P18
INVALID_COLUMN_REFERENCE
42P10
INVALID_OBJECT_DEFINITION
42P17
INSUFFICIENT_RESOURCES
53000
DISK_FULL
53100
OUT_OF_MEMORY
53200
TOO_MANY_CONNECTIONS
53300
PROGRAM_LIMIT_EXCEEDED
54000
OBJECT_NOT_IN_PREREQUISITE_STATE
55000
OBJECT_IN_USE
55006
LOCK_NOT_AVAILABLE
55P03
OPERATOR_INTERVENTION
57000
QUERY_CANCELED
57014
ADMIN_SHUTDOWN
57P01
CRASH_SHUTDOWN
57P02
CANNOT_CONNECT_NOW
57P03
DATABASE_DROPPED
57P04
IDLE_SESSION_TIMEOUT
57P05
SYSTEM_ERROR
58000
RAISE_EXCEPTION
P0001
NO_DATA_FOUND
P0002
TOO_MANY_ROWS
P0003
ASSERT_FAILURE
P0004
INTERNAL_ERROR
XX000
DATA_CORRUPTED
XX001
Pg_Errorstructexperimentalsince 0.27.0Source
:: 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).

io: Io.Io_Error
IO, TIMED_OUT, CANCELED: the Io error behind it
code: Sqlstate
SERVER
message: string
the primary message; for client errors, what happened
owner: *Error_Fields
Owns message and the strings of the fields: release it with free_error. Null when the error holds nothing (no error).
Error_Fieldsstructexperimentalsince 0.30.0Source
:: 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.

severity: string
"ERROR", "FATAL", "PANIC" (never localized), "NOTICE", ...
detail: string
hint: string
position: s32
1-based character in the query the error points at, or 0
internal_position: s32
internal_query: string
where: string
the call stack of a PL/pgSQL function, say
schema: string
table: string
column: string
data_type: string
constraint: string
file: string
the server's source location
line: string
routine: string
storage: [] u8
The bytes every string of the error points into, stored after this struct in the same block.
Ssl_Modeenumexperimentalsince 0.27.0Source
:: enum

How connect uses TLS, as libpq's sslmode.

DISABLE
never TLS
PREFER
TLS when the server offers it, no certificate check (the default, like libpq)
REQUIRE
TLS, no certificate check
VERIFY_CA
TLS, the chain must lead to a trusted root
VERIFY_FULL
TLS, the chain and the host name are checked
ALLOW
a plain connection first; TLS (no certificate check) only when the server refuses that
Target_Session_Attrsenumexperimentalsince 0.27.0Source
:: enum

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

ANY
READ_WRITE
transactions are not read-only (a primary, by default)
READ_ONLY
PRIMARY
not in hot standby
STANDBY
in hot standby
PREFER_STANDBY
a standby if any host is one, else any
Load_Balance_Hostsenumexperimentalsince 0.27.0Source
:: enum

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

DISABLE
as listed
RANDOM
shuffled for each connection
Ssl_Cert_Modeenumexperimentalsince 0.27.0Source
:: enum

Whether connect sends a client certificate.

DISABLE
never send a client certificate
ALLOW
send the certificate in ssl_cert_pem when there is one (the default)
REQUIRE
fail unless a certificate was sent
Ssl_Protocol_Versionenumexperimentalsince 0.27.0Source
:: 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.

TLS_1_0
TLS_1_1
TLS_1_2
TLS_1_3
Channel_Bindingenumexperimentalsince 0.27.0Source
:: enum

Whether SCRAM authentication binds to the TLS channel, as libpq's channel_binding.

DISABLE
plain SCRAM-SHA-256 only
PREFER
SCRAM-SHA-256-PLUS over TLS when the server offers it
REQUIRE
fail unless SCRAM-SHA-256-PLUS is used
Configstructexperimentalsince 0.27.0Source
:: struct

Everything connect needs: servers, login, TLS and session settings. The defaults suit a local server; parse_url fills one from a connection string.

host: := "localhost"
A host name or address, or a directory holding the server's Unix socket (starts with /).
port: u16
hostaddr: string
An address to connect to instead of looking host up. host is still the name the server's certificate is checked against and the password file is searched by.
hosts: [] Host_Port
Every host of a connection string such as host1:5432,host2:5433, the first one included. Empty for one host (host and port). With several, connect tries them in turn until one accepts the login and suits target_session_attrs; host, hostaddr and port are then only the first entry's copy.
target_session_attrs: := Target_Session_Attrs.ANY
load_balance_hosts: := Load_Balance_Hosts.DISABLE
user: string
empty: $USER
password: string
database: string
empty: the user name
application_name: := "jai"
options: string
sent as the options startup parameter: "-c search_path=app"
parameters: [] Startup_Parameter
More startup parameters, or replacements for the client's own (client_encoding, DateStyle, IntervalStyle), matched by name without regard to case. Text-format dates, times and intervals only decode with DateStyle ISO and IntervalStyle postgres.
send_styles: := true
Send DateStyle ISO and IntervalStyle postgres. false leaves both to the server's settings (as psql does); text-format dates, times and intervals may then fail to decode.
on_notice: Notice_Proc
The connection's on_notice and user_data from the start, so notices sent while logging in (a login event trigger's, say) reach them too. Null keeps the default, which logs.
user_data: *void
json_options: Json_Options
How json and jsonb columns decode into Json(T): unknown and missing fields, duplicate keys, depth and size limits. Each result starts with these, and its json_options can be changed before get.
ssl_mode: := Ssl_Mode.PREFER
ssl_direct: := false
Start TLS at once, with ALPN postgresql, instead of asking first (PostgreSQL 17 and later): saves a round trip. Needs ssl_mode REQUIRE or stricter.
ssl_root_cert_pem: string
The roots to trust, as PEM. With ssl_mode VERIFY_CA or VERIFY_FULL, empty means the system's roots. With REQUIRE or PREFER, roots given are checked all the same (the chain, not the name, as libpq does with a root.crt), and empty means no check. The chain must end in a self-signed certificate from this list.
ssl_crl_pem: string
Certificate revocation lists (PEM, any number, or one DER) for the chain: if the server's certificate is listed, or none of them was issued by its issuer, the connection fails.
ssl_cert_pem: string
The client certificate chain (PEM, the client's certificate first) and its private key (PEM or DER), for servers that ask for one. ssl_key_password decrypts an encrypted key. Both ssl_cert_pem and ssl_key are needed to send a certificate; ssl_key_file is only the name error messages use for the key.
ssl_key: string
ssl_key_password: string
ssl_key_file: string
ssl_cert_mode: := Ssl_Cert_Mode.ALLOW
ssl_sni: := true
Send the host name as SNI, unless it is an IP address or a socket directory.
ssl_key_log_file: string
Appends the TLS session's secrets to this file, in the key log format Wireshark reads, for debugging. A file that cannot be opened is reported on stderr; the connection goes on.
ssl_min_protocol_version: := Ssl_Protocol_Version.TLS_1_2
ssl_max_protocol_version: := Ssl_Protocol_Version.TLS_1_3
channel_binding: := Channel_Binding.PREFER
min_protocol_version: := Protocol_Version.V3_0
The protocol versions to use, like libpq's min_protocol_version and max_protocol_version. The startup message asks for the maximum; a server that speaks less answers with the version it does, which the connection uses unless it is below the minimum (then connecting fails). The default is 3.0, as libpq's: .LATEST asks for 3.2, which PostgreSQL 18 and later speak.
max_protocol_version: := Protocol_Version.V3_0
statement_cache: := Statement_Cache_Mode.ON
Named prepared statements, kept per connection for typed queries (Pg.query, execute, ...): the first run of a query parses it, later runs only bind. .OFF uses unnamed statements only, for poolers that do not track named ones (PgBouncer before 1.21). The cache holds statement_cache_size statements; the least recently used is closed to make room.
statement_cache_size: := 256
verify_statements: := false
The first time a connection prepares a statement, ask the server what it returns and fail with an error if its columns are not the ones the query was compiled against, instead of reading the rows wrongly after a migration under a running program. Costs one round trip per statement and connection.
statement_timeout: := 0
The server's statement_timeout for the session, in milliseconds; 0 keeps the server's own. Sent as a startup parameter, so a pooler that rejects unknown parameters needs options=-c statement_timeout=... or a role setting instead.
connect_timeout: := 10_000
The whole connect (TCP, TLS, authentication), in milliseconds; negative: no limit.
cancel_timeout: := 2_000
After a deadline or cancel ends a query: how long to spend asking the server to stop it and reading what is left of its answer, in milliseconds. The connection breaks if that fails.
max_scram_iterations: := 1_000_000
A server could ask for any number of SCRAM iterations to waste the client's time.
allow_cleartext_password: := false
Send the password in clear text when the server asks, without TLS.
passfile: string
The password file to look in when password is empty: lines of host:port:database:user:password, * matching anything, \ escaping : and \. Empty: $PGPASSFILE, then ~/.pgpass. A file others can read is ignored, as in libpq.
require_auth: string
The authentication methods the server may use, as libpq's require_auth: a comma separated list of password, md5, gss, sspi, scram-sha-256 and none, or the same each with a ! in front to forbid them. Empty: any.
owned: bool
set by parse_url: the strings belong to the config (free_config)
Startup_Parameterstructexperimentalsince 0.27.0Source
:: struct

One run-time setting sent in the startup message (work_mem, search_path, ...).

name: string
value: string
Host_Portstructexperimentalsince 0.27.0Source
:: struct

One server of a multi-host connection string.

host: string
hostaddr: string
port: u16
Conninfostructexperimentalsince 0.27.0Source
:: 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.

values: [] string
is_set: [] bool
Auth_Methodenum flagsexperimentalsince 0.27.0Source
:: enum_flags

Authentication methods a server can ask for; a set of them is what require_auth allows.

PASSWORD
the server asked for the password in clear text
MD5
GSS
SSPI
SCRAM_SHA_256
SASL
NONE
the server accepted the login without asking for anything
Auth_Policystructexperimentalsince 0.27.0Source
:: struct

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

restricted: bool
allowed: Auth_Method
Protocol_Versionenumexperimentalsince 0.27.0Source
:: 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.

V3_0
V3_2
LATEST
the newest this client speaks: 3.2
Transaction_Statusenumexperimentalsince 0.27.0Source
:: enum

Where the session is in a transaction, as the server last reported it.

IDLE
outside a transaction block
IN_TRANSACTION
inside BEGIN ... COMMIT
FAILED
inside a transaction block that failed: only ROLLBACK works
Parameterstructexperimentalsince 0.27.0Source
:: struct

A name and value, as the server reports run-time settings.

name: string
value: string
Notice_Proctypeexperimentalsince 0.27.0Source
:: #type (connection: *Connection, notice: *Pg_Error)

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

Notification_Proctypeexperimentalsince 0.27.0Source
:: #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).

Connectionstructexperimentalsince 0.27.0Source
:: struct

An open session. Made by connect, released with close. One connection runs one request at a time; use a Pool to share a database between fibers.

backend_pid: s32
server_version: s64
From server_version: 170011 for 17.11, 150004 for 15.4.
parameters: [..] Parameter
What the server reported with ParameterStatus: server_version, TimeZone, client_encoding, standard_conforming_strings, ... (parameter(connection, name) looks one up).
on_notice: Notice_Proc
user_data: *void
for the program: on_notice can find its state here
on_notification: Notification_Proc
notifications: [..] Notification
Notifications received and not yet taken, oldest first, when on_notification is not set.
on_trace: Trace_Proc
set_trace
trace_data: *void
protocol_minor: u32
The minor protocol version in use: 2 (PostgreSQL 18 and later) or 0.
protocol_requested: u32
the minor version the startup message asked for
protocol_required: u32
the lowest minor version min_protocol_version accepts
tls_used: bool
channel_binding_used: bool
Authentication used SCRAM-SHA-256-PLUS: the password exchange is bound to this TLS session.
broken: bool
Set when the connection can no longer be used: a failed read or write, a protocol error, or a FATAL error from the server.
busy: bool
a request is in flight
pool: *Pool
the pool it belongs to, if any
host: string
Below: the client's own state.
tls_name: string
The name the server's certificate is for (the host, not the address connected to), the name sent as SNI ("" for none), whether verify-full checks the name, and whether the handshake sent a client certificate.
tls_sni: string
key_log_file: string
sslkeylogfile, for the handshake.
tls_check_name: bool
tls_cert_sent: bool
port: u16
unix_socket: bool
ssl_direct: bool
cancel_timeout: s64
opened_at: s64
monotonic ms, for a pool's max_lifetime
cancel_key: [] u8
json_options: Json_Options
copied to each result
reader: Io.Reader
writer: Io.Writer
out: [..] u8
message_start: s64
message_type: u8
of the message being written; 0 for the startup messages
trace_regress: bool
pipeline: Pipeline
pipeline mode, when pipeline.active
statements: Statement_Cache
prepared statements, by query (statements.jai)
input: [..] u8
input_start: s64
Columnstructexperimentalsince 0.27.0Source
:: struct

One result column: its name and the type the server gave it.

name: string
table_oid: u32
the table the column comes from; 0 for computed columns
column_number: s16
its attribute number in that table; 0 for computed columns
type_oid: u32
see Oid
type_size: s16
pg_type.typlen: negative for variable-length types
type_modifier: s32
pg_attribute.atttypmod: varchar(n), numeric(p, s), ...
format: Format
origin: string
"table.column" when a typed query knows it (names decode errors); else empty
Resultstructexperimentalsince 0.27.0Source
:: 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.

columns: [] Column
row_count: s64
command: string
The command tag ("SELECT 3", "INSERT 0 1", "UPDATE 7", "CREATE TABLE") and the row count in it (0 when it has none).
rows_affected: s64
returns_rows: bool
The statement returns rows (it sent a row description), even if there were none or it has no columns: select returns one row of zero columns.
partial: bool
More rows of the same statement follow (Script_Hooks.chunk_rows); command is not set yet.
values: [..] Value_Span
row_count * columns.count of them, row by row
data: [..] u8
json_options: Json_Options
for Json(T) columns; the connection's to begin with
json_query: Json_Options
the query's own (json = ... on a typed query), over json_options
allocations: [..] *void
allocator: Allocator
context.allocator when the query ran: it owns everything above
Value_Spanstructexperimentalsince 0.27.0Source
:: struct
offset: s64
length: s64
-1 for NULL
Statement_Infostructexperimentalsince 0.27.0Source
:: 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).

parameter_types: [] u32
columns: [] Column
Script_Hooksstructexperimentalsince 0.27.0Source
:: struct

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

on_result: (hooks: *Script_Hooks, result: *Result)
Called once per statement that completes, in order, with its rows (when it returns rows) and command tag. The result is freed after the call unless the hook moves it out (and leaves result.* zeroed). Without this hook rows are not kept at all.
chunk_rows: s64
When positive, rows reach on_result in pieces of this many as they arrive, marked partial, so a large result never has to fit in memory. The statement's last call has the remaining rows (maybe none) and the command tag. A partial result must not be moved out: its rows are dropped after the call and its columns kept for the next piece.
on_copy: (hooks: *Script_Hooks, copy: *Copy_Info)
Called when the server starts a COPY, before any data moves.
copy_in: (hooks: *Script_Hooks, copy: *Copy_Info) -> data : string, done: bool, failure: string
COPY ... FROM STDIN: called until it says done, each time for the next piece of data (any split; rows need not line up with calls). A non-empty failure aborts the COPY with that message instead, and the server answers with an error. Without this hook the COPY fails.
copy_out: (hooks: *Script_Hooks, copy: *Copy_Info, data: [] u8)
COPY ... TO STDOUT: called with each piece of data the server sends (one row in text and CSV formats). Without this hook the data is dropped.
user_data: *void
Copy_Infostructexperimentalsince 0.27.0Source
:: struct

The shape of a COPY the server has started.

incoming: bool
FROM STDIN: the client sends; else TO STDOUT: the server sends
format: Format
the overall format: text (also CSV) or binary
column_formats: [] Format
Statement_Cache_Modeenumexperimentalsince 0.27.0Source
:: enum

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

ON
OFF
unnamed statements only, for poolers that do not track named ones
Planenumexperimentalsince 0.27.0Source
:: enum

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

AUTO
a named statement: the server may switch to a generic plan after five runs
CUSTOM
the unnamed statement, planned again with the actual values on every run
Copy_Out_Proctypeexperimentalsince 0.27.0Source
:: #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_Rowsstructexperimentalsince 0.27.0Source
:: 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.

rows: [] Row
result: Result
Oidenumexperimentalsince 0.27.0Source
:: 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.

BOOL
BYTEA
CHAR
NAME
INT8
INT2
INT4
TEXT
OID
JSON
JSON_ARRAY
FLOAT4
FLOAT8
UNKNOWN
BPCHAR
VARCHAR
DATE
TIME
TIMESTAMP
TIMESTAMPTZ
INTERVAL
NUMERIC
UUID
JSONB
BOOL_ARRAY
BYTEA_ARRAY
CHAR_ARRAY
NAME_ARRAY
INT2_ARRAY
INT4_ARRAY
TEXT_ARRAY
BPCHAR_ARRAY
VARCHAR_ARRAY
INT8_ARRAY
OID_ARRAY
FLOAT4_ARRAY
FLOAT8_ARRAY
TIMESTAMP_ARRAY
DATE_ARRAY
TIME_ARRAY
TIMESTAMPTZ_ARRAY
INTERVAL_ARRAY
NUMERIC_ARRAY
UUID_ARRAY
JSONB_ARRAY
Formatenumexperimentalsince 0.27.0Source
:: enum

How a value travels on the wire: server text, or its binary form.

TEXT
BINARY
Nullablestructexperimentalsince 0.27.0Source
:: struct

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

value: T
null: bool
Jsonstructexperimentalsince 0.27.0Source
:: 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.

value: T
write: (value: *T, builder: *String_Builder)
Json_Rawstructexperimentalsince 0.27.0Source
:: 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.

text: string
Uuidstructexperimentalsince 0.27.0Source
:: struct

uuid: the 16 bytes in network order.

bytes: [16] u8
Timestampstructexperimentalsince 0.27.0Source
:: 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.

microseconds: s64
Local_Timestampstructexperimentalsince 0.27.0Source
:: struct

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

microseconds: s64
Datestructexperimentalsince 0.27.0Source
:: struct

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

days: s32
Timestructexperimentalsince 0.27.0Source
:: struct

time (without time zone): microseconds since midnight.

microseconds: s64
Intervalstructexperimentalsince 0.27.0Source
:: 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.

months: s32
days: s32
microseconds: s64
Numericstructexperimentalsince 0.27.0Source
:: struct

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

text: string
Pool_Configstructexperimentalsince 0.27.0Source
:: struct

Sizing and recycling rules for a Pool. Times are in milliseconds.

max_size: := 10
min_idle: := 0
Kept open even when idle; opened in the background, so the first borrowers need not wait.
idle_timeout: := 10 * 60_000
An idle connection older than this is closed (ms; negative: never).
max_lifetime: := 30 * 60_000
A connection is closed after this long, less up to a tenth so that a fleet started together does not reconnect together (ms; negative: never).
health_check_after: := 30_000
A connection idle for longer is checked with an empty Sync before it is handed out (ms; negative: never).
acquire_timeout: := 30_000
How long acquire waits for a free connection (ms; negative: no limit).
Poolstructexperimentalsince 0.27.0Source
:: 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.

config: Config
settings: Pool_Config
idle: [..] Idle_Connection
Idle connections, the most recently used last: they are handed out first, so the rest go stale and are closed by idle_timeout when the load drops.
open: s64
Connections open or being opened, idle or borrowed: at most max_size.
changed: Io.Condition
A connection came back or a slot freed up.
closed: bool
maintaining: bool
the background fiber runs: the pool is not freed under it
thread_index: u32
maintainer: Io.Fiber_Handle
Isolationenumexperimentalsince 0.27.0Source
:: enum

Transaction isolation level for transaction.

DEFAULT
the server's default_transaction_isolation
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
Pipeline_Kindenumexperimentalsince 0.27.0Source
:: enum

What a pipeline reported next.

NONE
nothing is waiting for a result
RESULT
a statement finished: its rows and command tag, or what a prepare/describe/close did
ROWS
chunked rows mode: some rows of a statement that goes on (result.partial)
ERROR
the server rejected the statement: error
ABORTED
the statement was skipped because an earlier one of its batch failed
SYNC
a sync point was reached: the batch is over and the server is ready again
Pipeline_Resultstructexperimentalsince 0.27.0Source
:: struct

What one queued statement or sync point turned into.

result: Result
For RESULT and ROW: the columns, rows and command tag. After a describe, only the columns.
parameter_types: [] u32
After pipeline_describe_statement: the types the server inferred for $1, $2, ...
error: Pg_Error
for ERROR
Pipeline_Stepenumexperimentalsince 0.27.0Source
:: enum

The kind of request queued in a pipeline.

QUERY
Parse (unless prepared), Bind, Describe portal, Execute
PREPARE
DESCRIBE_STATEMENT
DESCRIBE_PORTAL
CLOSE
SYNC
Pipelinestructexperimentalsince 0.27.0Source
:: struct

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

connection: *Connection
active: bool
aborted: bool
A statement failed and its batch has not reached its sync point yet: the rest of the batch is skipped.
steps: [..] Pipeline_Step
queued, whose results are not all read; from head on
head: s64
unsynced: s64
statements queued since the last sync point
chunk_rows: s64
the statement being read delivers this many rows per result (0: all at once)
tag_ready: bool
the statement's last rows were delivered, and its command tag is in current
current: Pipeline_Result
the result being assembled: a deadline in the middle loses nothing
queued: s64
statements queued since pipeline_enter, sync points not counted
read: s64
of those, how many have their result read
early: [..] Early_Result
read on the way to a later typed result (take), not taken yet
Pendingstructexperimentalsince 0.27.0Source
:: struct

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

serial: s64
Notificationstructexperimentalsince 0.27.0Source
:: struct

A message from NOTIFY on a channel the connection LISTENs to.

channel: string
payload: string
empty when NOTIFY gave none
backend_pid: s32
the session that sent it
Listenerstructexperimentalsince 0.27.0Source
:: 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.

queue: [..] Notification
Notifications received and not yet taken, oldest first (receive). The reader waits when LISTENER_QUEUE of them are queued.
arrived: Io.Condition
closed: bool
no more will arrive
reconnects: s64
How many times the connection was replaced after a failure.
pool: *Pool
channels: [..] string
connection: *Connection
closing: bool
finished: bool
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_Proctypeexperimentalsince 0.27.0Source
:: #type (connection: *Connection, line: string)

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

Migrationstructexperimentalsince 0.27.0Source
:: struct

One migration file, embedded at compile time by load_migrations.

version: s64
the number the file name starts with
name: string
the file name without .sql
sql: string
sha256: string
hex digest of sql
transaction: := true
false for -- jacket: no-transaction
Migrate_Optionsstructexperimentalsince 0.27.0Source
:: struct

Relaxes the checks migrate makes before applying anything.

allow_modified: := false
Apply even when an applied migration's file changed or is missing from the list.
allow_out_of_order: := false
Apply migrations numbered below the newest applied one (merged from another branch). Without it they are refused, since they were written against an older schema.
Migration_Failurestructexperimentalsince 0.27.0Source
:: struct

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

name: string
the file name without .sql
file: string
its path, when the caller knows the folder
index: s64
in the list, from 0
line: s64
1-based line of the file the server pointed at, or 0 when it gave no position
column: s64
message: string
the server's message
Migration_Stateenumexperimentalsince 0.27.0Source
:: enum

Where one migration stands against a database.

PENDING
not applied yet
APPLIED
MODIFIED
applied, but the file changed since
MISSING
applied, but no longer in the list (the entry has only version and name)
OUT_OF_ORDER
pending, but numbered below the newest applied migration
Migration_Statusstructexperimentalsince 0.27.0Source
:: struct

A migration and whether it is applied, changed, missing or out of order.

version: s64
name: string
Json_Unknown_Fieldsenumexperimentalsince 0.27.0Source
:: enum

What decoding does with a JSON key the target struct has no field for.

INHERIT
IGNORE
the built-in default: a key the struct has no field for is skipped
DENY
it is a decode error
Json_Missing_Fieldsenumexperimentalsince 0.27.0Source
:: enum

What decoding does when a required field is absent from the JSON.

INHERIT
ERROR
the built-in default: a field that is not optional must be there
DEFAULT
it keeps its declared initializer
Json_Duplicate_Keysenumexperimentalsince 0.27.0Source
:: enum

What decoding does when a JSON object repeats a key.

INHERIT
LAST
the built-in default: what jsonb keeps, and what json text may contain
ERROR
Json_Optionsstructexperimentalsince 0.27.0Source
:: struct

How json and jsonb columns decode into structs. Layers of these (connection, query) merge; INHERIT defers to the layer below.

unknown_fields: := Json_Unknown_Fields.INHERIT
missing_fields: := Json_Missing_Fields.INHERIT
duplicate_keys: := Json_Duplicate_Keys.INHERIT
max_depth: := 256
nesting of arrays and objects
max_size: := 1 << 30
bytes of text
Cursorstructexperimentalsince 0.27.0Source
:: 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.

data: [] u8
at: s64
bad: bool
Scramstructexperimentalsince 0.27.0Source
:: struct
started: bool
verified: bool
the server proved it knows the password
plus: bool
channel binding is in use
nonce: string
client_first_bare: string
binding: string
the gs2 header, then the certificate hash for -PLUS
server_signature: [32] u8
Salted_Entrystructexperimentalsince 0.27.0Source
:: struct
key: [32] u8
salted: [32] u8
used: s64
full: bool
Rewrite_Editstructexperimentalsince 0.27.0Source
:: 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).

source_offset: s64
source_length: s64
rewritten_offset: s64
rewritten_length: s64
hole: s64
Rewritestructexperimentalsince 0.27.0Source
:: struct
sql: string
the statement the server sees
edits: [..] Rewrite_Edit
holes: s64
how many % holes the source has
error: string
empty when the source scanned cleanly
error_offset: s64
where in the source the error is
Forced_Nullabilityenumexperimentalsince 0.27.0Source
:: enum
NONE
NON_NULL
the alias said name!
NULLABLE
the alias said name?
Query_Typestructexperimentalsince 0.27.0Source
:: struct
type: string
how Postgres shows it: "bigint", "character varying(20)", "mood"
base: string
what decides the Jai type: domains resolved, arrays as "int4[]", enums as "enum"
Query_Columnstructexperimentalsince 0.27.0Source
:: struct
name: string
without the alias suffixes
type: string
base: string
nullable: bool
origin: string
schema.table.column, for a column read straight from a table
jai: string
the alias's Jai type, or empty
oid: u32
in the database that described it; not stored
Query_Depstructexperimentalsince 0.27.0Source
:: struct
key: string
fingerprint: string
Query_Entrystructexperimentalsince 0.27.0Source
:: struct
format: s64
ENTRY_FORMAT when written by this compiler; 0 when the file has none
sql: string
schema_hash: string
server_major: s64
search_path: string
deps: [..] Query_Dep
deps_complete: bool
params: [..] Query_Type
columns: [..] Query_Column
Describe_Requeststructexperimentalsince 0.27.0Source
:: struct
op: string
"describe", "fingerprints" or "ping"
url: string
the database; empty with managed: the service's own managed server
managed: bool
no URL: describe in the managed server (needs migrations; see managed.jai)
managed_version: string
which release the managed server runs; empty: the default
search_path: string
set before describing; empty: the server's own
sql: string
the rewritten statement ($1, $2, ...)
schema_hash: string
of the migrations; copied into the entry
keys: [] string
"fingerprints": the dependency keys to fingerprint
migrations: string
the migrations folder: answer from its schema database
setup: string
set by the service: SQL run first inside the describe's transaction
setup_marks: [] Setup_Mark
set by the service: where each migration lies in setup
stale: Migration_Failure
set by the service: the schema is the one before this failed migration
min_server_version: s64
the oldest server major version the project supports; 0: any
Schema_Databasesstructexperimentalsince 0.27.0Source
:: struct
first_seen: [..] Seen_Hash
When each set of migrations (by schema hash) was first asked about without a database.
failures: [..] Known_Failure
Sets of migrations (by schema hash) whose build failed, so the failure is answered again without building again.
Seen_Hashstructexperimentalsince 0.27.0Source
:: struct
hash: string
milliseconds: s64
Schema_Targetstructexperimentalsince 0.27.0Source
:: 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.

database: string
setup: string
setup_marks: [] Setup_Mark
built: bool
failed: bool
A migration failed while building the database for the whole set. database is then the schema before it (empty when no migration comes before it): answers from it are stale.
Setup_Markstructexperimentalsince 0.27.0Source
:: struct

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

name: string
index: s64
start: s64
byte offset in the setup text
length: s64
Partialstructexperimentalsince 0.27.0Source
:: 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_Strategyenumexperimentalsince 0.27.0Source
:: enum

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

PRELOAD
a second query for every row's related rows, as load_many does
LATERAL
one query: each related column comes back as an array from a lateral join
Loaded_Manystructexperimentalsince 0.27.0Source
:: 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.

items: [][] Item
rows: [] Item
Every item once; items are views into it.
result: 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_Onestructexperimentalsince 0.27.0Source
:: 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.

items: [] Nullable(Item)
rows: [] Item
result: Result
Null_Markerstructexperimentalsince 0.27.0Source
:: struct

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

Rowstructexperimentalsince 0.27.0Source
:: 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.

Rowsstructexperimentalsince 0.27.0Source
:: 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.

result: Result
map: [] s64
For a hand-written row type (query_as): the column behind each field, in field order.
error: Pg_Error
Set when a row failed to decode: the loop stops, and the error is here.
Row_Streamstructexperimentalsince 0.27.0Source
:: struct

Rows of a streamed typed query, read one at a time with a for loop.

pipeline: *Pipeline
null once the statement is over
error: *Pg_Error
meta: Statement_Meta
Scalarstructexperimentalsince 0.27.0Source
:: struct

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

Json_Columnstructexperimentalsince 0.27.0Source
:: struct

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

column: string
type: Type
options: Json_Options
Json_Columns :: Pg.Json_Column.[ .{ "users.settings", Settings, .{ unknown_fields = .DENY } } ];

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

Catalog_Enumstructexperimentalsince 0.27.0Source
:: struct

A Postgres enum.

key: string
"schema"."name", as Described_Type.qualified
schema: string
name: string
labels: [] string
in sort order
Catalog_Columnstructexperimentalsince 0.27.0Source
:: struct
name: string
sql_type: string
format_type, for the doc comment
base: string
what jai_type_of takes: "int8", "text[]", "json", or "enum" / "enum[]"
enum_key: string
the Catalog_Enum.key of an enum column or enum array
nullable: bool
primary_key: bool
has_default: bool
a default, identity or generated: an insert may leave it out
generated: bool
always generated: an insert may not give it a value
Catalog_Relationstructexperimentalsince 0.27.0Source
:: struct

A table, view, materialized view or foreign table.

schema: string
name: string
view: bool
columns: [] Catalog_Column
Catalog_Foreign_Keystructexperimentalsince 0.27.0Source
:: struct
schema: string
the referencing table
table: string
columns: [] string
target_schema: string
target_table: string
target_columns: [] string
Namesstructexperimentalsince 0.27.0Source
:: struct
enums: [] string
the Jai type of each enum, in the catalog's order
relations: [] string
and of each relation
Schema_Fieldstructexperimentalsince 0.27.0Source
:: struct

One field of a struct in the schema file.

name: string
type_text: string
as written, Pg.Nullable(...) included
primary_key: bool
has_default: bool
generated: bool
json_text: bool
@pg_json: a json column rather than jsonb
Schema_Structstructexperimentalsince 0.27.0Source
:: struct

A struct with an @pg_table note.

type_name: string
schema: string
table: string
belongs_to: [] string
the arguments of the @pg_belongs_to notes
fields: [] Schema_Field
Schema_Enumstructexperimentalsince 0.27.0Source
:: struct

An enum of the schema file: a Postgres enum.

type_name: string
schema: string
name: string
labels: [] string
in declaration order
Migration_Proposalstructexperimentalsince 0.27.0Source
:: struct

The migration diff proposes.

sql: string
changes: s64
statements that change the schema
todos: s64
review comments for changes it could not express
Enum_Refstructexperimentalsince 0.27.0Source
:: struct

How an enum of the file is called in Postgres.

type_name: string
schema: string
name: string
Heldstructexperimentalsince 0.27.0Source
:: 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.

url: string
database: string
failed_until: s64
monotonic ms: until then the last failure is the answer
backoff: s64
failure: string
Managed_Statestructexperimentalsince 0.27.0Source
:: 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.

ready: bool
url: string
failed_until: s64
monotonic ms: until then the last failure is the answer
failure: string
Managed_SettingsstructforeignexperimentalSource
:: struct
version: := MANAGED_VERSION
Which release to use; it needs an entry in MANAGED_ARCHIVES.
cache_dir: string
Where the downloaded binaries live. Empty: managed_cache_dir().
data_dir: string
The cluster's folder. Empty: <cache_dir>/cluster-<major version>.
socket_dir: string
Where the Unix socket goes (ignored on Windows). Empty: <data_dir>/sock, or a short folder under /tmp when that path is too long for a socket.
port: u16
0: 5432 for a Unix socket (it only names the socket file), a free-looking port on Windows.
timeout_seconds: := 60
Seconds pg_ctl waits for the server to start or stop.
extra_conf: string
Lines appended to the managed settings file, e.g. "max_connections = 50".
Managed_ServerstructforeignexperimentalSource
:: struct
config: Config
connect with this; user postgres, database postgres, no password
url: string
the same as a postgresql:// URL
bin_dir: string
data_dir: string
socket_dir: string
empty on Windows
port: u16
started_here: bool
false: a server was already running on data_dir and was reused
Managed_ArchivestructforeignexperimentalSource
:: struct
version: string
triple: string
sha256: string
of the .tar.gz, as committed here
OptionsstructforeignexperimentalSource
:: struct
command: string
root: string
build: string
prune: bool
out: string
name: string
the argument of new

Constants#

CONNINFO_KEYWORDSexperimentalsince 0.27.0Source
:: 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_CODESexperimentalsince 0.27.0Source
:: 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_STARTSexperimentalsince 0.27.0Source
:: 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_CHARSexperimentalsince 0.27.0Source
:: 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, ...
LISTENER_QUEUEexperimentalsince 0.27.0Source
:: 256

notifications held before the reader waits for the program

MAX_MESSAGE_BYTESexperimentalsince 0.27.0Source
:: 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_BYTESexperimentalsince 0.27.0Source
:: 5

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

PROTOCOL_3_2experimentalsince 0.27.0Source
:: 196610

PostgreSQL 18 and later; older servers answer with version 3.0

AUTH_OKexperimentalsince 0.27.0Source
:: 0

AuthenticationRequest codes.

SALTED_CACHE_SIZEexperimentalsince 0.27.0Source
:: 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_FORMATexperimentalsince 0.27.0Source
:: 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.

SCHEMA_BUILD_DELAY_MSexperimentalsince 0.27.0Source
:: 2_000

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

EVERY_ROWexperimentalsince 0.27.0Source
:: #code true

Defaults for select: no filter, no order.

MANAGED_VERSIONforeignexperimentalSource
:: "17.11.1"

The default release. Managed_Settings.version may name any other release the checksum table below pins: the latest of each supported major version (15, 16, 17 and 18).

MANAGED_ARCHIVESforeignexperimentalSource
:: Managed_Archive.[ .{ "15.19.1", "aarch64-apple-darwin", "02214be6486ec1be214eae66664f384be218b0893d29a9c151408afe5646a8ab" }, .{ "15.19.1", "x86_64-apple-darwin ...

SHA-256 of every release archive the managed mode accepts, taken from the release's .sha256 files and cross-checked against the digests GitHub reports for the assets. A download is compared with these, never with a checksum fetched next to it.

MANAGED_SOCKET_PORTforeignexperimentalSource
:: 5432

The port a Unix socket server uses by default (it only names the socket file), and the range Windows picks from (the dynamic ports).

COMMANDSforeignexperimentalSource
:: string.["prepare", "check", "generate", "migrate", "status", "new", "diff"]