NURLNURL registrynurl-lang.org →

← psql

psql 0.4.0 API

pg.nu

packages/psql/src/pg.nu — PostgreSQL frontend/backend protocol v3 in pure NURL. No libpq: the wire protocol, the authentication (trust / cleartext / MD5 / SCRAM-SHA-256) and the optional TLS upgrade (PostgreSQL's SSLRequest, then the pure-NURL TLS client) are all implemented here, so a secure connection works on a host with nothing installed.

( pg_connect host port user password database sslmode ) → !PgConn PgErr ( pg_query conn sql ) → !PgResult PgErr ( pg_close conn ) → v early release (optional)

A PgConn is a handle: every copy is the same connection, and the last owner closes it (Terminate, then the TLS session or the socket). PgResult is a plain value. Nothing here is released by hand.

sslmode: 0 disable · 1 prefer (TLS if offered, no verify) · 2 require (TLS mandatory, no cert check) · 3 verify-full (TLS + chain/hostname).

PgResult is a flat row-major cell table (see the accessors at the bottom): nested Vecs are avoided for robustness; NULLs are tracked in a parallel flag array.

API

: | PgErr

: | PgErr {
    PgConnFail  // could not open the TCP socket
    PgTls  // TLS upgrade failed (refused, or handshake/cert error)
    PgProtocol  // malformed/unexpected backend message
    PgAuth  // authentication failed or unsupported method
    PgServerError  // backend ErrorResponse — see ( pg_conn_lasterr conn )
    PgQuery  // query-time failure — see ( pg_conn_lasterr conn )
    PgNeedPassword  // server asked for a password but none was supplied
}

SSLRequest magic (1234 << 16 | 5679) and the protocol-3.0 version word.

@ pg_err_name PgErr e → s

: PgConnImpl

: PgConnImpl {
    i tls  // 0 plaintext, 1 TLS
    i raw  // raw socket handle (plaintext reads; fd the TLS layer took over)
    TcpConn tcp  // plaintext transport (writes via tcp_write_all)
    TlsConn tc  // TLS transport (when tls = 1)
    ( Vec u ) rxbuf  // backend bytes not yet split into messages
    String lasterr  // last ErrorResponse / failure text
    i be_pid
    i be_key
    String srv_ver  // server_version from ParameterStatus (for the banner)
    String db_name  // connection identity, for the banner and \conninfo
    String user_name
    String host_name
    i started  // 1 once the StartupMessage went out: say Terminate on close
}

% Drop PgConnImpl { @ drop PgConnImpl c → v { ( __pg_teardown . c started . c tls . c tc . c tcp . c raw ) } }

The transport is the connection's raw resource: its last owner says goodbye (a best-effort Terminate, once the server was spoken to) and closes the TLS session or the socket — what pg_close did by hand. The buffers and strings go with the drop glue.

: PgConn { s ctl }

@ PgConn_share PgConn h → PgConn

@ PgConn_drop sink PgConn h → v

@ pg_conn_lasterr PgConn c__h → String

The connection's text fields, lent: they live as long as the PgConn. lasterr is the last ErrorResponse ([SQLSTATE] message), empty if none.

@ pg_conn_server_version PgConn c__h → String

@ pg_conn_db_name PgConn c__h → String

@ pg_conn_user_name PgConn c__h → String

@ pg_conn_host_name PgConn c__h → String

@ pg_conn_tls PgConn c__h → i

1 when the connection runs over TLS, 0 when it is plaintext.

: PgMsg

: PgMsg {
    i mtype
    ( Vec u ) payload
}

: PgResult

: PgResult {
    i ncols
    i nrows
    ( Vec String ) colnames
    ( Vec String ) cells  // NULL rendered as empty String; see nulls
    ( Vec u ) nulls  // 1 = SQL NULL
    String tag  // CommandComplete tag (e.g. "SELECT 3")
}

Row-major result table. cell(r,c) lives at index r*ncols+c.

@ pg_connect s host i port s user s password s database i sslmode → !PgConn PgErr

@ pg_query PgConn c__h s sql → !PgResult PgErr

── simple query ──────────────────────────────────────────────────

@ pg_result_col PgResult r i idx → String

── result accessors ──────────────────────────────────────────────

@ pg_result_is_null PgResult r i row i col → b

@ pg_result_cell PgResult r i row i col → String

@ pg_result_free sink PgResult r → v

A plain value: let go of r now rather than at the end of its owner's scope.

@ pg_close sink PgConn c → v

── teardown ────────────────────────────────────────────────────── Let go of c now rather than at the end of its owner's scope (the last owner closes the connection).


scram.nu

packages/psql/src/scram.nu — SCRAM-SHA-256 client (RFC 5802 / RFC 7677), the SASL mechanism PostgreSQL uses for password authentication.

Pure NURL on top of the stdlib crypto (SHA-256 / HMAC / PBKDF2 / base64). Channel binding is not used ("n,," / "c=biws"), matching libpq's default SCRAM-SHA-256 (not the -PLUS variant).

( scram_client_first_bare nonce ) → String "n=,r=<nonce>" ( scram_compute password cfb server_first ) → ScramResult

password is the raw password bytes (SASLprep is the identity map for the ASCII passwords overwhelmingly used in practice). cfb is the client-first-bare returned above; server_first is the server's SASLContinue payload. The result carries the client-final message to send and the base64 server signature to check against the SASLFinal.

API

: ScramResult

: ScramResult {
    b ok
    String client_final
    String server_sig
}

@ scram_client_first_bare s nonce → String

client-first-bare: PostgreSQL ignores the SCRAM username (it uses the startup username), so we send an empty n=.

@ scram_compute ( Vec u ) password String cfb String server_first → ScramResult


main.nu

packages/psql/src/main.nu — the psql command: a pure-NURL PostgreSQL client. Connects over the v3 wire protocol with optional TLS (pure-NURL, no libpq / no OpenSSL needed on the target), authenticates with trust / cleartext / MD5 / SCRAM-SHA-256, and behaves like psql for everyday use: aligned result tables, backslash meta-commands, a multi-line REPL terminated by ';', and one-shot -c.

psql [flags] ["postgres://user:pass@host:port/db?sslmode=..."] -h HOST host (default $PGHOST or localhost) -p PORT port (default $PGPORT or 5432) -U USER user (default $PGUSER or postgres) -d DB database (default $PGDATABASE or the user) -c SQL run one command and exit; otherwise read from stdin --sslmode M disable | prefer | require | verify-full (default prefer) Password is taken from $PGPASSWORD.

Meta-commands (when no statement is pending): \dt list tables \l list databases \d TABLE describe a table \du list roles \dn list schemas \conninfo connection info \? this help \q quit

API

& c @ isatty i32 fd → i32

& c @ nurl_read_password s prompt → s

: ConnInfo

: ConnInfo {
    String host
    i port
    String user
    String password
    String database
    i sslmode
    b has_url
}

postgres://[user[:pass]@]host[:port][/db][?sslmode=M]

@ main → i