#Postgres Pool

    Single-event-loop PostgreSQL connection pooling for MoonBit.

    Package path: moonbit-community/postgres/pgpool.

    This package is for:

    • reusing PostgreSQL sessions
    • isolating long-running queries from short ones
    • letting multiple async tasks talk to PostgreSQL without sharing one connection timeline

    This package is not a multi-threaded throughput layer. MoonBit still runs on a single event loop, so pool sizing should reflect session isolation needs rather than CPU core count.

    #Quick Start

    ///|
    async fn _pool_quick_start(
    host : String,
    user : String,
    database : String,
    password : String,
    ) -> Unit {
    @async.with_task_group(group => {
    let config = Config::new(
    host,
    user~,
    dbname=database,
    password~,
    application_name="my-service",
    pool=PoolConfig::new(2),
    )
    let pool = Pool::new(config, group)

    let value : Int = pool.with_client(client => {
    client.query_one("select 1::int4 as value").get_name("value")
    })
    ignore(value)

    pool.close()
    })
    }

    Pool::with_client is the main entry point. It avoids the usual "borrow and forget to return" problem that shows up in MoonBit because there is no Rust style Drop hook to rely on.

    When one pooled operation needs explicit PostgreSQL cancellation, use Client::run_cancellable(...). The callback receives a short-lived OperationCancelToken plus an Operation handle that keeps the cancel scope tied to that one callback:

    ///|
    async fn _pool_cancellable_example(pool : Pool) -> Unit {
    @async.with_task_group(group => {
    ignore(
    pool.with_client(client => {
    client.run_cancellable((op, token) => {
    group.spawn_bg(no_wait=true, () => {
    @async.sleep(50)
    token.cancel()
    })
    let value : Int = op
    .query_one("select pg_sleep(5), 1::int4 as value")
    .get_name("value")
    value
    })
    }),
    ) catch {
    _ => ()
    }
    })
    }

    The pooled cancel token is best-effort and operation-scoped. After the callback returns, later cancel() calls become inert, so a stale handle cannot interrupt a later borrower that reuses the same physical PostgreSQL session.

    #Choose The Checkout Style

    Start with the smallest API that matches the ownership you need:

    • Pool::with_client(...): the normal entry point; borrow one client for one callback and release it automatically
    • Pool::get(): manual lease management when the client must outlive one callback
    • Client::with_transaction(f, options?): run one callback inside a transaction that auto-commits on success and rolls back best-effort on error
    • Client::with_stream(...), with_simple_query(...), with_copy_in(...), with_copy_out(...): callback-scoped access to low-level streaming APIs
    • Client::run_cancellable(...): exclusive one-request-at-a-time scope with a cancel token

    Use Pool::get() only when the automatic callback-scoped APIs are not enough. The lease must be released explicitly, and any open transaction or raw stream still belongs to that lease.

    #Multi-Target Config

    pgpool.Config is declarative. The pool normalizes it into one or more concrete @client.Config targets before any network connection is opened. Config::new(...) mirrors @client.Config::new(...) for the primary target and then adds pool-specific multi-target options such as hosts, hostaddrs, ports, target_session_attrs, and load_balance_hosts.

    The practical rules are:

    • host and hosts are appended together in order
    • port is broadcast to every target when ports is absent
    • hostaddr and hostaddrs let TCP routing differ from certificate identity
    • password is optional in the config, but each connection needs it when the server selects cleartext password or SCRAM authentication
    • application_name is required by Config::new and is copied to every target
    • target_session_attrs=ReadWrite rejects a target that reports transaction_read_only = on
    • load_balance_hosts=Random randomizes the order in which new connections try targets; it does not reshuffle already-open idle connections

    ///|
    fn _multi_target_config() -> Config raise {
    Config::new(
    "primary.db",
    user="moon",
    dbname="app",
    application_name="my-service",
    hosts=["replica.db"],
    ports=[5432, 5432],
    target_session_attrs=TargetSessionAttrs::read_write(),
    load_balance_hosts=Random,
    pool=PoolConfig::new(
    4,
    timeouts=Timeouts::new(
    wait_ms=Some(500),
    create_ms=Some(1000),
    recycle_ms=Some(250),
    ),
    queue_mode=QueueMode::lifo(),
    recycling_method=RecyclingMethod::verified(),
    ),
    )
    }

    Use host / hosts when certificate identity matters. Add hostaddr only when routing must use a different IP address than the hostname used for TLS.

    #Timeouts, Queue Mode, And Recycling

    PoolConfig.max_size is required. There is no implicit CPU-based default, because this pool is about session isolation, not thread-per-core throughput.

    Timeouts::new(...) controls three different waits:

    • wait_ms: time spent waiting for pool capacity
    • create_ms: time spent opening a new physical connection
    • recycle_ms: time spent validating or cleaning one idle connection

    If checkout is cancelled during recycling, including either recycle hook, the pool discards that connection and its statement cache and restores the reserved capacity slot. Cancellation still propagates to the caller; a later checkout can open a replacement connection.

    QueueMode::fifo() reuses the oldest idle connection first. QueueMode::lifo() reuses the most recently returned idle connection first. This changes idle-connection selection only; it does not reorder tasks already waiting in get().

    Recycling methods mean:

    • RecyclingMethod::fast(): trust the idle connection as-is
    • RecyclingMethod::verified(): run a lightweight health check
    • RecyclingMethod::clean(): reset session state before reuse
    • RecyclingMethod::custom(sql): run your own cleanup SQL during checkout

    Choose fast for simple stateless workloads, verified when broken idle connections are the main concern, and clean when borrowers regularly leave session-local state such as open portals/cursors, LISTEN state, advisory locks, or temporary objects behind. Cached prepared statements are managed separately through the statement-cache APIs.

    #Pool Options

    Pool::new(config, group, options?) accepts runtime extension hooks:

    • post_create: run after a brand-new connection opens
    • pre_recycle: run before recycle-time validation / cleanup of an idle connection
    • post_recycle: run after recycling succeeded and just before checkout

    Keep these hooks short and leave the session idle when they return. Good uses are SET search_path, session GUCs, or a custom validation query.

    ///|
    fn _pool_options_hook_example(group : @async.TaskGroup[Unit]) -> Pool raise {
    let config = Config::new(
    "db.example",
    user="moon",
    dbname="app",
    application_name="my-service",
    pool=PoolConfig::new(2),
    )
    let options = PoolOptions::new(
    post_create=client => client.batch_execute("set search_path to app, public"),
    pre_recycle=client => client.check_connection(),
    )
    Pool::new(config, group, options~)
    }

    #Safe API Surface

    Most high-level operations on Client, Operation, and Transaction are scope-safe:

    • fully-drained query helpers such as query_all, query_one, query_opt, and query_typed_all
    • command helpers such as execute, batch_execute, and check_connection
    • transaction helpers such as transaction, with_transaction, and with_savepoint
    • scope-bound prepared statement helpers such as with_prepared(...) and with_prepared_cached(...)
    • scope-bound low-level helpers such as with_stream(...), with_simple_query(...), with_copy_in(...), with_copy_out(...), and PreparedStatement::with_portal(...)

    There are still a few explicit ownership handoff APIs:

    • Pool::get() requires the caller to release the lease
    • Client::transaction() and Transaction::transaction() require an explicit commit() or rollback()
    • Client::detach_raw() permanently removes the connection from pool management

    Prepared statements are available, but only through callback-scoped PreparedStatement handles. The handle is released automatically when the callback finishes, so it cannot leak across pool reuse boundaries.

    Low-level streams, portals, and COPY handles are available only through callback-scoped wrappers. When the callback returns, the pool drains, aborts, or closes unfinished protocol state before the connection becomes reusable.

    If a with_transaction(...) or with_savepoint(...) callback raises or is cancelled, the pool attempts to roll back the unfinished transaction or savepoint before releasing its scope. Rollback cleanup is protected from task cancellation, including the wait for any in-flight operations to finish.

    Pool::close() rejects future checkouts immediately and closes idle connections, but it does not revoke already borrowed clients. Those leases keep working until they are released, and then their physical connections are closed instead of returning to the idle pool.

    #Statement Cache

    Prepared-statement caching is per physical connection, not per pool. A cache hit on one connection does not warm up other connections.

    Use with_prepared_cached(...) when you want the pool to create or reuse one cached statement for the current callback:

    ///|
    async fn _statement_cache_example(pool : Pool) -> Unit {
    pool.with_client(client => {
    client.with_prepared_cached("select $1::int4 as value", prepared => {
    let value = 7
    let params : Array[&@client.ToSql] = [value as &@client.ToSql]
    let row = prepared.query_one(params~)
    let decoded : Int = row.get_name("value")
    ignore(decoded)
    })
    })
    |> ignore

    let cache_size = pool.with_client(client => client.statement_cache().size())
    ignore(cache_size)

    pool.manager().statement_caches().clear()
    }

    Use the handles like this:

    • client.statement_cache(): manage the cache of the currently checked-out physical connection
    • transaction.statement_cache(): same, but from inside one pooled transaction
    • pool.manager().statement_caches(): clear or remove cached statements across every connection that is live right now

    #Detaching A Raw Client

    Client::detach_raw() is the escape hatch when you intentionally want to stop using the pool for one checked-out connection.

    After detaching:

    • the pool frees one capacity slot immediately
    • the returned @client.Client keeps using the existing background driver task
    • the pool no longer recycles, tracks, or closes that connection for you
    • you must close the raw client yourself

    ///|
    async fn _detach_raw_example(pool : Pool) -> Unit {
    let lease = pool.get()
    let raw = lease.detach_raw()
    let value : Int = raw.query_one("select 1::int4 as value").get_name("value")
    ignore(value)
    raw.close()
    }

    Use this only when you really need to transfer ownership out of the pool.

    GenericClient

    pub(open) trait GenericClient {
    async fn query_all(Self, String, params? : Array[&
    ToSql
    ]) -> Array[
    Row
    ]
    async fn query_one(Self, String, params? : Array[&
    ToSql
    ]) ->
    Row

    async fn query_opt(Self, String, params? : Array[&
    ToSql
    ]) ->
    Row
    ?
    async fn query_typed_all(Self, String, Array[
    Type
    ], params? : Array[&
    ToSql
    ]) -> Array[
    Row
    ]
    async fn execute(Self, String, params? : Array[&
    ToSql
    ]) -> Int
    async fn batch_execute(Self, String) -> Unit
    async fn check_connection(Self) -> Unit
    }

    Shared high-level query surface implemented by pooled clients and pooled transactions.

    Implementors provide fully materialized query helpers that are safe to use without handling raw protocol streams directly.

    ConfigError

    pub suberror ConfigError {
    UsernameEmpty
    DbnameEmpty
    HostPortArityMismatch
    InvalidConfig(String)
    } derive(Eq,
    Debug
    )

    Failures raised while validating or normalizing declarative pool input.

    These errors are reported before the pool opens any physical connection.

    PoolError

    pub suberror PoolError {
    Closed
    Timeout(TimeoutKind)
    LeaseReleased
    OperationInProgress
    RowCount(String)
    InvalidConfig(String)
    } derive(Eq,
    Debug
    )

    Failures raised by pool lifecycle, checkout, and scoped-handle operations.

    LeaseReleased reports use-after-scope on pooled handles, Closed rejects new checkouts after Pool::close(), Timeout identifies the checkout phase that expired, and OperationInProgress reports an exclusive scope conflict.

    Client

    type Client

    Checked-out pooled client lease.

    This handle represents temporary ownership of one physical PostgreSQL session. Releasing the lease returns the session to the pool unless it has been detached or the pool is closing.

    Client::batch_execute

    async fn Client::batch_execute(self : Client, sql : String) -> Unit

    Execute one or more SQL commands without returning row data.

    Side effects follow PostgreSQL batch_execute: every command in sql runs on the current session in order.

    Client::check_connection

    async fn Client::check_connection(self : Client) -> Unit

    Perform a lightweight connection health check on this lease.

    Client::detach_raw

    Permanently detach the raw client from pool management.

    Preconditions: the lease must still be active, and no operation, transaction-building scope, or cancellable scope may be in progress. Side effects: the pool forgets this physical connection, frees one capacity slot, and stops recycling or closing the detached client for you. The caller becomes responsible for closing the returned raw client. The returned @client.Client keeps using the existing background connection driver, so it can continue ordinary client operations immediately after detaching.

    Client::execute

    async fn Client::execute(self : Client, sql : String, params? : Array[&
    ToSql
    ]) -> Int

    Execute one command on this lease and return its affected row count.

    Client::query_all

    Run one query on this lease and collect all rows before returning.

    Side effects: fully drains the query result, so no stream state escapes the call boundary.

    Client::query_one

    Run one query and require exactly one row.

    Row-count behavior follows the underlying @client.Client::query_one.

    Client::query_opt

    Run one query and allow zero or one rows.

    Row-count behavior follows the underlying @client.Client::query_opt.

    Client::query_typed_all

    Run one typed query and collect all rows before returning.

    Client::release

    fn Client::release(self : Client) -> Unit

    Mark this pooled client lease for release.

    Side effects: new scoped operations start failing with PoolError::LeaseReleased. Existing in-flight operations are allowed to finish first, and the physical connection is returned to the pool only after the active-operation count reaches zero. Repeated calls are idempotent.

    Client::run_cancellable

    async fn[T] Client::run_cancellable(self : Client, f : async (Operation, OperationCancelToken) -> T) -> T

    Run one callback inside an exclusive cancellable request scope.

    Preconditions: the client lease must still be active and no other exclusive scope may already be open on it. During the callback, requests issued through Operation run one at a time so OperationCancelToken::cancel() can target the current request. When the callback finishes, the cancel token becomes inert and the scope waits for any late cancel-send task to finish. A common pattern is to pass the token to another task or timeout handler while the callback uses Operation for the actual query work.

    Client::statement_cache

    fn Client::statement_cache(self : Client) -> StatementCache raise

    Return the statement-cache handle bound to this physical connection.

    Preconditions: the pooled client lease must still be active. The returned handle manages cached prepared statements on this one connection only. It is not a pool-wide cache view; different physical connections have different statement caches.

    Client::transaction

    async fn Client::transaction(self : Client, options? : TransactionOptions) -> Transaction

    Start one transaction using PostgreSQL's default BEGIN settings or the given BEGIN options.

    Preconditions: no exclusive scope may already be active on this lease. The returned Transaction keeps the connection checked out until it is committed or rolled back.

    Client::with_copy_in

    async fn[T] Client::with_copy_in(self : Client, sql : String, f : async (CopyInSink) -> T) -> T

    Run one callback with a COPY FROM STDIN sink on this lease.

    The callback should normally call finish() or abort() explicitly. If it returns with COPY still open, the pool aborts the COPY operation before the lease is reused.

    Client::with_copy_out

    async fn[T] Client::with_copy_out(self : Client, sql : String, f : async (CopyOutStream) -> T) -> T

    Run one callback with a COPY TO STDOUT stream on this lease.

    As with other scoped stream helpers, unfinished stream state is cleaned up automatically before the lease can be reused.

    Client::with_prepared

    async fn[T] Client::with_prepared(self : Client, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare one non-cached statement scoped to this callback.

    Side effects: creates a fresh PostgreSQL prepared statement and closes it automatically when the callback finishes, even on error.

    Client::with_prepared_cached

    async fn[T] Client::with_prepared_cached(self : Client, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one cached statement scoped to this callback.

    The cache is connection-local. On a cache miss this creates and stores a new prepared statement; on a hit it reuses the existing statement and releases only the cache lease when the callback finishes.

    Client::with_prepared_typed

    async fn[T] Client::with_prepared_typed(self : Client, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare one typed non-cached statement scoped to this callback.

    param_types is sent when PostgreSQL parses the statement. The prepared statement is always closed when the callback ends.

    Client::with_prepared_typed_cached

    async fn[T] Client::with_prepared_typed_cached(self : Client, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one typed cached statement scoped to this callback.

    Matching uses both sql and param_types, so the same SQL text prepared with different inferred types occupies different cache entries.

    Client::with_simple_query

    async fn[T] Client::with_simple_query(self : Client, sql : String, f : async (SimpleQueryStream) -> T) -> T

    Run one callback with a simple-query protocol stream on this lease.

    The callback may read messages incrementally, collect them, finish the stream, or detach cleanup into the background.

    Client::with_stream

    async fn[T] Client::with_stream(self : Client, sql : String, params? : Array[&
    ToSql
    ], f : async (RowStream) -> T) -> T

    Run one callback with an extended-query row stream on this lease.

    Side effects: opens a protocol stream that keeps the physical connection busy until the stream is drained, finished, or detached into background cleanup. The helper guarantees stream cleanup before the lease becomes reusable.

    Client::with_transaction

    async fn[T] Client::with_transaction(self : Client, f : async (Transaction) -> T, options? : TransactionOptions) -> T

    Run one callback inside a transaction and auto-complete it.

    On normal return, the transaction is committed if the callback did not already finish it explicitly. If the callback raises or is cancelled, the transaction is rolled back best-effort unless it was already finished.

    Client::with_typed_stream

    async fn[T] Client::with_typed_stream(self : Client, sql : String, param_types : Array[
    Type
    ], params? : Array[&
    ToSql
    ], f : async (RowStream) -> T) -> T

    Run one callback with a typed extended-query row stream on this lease.

    param_types is passed to PostgreSQL when preparing the typed query. As with with_stream, the helper guarantees cleanup of unfinished stream state.

    Config

    pub struct Config {
    user : String
    password : String?
    dbname : String
    options : String?
    application_name : String
    ssl_mode :
    SslMode
    ?
    ssl_root_cert : String?
    channel_binding :
    ChannelBinding
    ?
    host : String?
    hosts : Array[String]?
    hostaddr : String?
    hostaddrs : Array[String]?
    port : Int?
    ports : Array[Int]?
    connect_timeout_ms : Int?
    keepalives : Bool?
    keepalives_idle_s : Int?
    target_session_attrs : TargetSessionAttrs?
    load_balance_hosts : LoadBalanceHosts?
    pool : PoolConfig
    } derive(Eq,
    Debug
    )

    Declarative input used to derive one pool plus one or more concrete connection targets.

    All fields are explicit. Validation is deferred until callers ask for normalized output via Pool::new, get_pool_config, or get_connection_targets.

    Config::get_connection_targets

    fn Config::get_connection_targets(self : Config) -> Array[
    Config
    ] raise

    Expand declarative host settings into concrete single-target client configs.

    Each returned @client.Config describes exactly one target address and is ready to pass to the connector. This step broadcasts host and port arrays when needed and rejects incomplete or contradictory inputs before any network connection is opened.

    Config::get_pool_config

    fn Config::get_pool_config(self : Config) -> PoolConfig raise

    Return the validated pool-capacity config for this declarative input.

    Config::new

    fn Config::new(host : String, hostaddr? : String, port? : Int, user~ : String, dbname? : String, password? : String, ssl_mode? :
    SslMode
    , ssl_root_cert? : String, channel_binding? :
    ChannelBinding
    , application_name~ : String, options? : String, connect_timeout_ms? : Int, keepalives? : Bool, keepalives_idle_s? : Int, hosts? : Array[String], hostaddrs? : Array[String], ports? : Array[Int], target_session_attrs? : TargetSessionAttrs, load_balance_hosts? : LoadBalanceHosts, pool~ : PoolConfig) -> Config

    Build declarative pool config with client-style required inputs.

    The first target is described by the same core fields as @client.Config, then optional pool-specific multi-target settings can be added on top. Defaults keep the secure client path: port 5432, ssl_mode = VerifyFull, and dbname = user.

    Connector

    type Connector

    Wrapper around the callback that opens one physical PostgreSQL connection.

    Pool options can replace the default @client.connect path during tests or custom integration.

    Connector::new

    Wrap a custom connection-opening callback for pool construction.

    The callback is responsible for returning both the high-level client handle and its background connection driver. It is invoked every time the pool must open a new physical connection.

    CopyInSink

    type CopyInSink

    COPY FROM STDIN sink scoped to one pooled callback.

    The callback must eventually call finish() or abort(). If it returns early, the pool aborts the COPY operation before the connection becomes reusable.

    CopyInSink::abort

    async fn CopyInSink::abort(self : CopyInSink, message? : String) -> Unit

    Abort the active COPY FROM STDIN operation.

    If the sink was not finished yet, this marks it finished and sends the abort request. If the sink was already finished, the abort is still forwarded to the raw sink, so repeated calls follow the raw client's edge-case behavior instead of silently becoming a no-op.

    CopyInSink::finish

    async fn CopyInSink::finish(self : CopyInSink) -> Int

    Finish the active COPY FROM STDIN operation and return PostgreSQL's row count.

    Side effects: marks the sink as finished so scope cleanup stops aborting it.

    CopyInSink::send

    async fn CopyInSink::send(self : CopyInSink, data : BytesView) -> Unit

    Send one chunk into the active COPY FROM STDIN operation.

    Preconditions: the COPY sink must still be active. After finish(), abort(), or callback-scope cleanup, further behavior follows the underlying raw sink and may raise.

    CopyOutStream

    pub struct CopyOutStream {
    formats : Array[
    WireFormat
    ]
    // private fields
    }

    COPY TO STDOUT stream scoped to one pooled callback.

    As with RowStream, the callback may consume it directly or detach cleanup into the background before returning.

    CopyOutStream::collect

    async fn CopyOutStream::collect(self : CopyOutStream) -> Array[Bytes]

    Collect every remaining chunk from this scoped COPY OUT stream.

    Side effects: drains the COPY response fully before returning.

    CopyOutStream::detach

    fn CopyOutStream::detach(self : CopyOutStream) -> Unit

    Detach background draining for this scoped COPY OUT stream.

    After detaching, direct use fails with PoolError::LeaseReleased, and pool cleanup waits for the detached task before reusing the connection.

    CopyOutStream::finish

    async fn CopyOutStream::finish(self : CopyOutStream) -> Unit

    Drain this scoped COPY OUT stream to completion.

    CopyOutStream::next

    async fn CopyOutStream::next(self : CopyOutStream) -> Bytes?

    Read the next chunk from this scoped COPY OUT stream.

    Preconditions: the stream must still be attached to the current callback scope. Returning None means COPY OUT finished normally.

    IsolationLevel

    pub enum IsolationLevel {
    ReadUncommitted
    ReadCommitted
    RepeatableRead
    Serializable
    } derive(Eq,
    Debug
    )

    PostgreSQL transaction isolation level accepted by TransactionOptions.

    IsolationLevel::read_committed

    fn IsolationLevel::read_committed() -> IsolationLevel

    Construct READ COMMITTED.

    IsolationLevel::read_uncommitted

    fn IsolationLevel::read_uncommitted() -> IsolationLevel

    Construct READ UNCOMMITTED.

    IsolationLevel::repeatable_read

    fn IsolationLevel::repeatable_read() -> IsolationLevel

    Construct REPEATABLE READ.

    IsolationLevel::serializable

    fn IsolationLevel::serializable() -> IsolationLevel

    Construct SERIALIZABLE.

    LoadBalanceHosts

    pub(all) enum LoadBalanceHosts {
    Disable
    Random
    } derive(Eq,
    Debug
    )

    Host-ordering strategy used before opening a new physical connection.

    This affects only the order in which candidate targets are tried for a new connection. It does not reshuffle already-open idle connections, and it does not move a checked-out client from one target to another.

    Manager

    type Manager

    Administrative handle for one pool instance.

    This separates operational controls, such as statement-cache management, from the regular checkout API on Pool.

    Manager::statement_caches

    fn Manager::statement_caches(self : Manager) -> StatementCaches

    Return a handle that can manage statement caches on all live connections.

    Operation

    type Operation

    Exclusive cancellable request scope created by Client::run_cancellable.

    Unlike plain Client operations, an Operation allows only one request at a time so the paired cancel token can target a single in-flight request.

    Operation::batch_execute

    async fn Operation::batch_execute(self : Operation, sql : String) -> Unit

    Execute one or more SQL commands inside the cancellable scope.

    Operation::check_connection

    async fn Operation::check_connection(self : Operation) -> Unit

    Perform a lightweight health check inside the cancellable scope.

    Operation::execute

    async fn Operation::execute(self : Operation, sql : String, params? : Array[&
    ToSql
    ]) -> Int

    Execute one command in the cancellable scope and return its affected row count.

    Operation::query_all

    Run one request in the cancellable scope and collect all rows.

    Preconditions: the surrounding run_cancellable scope must still be active. Only one request may be in flight on the Operation at a time.

    Operation::query_one

    Run one request in the cancellable scope and require exactly one row.

    Operation::query_opt

    Run one request in the cancellable scope and allow zero or one rows.

    Operation::query_typed_all

    Run one typed request in the cancellable scope and collect all rows.

    Operation::with_prepared

    async fn[T] Operation::with_prepared(self : Operation, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare one non-cached statement inside the active cancellable scope.

    The prepared statement is bound to the run_cancellable callback and is closed automatically when the prepared-statement callback ends.

    Operation::with_prepared_cached

    async fn[T] Operation::with_prepared_cached(self : Operation, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one cached statement inside the active cancellable scope.

    The cache is local to the underlying physical connection.

    Operation::with_prepared_typed

    async fn[T] Operation::with_prepared_typed(self : Operation, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare one typed non-cached statement inside the active cancellable scope.

    Operation::with_prepared_typed_cached

    async fn[T] Operation::with_prepared_typed_cached(self : Operation, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one typed cached statement inside the active cancellable scope.

    OperationCancelToken

    type OperationCancelToken

    Best-effort cancel handle paired with one Operation scope.

    The token is valid only while the owning run_cancellable callback is still active. After that it becomes inert instead of affecting a later borrower of the same physical connection.

    OperationCancelToken::cancel

    async fn OperationCancelToken::cancel(self : OperationCancelToken) -> Unit

    Best-effort cancel the request currently running inside the paired Operation.

    Edge cases: if the scope is already closing, no request is in flight, or the current request was already cancelled through this token, the call is a no-op. Any backend error while sending the cancel packet is suppressed.

    Pool

    type Pool

    Public handle to one connection-pool instance.

    A Pool owns capacity accounting, idle connections, and the policies used to create and recycle physical PostgreSQL sessions.

    Pool::close

    fn Pool::close(self : Pool) -> Unit

    Stop accepting new checkouts and close every idle connection immediately.

    Existing checked-out clients are not revoked. They can keep running until released, at which point their physical connections are closed instead of being returned to the idle queue. Repeated calls are idempotent.

    Pool::config

    fn Pool::config(self : Pool) -> PoolConfig

    Return the full pool config currently used by this pool.

    Pool::get

    async fn Pool::get(self : Pool) -> Client

    Borrow one pooled client using the pool's current default timeouts.

    Preconditions: the pool must not be closed. On success the caller receives a checked-out client lease that must eventually be released or consumed by a higher-level helper such as with_client.

    Pool::is_closed

    fn Pool::is_closed(self : Pool) -> Bool

    Return whether close() has started for this pool.

    Once this becomes true, new checkouts fail with PoolError::Closed.

    Pool::manager

    fn Pool::manager(self : Pool) -> Manager

    Return the administrative manager handle for this pool.

    Pool::new

    fn Pool::new(config : Config, group :
    TaskGroup
    [Unit], options? : PoolOptions) -> Pool raise

    Create a pool from declarative config and runtime options.

    This allocates bookkeeping state and checkout slots only. It does not open any physical PostgreSQL connection, so network failures still happen later during checkout.

    Pool::resize

    fn Pool::resize(self : Pool, max_size : Int) -> Unit raise

    Change the pool's maximum number of live physical connections.

    Preconditions: max_size must be at least 1. Shrinking the pool retires idle connections immediately until the new limit is respected or no idle connections remain. Checked-out clients are not interrupted; when they are later released above the new limit, they are closed instead of returning to the idle queue. Calling this after close() is a no-op.

    Pool::status

    fn Pool::status(self : Pool) -> Status

    Return a point-in-time snapshot of pool bookkeeping counters.

    The returned record is not live and may already be stale by the time the caller inspects it.

    Pool::timeout_get

    async fn Pool::timeout_get(self : Pool, timeouts : Timeouts) -> Client

    Borrow one pooled client using per-call timeout overrides.

    The provided timeouts are validated for this call only and do not mutate the pool's stored defaults. If checkout fails after capacity was reserved, the slot is restored before the error is re-raised. Cancelling checkout during recycling discards that connection and restores the capacity slot.

    Pool::timeouts

    fn Pool::timeouts(self : Pool) -> Timeouts

    Return the timeout config currently used by Pool::get().

    This reflects the pool's current stored config.

    Pool::with_client

    async fn[T] Pool::with_client(self : Pool, f : async (Client) -> T) -> T

    Borrow one pooled client for the duration of a callback.

    The lease is released in a defer, so it is returned to the pool on normal completion and also when the callback raises. If the callback detached the raw client, the deferred release becomes a no-op.

    PoolConfig

    pub struct PoolConfig {
    max_size : Int
    timeouts : Timeouts
    queue_mode : QueueMode
    recycling_method : RecyclingMethod
    } derive(Eq,
    Debug
    )

    Capacity and checkout policy for one pool instance.

    PoolConfig::new

    fn PoolConfig::new(max_size : Int, timeouts? : Timeouts, queue_mode? : QueueMode, recycling_method? : RecyclingMethod) -> PoolConfig raise

    Construct and validate one pool configuration.

    max_size must be at least 1, and each configured timeout must be non-negative when present. On success this function has no side effects.

    PoolOptions

    pub struct PoolOptions {
    connector : Connector?
    post_create : async (
    Client
    ) -> Unit?
    pre_recycle : async (
    Client
    ) -> Unit?
    post_recycle : async (
    Client
    ) -> Unit?
    }

    Runtime extension points used when constructing a pool.

    PoolOptions::new

    fn PoolOptions::new(connector? : Connector, post_create? : async (
    Client
    ) -> Unit, pre_recycle? : async (
    Client
    ) -> Unit, post_recycle? : async (
    Client
    ) -> Unit) -> PoolOptions

    Build runtime options for pool construction.

    Portal

    pub struct Portal {
    columns : Array[
    Column
    ]
    // private fields
    }

    Bound portal scoped to one pooled callback.

    A Portal can produce multiple fetch streams over time until it is closed. It remains tied to the prepared-statement scope that created it.

    Portal::close

    async fn Portal::close(self : Portal) -> Unit

    Close this scoped portal.

    Repeated calls are idempotent. Closing uses the owning pooled scope so the close request is ordered correctly with respect to other operations on the same connection.

    Portal::with_stream

    async fn[T] Portal::with_stream(self : Portal, max_rows : Int, f : async (RowStream) -> T) -> T

    Fetch rows from this portal and expose them as a callback-scoped row stream.

    Preconditions: the portal must not already be closed. max_rows is passed directly to PostgreSQL's portal fetch. The portal itself remains open after the row-stream callback ends, so callers may fetch again or close it explicitly.

    PreparedStatement

    pub struct PreparedStatement {
    params : Array[
    Type
    ]
    columns : Array[
    Column
    ]
    // private fields
    }

    Prepared statement handle scoped to the current pooled callback.

    The handle may wrap either a temporary statement or a lease on a cached statement entry. It becomes invalid once the owning callback ends or after close() is called explicitly.

    PreparedStatement::bind

    Bind parameters to this scoped prepared statement and create one portal.

    Preconditions: the prepared statement must still be active. The returned portal remains open until Portal::close() runs or higher-level cleanup such as with_portal() closes it for you.

    PreparedStatement::close

    async fn PreparedStatement::close(self : PreparedStatement) -> Unit

    Explicitly release this scoped prepared statement early.

    Repeated calls are idempotent. For temporary statements this closes the raw PostgreSQL statement; for cached statements it only releases the cache lease unless the entry was already evicted.

    PreparedStatement::execute

    Execute this scoped prepared statement and return the affected row count.

    PreparedStatement::query_all

    Execute this scoped prepared statement and collect all rows.

    Preconditions: the statement must still be active inside its callback scope.

    PreparedStatement::query_one

    Execute this scoped prepared statement and require exactly one row.

    Edge behavior: materializes all rows first and raises PoolError::RowCount when the result does not contain exactly one row.

    PreparedStatement::query_opt

    Execute this scoped prepared statement and allow zero or one rows.

    Edge behavior: raises PoolError::RowCount when more than one row is returned.

    PreparedStatement::with_portal

    async fn[T] PreparedStatement::with_portal(self : PreparedStatement, params? : Array[&
    ToSql
    ], f : async (Portal) -> T) -> T

    Run one callback with a freshly bound scoped portal.

    Side effects: binds the parameters, runs the callback, and then closes the portal on both success and error paths.

    QueueMode

    pub enum QueueMode {
    Fifo
    Lifo
    } derive(Eq,
    Debug
    )

    Strategy used when choosing which idle physical connection to reuse next.

    This setting affects only the order of the pool's idle list. It does not reorder waiting tasks, and it has no effect while the pool must open a new connection because no idle connection is available.

    QueueMode::fifo

    fn QueueMode::fifo() -> QueueMode

    Construct FIFO queue mode.

    The oldest idle connection is reused first.

    QueueMode::lifo

    fn QueueMode::lifo() -> QueueMode

    Construct LIFO queue mode.

    The most recently returned idle connection is reused first.

    RecyclingMethod

    pub enum RecyclingMethod {
    Fast
    Verified
    Clean
    Custom(String)
    } derive(Eq,
    Debug
    )

    Checkout-time cleanup policy for already-open physical connections.

    This policy runs only when the pool reuses an idle connection. New connections skip it and instead run the optional post_create hook.

    RecyclingMethod::clean

    Construct clean recycling mode.

    The pool runs the same cleanup SQL sequence used by deadpool-postgres-style "clean" recycling. In practice this closes open portals/cursors, resets session settings, clears LISTEN state, releases advisory locks, and discards temporary objects before reuse.

    RecyclingMethod::custom

    fn RecyclingMethod::custom(sql : String) -> RecyclingMethod

    Construct custom recycling mode.

    sql is executed with batch_execute during checkout of an idle connection. If it fails or times out, that physical connection is discarded.

    RecyclingMethod::fast

    Construct fast recycling mode.

    The pool trusts the idle connection without sending any SQL.

    RecyclingMethod::verified

    Construct verified recycling mode.

    The pool runs a lightweight connection check before handing the connection out again.

    RowStream

    pub struct RowStream {
    columns : Array[
    Column
    ]
    // private fields
    }

    Extended-query row stream scoped to one pooled callback.

    The stream keeps low-level protocol state open until it is drained, finished, or detached into background cleanup. The owning callback must not leak it beyond the pool scope.

    RowStream::collect

    Collect every remaining row from this scoped row stream.

    Side effects: drains the underlying protocol stream to completion, updates columns with the latest metadata, and runs deferred cleanup before returning. If the collect operation fails, cleanup still runs best-effort.

    RowStream::detach

    fn RowStream::detach(self : RowStream) -> Unit

    Detach background draining for this scoped row stream.

    After detaching, direct calls such as next(), collect(), and finish() fail with PoolError::LeaseReleased. The pool waits for the detached task when the owning callback exits so the underlying connection is still cleaned up before reuse.

    RowStream::finish

    Drain this scoped row stream and return the final query summary.

    This is the explicit close path for callers that need command-tag metadata instead of materializing all rows. As with collect(), cleanup runs before the call returns and also runs best-effort on error.

    RowStream::next

    Read the next row from this scoped row stream.

    Preconditions: the stream must still be attached to the current callback scope, meaning detach() has not been called. Returning None means the stream is exhausted; in that case the package also runs any deferred cleanup needed before the underlying connection can be reused.

    SimpleQueryStream

    type SimpleQueryStream

    Simple-query protocol stream scoped to one pooled callback.

    This is the low-level counterpart to Client::with_simple_query.

    SimpleQueryStream::collect

    Collect every remaining message from this scoped simple-query stream.

    Side effects: drains the underlying stream fully before returning.

    SimpleQueryStream::detach

    fn SimpleQueryStream::detach(self : SimpleQueryStream) -> Unit

    Detach background draining for this scoped simple-query stream.

    After detaching, direct use fails with PoolError::LeaseReleased, and the owning callback waits for the detached drain before the connection is reused.

    SimpleQueryStream::finish

    async fn SimpleQueryStream::finish(self : SimpleQueryStream) -> Unit

    Drain this scoped simple-query stream to completion.

    SimpleQueryStream::next

    Read the next message from this scoped simple-query stream.

    Preconditions: the stream must not have been detached. Returning None means PostgreSQL finished the simple-query response stream.

    StatementCache

    type StatementCache

    Administrative handle for the statement cache attached to one physical PostgreSQL session.

    A StatementCache is scoped to the connection, not to SQL text globally across the pool. Callers should treat it as valid only while they still own the client or transaction lease that exposed it.

    StatementCache::clear

    async fn StatementCache::clear(self : StatementCache) -> Unit

    Clear this connection's statement cache.

    In-use statements are evicted immediately from the cache and are closed once the last active lease on them ends.

    StatementCache::remove

    async fn StatementCache::remove(self : StatementCache, sql : String, param_types? : Array[
    Type
    ]) -> Unit

    Remove one cached statement key from this connection's statement cache.

    Matching uses both sql and param_types. Missing entries are ignored.

    StatementCache::size

    fn StatementCache::size(self : StatementCache) -> Int raise

    Return the current number of cached prepared statements on this connection.

    Preconditions: the underlying physical connection must still be live.

    StatementCaches

    type StatementCaches

    Administrative handle spanning every currently live statement cache in a pool.

    The handle affects only connections that already exist when a method runs. Future connections start with fresh empty caches.

    StatementCaches::clear

    async fn StatementCaches::clear(self : StatementCaches) -> Unit

    Clear every statement cache on connections that are live right now.

    In-use cached statements are marked for eviction and close once their active leases finish. Connections opened after this call are unaffected.

    StatementCaches::remove

    async fn StatementCaches::remove(self : StatementCaches, sql : String, param_types? : Array[
    Type
    ]) -> Unit

    Remove one cached statement key from every statement cache that is live now.

    Matching uses both sql and param_types. Missing entries are ignored.

    Status

    pub struct Status {
    size : Int
    available : Int
    waiting : Int
    max_size : Int
    closed : Bool
    } derive(Eq,
    Debug
    )

    Point-in-time snapshot of one pool's runtime state.

    TargetSessionAttrs

    pub enum TargetSessionAttrs {
    Any
    ReadWrite
    } derive(Eq,
    Debug
    )

    PostgreSQL target-session policy checked right after a new connection opens.

    Any accepts the first successfully opened target. ReadWrite immediately runs show transaction_read_only and rejects a target whose value is on, which is useful when the target list may contain replicas.

    TargetSessionAttrs::any

    Construct Any.

    TargetSessionAttrs::read_write

    Construct ReadWrite.

    TimeoutKind

    pub enum TimeoutKind {
    Wait
    Create
    Recycle
    } derive(Eq,
    Debug
    )

    Phase of checkout reported by PoolError::Timeout.

    Wait means the caller could not obtain pool capacity in time, Create means opening a new physical connection timed out, and Recycle means checkout-time validation or cleanup of an idle connection timed out.

    Timeouts

    pub struct Timeouts {
    wait_ms : Int?
    create_ms : Int?
    recycle_ms : Int?
    } derive(Eq,
    Debug
    )

    Optional checkout timeout overrides in milliseconds.

    These values control how long the pool waits in each phase of get(): waiting for capacity, creating a new connection, and recycling an idle connection. None leaves the phase unbounded. Negative values are rejected later when the timeouts are validated by PoolConfig::new, Pool::get, or Pool::timeout_get.

    Timeouts::new

    fn Timeouts::new(wait_ms? : Int?, create_ms? : Int?, recycle_ms? : Int?) -> Timeouts

    Construct one timeout record.

    This constructor stores the values as-is and does not validate them yet. None disables the corresponding timeout.

    Transaction

    type Transaction

    Pooled transaction lease.

    This wraps a live PostgreSQL transaction or savepoint-backed nested transaction while keeping the underlying pooled connection checked out. Callers should prefer helpers such as with_transaction or run unless they are prepared to commit or roll back explicitly.

    Transaction::batch_execute

    async fn Transaction::batch_execute(self : Transaction, sql : String) -> Unit

    Execute one or more SQL commands inside this transaction.

    Transaction::check_connection

    async fn Transaction::check_connection(self : Transaction) -> Unit

    Perform a lightweight health check while the transaction is open.

    Side effects: executes select 1::int4 as value inside the current transaction instead of using a transaction-independent probe.

    Transaction::commit

    async fn Transaction::commit(self : Transaction) -> Unit

    Commit this pooled transaction explicitly.

    Preconditions: the transaction must not already be finished, and no nested transaction/savepoint scope may still be active. The call waits for any in-flight non-exclusive operation to finish first.

    Transaction::execute

    async fn Transaction::execute(self : Transaction, sql : String, params? : Array[&
    ToSql
    ]) -> Int

    Execute one command inside this transaction and return its affected row count.

    Transaction::query_all

    Run one query inside this transaction and collect all rows.

    Transaction::query_one

    Run one query inside this transaction and require exactly one row.

    Edge behavior: this helper materializes all rows first and raises PoolError::RowCount when PostgreSQL returned anything other than exactly one row.

    Transaction::query_opt

    Run one query inside this transaction and allow zero or one rows.

    Edge behavior: raises PoolError::RowCount when more than one row is returned.

    Transaction::query_typed_all

    Run one typed query inside the transaction and collect all rows.

    Side effects: prepares a temporary typed statement for this call and closes it before returning.

    Transaction::rollback

    async fn Transaction::rollback(self : Transaction) -> Unit

    Roll back this pooled transaction explicitly.

    Preconditions and completion behavior match commit(), except the final SQL action is ROLLBACK.

    Transaction::savepoint

    async fn Transaction::savepoint(self : Transaction, name : String) -> Transaction

    Start one nested transaction backed by a named savepoint.

    name is passed directly to the underlying client savepoint API.

    Transaction::statement_cache

    fn Transaction::statement_cache(self : Transaction) -> StatementCache raise

    Return the statement-cache handle for this transaction's physical connection.

    Preconditions: the transaction must not already be finished.

    Transaction::transaction

    async fn Transaction::transaction(self : Transaction) -> Transaction

    Start one nested transaction backed by an unnamed savepoint.

    Preconditions: the parent transaction must still be active and must not already be inside another nested transaction scope. The returned nested transaction must also be committed or rolled back.

    Transaction::with_prepared

    async fn[T] Transaction::with_prepared(self : Transaction, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare one non-cached statement scoped to the current transaction callback.

    The statement is closed automatically when the prepared-statement callback ends, even if that callback raises.

    Transaction::with_prepared_cached

    async fn[T] Transaction::with_prepared_cached(self : Transaction, sql : String, f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one cached statement scoped to the current transaction callback.

    The cache is local to the underlying physical connection.

    Transaction::with_prepared_typed

    async fn[T] Transaction::with_prepared_typed(self : Transaction, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare one typed non-cached statement scoped to the current transaction callback.

    param_types is sent when PostgreSQL parses the statement.

    Transaction::with_prepared_typed_cached

    async fn[T] Transaction::with_prepared_typed_cached(self : Transaction, sql : String, param_types : Array[
    Type
    ], f : async (PreparedStatement) -> T) -> T

    Prepare or reuse one typed cached statement scoped to the current transaction callback.

    Matching uses both sql and param_types.

    Transaction::with_savepoint

    async fn[T] Transaction::with_savepoint(self : Transaction, name : String, f : async (Transaction) -> T) -> T

    Run one callback inside a named savepoint-backed nested transaction.

    On success the nested transaction is committed if still unfinished; on error or cancellation it is rolled back best-effort.

    Transaction::with_stream

    async fn[T] Transaction::with_stream(self : Transaction, sql : String, params? : Array[&
    ToSql
    ], f : async (RowStream) -> T) -> T

    Run one callback with an extended-query row stream inside this transaction.

    The stream is callback-scoped and cleaned up before the transaction becomes available for later operations.

    Transaction::with_transaction

    async fn[T] Transaction::with_transaction(self : Transaction, f : async (Transaction) -> T) -> T

    Run one callback inside an unnamed savepoint-backed nested transaction.

    On success the nested transaction is committed if still unfinished; on error or cancellation it is rolled back best-effort.

    Transaction::with_typed_stream

    async fn[T] Transaction::with_typed_stream(self : Transaction, sql : String, param_types : Array[
    Type
    ], params? : Array[&
    ToSql
    ], f : async (RowStream) -> T) -> T

    Run one callback with a typed extended-query row stream inside this transaction.

    Side effects: prepares a temporary typed statement, uses it to create the stream, and closes that statement during stream cleanup.

    TransactionOptions

    pub struct TransactionOptions {
    isolation_level : IsolationLevel?
    read_only : Bool?
    deferrable : Bool?
    } derive(Eq,
    Debug
    )

    Options for the BEGIN command used when opening a pooled transaction.

    TransactionOptions::new

    fn TransactionOptions::new(isolation_level? : IsolationLevel, read_only? : Bool, deferrable? : Bool) -> TransactionOptions

    Build transaction options for a future BEGIN command.