turbine-orm 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -39
- package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
- package/dist/cjs/adapters/index.d.ts +110 -0
- package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
- package/dist/cjs/cli/config.d.ts +181 -0
- package/dist/cjs/cli/config.js +32 -6
- package/dist/cjs/cli/destructive.d.ts +38 -0
- package/dist/cjs/cli/index.d.ts +359 -0
- package/dist/cjs/cli/index.js +228 -56
- package/dist/cjs/cli/loader.d.ts +61 -0
- package/dist/cjs/cli/mcp.d.ts +42 -0
- package/dist/cjs/cli/migrate.d.ts +356 -0
- package/dist/cjs/cli/migrate.js +131 -40
- package/dist/cjs/cli/observe-ui.d.ts +1 -0
- package/dist/cjs/cli/observe-ui.js +14 -5
- package/dist/cjs/cli/observe.d.ts +25 -0
- package/dist/cjs/cli/observe.js +49 -12
- package/dist/cjs/cli/pii-tags.d.ts +53 -0
- package/dist/cjs/cli/prisma-report.d.ts +33 -0
- package/dist/cjs/cli/prisma-report.js +73 -0
- package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
- package/dist/cjs/cli/prisma-resolve.js +1 -0
- package/dist/cjs/cli/prisma-schema.d.ts +176 -0
- package/dist/cjs/cli/prisma-schema.js +82 -4
- package/dist/cjs/cli/rate-limit.d.ts +32 -0
- package/dist/cjs/cli/rate-limit.js +45 -0
- package/dist/cjs/cli/studio-demo.d.ts +43 -0
- package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
- package/dist/cjs/cli/studio.d.ts +207 -0
- package/dist/cjs/cli/studio.js +136 -71
- package/dist/cjs/cli/ui.d.ts +73 -0
- package/dist/cjs/cli/ui.js +51 -9
- package/dist/cjs/client.d.ts +837 -0
- package/dist/cjs/client.js +3 -0
- package/dist/cjs/dialect.d.ts +516 -0
- package/dist/cjs/dialect.js +37 -12
- package/dist/cjs/errors.d.ts +370 -0
- package/dist/cjs/generate.d.ts +137 -0
- package/dist/cjs/generate.js +39 -6
- package/dist/cjs/index-advisor.d.ts +153 -0
- package/dist/cjs/index-stats.d.ts +384 -0
- package/dist/cjs/index.d.ts +55 -0
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/introspect.d.ts +269 -0
- package/dist/cjs/mssql.d.ts +232 -0
- package/dist/cjs/mssql.js +6 -0
- package/dist/cjs/mysql.d.ts +173 -0
- package/dist/cjs/mysql.js +16 -0
- package/dist/cjs/nested-write.d.ts +96 -0
- package/dist/cjs/nested-write.js +414 -24
- package/dist/cjs/observe.d.ts +115 -0
- package/dist/cjs/optional-peer-import.d.cts +72 -0
- package/dist/cjs/pipeline-submittable.d.ts +93 -0
- package/dist/cjs/pipeline.d.ts +71 -0
- package/dist/cjs/powdb-introspect.d.ts +84 -0
- package/dist/cjs/powdb.d.ts +931 -0
- package/dist/cjs/powdb.js +106 -21
- package/dist/cjs/powql.d.ts +592 -0
- package/dist/cjs/powql.js +42 -6
- package/dist/cjs/prisma-compat.d.ts +283 -0
- package/dist/cjs/prisma-compat.js +167 -9
- package/dist/cjs/query/aggregates.d.ts +92 -0
- package/dist/cjs/query/aggregates.js +7 -3
- package/dist/cjs/query/batched-loader.d.ts +193 -0
- package/dist/cjs/query/builder.d.ts +849 -0
- package/dist/cjs/query/builder.js +571 -65
- package/dist/cjs/query/compound-unique.d.ts +51 -0
- package/dist/cjs/query/deferred.d.ts +223 -0
- package/dist/cjs/query/filters.d.ts +201 -0
- package/dist/cjs/query/index.d.ts +14 -0
- package/dist/cjs/query/index.js +6 -1
- package/dist/cjs/query/relations.d.ts +609 -0
- package/dist/cjs/query/relations.js +693 -46
- package/dist/cjs/query/types.d.ts +1300 -0
- package/dist/cjs/query/utils.d.ts +209 -0
- package/dist/cjs/query/utils.js +208 -1
- package/dist/cjs/query/warn-registry.d.ts +68 -0
- package/dist/cjs/query/warn-registry.js +9 -0
- package/dist/cjs/query/where-compile.d.ts +139 -0
- package/dist/cjs/query/where.d.ts +548 -0
- package/dist/cjs/query/where.js +58 -22
- package/dist/cjs/query/writes.d.ts +172 -0
- package/dist/cjs/query/writes.js +105 -12
- package/dist/cjs/realtime.d.ts +70 -0
- package/dist/cjs/schema-builder.d.ts +354 -0
- package/dist/cjs/schema-metadata.d.ts +83 -0
- package/dist/cjs/schema-sql.d.ts +217 -0
- package/dist/cjs/schema-sql.js +23 -5
- package/dist/cjs/schema.d.ts +356 -0
- package/dist/cjs/schema.js +125 -0
- package/dist/cjs/seed.d.ts +15 -0
- package/dist/cjs/serverless.d.ts +142 -0
- package/dist/cjs/sqlite.d.ts +143 -0
- package/dist/cjs/sqlite.js +4 -0
- package/dist/cjs/typed-sql.d.ts +102 -0
- package/dist/cli/config.d.ts +18 -4
- package/dist/cli/config.js +31 -6
- package/dist/cli/index.d.ts +123 -0
- package/dist/cli/index.js +223 -58
- package/dist/cli/migrate.d.ts +59 -10
- package/dist/cli/migrate.js +128 -41
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +14 -5
- package/dist/cli/observe.d.ts +7 -1
- package/dist/cli/observe.js +48 -12
- package/dist/cli/prisma-report.d.ts +14 -0
- package/dist/cli/prisma-report.js +72 -0
- package/dist/cli/prisma-resolve.d.ts +6 -0
- package/dist/cli/prisma-resolve.js +1 -0
- package/dist/cli/prisma-schema.d.ts +62 -2
- package/dist/cli/prisma-schema.js +81 -4
- package/dist/cli/rate-limit.d.ts +32 -0
- package/dist/cli/rate-limit.js +40 -0
- package/dist/cli/studio.d.ts +5 -5
- package/dist/cli/studio.js +135 -70
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +51 -9
- package/dist/client.d.ts +40 -0
- package/dist/client.js +3 -0
- package/dist/dialect.d.ts +17 -1
- package/dist/dialect.js +37 -12
- package/dist/generate.js +40 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +6 -0
- package/dist/mysql.js +16 -0
- package/dist/nested-write.d.ts +2 -0
- package/dist/nested-write.js +415 -25
- package/dist/powdb.d.ts +4 -2
- package/dist/powdb.js +106 -21
- package/dist/powql.d.ts +5 -0
- package/dist/powql.js +42 -6
- package/dist/prisma-compat.d.ts +2 -0
- package/dist/prisma-compat.js +166 -8
- package/dist/query/aggregates.js +7 -3
- package/dist/query/builder.d.ts +292 -21
- package/dist/query/builder.js +570 -64
- package/dist/query/deferred.d.ts +39 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +173 -5
- package/dist/query/relations.js +688 -47
- package/dist/query/types.d.ts +123 -39
- package/dist/query/utils.d.ts +116 -0
- package/dist/query/utils.js +198 -0
- package/dist/query/warn-registry.d.ts +9 -0
- package/dist/query/warn-registry.js +9 -0
- package/dist/query/where.d.ts +38 -1
- package/dist/query/where.js +58 -23
- package/dist/query/writes.d.ts +42 -1
- package/dist/query/writes.js +104 -13
- package/dist/schema-sql.d.ts +14 -0
- package/dist/schema-sql.js +23 -5
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +123 -0
- package/dist/sqlite.js +4 -0
- package/package.json +77 -28
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PowqlInterface — Turbine's PowQL query generator (the PowDB analogue of
|
|
3
|
+
* {@link QueryInterface}). It exposes the same public method surface as the SQL
|
|
4
|
+
* `QueryInterface` (`findMany`, `create`, `update`, …) but emits **PowQL** — a
|
|
5
|
+
* pipeline language, not SQL — executed through {@link PowdbPool}.
|
|
6
|
+
*
|
|
7
|
+
* It is a *parallel* implementation rather than a `Dialect` of the SQL builder:
|
|
8
|
+
* PowQL's grammar (`T filter <e> order <k> { .col }`) shares no surface with
|
|
9
|
+
* `SELECT … FROM … WHERE`, so the SQL `Dialect` seam cannot express it. Keeping
|
|
10
|
+
* it separate also means the four SQL engines are untouched.
|
|
11
|
+
*
|
|
12
|
+
* Behavioural deltas from the SQL path, all driven by PowDB's wire reality (see
|
|
13
|
+
* `docs/internal/strategy/powdb-parity-matrix.md`, every row verified against a live
|
|
14
|
+
* server):
|
|
15
|
+
* - `create`/`createMany`/`update`/`delete` use PowDB 0.7.0's trailing
|
|
16
|
+
* `returning` keyword (`RETURNING *`, all columns) to surface affected rows
|
|
17
|
+
* in one round-trip. `upsert` is the lone exception — its statement does not
|
|
18
|
+
* accept `returning`, so it reselects the row by PK (a composite-PK upsert
|
|
19
|
+
* reselects-or-writes inside one flat transaction).
|
|
20
|
+
* - The PK is server-assigned when the column is `isGenerated` (PowDB's `auto`
|
|
21
|
+
* int — read back via `returning`); otherwise a defaulted **string** PK is
|
|
22
|
+
* generated client-side (UUID).
|
|
23
|
+
* - `with` (nested relations) uses **batched N+1 loaders** — D round-trips for
|
|
24
|
+
* depth D, not one query — including manyToMany (junction → targets).
|
|
25
|
+
* - **Relation filters** (`some`/`none`/`every`, all cardinalities incl. m2m)
|
|
26
|
+
* are resolved client-side to a literal `in (…)` list, never an IN-subquery:
|
|
27
|
+
* PowDB's executor caches a subquery's result by plan shape and would return
|
|
28
|
+
* a stale prior result for a later subquery of the same shape.
|
|
29
|
+
* - **Nested writes** (relation ops in `create`/`update` data) run through the
|
|
30
|
+
* shared nested-write engine as one flat top-level transaction (PowDB is
|
|
31
|
+
* single-writer, no savepoints).
|
|
32
|
+
* - pgvector / JSON / array filters and cursor streaming throw
|
|
33
|
+
* {@link UnsupportedFeatureError} (E017) — they have no PowDB equivalent.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import { type PowdbPool } from './powdb.js';
|
|
38
|
+
import type { MiddlewareFn, QueryInterfaceOptions } from './query/index.js';
|
|
39
|
+
import type { AggregateArgs, AggregateResult, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindUniqueArgs, GroupByArgs, UpdateArgs, UpdateManyArgs, UpsertArgs } from './query/types.js';
|
|
40
|
+
import { type SchemaMetadata } from './schema.js';
|
|
41
|
+
/**
|
|
42
|
+
* The PowQL query interface. Constructed by `turbinePowDB` via the
|
|
43
|
+
* `queryInterfaceFactory` seam and cast to `QueryInterface<object>` so
|
|
44
|
+
* `TurbineClient.table()` can return it transparently.
|
|
45
|
+
*/
|
|
46
|
+
export declare class PowqlInterface<T extends object = Record<string, unknown>> {
|
|
47
|
+
private readonly pool;
|
|
48
|
+
private readonly table;
|
|
49
|
+
private readonly schema;
|
|
50
|
+
private readonly middlewares;
|
|
51
|
+
private readonly options;
|
|
52
|
+
private readonly meta;
|
|
53
|
+
private readonly defaultLimit?;
|
|
54
|
+
private readonly warnOnUnlimited;
|
|
55
|
+
private readonly onQuery?;
|
|
56
|
+
private warnedUnlimited;
|
|
57
|
+
constructor(pool: PowdbPool, table: string, schema: SchemaMetadata, middlewares?: MiddlewareFn[], options?: QueryInterfaceOptions);
|
|
58
|
+
/** Resolve a camelCase field name (or raw snake) to its column metadata. */
|
|
59
|
+
private column;
|
|
60
|
+
/**
|
|
61
|
+
* PowQL column reference for a field. Unqualified it is a dotted field
|
|
62
|
+
* reference (`.snake_name`), which bypasses keyword lookup. When an `alias`
|
|
63
|
+
* is supplied (the F2 join path) it is qualified (`alias.snake_name`) and the
|
|
64
|
+
* column name is backtick-quoted if it is a reserved word (a qualified
|
|
65
|
+
* `p.order` does NOT bypass keyword lookup, unlike the dotted `.order`).
|
|
66
|
+
*/
|
|
67
|
+
private ref;
|
|
68
|
+
/** Render a raw column name as a PowQL reference, qualified with `alias` when given. */
|
|
69
|
+
private colRefName;
|
|
70
|
+
/**
|
|
71
|
+
* Push a value into the param array and return its `$N` placeholder. When the
|
|
72
|
+
* value targets a `float` column it is wrapped in {@link PowdbFloatParam} so
|
|
73
|
+
* the *embedded* literal encoder emits a float-form literal even for an
|
|
74
|
+
* integer value (PowQL's `42` is an int literal, `42.0` a float). The
|
|
75
|
+
* networked driver unwraps the marker back to the plain number in
|
|
76
|
+
* {@link toPowdbParam}, so the wire param is unchanged.
|
|
77
|
+
*/
|
|
78
|
+
private param;
|
|
79
|
+
/**
|
|
80
|
+
* Render a value for a write *assignment* (`col := …`). Every value — float
|
|
81
|
+
* columns included — is sent as a positional `$N` param. PowDB ≥ 0.7.0 fixed
|
|
82
|
+
* the int→float UPDATE coercion bug (`score := $n` with an integer param now
|
|
83
|
+
* reads back the integer value, not the raw i64 bits), so the float-literal
|
|
84
|
+
* inlining workaround Turbine carried for ≤ 0.6.2 is gone. Marks the column as
|
|
85
|
+
* float-typed for the *embedded* literal encoder (which materializes params
|
|
86
|
+
* into PowQL text) so an integer-valued float still encodes as a float literal
|
|
87
|
+
* and coercion stays unambiguous. Non-finite floats are still rejected.
|
|
88
|
+
*/
|
|
89
|
+
private writeRef;
|
|
90
|
+
private isFloatCol;
|
|
91
|
+
/**
|
|
92
|
+
* The bound pool's {@link PowdbCapabilities}. Falls back to the trusted-caller
|
|
93
|
+
* default (all feature gates on, `nativeRaw` off) when a directly-constructed
|
|
94
|
+
* pool did not carry them, matching {@link PowdbPool}'s own constructor
|
|
95
|
+
* default so a hand-built test pool never crashes the version gates.
|
|
96
|
+
*/
|
|
97
|
+
private get capabilities();
|
|
98
|
+
/** A predicate that is always false — the empty-`in` / contradiction sentinel. */
|
|
99
|
+
private alwaysFalse;
|
|
100
|
+
/**
|
|
101
|
+
* Compile a {@link WhereClause} into a PowQL filter expression, pushing every
|
|
102
|
+
* value as a positional `$N` param. Returns `''` when there are no conditions.
|
|
103
|
+
*
|
|
104
|
+
* When `alias` is supplied (the F2 native-join path) every field reference is
|
|
105
|
+
* qualified with it (`.col` → `alias.col`, JSON path bases too); params bind
|
|
106
|
+
* exactly as in the unqualified path. The caller only ever passes an alias for
|
|
107
|
+
* an already-RESOLVED where (relation filters pre-resolved to literal in-lists
|
|
108
|
+
* by {@link resolveRelationFilters}): the relation-key branch below still
|
|
109
|
+
* throws, so an unresolved relation filter can never leak into a join.
|
|
110
|
+
*/
|
|
111
|
+
private buildWhere;
|
|
112
|
+
/** Build a single `field: value | operator` condition. */
|
|
113
|
+
private buildFieldCondition;
|
|
114
|
+
/**
|
|
115
|
+
* PowQL JSON path expression `.col->$a->$b…`, binding EVERY path segment as a
|
|
116
|
+
* positional param (a string segment as a `str` token, an integer index as an
|
|
117
|
+
* `int` token). `->` binds tighter than every operator, so no parens are
|
|
118
|
+
* needed around the path in a comparison. Segments are bound (never inlined)
|
|
119
|
+
* to keep {@link materializePowql}'s `$N`-scan invariant intact: a segment
|
|
120
|
+
* that literally contained `$1` would otherwise be rewritten. Shared by the
|
|
121
|
+
* F1 where-filter path and the F2 orderBy / groupBy path emitters.
|
|
122
|
+
*
|
|
123
|
+
* A digit-only STRING segment (`'0'`) binds as an `int` array index, matching
|
|
124
|
+
* the SQL engines: `JsonFilter.path` is typed `string[]`, so an array index
|
|
125
|
+
* can only be expressed as a digit string, and the SQL builder converts it the
|
|
126
|
+
* same way (`/^\d+$/ → [n]`, query/builder.ts). Without this, PowDB's typed
|
|
127
|
+
* `->` treats `'0'` as a string KEY and silently matches nothing on an array
|
|
128
|
+
* (a wrong result, not an error). Same object-key-`'0'` caveat SQL accepts: a
|
|
129
|
+
* json object whose key is literally `"0"` is addressed as an array index.
|
|
130
|
+
*/
|
|
131
|
+
private jsonPathExpr;
|
|
132
|
+
/**
|
|
133
|
+
* Compile a {@link JsonFilter} on a json document column into a PowQL filter
|
|
134
|
+
* (≥ 0.12). Operators PowQL cannot express EXACTLY throw a per-operator E017
|
|
135
|
+
* (never a wrong result): containment (`contains`, and `equals` without a
|
|
136
|
+
* `path`) has no PowQL operator. The mapped shapes:
|
|
137
|
+
* - `{ path, equals: v }` → `P = $n` (typed: string→str, bool→bool,
|
|
138
|
+
* integral number→int, fractional→float; NOT stringified)
|
|
139
|
+
* - `{ path, equals: null }` → `P is null` (matches JSON null AND a missing
|
|
140
|
+
* key, a deliberate divergence from the PG driver, documented on
|
|
141
|
+
* {@link JsonFilter})
|
|
142
|
+
* - `{ path, gt|gte|lt|lte: v }` → `P > $n` … (range ops require `path`; the
|
|
143
|
+
* engine coerces int/float numerically)
|
|
144
|
+
* - `{ hasKey: k }` → `json_type(.col->$n) is not null` (top-level key test,
|
|
145
|
+
* ignoring `path`, mirroring PG `col ? key`; includes keys holding JSON
|
|
146
|
+
* null)
|
|
147
|
+
* A bare `{ path }` with no operators compiles to zero clauses (byte-parity
|
|
148
|
+
* with SQL), so a mutation whose only `where` is a bare `{ path }` is refused
|
|
149
|
+
* by the empty-where guard.
|
|
150
|
+
*/
|
|
151
|
+
private buildJsonPathCondition;
|
|
152
|
+
/** Bind a value, lowercasing for case-insensitive comparisons. */
|
|
153
|
+
private bind;
|
|
154
|
+
/** Bind a LIKE pattern (already escaped), lowercasing for insensitive mode. */
|
|
155
|
+
private bindLike;
|
|
156
|
+
/** `lhs [not] in ($1, $2, …)` — empty list collapses to a constant. */
|
|
157
|
+
private buildInList;
|
|
158
|
+
/**
|
|
159
|
+
* Pre-resolve every relation filter (`some`/`none`/`every`) in a where clause
|
|
160
|
+
* into a plain scalar `in`/`notIn` condition on the **local key**, by running
|
|
161
|
+
* the inner predicate as its own query and materializing the matching keys as
|
|
162
|
+
* a literal list. The compiled where is then relation-free and `buildWhere`
|
|
163
|
+
* emits only `in (<literal list>)`.
|
|
164
|
+
*
|
|
165
|
+
* Why not an IN-subquery (`.k in (Target filter <e> { .fk })`)? PowDB's
|
|
166
|
+
* executor caches a subquery's result by **plan shape, ignoring the literal**,
|
|
167
|
+
* so a second subquery of the same shape with a different value returns the
|
|
168
|
+
* first one's stale rows (reproduced live on the embedded engine; the
|
|
169
|
+
* single-statement literal `in (list)` form is always correct). Resolving
|
|
170
|
+
* client-side trades extra round-trips for correctness, and recurses — nested
|
|
171
|
+
* relation filters in the inner predicate resolve when the target query runs.
|
|
172
|
+
*/
|
|
173
|
+
private resolveRelationFilters;
|
|
174
|
+
/** Resolve one hasMany/hasOne/belongsTo filter to `{ localField: { in|notIn: [...] } }`. */
|
|
175
|
+
private resolveRelationCondition;
|
|
176
|
+
/** Resolve a manyToMany filter through the junction to `{ sourceRefField: { in|notIn: [...] } }`. */
|
|
177
|
+
private resolveManyToManyCondition;
|
|
178
|
+
/**
|
|
179
|
+
* Resolve the set of columns to project, honouring `select` / `omit` and the
|
|
180
|
+
* query-level `includePii` opt-in. PII-tagged (`defineSchema` `pii: true`)
|
|
181
|
+
* columns are EXCLUDED from a default (or omit-only) projection unless
|
|
182
|
+
* `includePii` is true; an explicit `select` naming a PII column IS the opt-in
|
|
183
|
+
* and returns it regardless. Untagged tables project exactly as before.
|
|
184
|
+
*/
|
|
185
|
+
private projectedColumns;
|
|
186
|
+
/**
|
|
187
|
+
* The snake_case names of this table's PII-tagged columns. Empty for a table
|
|
188
|
+
* with no `pii: true` column, so untagged tables keep their prior projection.
|
|
189
|
+
*/
|
|
190
|
+
private piiColumnNames;
|
|
191
|
+
/**
|
|
192
|
+
* The camelCase field names of this table's PII-tagged columns: the read
|
|
193
|
+
* policy applied to a write's returned row (create/update/upsert/delete accept
|
|
194
|
+
* no `includePii`/`select`, so their result always drops PII; you may still
|
|
195
|
+
* write PII fields freely).
|
|
196
|
+
*
|
|
197
|
+
* SPEC LIMITATION (PowQL): the driver contract
|
|
198
|
+
* (`docs/integrations/powql-for-drivers.md`) exposes `returning` only as a
|
|
199
|
+
* bare keyword that hands back every column; it accepts NO column list, so
|
|
200
|
+
* (unlike the SQL engines, which emit an explicit non-PII `RETURNING`/`OUTPUT`
|
|
201
|
+
* projection) the create/update/delete `returning` paths cannot exclude PII at
|
|
202
|
+
* the query-language level and must strip it here after the fact. This is the
|
|
203
|
+
* client-side strip of last resort, not defense-in-depth, for those paths; we
|
|
204
|
+
* do NOT reverse-engineer an undocumented projection form. The upsert path is
|
|
205
|
+
* different: it has no `returning` and reselects by PK through the read
|
|
206
|
+
* projection ({@link projectedColumns}), which already omits PII, so PII never
|
|
207
|
+
* crosses the wire there. If a future spec revision lets `returning` take a
|
|
208
|
+
* projection, switch the write paths to emit the non-PII list and this strip
|
|
209
|
+
* becomes a no-op like {@link parseWriteRow} on the SQL engines.
|
|
210
|
+
*/
|
|
211
|
+
private stripWritePii;
|
|
212
|
+
/** `{ .c1, .c2, … }` projection clause. */
|
|
213
|
+
private projection;
|
|
214
|
+
/**
|
|
215
|
+
* `order .c1 asc, .c2 desc` clause (empty string when no orderBy). Supports,
|
|
216
|
+
* besides a plain direction:
|
|
217
|
+
* - {@link JsonPathOrderBy} on a json column (≥ 0.12): `{ data: { path: […],
|
|
218
|
+
* type?, direction? } }` → `order .data->$n asc` (or
|
|
219
|
+
* `cast(.data->$n, "float")` for `type: 'numeric'`);
|
|
220
|
+
* - {@link OrderBySpec} `{ sort, nulls }`: `nulls: 'last'` is accepted as a
|
|
221
|
+
* no-op (PowDB is always nulls-last), `nulls: 'first'` throws E017.
|
|
222
|
+
*
|
|
223
|
+
* PowDB orders missing / JSON-null keys LAST in BOTH directions (an engine
|
|
224
|
+
* contract): for identical cross-engine results pass `nulls: 'last'`
|
|
225
|
+
* explicitly on Postgres, which defaults nulls-first for `desc`.
|
|
226
|
+
*/
|
|
227
|
+
private buildOrder;
|
|
228
|
+
/** Compile one {@link JsonPathOrderBy} entry to `order .col->$n asc` (+ optional numeric cast). */
|
|
229
|
+
private buildJsonPathOrder;
|
|
230
|
+
/**
|
|
231
|
+
* Run PowQL with optional timeout, emitting a query event either way. The
|
|
232
|
+
* `action` is passed PER CALL (never read from shared instance state) so the
|
|
233
|
+
* retry-eligibility and the emitted event action stay correct even when a
|
|
234
|
+
* concurrent operation runs on the same cached interface: a WRITE statement
|
|
235
|
+
* carries a write action and can therefore never be mistaken for a replayable
|
|
236
|
+
* read. Read statements pass a read-shaped action from {@link POWQL_READ_ACTIONS}.
|
|
237
|
+
*/
|
|
238
|
+
private exec;
|
|
239
|
+
/** Build the E018 refusal for a write / `begin` on a read-only pool. */
|
|
240
|
+
private readOnlyError;
|
|
241
|
+
/**
|
|
242
|
+
* Execute one statement, with the opt-in single stale-frame READ replay. When
|
|
243
|
+
* `retryStaleReads` is on and a first-statement READ fails with the stale-wire
|
|
244
|
+
* {@link isStaleFramePowdbError} ConnectionError (a socket idle-gap "received
|
|
245
|
+
* unexpected frame" that the client cannot recover), the statement is retried
|
|
246
|
+
* exactly once on a fresh pooled connection (the broken one was destroyed).
|
|
247
|
+
* The replay is refused for writes (an ambiguous mutation reply is unsafe to
|
|
248
|
+
* replay) and inside a transaction (a mid-tx statement cannot move connection),
|
|
249
|
+
* so only the read-shaped actions in {@link POWQL_READ_ACTIONS}, outside a
|
|
250
|
+
* `_txScoped` interface, are eligible. `action` is a per-call argument (never
|
|
251
|
+
* `this`-state), so a concurrent op flipping instance fields cannot turn a
|
|
252
|
+
* write into a retryable read.
|
|
253
|
+
*/
|
|
254
|
+
private execOnce;
|
|
255
|
+
/** Is `err` a replayable stale-frame failure for THIS (per-call) read-shaped, non-tx action? */
|
|
256
|
+
private shouldRetryStaleRead;
|
|
257
|
+
private emit;
|
|
258
|
+
/** Run a method body through the middleware chain (mirrors QueryInterface). */
|
|
259
|
+
private withMiddleware;
|
|
260
|
+
/** Map raw rows to typed entities. `native` is the wire that ACTUALLY served
|
|
261
|
+
* this result (threaded from {@link execOnce}, not the pool-level capability),
|
|
262
|
+
* so cells that arrived pre-typed over `queryNativeRaw` (F3) skip the legacy
|
|
263
|
+
* string coercion (a genuine str `"null"` stays `"null"` instead of collapsing
|
|
264
|
+
* to null) while a per-call legacy fallback on a native-capable pool still
|
|
265
|
+
* coerces its string cells correctly. Defaults to the pool capability for the
|
|
266
|
+
* rare caller with no per-result flag (hand-built test pools). */
|
|
267
|
+
private shape;
|
|
268
|
+
findMany(args?: FindManyArgs<T>): Promise<T[]>;
|
|
269
|
+
/**
|
|
270
|
+
* Compile the findMany select into PowQL (no execution), pushing values into
|
|
271
|
+
* `params`. Returns the query plus the RESOLVED where (relation filters
|
|
272
|
+
* already collapsed to literal in-lists) so the F2 join path can re-emit the
|
|
273
|
+
* exact parent predicate alias-qualified, and so {@link explain} can wrap it.
|
|
274
|
+
*
|
|
275
|
+
* When the engine supports nested projections (>= 0.18) and the strategy
|
|
276
|
+
* does not opt out, eligible `with` relations compile INTO this statement as
|
|
277
|
+
* nested-projection blocks (`nestedPlans`) — one round-trip for the whole
|
|
278
|
+
* shape — and only the ineligible remainder (`residualWith`) goes to the
|
|
279
|
+
* post-execution loaders. Without nesting the emitted PowQL is byte-identical
|
|
280
|
+
* to the pre-0.18 output (no alias, `.col` refs).
|
|
281
|
+
*/
|
|
282
|
+
private buildFind;
|
|
283
|
+
/** Build + run the findMany select; returns raw rows, the serving wire, the resolved where, and the `with` partition. */
|
|
284
|
+
private runFind;
|
|
285
|
+
/**
|
|
286
|
+
* Diagnostic surface: compile the same PowQL {@link findMany} would run for
|
|
287
|
+
* `args` (no cache) and return the engine's plan as one string per line.
|
|
288
|
+
*
|
|
289
|
+
* Runs as a READ (`explain <query>`), so it is safe on a read-only pool and
|
|
290
|
+
* eligible for the stale-read replay. The line content is engine-owned and is
|
|
291
|
+
* NOT covered by semver (match plan node names / tree shape, never exact
|
|
292
|
+
* bytes; mirrors PowDB's own `explain` contract).
|
|
293
|
+
*
|
|
294
|
+
* Does NOT run through the middleware chain: plan text is a diagnostic, not
|
|
295
|
+
* entity rows, and `QueryInterface.explain` deliberately bypasses middleware
|
|
296
|
+
* too, so both engines agree.
|
|
297
|
+
*/
|
|
298
|
+
explain(args?: FindManyArgs<T>): Promise<string[]>;
|
|
299
|
+
findUnique(args: FindUniqueArgs<T>): Promise<T | null>;
|
|
300
|
+
findFirst(args?: FindManyArgs<T>): Promise<T | null>;
|
|
301
|
+
findUniqueOrThrow(args: FindUniqueArgs<T>): Promise<T>;
|
|
302
|
+
findFirstOrThrow(args?: FindManyArgs<T>): Promise<T>;
|
|
303
|
+
/**
|
|
304
|
+
* Load each requested relation for `parents` and attach it onto each row.
|
|
305
|
+
*
|
|
306
|
+
* `parent` is supplied ONLY by the top-level {@link findMany} (its args +
|
|
307
|
+
* resolved where). When the effective `relationLoadStrategy` resolves to an
|
|
308
|
+
* explicit `'join'` and the pool advertises `serverJoins`, an eligible
|
|
309
|
+
* top-level relation is loaded with a native PowQL join instead of the keyed
|
|
310
|
+
* loaders (F2); everything else (nested `with` levels, ineligible shapes, and
|
|
311
|
+
* the default `'batched'` strategy) keeps the loaders. Output is byte-equal
|
|
312
|
+
* either way (the join reuses the same stitch / shape helpers).
|
|
313
|
+
*/
|
|
314
|
+
private loadRelations;
|
|
315
|
+
/**
|
|
316
|
+
* manyToMany nested read — a three-hop batched loader (no `json_agg`/join
|
|
317
|
+
* pushdown): (1) read the junction rows for all parents in `sourceKey in (…)`
|
|
318
|
+
* chunks, (2) read the target rows for the collected `targetKey`s, (3) stitch
|
|
319
|
+
* each parent → its junction rows → its targets in memory. Mirrors the
|
|
320
|
+
* single-key N+1 loaders; the junction's source/target columns must be single
|
|
321
|
+
* (composite junction keys would need PowQL tuple-`in`, which it lacks).
|
|
322
|
+
*/
|
|
323
|
+
private loadManyToMany;
|
|
324
|
+
/**
|
|
325
|
+
* Resolve the effective relation-load strategy: the per-query arg wins, then
|
|
326
|
+
* the client config, then the PowDB default of `'batched'` (the keyed
|
|
327
|
+
* loaders). PowDB deliberately does NOT inherit the SQL-side implicit `'join'`
|
|
328
|
+
* default (that would silently flip every existing PowDB user onto brand-new
|
|
329
|
+
* join generation). Only a value the user actually set to `'join'` activates it.
|
|
330
|
+
*/
|
|
331
|
+
private resolveStrategy;
|
|
332
|
+
/**
|
|
333
|
+
* Per-relation eligibility for the join path (checked before the serverJoins
|
|
334
|
+
* capability). Any `false` here is a SILENT fallback to the keyed loaders (it
|
|
335
|
+
* is never an error), so an off-page or nested-`with` shape still returns
|
|
336
|
+
* correct rows:
|
|
337
|
+
* - the parent query must not be paged (`limit`/`offset`/`take`, including the
|
|
338
|
+
* configured `defaultLimit`): a parent-filter join under a page would scan
|
|
339
|
+
* children of off-page parents, where the loaders are strictly better;
|
|
340
|
+
* - the relation must not request a nested `with` (its subtree stays on the
|
|
341
|
+
* loaders this round) or a `distinct`;
|
|
342
|
+
* - single-column relation keys only (a composite key falls to the loader,
|
|
343
|
+
* which throws the same E017 as today);
|
|
344
|
+
* - the PARENT-SIDE correlation column must be a single-column PK or unique
|
|
345
|
+
* column, or the INNER join would re-emit one child copy per matching
|
|
346
|
+
* parent row (a non-unique correlation key produces duplicate children the
|
|
347
|
+
* loader never would). For hasMany/hasOne/m2m that column is the relation's
|
|
348
|
+
* `referenceKey` on THIS (fetched) table; for belongsTo it is the
|
|
349
|
+
* `referenceKey` on the TARGET table (the join's non-fetched side);
|
|
350
|
+
* - m2m keeps any `orderBy`/`limit`/`offset` on the loader (the junction-order
|
|
351
|
+
* stitch can't be reproduced by the 3-table join deterministically);
|
|
352
|
+
* - a to-one relation `limit`/`offset` (meaningless) stays on the loader, as
|
|
353
|
+
* does a to-many relation `limit`/`offset` when the parent set spills past
|
|
354
|
+
* one loader chunk (the loader limits per chunk, the join once globally).
|
|
355
|
+
*/
|
|
356
|
+
private joinEligible;
|
|
357
|
+
/**
|
|
358
|
+
* True when `col` is a single-column unique key of `tableMeta`: the sole
|
|
359
|
+
* primary-key column, a single-column entry in `uniqueColumns` (where a
|
|
360
|
+
* per-column `unique: true` and an introspected single-column unique constraint
|
|
361
|
+
* both land), or a single-column unique index. Used by {@link joinEligible} to
|
|
362
|
+
* keep the INNER-join path off relations whose parent-side correlation column
|
|
363
|
+
* can repeat (which would duplicate children).
|
|
364
|
+
*/
|
|
365
|
+
private isSingleColumnUnique;
|
|
366
|
+
/** Dispatch one eligible relation to the correct native-join loader. */
|
|
367
|
+
private loadRelationViaJoin;
|
|
368
|
+
/**
|
|
369
|
+
* manyToMany via chained joins: the target (alias `t`) → junction (alias `j`)
|
|
370
|
+
* → the already-fetched side (alias `p`), correlating `__tpk` from the
|
|
371
|
+
* junction's source key. Always a list, stitched exactly like the loader.
|
|
372
|
+
*/
|
|
373
|
+
private loadManyToManyViaJoin;
|
|
374
|
+
/**
|
|
375
|
+
* The target column list to project through the join (honouring select/omit),
|
|
376
|
+
* with a loud guard: a real column named `__tpk` would collide with the
|
|
377
|
+
* reserved correlation alias, so refuse rather than silently mis-stitch.
|
|
378
|
+
*/
|
|
379
|
+
private joinChildCols;
|
|
380
|
+
/**
|
|
381
|
+
* `{ __tpk: <tpkExpr>, <col>: <childAlias>.<col>, … }`. Each child column is
|
|
382
|
+
* ALIASED to its bare name (a bare qualified ref `c.col` would come back named
|
|
383
|
+
* `c.col`, not `col`) so the stitched rows shape identically to a flat select.
|
|
384
|
+
*/
|
|
385
|
+
private joinProjection;
|
|
386
|
+
/**
|
|
387
|
+
* `filter <parentWhere qualified p> [and <relationWhere qualified childAlias>]`.
|
|
388
|
+
* The parent where is the ALREADY-RESOLVED predicate (relation filters collapsed
|
|
389
|
+
* to literal in-lists before the base query ran); the relation where is resolved
|
|
390
|
+
* on the target the same way before qualifying, so a nested relation filter in
|
|
391
|
+
* the relation `where` never reaches the join unresolved. Params bind in order.
|
|
392
|
+
*/
|
|
393
|
+
private joinFilter;
|
|
394
|
+
/** Group join rows by their (normalized) `__tpk`, stripping it and shaping each child. */
|
|
395
|
+
private bucketByTpk;
|
|
396
|
+
/**
|
|
397
|
+
* Normalize a correlation key to a stable string map key so a parent's key
|
|
398
|
+
* value (a shaped entity field) and a child row's `__tpk` cell match across
|
|
399
|
+
* wires and column types. A `Date` maps to microseconds
|
|
400
|
+
* (`getTime()` ms times 1000), because a datetime correlation cell arrives as
|
|
401
|
+
* raw micros (bigint on the native wire, a micros string on the legacy wire),
|
|
402
|
+
* never as ms. bigint / number / string all stringify to the same digits, so
|
|
403
|
+
* an int key matches whether it came back typed or as text.
|
|
404
|
+
*/
|
|
405
|
+
private joinKey;
|
|
406
|
+
/**
|
|
407
|
+
* Should this query's `with` compile to nested-projection blocks? Requires
|
|
408
|
+
* the engine capability (>= 0.18), and an EXPLICIT `relationLoadStrategy:
|
|
409
|
+
* 'batched'` (per-query or client-level) opts back out to the keyed loaders.
|
|
410
|
+
* The default and `'join'` both prefer nesting: unlike the F2 INNER join it
|
|
411
|
+
* has no fan-out, keeps childless parents, works under parent paging, and
|
|
412
|
+
* applies per-parent `order`/`limit`/`offset` natively, so it is the
|
|
413
|
+
* strictly-better single-statement path wherever it is eligible. Ineligible
|
|
414
|
+
* relations fall through to the existing strategy resolution untouched.
|
|
415
|
+
*/
|
|
416
|
+
private nestedProjectionsPreferred;
|
|
417
|
+
/**
|
|
418
|
+
* Plan one `with` relation as a nested-projection block, or return `null`
|
|
419
|
+
* when the shape must stay on the loaders (ALWAYS a silent fallback with
|
|
420
|
+
* identical output, never an error):
|
|
421
|
+
* - m2m (the block takes exactly one child table; the junction-order
|
|
422
|
+
* stitch has no nested equivalent), and composite relation keys;
|
|
423
|
+
* - a to-one relation carrying `limit`/`offset` (the loaders' semantics);
|
|
424
|
+
* - `distinct` inside the relation options (no nested grammar for it);
|
|
425
|
+
* - a projected child column whose tsType is `bigint` or `Uint8Array`
|
|
426
|
+
* (values ride a JSON array, which cannot carry them losslessly);
|
|
427
|
+
* - a projection key collision (a child column named like a sub-relation);
|
|
428
|
+
* - depth >= 10 (the loader path enforces the same cap by throwing, so the
|
|
429
|
+
* fallback surfaces the identical E003 today's users get);
|
|
430
|
+
* - any ineligible descendant (the WHOLE relation falls back, so one
|
|
431
|
+
* statement never mixes nested and loader semantics mid-subtree).
|
|
432
|
+
*/
|
|
433
|
+
private planNestedRelation;
|
|
434
|
+
/**
|
|
435
|
+
* Compile one {@link NestedRelationPlan} into its projection-field block:
|
|
436
|
+
* `name: Target as tN filter <correlation> [and (<child where>)] [order …]
|
|
437
|
+
* [limit …] [offset …] { col: tN.col, …, <sub-blocks> }`. Params bind in
|
|
438
|
+
* emission order (the projection is the statement's final clause, so nested
|
|
439
|
+
* params always follow the parent's filter/order/limit/offset params).
|
|
440
|
+
* Aliases share the parent statement's counter, so arbitrarily deep and
|
|
441
|
+
* self-referential trees stay collision-free (the same discipline as the SQL
|
|
442
|
+
* engine's json_agg subqueries).
|
|
443
|
+
*/
|
|
444
|
+
private buildNestedBlock;
|
|
445
|
+
/**
|
|
446
|
+
* Shape the nested JSON children back into typed entities on every parent
|
|
447
|
+
* row. The nested field arrives as a decoded JSON array on the native wire
|
|
448
|
+
* (or JSON text on the legacy wire — parsed here); its values are real JSON
|
|
449
|
+
* types, so each child object goes through the NATIVE coercion policy
|
|
450
|
+
* (`rowToEntity(…, true)`: a date column's micros number becomes a `Date`, a
|
|
451
|
+
* json column's document passes through, a str `"null"` stays a string).
|
|
452
|
+
* to-one relations unwrap to `[0] ?? null`, matching the loaders exactly.
|
|
453
|
+
*/
|
|
454
|
+
private attachNestedRows;
|
|
455
|
+
private attachOneNested;
|
|
456
|
+
/**
|
|
457
|
+
* The `schema links` snapshot for this pool, fetched at most once and cached on
|
|
458
|
+
* the pool identity. A fetch failure resolves to `[]` (a missing listing is a
|
|
459
|
+
* silent fallback to loaders, never an error). Only called when the `linkPaths`
|
|
460
|
+
* capability is on, so the listing statement is guaranteed to exist.
|
|
461
|
+
*/
|
|
462
|
+
private linksSnapshot;
|
|
463
|
+
private fetchLinksSnapshot;
|
|
464
|
+
/** True when `name` is a bare PowQL identifier (quoting leaves it unchanged). */
|
|
465
|
+
private isBareIdent;
|
|
466
|
+
/**
|
|
467
|
+
* Find the declared to-one link on THIS table that matches `rel` exactly: same
|
|
468
|
+
* target, same correlation columns (owner localKey = the FK on this table, target
|
|
469
|
+
* targetKey = the referenced key on target), cardinality `"to-one"`. Returns the
|
|
470
|
+
* declared link (whose NAME drives the path spelling, which may differ from the
|
|
471
|
+
* relation's own name) or `null` for no verifiable match (→ silent loader
|
|
472
|
+
* fallback, never an error).
|
|
473
|
+
*/
|
|
474
|
+
private findMatchingLink;
|
|
475
|
+
/**
|
|
476
|
+
* Plan one belongsTo `with` as a scalar link-path relation, or `null` when it
|
|
477
|
+
* must stay on the loaders (ALWAYS a silent fallback with identical output).
|
|
478
|
+
*
|
|
479
|
+
* SCOPED TIGHT: this fires ONLY for a to-one relation whose child projection
|
|
480
|
+
* includes a bigint/bytes column — exactly the case a JSON nested block cannot
|
|
481
|
+
* carry, so nested projections have already fallen back to a per-relation loader
|
|
482
|
+
* (`planNestedRelation` returned `null` for the same shape). Cases nested
|
|
483
|
+
* projections DO serve keep nested projections: link-bearing statements are
|
|
484
|
+
* NEVER plan-cached upstream, so replacing a cacheable nested projection with a
|
|
485
|
+
* link path would regress a hot path for no gain. Requires: single-column
|
|
486
|
+
* belongsTo; no relation `with` / `where` / `distinct` / `orderBy` /
|
|
487
|
+
* `limit` / `offset` (a scalar path has no per-hop filter/order and cannot
|
|
488
|
+
* reproduce those — such inputs stay on the loader for exact parity); a link
|
|
489
|
+
* name and all projected columns that are bare identifiers (a quoted segment in
|
|
490
|
+
* a dotted link path is outside the verified spelling — fall back); and a
|
|
491
|
+
* DECLARED link that verifiably matches (`findMatchingLink`).
|
|
492
|
+
*/
|
|
493
|
+
private planLinkPathRelation;
|
|
494
|
+
/** The flat `l<i>_<col>: t0.<linkName>.<col>` projection fields for one link plan. */
|
|
495
|
+
private linkPathFields;
|
|
496
|
+
/**
|
|
497
|
+
* Reconstruct each link-path relation's child entity from its flat hop fields
|
|
498
|
+
* and attach it under the relation name — output indistinguishable from the
|
|
499
|
+
* loader (same keys, same coercions). Presence: the target PK cell arriving
|
|
500
|
+
* Empty (a null/dangling FK at the hop) means no linked row → `null`, matching
|
|
501
|
+
* the loader's `matches[0] ?? null`. Otherwise the gathered snake cells go
|
|
502
|
+
* through the SAME `rowToEntity` policy the loader uses (native micros → Date,
|
|
503
|
+
* bigint per the int8 policy), and the PK is stripped back off if the user did
|
|
504
|
+
* not project it. `native` is the wire that actually served the parent row.
|
|
505
|
+
*/
|
|
506
|
+
private attachLinkRows;
|
|
507
|
+
/** Split `data` into scalar assignments; reject relation (nested-write) keys. */
|
|
508
|
+
private scalarData;
|
|
509
|
+
/**
|
|
510
|
+
* Fill in a client-generated UUID for a defaulted **string** PK that wasn't
|
|
511
|
+
* supplied. A server-generated PK ({@link ColumnMetadata.isGenerated}, e.g. an
|
|
512
|
+
* `int` column with PowDB's `auto` modifier) is left untouched — PowDB assigns
|
|
513
|
+
* it and the trailing `returning` reads it back — as is any non-string PK.
|
|
514
|
+
*/
|
|
515
|
+
private applyPkDefault;
|
|
516
|
+
/**
|
|
517
|
+
* The table name as a PowQL type reference — backtick-quoted when it is a
|
|
518
|
+
* reserved word (e.g. a table named `order`). Used in every emitted
|
|
519
|
+
* statement; plain `this.table` stays in error messages.
|
|
520
|
+
*/
|
|
521
|
+
private get qt();
|
|
522
|
+
create(args: CreateArgs<T>): Promise<T>;
|
|
523
|
+
createMany(args: CreateManyArgs<T>): Promise<T[]>;
|
|
524
|
+
update(args: UpdateArgs<T>): Promise<T>;
|
|
525
|
+
updateMany(args: UpdateManyArgs<T>): Promise<{
|
|
526
|
+
count: number;
|
|
527
|
+
}>;
|
|
528
|
+
/** Compile `data` into PowQL update assignments, including atomic operators. */
|
|
529
|
+
private buildUpdateAssignments;
|
|
530
|
+
private isTxScoped;
|
|
531
|
+
private nestedCreate;
|
|
532
|
+
private nestedUpdate;
|
|
533
|
+
/** Open a flat PowDB transaction on a pinned connection and run `fn` inside it. */
|
|
534
|
+
private runInImplicitTx;
|
|
535
|
+
/** Already inside a transaction: build a context whose table accessors reuse the pinned pool. */
|
|
536
|
+
private buildNestedCtx;
|
|
537
|
+
delete(args: DeleteArgs<T>): Promise<T>;
|
|
538
|
+
deleteMany(args: DeleteManyArgs<T>): Promise<{
|
|
539
|
+
count: number;
|
|
540
|
+
}>;
|
|
541
|
+
upsert(args: UpsertArgs<T>): Promise<T>;
|
|
542
|
+
/**
|
|
543
|
+
* Composite-key upsert: PowQL's `upsert … on .col` only takes one conflict
|
|
544
|
+
* column, so reselect by the full composite PK and update-or-create inside one
|
|
545
|
+
* flat transaction (PowDB single-writer makes the read-then-write safe from
|
|
546
|
+
* concurrent writers; the transaction makes it atomic with the write).
|
|
547
|
+
*/
|
|
548
|
+
private upsertComposite;
|
|
549
|
+
count(args?: CountArgs<T>): Promise<number>;
|
|
550
|
+
aggregate(args: AggregateArgs<T>): Promise<AggregateResult<T>>;
|
|
551
|
+
groupBy(args: GroupByArgs<T>): Promise<Record<string, unknown>[]>;
|
|
552
|
+
/** Validate a JSON-path target (group key / aggregate target): non-empty array of keys/indexes. */
|
|
553
|
+
private assertJsonPath;
|
|
554
|
+
/**
|
|
555
|
+
* `having <expr>` over group aggregates. `_count` compares `count(*)` (parity
|
|
556
|
+
* with the projection); a per-field aggregate re-emits its inner expression
|
|
557
|
+
* (from `aggInner` when the field is a requested aggregate, so a JSON-path
|
|
558
|
+
* aggregate reuses its bound placeholders, else `.field` for a plain column).
|
|
559
|
+
*
|
|
560
|
+
* Every token that reaches the PowQL text is builder-controlled: the field is
|
|
561
|
+
* validated by `ref()`, the aggregate function comes from
|
|
562
|
+
* {@link POWQL_HAVING_AGG_FNS} (unknown keys throw E003), the operator from a
|
|
563
|
+
* fixed map, and every compared value is bound as a `$N` param.
|
|
564
|
+
*/
|
|
565
|
+
private buildHaving;
|
|
566
|
+
/**
|
|
567
|
+
* Compile a groupBy `orderBy` into a PowQL `order` body over the group RESULT
|
|
568
|
+
* columns (by-fields, JSON group-key aliases, and requested aggregates). PowQL
|
|
569
|
+
* cannot re-emit an aggregate EXPRESSION in `order` (engine error), but CAN
|
|
570
|
+
* order by a projection alias on a grouped query (probed), so each key maps to
|
|
571
|
+
* its projected alias (`.agg_N` / `.gk_N` / `.col`). Semantics and error
|
|
572
|
+
* surface mirror the SQL `buildGroupByOrderBy` (0.32.2 R3-1): an aggregate not
|
|
573
|
+
* requested in this call, or an unknown by-key, throws E003 listing the valid
|
|
574
|
+
* keys. `nulls: 'first'` stays E017 (PowDB has no NULLS placement grammar).
|
|
575
|
+
*/
|
|
576
|
+
private buildGroupOrder;
|
|
577
|
+
/** Resolve a groupBy order direction, refusing `nulls: 'first'` (E017); `nulls: 'last'` is a no-op. */
|
|
578
|
+
private groupOrderDir;
|
|
579
|
+
findManyStream(): AsyncGenerator<T>;
|
|
580
|
+
/** Reselect a single row by its single-column primary key value. */
|
|
581
|
+
private reselectByPk;
|
|
582
|
+
/**
|
|
583
|
+
* Empty-where guard — blocks accidental whole-table writes. Mirrors the SQL
|
|
584
|
+
* path's `assertMutationHasPredicate` (query/builder.ts): it gates on the
|
|
585
|
+
* *compiled* PowQL filter fragment, NOT the shape of the `where` object. A
|
|
586
|
+
* `where` whose conditions all evaporate during compilation — `{}`,
|
|
587
|
+
* `{ id: undefined }`, `{ OR: [] }`, `{ AND: [] }`, `{ NOT: {} }`,
|
|
588
|
+
* `{ OR: [{ f: undefined }] }` — compiles to the empty string and is refused,
|
|
589
|
+
* because emitting a filter-less write would hit every row.
|
|
590
|
+
*/
|
|
591
|
+
private assertCompiledWhere;
|
|
592
|
+
}
|