import type { QueryAnnotation } from "../abstract/QueryAnnotation";
import { Runner } from "../abstract/Runner";
import type { Schema } from "../abstract/Schema";
import type { Field, FieldAliased, Hints, Literal, Table, Where } from "../types";
import type { PgClient } from "./PgClient";
/**
 * A convenient pile of helper methods usable by most of PgQuery* classes. In
 * some sense it's an anti-pattern, but still reduces the boilerplate.
 *
 * PgRunner is also responsible for stringifying the values passed to the
 * queries and parsing values returned from the DB according to the field types
 * specs.
 */
export declare abstract class PgRunner<TTable extends Table, TInput, TOutput> extends Runner<TInput, TOutput> {
    readonly schema: Schema<TTable>;
    private client;
    private escapers;
    private oneOfBuilders;
    private dbValueToJs;
    private stringify;
    ["constructor"]: typeof PgRunner;
    protected clientQuery<TOutput extends object>(sql: string, annotations: QueryAnnotation[], batchFactor: number, hints?: Hints): Promise<TOutput[]>;
    /**
     * Formats prefixes/suffixes of various compound SQL clauses. Don't use on
     * performance-critical path!
     */
    protected fmt(template: string, args?: {
        fields?: Array<FieldAliased<TTable>>;
        autos?: Partial<Record<string, string>>;
        normalize?: boolean;
    }): string;
    /**
     * Escapes a value at runtime using the codegen functions created above. We
     * use escapers table and the codegen for the following reasons:
     * 1. We want to be sure that we know in advance, how to escape all table
     *    fields (and not fail at runtime).
     * 2. We want to make createEscapeCode() the single source of truth about
     *    fields escaping, even at runtime.
     */
    protected escapeValue(field: Field<TTable>, value: unknown): string;
    /**
     * Escapes field name identifier.
     * - In case it's a composite primary key, returns its `ROW(f1,f2,...)`
     *   representation.
     * - A field may be aliased, e.g. if `{ field: "abc", alias: "$cas.abc" }` is
     *   passed, then the returned value will be `"$cas.abc"`. Basically, `field`
     *   name is used only to verify that such field is presented in the schema.
     * - If `autos` is passed, and the field is in it, returns the value of the
     *   `autos` record for the field. This is used to inject an
     *   autoInsert/autoUpdate SQL expression at the position of some field.
     */
    protected escapeField(info: FieldAliased<TTable>, { withTable, normalize }?: {
        withTable?: string;
        normalize?: boolean;
    }): string;
    /**
     * Returns a newly created JS function which, when called with a row set,
     * returns the following SQL clause:
     *
     * ```
     * WITH rows(id, a, b, _key) AS (VALUES
     *   ((NULL::tbl).id, (NULL::tbl).a, (NULL::tbl).b, 'k0'),
     *   ('123',          'xyz',         'nn',          'kSome'),
     *   ('456',          'abc',         'nn',          'kOther'),
     *   ...
     * )
     * {suffix}
     * ```
     *
     * For composite primary key, its parts (fields) are always prepended. The set
     * of columns is passed in specs.
     */
    protected createWithBuilder({ fields, skipSorting, suffix }: {
        fields: ReadonlyArray<FieldAliased<TTable>>;
        skipSorting?: boolean;
        suffix: string;
    }): {
        prefix: string;
        func: (entries: Iterable<[key: string, input: object]>) => string;
        suffix: string;
    };
    /**
     * Returns a newly created JS function which, when called with a row set,
     * returns the following SQL clause:
     *
     * When called with withKey=true:
     *
     * ```
     * ('123', 'xyz', 'nn', 'kSome'),
     * ('456', 'abc', 'nn', 'kOther'),
     * ```
     *
     * When called with withKey=true, but with empty `fields`:
     *
     * ```
     * ('kSome'),
     * ('kOther'),
     * ```
     *
     * When called with withKey=false:
     *
     * ```
     * ('123', 'xyz', 'nn'),
     * ('456', 'abc', 'nn'),
     * ```
     *
     * The list of column names is passed in `fields`.
     *
     * When the builder func is called, the actual values for some field in a row
     * is extracted from the same-named prop of the row, but if `{ field, alias }`
     * object is passed in `fields` array, then the value is extracted from the
     * `alias` sub-prop of the row. This is used to e.g. access `row.$cas.blah`
     * value for a field named blah (in this case, `alias="$cas"`).
     *
     * Notice that either a simple primary key or a composite primary key columns
     * are always prepended to the list of values since it makes no sense to
     * generate VALUES clause without exact identification of the destination.
     */
    protected createValuesBuilder<TInput extends object>({ prefix, indent, fields, withKey, skipSorting, suffix }: {
        prefix: string;
        indent: string;
        fields: ReadonlyArray<FieldAliased<TTable>>;
        withKey?: boolean;
        skipSorting?: boolean;
        suffix: string;
    }): {
        prefix: string;
        func: (entries: Iterable<[key: string, input: TInput]>) => string;
        suffix: string;
    };
    /**
     * Returns a newly created JS function which, when called with an object,
     * returns the following SQL clause:
     *
     * id='123', a='xyz', b='nnn' [, {literal}]
     *
     * The set of columns is passed in specs, all other columns are ignored.
     */
    protected createUpdateKVsBuilder(fields: Array<Field<TTable>>): (input: object, literal?: Literal) => string;
    /**
     * Prefers to do utilize createAnyBuilder() if it can (i.e. build
     * a=ANY('{...}') clause). Otherwise, builds an IN(...) clause.
     */
    protected createOneOfBuilder(field: Field<TTable>, fieldValCode?: string): (values: Iterable<unknown>) => string;
    /**
     * Given a list of fields, returns two builders:
     *
     * 1. "Optimized": a newly created JS function which, when called with a row
     *    set, returns one the following SQL clauses:
     *
     * ```
     * WHERE (field1, field2) IN(VALUES
     *   ((NULL::tbl).field1, (NULL::tbl).field2),
     *   ('aa', 'bb'),
     *   ('cc', 'dd'))
     *
     * or
     *
     * WHERE (field1='a' AND field2='b' AND field3 IN('a', 'b', 'c', ...)) OR (...)
     *        ^^^^^^^^^^prefix^^^^^^^^^               ^^^^^^^^ins^^^^^^^
     * ```
     *
     * 2. "Plain": the last one builder mentioned above (good to always use for
     *    non-batched queries for instance).
     */
    protected createWhereBuildersFieldsEq<TInput extends object>(args: {
        prefix: string;
        fields: ReadonlyArray<Field<TTable>>;
        suffix: string;
    }): {
        plain: {
            prefix: string;
            func: (inputs: Iterable<[key: string, input: TInput]>) => string;
            suffix: string;
        };
        optimized: {
            prefix: string;
            func: (inputs: Iterable<[key: string, input: TInput]>) => string;
            suffix: string;
        };
    };
    /**
     * Returns a newly created JS function which, when called with a Where object,
     * returns the generated SQL WHERE clause.
     *
     * - The building is relatively expensive, since it traverses the Where object
     *   at run-time and doesn't know the shape beforehand.
     * - If the Where object is undefined, skips the entire WHERE clause.
     */
    protected createWhereBuilder({ prefix, suffix }: {
        prefix: string;
        suffix: string;
    }): {
        prefix: string;
        func: (where: Where<TTable>) => string;
        suffix: string;
    };
    /**
     * Prepends or appends a primary key to the list of fields. In case the
     * primary key is plain (i.e. "id" field), it's just added as a field;
     * otherwise, the unique key fields are added.
     *
     * For INSERT/UPSERT operations, we want to append the primary key, since it's
     * often types pre-generated as a random-looking value. In many places, we
     * sort batched lists of rows before e.g. inserting them, so we order them by
     * their natural data order which prevents deadlocks on unique key conflict
     * when multiple concurrent transactions try to insert the same set of rows in
     * different order ("while inserting index tuple").
     *
     * For UPDATE operations though, we want to prepend the primary key, to make
     * sure we run batched updates in the same order in multiple concurrent
     * transactions. This lowers the chances of deadlocks too.
     */
    protected addPK(fields: ReadonlyArray<Field<TTable>>, mode: "prepend" | "append"): string[];
    constructor(schema: Schema<TTable>, client: PgClient);
    delayForSingleQueryRetryOnError(e: unknown): number | "immediate_retry" | "no_retry";
    shouldDebatchOnError(e: unknown): boolean;
    /**
     * Given a list of fields, returns a newly created JS function which, when
     * called with a row set, returns the following SQL clause:
     *
     * ```
     * WHERE (field1='a' AND field2='b' AND field3 IN('a', 'b', 'c', ...)) OR (...)
     *        ^^^^^^^^^^prefix^^^^^^^^^               ^^^^^^^^ins^^^^^^^
     * ```
     *
     * The assumption is that the last field in the list is the most variable,
     * whilst all previous fields compose a more or less static prefix
     *
     * - ATTENTION: if at least one OR is produced, it will likely result in a
     *   slower Bitmap Index Scan.
     * - Used in runSingle() (no ORs there) or when optimized builder is not
     *   available (e.g. when unique key contains nullable fields).
     */
    private createWhereBuilderFieldsEqOrBased;
    /**
     * Given a list of fields, returns a newly created JS function which, when
     * called with a row set, returns the following SQL clause:
     *
     * ```
     * WHERE (field1, field2) IN(VALUES
     *   ((NULL::tbl).field1, (NULL::tbl).field2),
     *   ('aa', 'bb'),
     *   ('cc', 'dd'))
     * ```
     *
     * The assumption is that all fields are non-nullable.
     *
     * - This clause always produces an Index Scan (not Bitmap Index Scan).
     * - Used in most of the cases in runBatch(), e.g. when unique key has >1
     *   fields, and they are all non-nullable.
     */
    private createWhereBuilderFieldsEqTuplesBased;
    private buildWhere;
    private buildFieldBinOp;
    private buildFieldIsDistinctFrom;
    private buildFieldEq;
    private buildLogical;
    private buildNot;
    private buildFieldNe;
    /**
     * Returns a newly created JS function which, when called with an array of
     * values, returns one of following SQL clauses:
     *
     * - $field=ANY('{aaa,bbb,ccc}')
     * - ($field=ANY('{aaa,bbb}') OR $field IS NULL)
     * - $field='aaa' (see below, why)
     * - ($field='aaa' OR $field IS NULL)
     * - $field IS NULL
     * - false
     */
    private createAnyBuilder;
    /**
     * Returns a newly created JS function which, when called with an array of
     * values, returns one of following SQL clauses:
     *
     * - $field IN('aaa', 'bbb', 'ccc')
     * - ($field IN('aaa', 'bbb') OR $field IS NULL)
     * - $field IS NULL
     * - false
     *
     * This only works for primitive types.
     */
    private createInBuilder;
    /**
     * For codegen, returns the following piece of JS code:
     *
     * '($fieldValCode !== undefined ? this.escapeXyz($fieldValCode) : "$defSQL")'
     *
     * It's expected that, while running the generated code, `this` points to an
     * object with a) `escapeXyz()` functions, b) `stringify` object containing
     * the table fields custom to-string converters.
     */
    private createEscapeCode;
    /**
     * Compiles a function body with `this` bound to some well-known properties
     * which are available in the body.
     *
     * For each table, we compile frequently accessible pieces of code which
     * serialize data in SQL format. This allows to remove lots of logic and "ifs"
     * from runtime and speed up hot code paths.
     */
    private newFunction;
    /**
     * The problem: PG is not working fine with queries like:
     *
     * ```
     * WITH rows(composite_id, c) AS (
     *   VALUES
     *   ( ROW((NULL::tbl).x, (NULL::tbl).y), (NULL::tbl).c ),
     *   ( ROW(1,2),                          3             ),
     *   ( ROW(3,4),                          5             )
     * )
     * UPDATE tbl SET c=rows.c
     * FROM rows WHERE ROW(tbl.x, tbl.y)=composite_id
     * ```
     *
     * It cannot match the type of composite_id with the row, and even the trick
     * with NULLs doesn't help it to infer types. It's a limitation of WITH clause
     * (because in INSERT ... VALUES, there is no such problem).
     *
     * So the only solution is to parse/decompose the row string into individual
     * unique key columns at runtime for batched UPDATEs. And yes, it's slow.
     *
     * ```
     * WITH rows(x, y, c) AS (
     *   VALUES
     *   ( (NULL::tbl).x, (NULL::tbl).y, (NULL::tbl).c ),
     *   ( 1,             2,             3             ),
     *   ( 3,             4,             5             )
     * )
     * UPDATE tbl SET c=rows.c
     * FROM rows WHERE ROW(tbl.x, tbl.y)=ROW(rows.x, ROW.y)
     * ```
     */
    private unfoldCompositePK;
    /**
     * Some data types are different between PG and JS. Here we have a chance to
     * "normalize" them. E.g. in JS, Date is truncated to milliseconds (3 digits),
     * whilst in PG, it's by default of 6 digits precision (so if we didn't
     * normalize, then JS Date would've been never equal to a PG timestamp).
     */
    private normalizeSQLExpr;
    /**
     * Throws an exception about some field being not mentioned in the table
     * schema if the passed data is undefined. Notice that ID is treated as always
     * available in this message.
     */
    private nullThrowsUnknownField;
}
//# sourceMappingURL=PgRunner.d.ts.map