// Core runtime state shared by all files in the client package.
///|
/// Internal transport abstraction that hides whether startup negotiated TLS.
///
/// A secure stream retains both the TLS state and its original TCP transport.
/// `Tls::close` releases only TLS resources, so the TCP handle must remain
/// available for deterministic transport teardown.
priv enum Stream {
Plain(@socket.Tcp)
Secure(@tls.Tls, @socket.Tcp)
}
///|
/// Release protocol state before closing the underlying transport.
fn Stream::close(self : Stream) -> Unit {
match self {
Plain(conn) => conn.close()
Secure(tls, conn) => {
tls.close()
conn.close()
}
}
}
///|
/// Forward raw reads to the active transport implementation.
impl @io.Reader for Stream with fn _direct_read(self, buf, offset~, max_len~) {
match self {
Plain(conn) => @io.Reader::_direct_read(conn, buf, offset~, max_len~)
Secure(tls, _) => @io.Reader::_direct_read(tls, buf, offset~, max_len~)
}
}
///|
/// Reuse the underlying transport's internal read buffer.
impl @io.Reader for Stream with fn _get_internal_buffer(self) {
match self {
Plain(conn) => @io.Reader::_get_internal_buffer(conn)
Secure(tls, _) => @io.Reader::_get_internal_buffer(tls)
}
}
///|
extend Stream with @io.Reader::{read_exactly}
///|
/// Forward writes to the active transport implementation.
impl @io.Writer for Stream with fn write_once(self, buf, offset~, len~) {
match self {
Plain(conn) => conn.write_once(buf, offset~, len~)
Secure(tls, _) => tls.write_once(buf, offset~, len~)
}
}
///|
extend Stream with @io.Writer::{write}
///|
/// High-level request categories sent from `Client` to the connection loop.
///
/// `Messages` covers ordinary simple and extended protocol traffic.
/// `CopyIn` marks a request that must interleave outbound `CopyData` frames with
/// inbound backend messages.
/// `Terminate` requests graceful shutdown after earlier logical operations.
priv enum RequestKind {
Messages
CopyIn
Terminate
}
///|
/// Items the client can push into an active COPY FROM STDIN request.
priv enum CopyInAction {
/// Send one `CopyData` payload.
Data(Bytes)
/// Finish COPY successfully with `CopyDone`.
Finish
/// Abort COPY with a textual error message.
Fail(String)
}
///|
/// A small bounded per-request buffer prevents unread streams from accumulating
/// responses without bound.
let request_response_queue_kind : @aqueue.Kind = Blocking(8)
///|
/// One outbound request queued for the private driver.
///
/// The request owns the exact bytes to write on the wire and the queue that the
/// connection loop will feed with backend messages for this request.
priv struct Request {
kind : RequestKind
bytes : Bytes
responses : @async.Queue[@backend.Message]
copy_input : @async.Queue[CopyInAction]?
copy_error : @ref.Ref[DatabaseError?]?
}
///|
/// FIFO gate that gives one logical operation exclusive use of a connection.
///
/// The permit covers the whole operation rather than one wire request. A query,
/// for example, may prepare a statement, inspect type metadata, execute it, and
/// close temporary server state before releasing the connection.
priv struct OperationGate {
mutex : @async.Mutex
}
///|
/// One idempotently releasable acquisition of an `OperationGate`.
priv struct OperationPermit {
gate : OperationGate
released : @ref.Ref[Bool]
}
///|
/// Private capability passed through nested pieces of one logical operation.
///
/// Internal helpers that receive this context must not reacquire the client's
/// operation gate. This is what lets type-cache catalog queries and deferred
/// statement cleanup remain part of their parent operation without deadlocking.
priv struct OpContext {
client : Client
}
///|
/// Shared mutable state visible to every handle created from one connection.
///
/// The client, prepared statements, transactions, cancellation tokens, and
/// private driver task reference this struct to coordinate lifecycle, cache
/// type metadata, and publish async messages. Only connection identity allocation
/// uses a module-level counter.
priv struct Shared {
connection_id : UInt64
config : Config
requests : @async.Queue[Request]
async_messages : @async.Queue[AsyncMessage]
dropped_async_messages : @ref.Ref[Int64]
async_message_handler : @ref.Ref[((AsyncMessage) -> Unit)?]
root_transaction_occupancy : @ref.Ref[Int32]
control : DrainControl
ready : Completion
done : Completion
started : @ref.Ref[Bool]
runtime_error : @ref.Ref[Error?]
parameters : Map[String, String]
types : Map[@proto.Oid, Type]
process_id : @ref.Ref[Int32]
secret_key : @ref.Ref[Int32]
transaction_status : @ref.Ref[Byte]
closed : @ref.Ref[Bool]
closing : @ref.Ref[Bool]
close_requested : @ref.Ref[Bool]
aborted : @ref.Ref[Bool]
operation_gate : OperationGate
driver_task : @ref.Ref[@async.Task[Unit]?]
next_id : @ref.Ref[Int32]
}
///|
/// User-facing handle used to enqueue work onto the private driver.
///
/// `Client` is intentionally lightweight. It does not own the socket and can be
/// cloned freely by value because all mutable state lives inside `Shared`.
struct Client {
shared : Shared
}
///|
/// Owns the socket and all background work for a single client.
/// Spawn `run` once, then await `Client::ready` before using startup metadata.
struct ClientExecutor {
shared : Shared
}
///|
/// Deferred cleanup performed once a row stream reaches its terminal state.
///
/// Helper APIs that create temporary prepared statements attach one of these to
/// the returned `RowStream` so the statement is closed only after all rows and
/// trailing protocol messages have been consumed.
priv struct StreamCleanup {
context : OpContext
bytes : Bytes
mut done : Bool
}
///|
/// Socket-owning private runtime task.
priv struct Driver {
shared : Shared
stream : Stream
}
///|
/// Prepared statement stored on the PostgreSQL server.
///
/// A statement belongs to its creating connection and may be reused across
/// transactions on that connection.
/// Read-only metadata views expose the server-inferred parameter and result
/// types so callers can inspect what PostgreSQL resolved during preparation.
struct Statement {
client : Client
name : Bytes
/// Parameter types in server order.
params : Array[Type]
/// Result columns produced by executing the statement.
columns : Array[Column]
closed : @ref.Ref[Bool]
}
///|
/// Return a read-only view of the server-inferred parameter types.
pub fn Statement::params(self : Statement) -> ArrayView[Type] {
self.params[:]
}
///|
/// Return a read-only view of the statement's result columns.
pub fn Statement::columns(self : Statement) -> ArrayView[Column] {
self.columns[:]
}
///|
/// Capability object used to send a PostgreSQL cancel request.
///
/// PostgreSQL cancellation happens over a separate short-lived TCP connection,
/// so the token stores the backend process ID and secret key learned during
/// startup rather than a direct handle to the existing socket.
struct CancelToken {
config : Config
process_id : @ref.Ref[Int32]
secret_key : @ref.Ref[Int32]
closed : @ref.Ref[Bool]
}
///|
/// Client-side sink for an active `COPY ... FROM STDIN` operation.
///
/// The sink feeds `CopyInAction` values to the connection loop while the paired
/// response queue waits for the backend's completion status.
struct CopyInSink {
control : DrainControl
input : @async.Queue[CopyInAction]
responses : @async.Queue[@backend.Message]
finished : @ref.Ref[Bool]
abandoned_done : @ref.Ref[Completion?]
permit : OperationPermit
}
///|
/// Transaction handle that enforces single-use commit or rollback semantics.
///
/// Nested transactions are implemented with savepoints. The same handle shape
/// is reused for the top-level transaction and for nested savepoint scopes.
struct Transaction {
client : Client
context : OpContext
scope_gate : OperationGate
child : @ref.Ref[TransactionChild?]
scoped_resources : @ref.Ref[Int32]
closing : @ref.Ref[Completion?]
root_permit : OperationPermit?
parent_permit : OperationPermit?
// The savepoint name is already quoted for PostgreSQL SQL text.
savepoint : String?
finished : @ref.Ref[Bool]
}
///|
// A child owns the scope permit until its protocol work has completed.
// Creating is published before startup can suspend, so completion can wait
// without trying to acquire the permit held by the creator.
priv enum TransactionChild {
Creating(Completion)
Stream(RowStream)
Nested(Transaction)
}