import { type Table as ArrowTable } from 'apache-arrow'; import { KitDatabase } from './db.js'; import type { TableSpec, ColumnSpec } from './types.js'; import type { Row, Insert, Update } from './types.js'; declare module './db.js' { interface KitDatabase { selectFrom(table: T): SelectBuilder; insertInto(table: T): InsertBuilder; updateTable(table: T): UpdateBuilder; deleteFrom(table: T): DeleteBuilder; /** One-shot filtered update: `updateTable(table).set(patch).where(pred)`. * Convenience twin of Rust/Python `update_where`. */ updateWhere(table: T, patch: Update, predicate: Predicate): Row[]; /** One-shot filtered delete: `deleteFrom(table).where(pred)`. Returns the * deleted count as a `bigint`. Convenience twin of Rust/Python * `delete_where`. */ deleteWhere(table: T, predicate: Predicate): bigint; truncateTable(tableName: string): void; /** * Materialize `builder` as a named CTE and return a scope whose * `selectFrom(name)` reads those rows in memory. Chain `.with(...)` for * additional CTEs. */ with(name: string, builder: { _materialize(): { rows: Record[]; columns: ColumnSpec[]; }; }): CteScope; /** * Incrementally-maintained aggregate (`count`/`sum`/`min`/`max`/`avg`) * over `table`, optionally filtered by an exact `where` predicate. The * `value` is always exact; `incremental` reports whether the engine folded * in only the delta of newly-committed rows. `column` is required for * sum/min/max/avg. A filter with a residual (e.g. `contains`/`like`) is * rejected — it would aggregate the wrong rows. */ incrementalAggregate(table: string, agg: 'count' | 'sum' | 'min' | 'max' | 'avg', column?: string, filter?: Predicate): IncrementalAggregate; } } /** The result of `KitDatabase.incrementalAggregate`. */ export interface IncrementalAggregate { /** Exact aggregate value: a JSON number, or `null` when no rows matched. */ value: number | null; /** True when computed by merging only newly-committed rows (fast path). */ incremental: boolean; /** Rows processed in the delta pass (0 for a full recompute). */ delta_rows: number; } type ApplicationTypeMap = { bool: boolean; int64: bigint; float64: number; timestamp: string; date: string; text: string; bytes: unknown; json: unknown; }; export type ColumnValue = T['applicationType'] extends keyof ApplicationTypeMap ? ApplicationTypeMap[T['applicationType']] : unknown; /** * Minimal contract a {@link SelectBuilder} satisfies so it can supply the value * set / existence test for `inSubquery` / `exists` / `notExists`. Decoupled from * the builder's generic parameters so `Predicate` need not depend on them. */ export interface Subquery { /** Values of the subquery's single selected column (for `IN (...)`). */ scalarValuesSync(): unknown[]; /** True when the subquery matches at least one row (for `EXISTS`). */ hasRowsSync(): boolean; } export type Predicate = { kind: 'and'; predicates: Predicate[]; } | { kind: 'or'; predicates: Predicate[]; } | { kind: 'not'; predicate: Predicate; } | { kind: 'eq'; column: ColumnSpec; value: unknown; } | { kind: 'ne'; column: ColumnSpec; value: unknown; } | { kind: 'gt'; column: ColumnSpec; value: unknown; } | { kind: 'gte'; column: ColumnSpec; value: unknown; } | { kind: 'lt'; column: ColumnSpec; value: unknown; } | { kind: 'lte'; column: ColumnSpec; value: unknown; } | { kind: 'null'; column: ColumnSpec; not: boolean; } | { kind: 'in'; column: ColumnSpec; values: unknown[]; } | { kind: 'notIn'; column: ColumnSpec; values: unknown[]; } | { kind: 'like'; column: ColumnSpec; pattern: string; } | { kind: 'contains'; column: ColumnSpec; substr: string; } | { kind: 'bytesPrefix'; column: ColumnSpec; prefix: string; } | { kind: 'inSub'; column: ColumnSpec; subquery: Subquery; } | { kind: 'exists'; subquery: Subquery; negate: boolean; }; export type OrderBy = { column: ColumnSpec; direction: 'asc' | 'desc'; }; type MatchedRow = { rowId: bigint; row: Record; }; /** Scalar aggregate kinds computed over a value set (count is handled apart). */ type ScalarAggKind = 'sum' | 'min' | 'max' | 'avg'; /** Result row of a join: keyed by table name, each side a row or null. */ export type JoinRow = Record | null>; /** Join condition / post-join filter evaluated in JS over a {@link JoinRow}. */ export type JoinPredicate = (row: JoinRow) => boolean; /** The column-equality a {@link joinEq} predicate carries so the join builder * can probe the right table by index instead of full-scanning it. */ export interface JoinEqKey { leftTable: string; leftColumn: ColumnSpec; rightTable: string; rightColumn: ColumnSpec; } /** * A declarative join predicate equating `leftTable.leftColumn` with * `rightTable.rightColumn`. Behaves like the closure form, but the builder can * introspect the equality to fetch the right table by an index probe over the * distinct left keys instead of a full scan. Prefer this over a hand-written * closure for FK joins. */ export declare function joinEq(leftTable: TableSpec, leftColumn: ColumnSpec, rightTable: TableSpec, rightColumn: ColumnSpec): JoinPredicate; /** A single column aggregate used inside `GroupBuilder.aggregate`. `distinct` * de-duplicates the column's values (e.g. `COUNT(DISTINCT col)`); it requires a * `column` and is a no-op for `min`/`max`. */ export type AggregateSpec = { fn: 'count' | ScalarAggKind; column?: ColumnSpec; distinct?: boolean; }; /** One result row from a grouped query: group columns plus aggregate aliases. */ export type GroupRow = Record; export declare function eq(column: T, value: ColumnValue): Predicate; export declare function ne(column: T, value: ColumnValue): Predicate; export declare function gt(column: T, value: ColumnValue): Predicate; export declare function gte(column: T, value: ColumnValue): Predicate; export declare function lt(column: T, value: ColumnValue): Predicate; export declare function lte(column: T, value: ColumnValue): Predicate; export declare function isNull(column: ColumnSpec): Predicate; export declare function isNotNull(column: ColumnSpec): Predicate; export declare function inList(column: T, values: ColumnValue[]): Predicate; export declare function and(...predicates: Predicate[]): Predicate; export declare function or(...predicates: Predicate[]): Predicate; export declare function asc(column: ColumnSpec): OrderBy; export declare function desc(column: ColumnSpec): OrderBy; /** Negates a predicate (logical NOT). */ export declare function not(predicate: Predicate): Predicate; /** `column NOT IN (values)`. */ export declare function notInList(column: T, values: ColumnValue[]): Predicate; /** * SQL `LIKE` against a text column. `%` matches any run of characters and `_` * matches a single character; all other characters are literal. Case-sensitive. */ export declare function like(column: T, pattern: string): Predicate; /** Case-sensitive substring match: `column LIKE '%substr%'` with no wildcards. */ export declare function contains(column: T, substr: string): Predicate; /** * Anchored prefix match `column LIKE 'prefix%'` on a Bytes column with a * bitmap index. Pushed down exactly to the engine's `BytesPrefix` condition * (no residual re-check) — tighter than {@link contains} for anchored matches. * Falls back to a residual `startsWith` scan when the column has no bitmap * index. */ export declare function bytesPrefix(column: T, prefix: string): Predicate; /** `column IN (subquery)`. The subquery must select exactly one column. */ export declare function inSubquery(column: T, subquery: Subquery): Predicate; /** * `EXISTS (subquery)`. The subquery is uncorrelated: it is evaluated once and * gates the whole outer scan. * // ponytail: no correlated-subquery support; correlation would require * // re-binding the outer row into the subquery per candidate. */ export declare function exists(subquery: Subquery): Predicate; /** `NOT EXISTS (subquery)`. Uncorrelated, like {@link exists}. */ export declare function notExists(subquery: Subquery): Predicate; /** Aggregate descriptor: `COUNT(*)` for a group. */ export declare function count(): AggregateSpec; /** Aggregate descriptor: `COUNT(column)` — non-null values in a group. */ export declare function countColumn(column: ColumnSpec): AggregateSpec; /** Aggregate descriptor: `COUNT(DISTINCT column)` — unique non-null values. */ export declare function countDistinct(column: ColumnSpec): AggregateSpec; /** Aggregate descriptor: `SUM(column)` for a group. */ export declare function sum(column: ColumnSpec): AggregateSpec; /** Aggregate descriptor: `MIN(column)` for a group. */ export declare function min(column: ColumnSpec): AggregateSpec; /** Aggregate descriptor: `MAX(column)` for a group. */ export declare function max(column: ColumnSpec): AggregateSpec; /** Aggregate descriptor: `AVG(column)` for a group (always a float). */ export declare function avg(column: ColumnSpec): AggregateSpec; /** How a `where` predicate would execute: which native conditions push down. */ export type ExplainPlan = { indexAccelerated: boolean; exact: boolean; pushedConditions: string[]; }; /** SUM over an int64 column yields bigint; over a float64 column yields number. */ type SumResult = C['applicationType'] extends 'int64' ? bigint : number; export declare class SelectBuilder[]> implements Subquery { private readonly kit; private readonly table; private _where?; private _orderBy; private _limit?; private _offset?; private _columns?; private _count; private _distinct; private _aggregate?; private _ann?; private _sparse?; /** Internal: in-memory rows backing a CTE source instead of a native table. */ _source?: MatchedRow[]; constructor(kit: KitDatabase, table: T); where(predicate: Predicate): SelectBuilder; orderBy(...orders: OrderBy[]): SelectBuilder; limit(n: number): SelectBuilder; offset(n: number): SelectBuilder; /** Remove duplicate result rows (over the selected columns). */ distinct(): SelectBuilder; select(columns: C[]): SelectBuilder, C['name']>>; private cloneScalar; selectCount(): SelectBuilder; selectSum(column: C): SelectBuilder>; selectAvg(column: ColumnSpec): SelectBuilder; selectMin(column: C): SelectBuilder | null>; selectMax(column: C): SelectBuilder | null>; /** Start an INNER JOIN. The `on` predicate runs in JS over the joined row. */ innerJoin(table: TableSpec, on: JoinPredicate): JoinBuilder; /** Start a LEFT JOIN; unmatched right side is null in the result row. */ leftJoin(table: TableSpec, on: JoinPredicate): JoinBuilder; /** Start a CROSS JOIN (cartesian product; no predicate). */ crossJoin(table: TableSpec): JoinBuilder; private startJoin; /** Group matched rows by the given columns and compute aggregates per group. */ groupBy(...columns: ColumnSpec[]): GroupBuilder; private resolveMatched; /** Bind an in-memory source (used by CTE materialization). Internal. */ _bindSource(rows: MatchedRow[]): this; /** Run the query and capture its rows + output columns for CTE materialization. */ _materialize(): { rows: Record[]; columns: ColumnSpec[]; }; scalarValuesSync(): unknown[]; hasRowsSync(): boolean; /** * Approximate nearest-neighbour search: return the `k` rows whose `column` * (an `embedding`) is closest to `vector`, resolved by the column's ANN * index. Terminal — call `executeSync()`/`execute()` next. */ annSearch(column: ColumnSpec, vector: number[], k: number): SelectBuilder[]>; /** * Learned-sparse (SPLADE) retrieval: return the `k` rows whose `column` (a * sparse token vector) best matches the weighted `query` `[token, weight]` * pairs. Terminal — call `executeSync()`/`execute()` next. */ sparseMatch(column: ColumnSpec, query: [number, number][], k: number): SelectBuilder[]>; executeSync(): TResult; execute(): Promise; /** * Execute against the native engine and return the matching rows as an Arrow * (columnar) table — zero-copy from the engine. TypeScript-only: the * Rust/Python kit returns row maps. * * The native Arrow path is index-driven and needs at least one pushed-down * condition, so a `where`/`annSearch`/`sparseMatch` clause is required. It * applies only the pushed-down predicate (exact for `=`/range/`in`, a * superset for `contains`/`like`) and returns every column — `orderBy`, * `limit`, `offset`, and column projection are NOT applied. Use * {@link executeSync} for full query semantics. */ executeArrow(): ArrowTable; /** * Describe how this query's `where`/`annSearch`/`sparseMatch` clause would * push down to native index conditions — a diagnostic that plans but does * not run the query. `exact` is true when the whole predicate translated (no * JS residual re-filtering). */ explain(): ExplainPlan; } /** * Nested-loop join executed entirely in JS. The result is a {@link JoinRow} * keyed by table name — e.g. `{ users: { ... }, orders: { ... } }`. For a LEFT * JOIN with no match, the joined side is `null`. */ export declare class JoinBuilder { private readonly kit; private readonly baseTable; private readonly baseWhere?; private readonly baseSource?; private readonly clauses; private _where?; private _limit?; private _offset?; constructor(kit: KitDatabase, baseTable: TableSpec, baseWhere?: Predicate | undefined, baseSource?: MatchedRow[] | undefined); innerJoin(table: TableSpec, on: JoinPredicate): this; leftJoin(table: TableSpec, on: JoinPredicate): this; crossJoin(table: TableSpec): this; /** Post-join filter over the assembled {@link JoinRow}. */ where(predicate: JoinPredicate): this; limit(n: number): this; offset(n: number): this; executeSync(): JoinRow[]; execute(): Promise; } /** * Grouped aggregation executed in JS. Each result row carries the group-by * column values plus one entry per named aggregate. */ export declare class GroupBuilder { private readonly kit; private readonly table; private readonly groupColumns; private readonly _where?; private readonly _source?; private _aggregates; private _having?; constructor(kit: KitDatabase, table: T, groupColumns: ColumnSpec[], _where?: Predicate | undefined, _source?: MatchedRow[] | undefined); /** Declare the named aggregates to compute per group. */ aggregate(spec: Record): this; /** Filter groups after aggregation (HAVING), over the assembled group row. */ having(predicate: (row: GroupRow) => boolean): this; executeSync(): GroupRow[]; execute(): Promise; } /** * A scope of materialized common table expressions (CTEs). Each `with` runs its * builder eagerly and stores the result rows in memory so a later `selectFrom` * can read them as if they were a table. * // ponytail: full in-memory materialization — CTEs are not lazy/recursive. */ export declare class CteScope { private readonly kit; private readonly ctes; constructor(kit: KitDatabase); with(name: string, builder: { _materialize(): { rows: Record[]; columns: ColumnSpec[]; }; }): CteScope; selectFrom(name: string): SelectBuilder[]>; } export declare class InsertBuilder> { private readonly kit; private readonly table; private _row?; private _returning?; private _onConflict?; constructor(kit: KitDatabase, table: T); values(row: Insert): this; /** * Insert many rows in a single transaction. Each row still passes through * defaults, validation, and constraint checks, but the whole batch commits * once — far faster than a row-at-a-time loop for bulk loads. */ valuesMany(rows: Insert[]): InsertManyBuilder; returning(...columns: [...C]): InsertBuilder, C[number]['name']>>; onConflictDoNothing(): InsertBuilder; onConflictDoUpdate(patch: Partial>): InsertBuilder; executeSync(): TResult; execute(): Promise; } export declare class InsertManyBuilder { private readonly kit; private readonly table; private readonly rows; constructor(kit: KitDatabase, table: T, rows: Insert[]); executeSync(): Row[]; execute(): Promise[]>; } export declare class UpdateBuilder[]> { private readonly kit; private readonly table; private _patch?; private _where?; private _returning?; constructor(kit: KitDatabase, table: T); set(patch: Update): this; where(predicate: Predicate): this; returning(...columns: [...C]): UpdateBuilder, C[number]['name']>[]>; executeSync(): TResult; execute(): Promise; } export declare class DeleteBuilder { private readonly kit; private readonly table; private _where?; private _returning?; constructor(kit: KitDatabase, table: T); where(predicate: Predicate): this; returning(...columns: [...C]): DeleteBuilder, C[number]['name']>[]>; executeSync(): TResult; execute(): Promise; } export {}; //# sourceMappingURL=query.d.ts.map