import type pg from "pg";
import type { ClientConnectionIssue, ClientOptions, ClientPingInput, ClientRole } from "../abstract/Client";
import { Client } from "../abstract/Client";
import type { ClientQueryLoggerProps } from "../abstract/Loggers";
import type { QueryAnnotation } from "../abstract/QueryAnnotation";
import { TimelineManager } from "../abstract/TimelineManager";
import type { MaybeCallable, PickPartial } from "../internal/misc";
import type { Hints, Literal } from "../types";
/**
 * Options for PgClient constructor.
 */
export interface PgClientOptions extends ClientOptions {
    /** Info on how to discover the shards. */
    shards?: {
        /** Name of a PG shard schema (e.g. "sh%04d"). */
        nameFormat: string;
        /** A SQL query which should return the names of shard schemas served by
         * this Client. */
        discoverQuery: MaybeCallable<string>;
    } | null;
    /** PG "SET key=value" hints to run before each query. Often times we use it
     * to pass statement_timeout option since e.g. PGBouncer doesn't support
     * per-connection statement timeout in transaction pooling mode: it throws
     * "unsupported startup parameter" error. I.e. we may want to emit "SET
     * statement_timeout TO ..." before each query in multi-query mode. */
    hints?: MaybeCallable<Hints> | null;
    /** After how many milliseconds we give up waiting for the replica to catch up
     * with the master. When role="replica", then this option is the only way to
     * "unlatch" the reads from the master node after a write. */
    maxReplicationLagMs?: MaybeCallable<number>;
    /** Sometimes, the role of this Client is known statically, e.g. when pointing
     * to AWS Aurora writer and reader endpoints. If "master" or "replica" are
     * provided, then no attempt is made to use functions like
     * pg_current_wal_insert_lsn() etc. (they are barely supported in e.g. AWS
     * Aurora). Instead, for "replica" role, it is treated as "always lagging up
     * until maxReplicationLagMs after the last write". If role="unknown", then
     * auto-detection and automatic lag tracking is performed using
     * pg_current_wal_insert_lsn() and other built-in PostgreSQL functions. */
    role?: ClientRole;
    /** Up to how often we call TimelineManager#triggerRefresh(). */
    replicaTimelinePosRefreshMs?: MaybeCallable<number>;
}
/**
 * An opened low-level PostgreSQL connection.
 */
export interface PgClientConn extends pg.PoolClient {
    /** Undocumented property of node-postgres, see:
     * https://github.com/brianc/node-postgres/issues/2665 */
    processID?: number | null;
    /** An additional property to the vanilla client: auto-incrementing ID of the
     * connection for logging purposes. */
    id?: number;
    /** An additional property to the vanilla client: number of queries sent
     * within this connection. */
    queriesSent?: number;
    /** An additional property to the vanilla client: when do we want to
     * hard-close that connection. */
    closeAt?: number;
}
/**
 * An abstract PostgreSQL Client which doesn't know how to acquire an actual
 * connection and send queries; these things are up to the derived classes to
 * implement.
 *
 * The idea is that in each particular project, people may have they own classes
 * derived from PgClient, in case the codebase already has some existing
 * connection pooling solution. They don't have to use PgClientPool.
 *
 * Since the class is cloneable internally (using the prototype substitution
 * technique), the contract of this class is that ALL its derived classes may
 * only have readonly immediate properties.
 */
export declare abstract class PgClient extends Client {
    /** Default values for the constructor options. */
    static readonly DEFAULT_OPTIONS: Required<PickPartial<PgClientOptions>>;
    /** Number of decimal digits in an ID allocated for shard number. Calculated
     * dynamically based on shards.nameFormat (e.g. for "sh%04d", it will be 4
     * since it expands to "sh0012"). */
    private readonly shardNoPadLen;
    /** This value is set after each request to reflect the actual role of the
     * client. The idea is that master/replica role may change online, without
     * reconnecting the Client, so we need to refresh it after each request and be
     * ready for a fallback. The expectation is that the initial value is
     * populated during the very first shardNos() call. */
    private readonly reportedRoleAfterLastQuery;
    /** This value is non-null if there was an unsuccessful connection attempt
     * (i.e. the PG is down), and there were no successful queries since then. */
    private readonly reportedConnectionIssue;
    /** PgClient configuration options. */
    readonly options: Required<PgClientOptions>;
    /** Name of the shard associated to this Client. */
    readonly shardName: string;
    /** An active TimelineManager for this particular Client. */
    readonly timelineManager: TimelineManager;
    /**
     * Returns statistics about the connection pool.
     */
    abstract poolStats(): ClientQueryLoggerProps["poolStats"];
    /**
     * Called when the Client needs a connection to run a query against. Implies
     * than the caller MUST call release() method on the returned object.
     */
    abstract acquireConn(): Promise<PgClientConn>;
    /**
     * Initializes an instance of PgClient.
     */
    constructor(options: PgClientOptions);
    /**
     * Sends a query (internally, a multi-query). After the query finishes, we
     * should expect that role() returns the actual master/replica role.
     */
    query<TRow>({ query: queryLiteral, hints, isWrite, annotations, op, table, batchFactor, }: {
        query: Literal;
        hints?: Hints;
        isWrite: boolean;
        annotations: QueryAnnotation[];
        op: string;
        table: string;
        batchFactor?: number;
    }): Promise<TRow[]>;
    shardNos(): Promise<readonly number[]>;
    ping({ execTimeMs, isWrite, annotation, }: ClientPingInput): Promise<void>;
    shardNoByID(id: string): number;
    withShard(no: number): this;
    role(): ClientRole;
    connectionIssue(): ClientConnectionIssue | null;
    /**
     * Prepares a PG Client multi-query from the query literal and hints.
     */
    private buildMultiQuery;
    /**
     * Sends a multi-query to PG Client.
     *
     * A good and simple explanation of the protocol is here:
     * https://www.postgresql.org/docs/13/protocol-flow.html. In short, we can't
     * use prepared-statement-based operations even theoretically, because this
     * mode doesn't support multi-queries. Also notice that TS typing is doomed
     * for multi-queries:
     * https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33297
     */
    private sendMultiQuery;
    /**
     * Builds the schema name (aka "Shard name") by Shard number using
     * `options#shards#nameFormat`.
     *
     * E.g. nameFormat="sh%04d" generates names like "sh0042".
     */
    private buildShardName;
}
//# sourceMappingURL=PgClient.d.ts.map