class PG::Connection

A session with the server: one task's at a time. Moving it to another task goes through a PG::Lease.

src/connection.qz:97

Constants

STATEMENT_CACHE_SIZE: Int

How many prepared statements #query keeps by default.

src/connection.qz:99

QUOTED: Array[Bytes]

The bytes that make an array element quoted.

src/connection.qz:756

Class methods

def self.open(config: PG::Config) -> PG::Connection

Connects with config and authenticates (see PG.connect).

src/connection.qz:121

def self.where(config: PG::Config) -> String

Where config points, for messages.

src/connection.qz:255

def self.md5_password(user: String, password: String, salt: Bytes) -> String

What md5 authentication sends: md5 and the hex digest of the hex digest of password and user, then salt.

src/connection.qz:261

def self.new(config: PG::Config, io: IO) -> PG::Connection

A connection over io, not started: PG.connect makes them.

src/connection.qz:266

def self.new(lease: PG::Lease) -> PG::Connection

The connection lease describes (see .adopt).

src/connection.qz:272

def self.adopt(lease: PG::Lease) -> PG::Connection

Makes a connection of lease in the calling task: the session goes on where #release left it (idle, its prepared statements kept). A lease is used once.

src/connection.qz:288

def self.quote_ident(name: String) -> String

name as an SQL identifier: double-quoted, a " in it doubled. Raises ArgumentError for an empty name, one holding a NUL, or one of 64 bytes or more (the server cuts an identifier to 63 bytes).

PG::Connection.quote_ident("jobs")      # => "\"jobs\""
PG::Connection.quote_ident("a\"b c")    # => "\"a\"\"b c\""

src/connection.qz:463

def self.encode(value: Object?) -> Bytes?

A parameter's text, as the server reads it (nil for NULL). A Time is sent in UTC, so that a timestamp column (without zone), which drops the offset, keeps the instant as PG::Row#time reads it back. An Array is a one-dimensional PostgreSQL array (encode_array).

PG::Connection.encode(true)                # => b"t"
PG::Connection.encode(b"\x01\xff")       # => b"\\x01ff"
PG::Connection.encode(-Float::INFINITY)    # => b"-Infinity"
PG::Connection.encode([1, nil, 3])         # => b"{1,NULL,3}"

src/connection.qz:641

def self.encode_array(xs: Array[Object?]) -> Bytes

An array parameter's text: {1,2,3}, each element as encode gives it, double-quoted when it is empty, spells NULL or holds ,, {, }, ", \ or white space (" and \ escaped with \); a nil element is NULL, the empty array {}. Raises ArgumentError for elements of different classes or a nested array (one dimension for now).

PG::Connection.encode_array(["a", "b,c", "d\"e", ""])   # => b"{a,\"b,c\",\"d\\\"e\",\"\"}"
PG::Connection.encode_array(Array[Int].new)            # => b"{}"

src/connection.qz:676

def self.element_class(xs: Array[Object?]) -> Type[Object]?

The class of an array parameter's elements, nil when it has none but nils; ArgumentError for mixed classes or a nested array.

src/connection.qz:700

def self.types(params: Array[Object?]) -> Array[Int]

The type OIDs the Parse message gives for params, up to the last one that is not 0: the array's type for an array of Ints (int8[] 1016), Bools (bool[] 1000) or Bytes (bytea[] 1001), so that a bare unnest($1) resolves without a cast; 0 for the rest, as for every scalar: the server infers the type from the context, so that the column decides (a float8[] would compare a numeric or real column as double precision, a text[] would not meet a uuid, date, enum or citext column, a timestamptz[] would shift a timestamp column's values by the session's time zone). An empty array or one of nils is 0 too.

PG::Connection.types([1, [1, 2], "a"])   # => [0, 1016]
PG::Connection.types([Array[Int].new])   # => []

src/connection.qz:737

Methods

def release -> PG::Lease?

Gives the session up as a PG::Lease, for another task to .adopt; this object is closed for use afterwards (its socket stays open). Only an idle session is given up: one in a transaction is rolled back first. Returns nil, closing the socket, when the session cannot go on: it is closed or broken, or the rollback failed. Never raises.

src/connection.qz:295

def parameters -> Hash[String, String]

The run-time parameters the server reported (server_version, TimeZone, client_encoding, standard_conforming_strings, ...), kept up to date.

src/connection.qz:316

def server_version -> String

The server's version, as server_version reports it ("17.11 (Debian 17.11-0+deb13u1)").

src/connection.qz:320

def process_id -> Int

The server process serving this session.

src/connection.qz:323

def transaction_status -> Symbol

:idle, :in_transaction or :failed (a transaction an error aborted), as of the last time the server was ready.

src/connection.qz:327

def listen(channel: String) -> Void

Listens to channel (LISTEN, the name quoted as an identifier, so that any name of 1 to 63 bytes works, case kept): the server then sends this session every NOTIFY on it, which #wait_for_notify and #notifications give. Listening again to a channel does nothing. A LISTEN takes effect when its transaction commits (at once outside one). Raises ArgumentError for an empty name, one holding a NUL, or one of 64 bytes or more (the server would cut it to 63).

Listen through this method rather than exec("LISTEN ..."): only it is tracked (#listening?), which PG::Pool relies on.

src/connection.qz:345

def unlisten(channel: String) -> Void

Stops listening to channel (UNLISTEN). Notifications already received stay queued. Raises ArgumentError for a name #listen refuses.

src/connection.qz:353

def unlisten_all -> Void

Stops listening to every channel (UNLISTEN *), whichever way it was listened to. Notifications already received stay queued.

src/connection.qz:361

def listening? -> Bool

Whether a LISTEN through #listen may be in force on this session: a channel listened to and not unlistened, or an #unlisten in a transaction (a rollback would undo it). A LISTEN sent with #exec is not seen.

src/connection.qz:371

def notify(channel: String, payload: String = …) -> Void

Sends a notification on channel with payload (pg_notify, as a query with parameters: no quoting). Inside a transaction it is delivered when the transaction commits, and not at all if it rolls back. Raises ArgumentError for a channel name #listen refuses, and PG::ServerError for what the server refuses (a payload of 8000 bytes or more).

src/connection.qz:379

def notifications -> Array[PG::Notification]

The notifications received so far and not yet taken, oldest first, taking them: those that arrived while the connection ran queries, or read by #wait_for_notify along with others. Does not read the socket.

src/connection.qz:389

def wait_for_notify(timeout: Duration? = …) -> PG::Notification?

The next notification: a queued one (see #notifications), else one read from the socket, waiting up to timeout (nil: without limit). Returns nil when timeout passes first. Only the calling task waits; Task.timeout and cancel interrupt the wait. An interrupted wait leaves the connection usable when it was waiting between messages, as it nearly always is; one interrupted in the middle of a message (the rest of it not arrived yet) closes the connection, as #exec says. The server delivers notifications only between transactions: inside one (#transaction), this waits for its timeout. Raises PG::ConnectionError when the connection is closed or breaks, PG::ServerError when the server ends the session (an administrator's pg_terminate_backend, a shutdown).

src/connection.qz:407

def on_notice(&handler: (Hash[String, String]) -> Void) -> Void

Calls the block with each notice or warning the server sends (its fields, as PG::ServerError#fields); without a handler they are dropped.

src/connection.qz:470

def exec(sql: String) -> PG::Result

Runs sql (one statement or several, separated by ;) with the simple protocol, and returns the last statement's result. Raises PG::ServerError for the first statement that fails, after which the statements after it do not run (and, outside a transaction block, the ones before it stay done). Takes no parameters: use #query for values.

A call that does not read the server's whole answer (its task was cancelled or timed out, or a notice handler raised) closes the connection: what is left of the answer would be taken for the next one's.

src/connection.qz:485

def exec_all(sql: String) -> Array[PG::Result]

As #exec, but returns every statement's result, in order.

src/connection.qz:488

def query(sql: String, params: Array[Object?] = …) -> PG::Result

Runs one statement with the extended protocol, params standing for $1, $2, ...: each is sent apart from the SQL, so no value needs quoting. A parameter may be nil (NULL), an Int, a Float, a String, a Bool, Bytes (bytea), a Time (sent in UTC) or a Symbol (its name); another raises ArgumentError. Raises PG::ServerError when the statement fails. A call cut short closes the connection, as #exec says.

The statement is prepared once and kept, by its SQL text, among the connection's statement_cache_size most recently used. One the server no longer has (SQLSTATE 26000), or says changed under it (0A000, a table altered) outside a transaction, is prepared again and the call retried once; inside a transaction the 0A000 is raised (the transaction is aborted), and the next call prepares it again.

src/connection.qz:528

def prepared_statements -> Int

How many prepared statements the connection keeps.

src/connection.qz:594

def transaction[R](&: (PG::Connection) -> R) -> R

Runs the block in a transaction: BEGIN, then COMMIT when the block returns, or ROLLBACK when it raises (the exception goes on) or leaves otherwise (return, break). When a statement in the block failed and the block went on after rescuing it, the server rolls the transaction back at COMMIT; this raises PG::ServerError (SQLSTATE 25P02) then, rather than returning as if it had been committed. Returns the block's value. Raises PG::TransactionError when the connection is in a transaction already: there are no nested transactions (no savepoints) yet.

src/connection.qz:605

def closed? -> Bool

Whether #close was called, or the connection broke.

src/connection.qz:815

def tls? -> Bool

Whether the session goes over TLS (see sslmode in PG.connect).

src/connection.qz:818

def close -> Void

Ends the session: tells the server, and closes the socket. Closing a closed connection does nothing.

src/connection.qz:822

def startup -> Void

The startup exchange: authentication, then the session's parameters, up to the first ReadyForQuery. Called by PG.connect.

src/connection.qz:835