turbine-orm 0.77.1 → 0.78.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 +13 -9
- package/dist/cjs/cli/config.d.ts +7 -1
- package/dist/cjs/cli/config.js +11 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +307 -9
- package/dist/cjs/cli/index.js +252 -42
- package/dist/cjs/cli/mcp.d.ts +23 -0
- package/dist/cjs/cli/mcp.js +190 -152
- package/dist/cjs/cli/migrate.d.ts +243 -3
- package/dist/cjs/cli/migrate.js +432 -43
- package/dist/cjs/cli/sql-statements.js +27 -0
- package/dist/cjs/cli/studio.d.ts +0 -1
- package/dist/cjs/cli/studio.js +9 -7
- package/dist/cjs/client.d.ts +8 -1
- package/dist/cjs/client.js +7 -0
- package/dist/cjs/connection-url.d.ts +82 -0
- package/dist/cjs/connection-url.js +187 -1
- package/dist/cjs/errors.d.ts +112 -12
- package/dist/cjs/errors.js +558 -114
- package/dist/cjs/generate.js +47 -15
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/introspect.d.ts +33 -0
- package/dist/cjs/introspect.js +54 -1
- package/dist/cjs/mssql.js +21 -1
- package/dist/cjs/nested-write.js +85 -14
- package/dist/cjs/pipeline-submittable.d.ts +2 -0
- package/dist/cjs/pipeline-submittable.js +88 -3
- package/dist/cjs/pipeline.js +13 -1
- package/dist/cjs/powdb-introspect.d.ts +5 -1
- package/dist/cjs/powdb-introspect.js +5 -1
- package/dist/cjs/powql.d.ts +14 -0
- package/dist/cjs/powql.js +44 -4
- package/dist/cjs/prisma-compat.js +95 -8
- package/dist/cjs/query/aggregates.js +56 -6
- package/dist/cjs/query/builder.d.ts +76 -13
- package/dist/cjs/query/builder.js +188 -58
- package/dist/cjs/query/compound-unique.d.ts +76 -0
- package/dist/cjs/query/compound-unique.js +129 -0
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/types.d.ts +65 -11
- package/dist/cjs/query/where.d.ts +85 -19
- package/dist/cjs/query/where.js +262 -47
- package/dist/cjs/query/writes.d.ts +11 -2
- package/dist/cjs/query/writes.js +116 -21
- package/dist/cjs/seed.d.ts +16 -0
- package/dist/cjs/seed.js +16 -0
- package/dist/cli/config.d.ts +7 -1
- package/dist/cli/config.js +11 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +307 -9
- package/dist/cli/index.js +254 -44
- package/dist/cli/mcp.d.ts +23 -0
- package/dist/cli/mcp.js +187 -150
- package/dist/cli/migrate.d.ts +243 -3
- package/dist/cli/migrate.js +423 -45
- package/dist/cli/sql-statements.js +27 -0
- package/dist/cli/studio.d.ts +0 -1
- package/dist/cli/studio.js +10 -7
- package/dist/client.d.ts +8 -1
- package/dist/client.js +7 -0
- package/dist/connection-url.d.ts +82 -0
- package/dist/connection-url.js +183 -0
- package/dist/errors.d.ts +112 -12
- package/dist/errors.js +558 -114
- package/dist/generate.js +47 -15
- package/dist/index.d.ts +1 -1
- package/dist/introspect.d.ts +33 -0
- package/dist/introspect.js +53 -1
- package/dist/mssql.js +21 -1
- package/dist/nested-write.js +85 -14
- package/dist/pipeline-submittable.d.ts +2 -0
- package/dist/pipeline-submittable.js +87 -3
- package/dist/pipeline.js +14 -2
- package/dist/powdb-introspect.d.ts +5 -1
- package/dist/powdb-introspect.js +5 -1
- package/dist/powql.d.ts +14 -0
- package/dist/powql.js +45 -5
- package/dist/prisma-compat.js +96 -9
- package/dist/query/aggregates.js +56 -6
- package/dist/query/builder.d.ts +76 -13
- package/dist/query/builder.js +188 -58
- package/dist/query/compound-unique.d.ts +76 -0
- package/dist/query/compound-unique.js +126 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +65 -11
- package/dist/query/where.d.ts +85 -19
- package/dist/query/where.js +260 -47
- package/dist/query/writes.d.ts +11 -2
- package/dist/query/writes.js +117 -22
- package/dist/seed.d.ts +16 -0
- package/dist/seed.js +16 -0
- package/package.json +3 -3
- package/skills/turbine-orm/SKILL.md +37 -10
package/dist/query/builder.js
CHANGED
|
@@ -1134,9 +1134,11 @@ export class QueryInterface {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
/**
|
|
1136
1136
|
* The field names a `cursor` actually seeks on (its own keys with a defined
|
|
1137
|
-
* value), in
|
|
1138
|
-
*
|
|
1139
|
-
*
|
|
1137
|
+
* value), in canonical sorted order. This is the diagnostic / implicit-
|
|
1138
|
+
* ordering view; the seek itself orders the fields by their `orderBy`
|
|
1139
|
+
* precedence (see {@link cursorSeekEntries}). Empty for a missing cursor or
|
|
1140
|
+
* one whose every value is `undefined` (which emits no seek condition at
|
|
1141
|
+
* all, so it does not paginate).
|
|
1140
1142
|
*/
|
|
1141
1143
|
cursorFields(cursor) {
|
|
1142
1144
|
if (!cursor || typeof cursor !== 'object')
|
|
@@ -1150,18 +1152,20 @@ export class QueryInterface {
|
|
|
1150
1152
|
* The ascending ordering implied by a `cursor`, or `undefined` when the shape
|
|
1151
1153
|
* is too ambiguous to order safely.
|
|
1152
1154
|
*
|
|
1153
|
-
* A cursor seek
|
|
1154
|
-
* so the ONLY ordering coherent with it is on
|
|
1155
|
-
* a seek on column X by column Y walks the table in an
|
|
1156
|
-
* follow, which skips and repeats rows just as badly
|
|
1157
|
-
* is why this orders on the cursor field rather than
|
|
1158
|
-
* key when the two differ.
|
|
1155
|
+
* A cursor seek compares against the cursor's own field(s) (`col > $n`, `<`
|
|
1156
|
+
* when the orderBy says desc), so the ONLY ordering coherent with it is on
|
|
1157
|
+
* those fields: ordering a seek on column X by column Y walks the table in an
|
|
1158
|
+
* order the seek does not follow, which skips and repeats rows just as badly
|
|
1159
|
+
* as no order at all. That is why this orders on the cursor field rather than
|
|
1160
|
+
* blindly on the primary key when the two differ.
|
|
1159
1161
|
*
|
|
1160
1162
|
* Returns `undefined` (warn, inject nothing) for two shapes:
|
|
1161
|
-
* - a MULTI-field cursor.
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1163
|
+
* - a MULTI-field cursor. The seek is a proper keyset predicate, but its
|
|
1164
|
+
* column PRECEDENCE comes from the `orderBy` ({@link cursorSeekEntries}),
|
|
1165
|
+
* and with no orderBy there is nothing to derive it from: the cursor
|
|
1166
|
+
* object's key order is canonicalized away, so `(a asc, b asc)` would be a
|
|
1167
|
+
* guess at which key the caller meant to lead. The caller must state the
|
|
1168
|
+
* order, and the unordered-page warning says so.
|
|
1165
1169
|
* - a field that does not resolve to a real column. Column validation belongs
|
|
1166
1170
|
* to the normal build path, which raises a precise error; synthesizing an
|
|
1167
1171
|
* ORDER BY on it here would only change which error the caller sees.
|
|
@@ -1179,6 +1183,83 @@ export class QueryInterface {
|
|
|
1179
1183
|
}
|
|
1180
1184
|
return { [field]: 'asc' };
|
|
1181
1185
|
}
|
|
1186
|
+
/**
|
|
1187
|
+
* The fields a `cursor` seeks on, in KEYSET order with their seek direction.
|
|
1188
|
+
* THE single authority the cache fingerprint (`cur=`), the SQL build and the
|
|
1189
|
+
* cache-hit param collect all consume, so the three cannot disagree about
|
|
1190
|
+
* which value binds to which `$n`.
|
|
1191
|
+
*
|
|
1192
|
+
* Keyset order is the `orderBy` precedence: the predicate for
|
|
1193
|
+
* `orderBy: [{ viewCount }, { id }]` must test `view_count` first and `id`
|
|
1194
|
+
* only within equal `view_count`, whatever order the caller wrote the cursor
|
|
1195
|
+
* object in (its keys are canonicalized for the cache anyway). A cursor field
|
|
1196
|
+
* the orderBy does not name trails the named ones in sorted key order and
|
|
1197
|
+
* seeks ascending, the same default a single-field cursor with no orderBy has
|
|
1198
|
+
* always had.
|
|
1199
|
+
*
|
|
1200
|
+
* Directions are indexed by the RESOLVED COLUMN, never the caller's key: both
|
|
1201
|
+
* `cursor` and `orderBy` take either spelling, so a cursor written
|
|
1202
|
+
* `{ created_at }` against `orderBy: { createdAt: 'desc' }` used to miss the
|
|
1203
|
+
* lookup, default to ascending, and seek the wrong side of the page. The
|
|
1204
|
+
* `{ sort, nulls }` spec form is normalized for the same reason. A relation /
|
|
1205
|
+
* JSON-path / vector orderBy key resolves to no column and is skipped.
|
|
1206
|
+
*
|
|
1207
|
+
* An unknown cursor field throws the same E003 here that the build path
|
|
1208
|
+
* threw before this helper existed; the fingerprint simply meets it first.
|
|
1209
|
+
*/
|
|
1210
|
+
cursorSeekEntries(cursor, orderBy) {
|
|
1211
|
+
const orderPos = new Map();
|
|
1212
|
+
const orderDesc = new Map();
|
|
1213
|
+
for (const [ok, od] of orderByEntries(orderBy)) {
|
|
1214
|
+
const ocol = resolveColumnName(this.tableMeta, ok);
|
|
1215
|
+
if (ocol === undefined)
|
|
1216
|
+
continue;
|
|
1217
|
+
if (!orderPos.has(ocol))
|
|
1218
|
+
orderPos.set(ocol, orderPos.size);
|
|
1219
|
+
// Last wins, matching object-key semantics (duplicates are refused
|
|
1220
|
+
// upstream anyway, so in practice there is exactly one).
|
|
1221
|
+
orderDesc.set(ocol, isOrderBySpec(od) ? od.sort === 'desc' : od === 'desc');
|
|
1222
|
+
}
|
|
1223
|
+
const entries = sortedEntries(cursor)
|
|
1224
|
+
.filter(([, v]) => v !== undefined)
|
|
1225
|
+
.map(([k, value]) => {
|
|
1226
|
+
const column = this.toColumn(k);
|
|
1227
|
+
return { column, value, desc: orderDesc.get(column) === true };
|
|
1228
|
+
});
|
|
1229
|
+
// Stable sort over the canonical (sorted-key) list: orderBy precedence
|
|
1230
|
+
// first, unnamed fields after it in their sorted order.
|
|
1231
|
+
return entries.sort((a, b) => (orderPos.get(a.column) ?? Number.MAX_SAFE_INTEGER) - (orderPos.get(b.column) ?? Number.MAX_SAFE_INTEGER));
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* The keyset predicate for cursor terms in keyset order.
|
|
1235
|
+
*
|
|
1236
|
+
* One field is the plain `col > $1` (byte-identical to every single-field
|
|
1237
|
+
* cursor ever emitted). Two or more expand to
|
|
1238
|
+
* `(c1 > $1 OR (c1 = $1 AND c2 > $2) OR (c1 = $1 AND c2 = $2 AND c3 > $3))`,
|
|
1239
|
+
* with `<` for a `desc` field: the row-value form `(c1, c2) > ($1, $2)` is
|
|
1240
|
+
* shorter, but it cannot express a MIXED direction at all and SQL Server has
|
|
1241
|
+
* no row-value comparison, so the expanded form is the one path every engine
|
|
1242
|
+
* and every direction set share. Each value is bound ONCE and its `$n` is
|
|
1243
|
+
* referenced by every branch that needs it (legal on every supported
|
|
1244
|
+
* placeholder syntax), which is what keeps the param count equal to the field
|
|
1245
|
+
* count on both the build and the collect side.
|
|
1246
|
+
*
|
|
1247
|
+
* The former `c1 > $1 AND c2 > $2` was a conjunction, not a seek: it skipped
|
|
1248
|
+
* every row whose leading key EQUALED the cursor's, so a table with few
|
|
1249
|
+
* distinct leading values lost most of its rows across a page walk, silently.
|
|
1250
|
+
*/
|
|
1251
|
+
keysetPredicate(terms) {
|
|
1252
|
+
const compare = (t) => `${t.column} ${t.desc ? '<' : '>'} ${t.placeholder}`;
|
|
1253
|
+
if (terms.length === 1)
|
|
1254
|
+
return compare(terms[0]);
|
|
1255
|
+
const branches = terms.map((term, i) => {
|
|
1256
|
+
if (i === 0)
|
|
1257
|
+
return compare(term);
|
|
1258
|
+
const prefix = terms.slice(0, i).map((t) => `${t.column} = ${t.placeholder}`);
|
|
1259
|
+
return `(${[...prefix, compare(term)].join(' AND ')})`;
|
|
1260
|
+
});
|
|
1261
|
+
return `(${branches.join(' OR ')})`;
|
|
1262
|
+
}
|
|
1182
1263
|
/**
|
|
1183
1264
|
* Whether a findMany paginates (`limit` / `take` / `offset` / `cursor`) but
|
|
1184
1265
|
* declares no ordering, which makes the returned page NON-DETERMINISTIC:
|
|
@@ -1663,17 +1744,27 @@ export class QueryInterface {
|
|
|
1663
1744
|
// Query-level opt-in threaded onto every follow-up child `buildFindMany`,
|
|
1664
1745
|
// so a batched load excludes/includes PII exactly as the join strategy.
|
|
1665
1746
|
includePii,
|
|
1666
|
-
|
|
1747
|
+
// `ref` is the follow-up query's FROM-item AS THE SQL REFERS TO IT: the
|
|
1748
|
+
// quoted child table for a hasMany `_count`, the bare junction alias `t`
|
|
1749
|
+
// for the m2m one. Compiled through the rendered-reference scope, which
|
|
1750
|
+
// uses it verbatim for the column qualifier and for a nested relation
|
|
1751
|
+
// filter's correlation parent alike. It went through the BARE-alias scope
|
|
1752
|
+
// before, which quotes what it is given, so a `globalFilters` entry that
|
|
1753
|
+
// was itself a relation filter emitted `"""posts"""` into the EXISTS
|
|
1754
|
+
// body and the count failed 42P01. Reproduced live; the plain batched
|
|
1755
|
+
// relation fetch was never affected, because that one goes through the
|
|
1756
|
+
// child's own buildFindMany and merges the filter unqualified.
|
|
1757
|
+
tableGlobalFilter: (table, ref, precedingParams) => {
|
|
1667
1758
|
const gf = this.resolveGlobalFilter(table, resolvedSkip);
|
|
1668
1759
|
if (!gf)
|
|
1669
1760
|
return null;
|
|
1670
1761
|
const meta = this.schema.tables[table];
|
|
1671
1762
|
if (!meta)
|
|
1672
1763
|
return null;
|
|
1673
|
-
// Seed the param array with `precedingParams` placeholders so
|
|
1674
|
-
//
|
|
1764
|
+
// Seed the param array with `precedingParams` placeholders so the
|
|
1765
|
+
// builder numbers the gf params after the already-bound ones.
|
|
1675
1766
|
const seeded = new Array(precedingParams).fill(undefined);
|
|
1676
|
-
const clause = this.
|
|
1767
|
+
const clause = whereMod.buildRenderedRefWhere(this.ctx, table, meta, ref, gf, seeded);
|
|
1677
1768
|
if (!clause)
|
|
1678
1769
|
return null;
|
|
1679
1770
|
return { clause, params: seeded.slice(precedingParams) };
|
|
@@ -2697,10 +2788,12 @@ export class QueryInterface {
|
|
|
2697
2788
|
})
|
|
2698
2789
|
.join(',')
|
|
2699
2790
|
: '';
|
|
2791
|
+
// Keyset order AND per-field direction: a multi-field cursor's predicate
|
|
2792
|
+
// is one OR-branch per field in orderBy precedence, and `<` / `>` are SQL
|
|
2793
|
+
// text, so `asc` and `desc` (or two precedences) can never share a template.
|
|
2700
2794
|
const cursorFp = args?.cursor
|
|
2701
|
-
?
|
|
2702
|
-
.
|
|
2703
|
-
.sort()
|
|
2795
|
+
? this.cursorSeekEntries(args.cursor, args.orderBy)
|
|
2796
|
+
.map((e) => `${e.column}:${e.desc ? 'd' : 'a'}`)
|
|
2704
2797
|
.join(',')
|
|
2705
2798
|
: '';
|
|
2706
2799
|
// distinct must fingerprint in USER order: the SQL emits `DISTINCT ON` in
|
|
@@ -2810,39 +2903,16 @@ export class QueryInterface {
|
|
|
2810
2903
|
// where → cursor order (the collect path mirrors this exactly).
|
|
2811
2904
|
let tail = freshWhereSql;
|
|
2812
2905
|
if (args?.cursor) {
|
|
2813
|
-
//
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
// Indexed by the RESOLVED COLUMN, never the caller's key: both
|
|
2821
|
-
// `cursor` and `orderBy` take either spelling, so a cursor written
|
|
2822
|
-
// `{ created_at }` against `orderBy: { createdAt: 'desc' }` missed
|
|
2823
|
-
// this lookup, defaulted to ascending, and emitted `created_at > $n`
|
|
2824
|
-
// under `ORDER BY created_at DESC` — the wrong page, silently. Same
|
|
2825
|
-
// failure the `{ sort, nulls }` normalization below prevents, reached
|
|
2826
|
-
// through the spelling instead of the value shape. A relation /
|
|
2827
|
-
// JSON-path / vector key resolves to no column and is skipped.
|
|
2828
|
-
const orderDirByColumn = new Map();
|
|
2829
|
-
for (const [ok, od] of orderByEntries(args.orderBy)) {
|
|
2830
|
-
const ocol = resolveColumnName(this.tableMeta, ok);
|
|
2831
|
-
if (ocol !== undefined)
|
|
2832
|
-
orderDirByColumn.set(ocol, od);
|
|
2833
|
-
}
|
|
2834
|
-
const cursorConditions = cursorEntries.map(([k, v]) => {
|
|
2835
|
-
const rawCol = this.toColumn(k);
|
|
2836
|
-
const col = this.q(rawCol);
|
|
2837
|
-
// orderBy values can be the { sort, nulls } spec form: normalize
|
|
2838
|
-
// before comparing, or a desc spec would seek the ascending side.
|
|
2839
|
-
const dir = orderDirByColumn.get(rawCol);
|
|
2840
|
-
const desc = isOrderBySpec(dir) ? dir.sort === 'desc' : dir === 'desc';
|
|
2841
|
-
const op = desc ? '<' : '>';
|
|
2842
|
-
freshParams.push(v);
|
|
2843
|
-
return `${qt}.${col} ${op} ${this.p(freshParams.length)}`;
|
|
2906
|
+
// Keyset order (orderBy precedence), MUST match cursorFp and the
|
|
2907
|
+
// cache-hit collect below: both go through cursorSeekEntries.
|
|
2908
|
+
const seek = this.cursorSeekEntries(args.cursor, args.orderBy);
|
|
2909
|
+
if (seek.length > 0) {
|
|
2910
|
+
const terms = seek.map((e) => {
|
|
2911
|
+
freshParams.push(e.value);
|
|
2912
|
+
return { column: `${qt}.${this.q(e.column)}`, placeholder: this.p(freshParams.length), desc: e.desc };
|
|
2844
2913
|
});
|
|
2845
|
-
|
|
2914
|
+
const predicate = this.keysetPredicate(terms);
|
|
2915
|
+
tail += freshWhereSql ? ` AND ${predicate}` : ` WHERE ${predicate}`;
|
|
2846
2916
|
}
|
|
2847
2917
|
}
|
|
2848
2918
|
// ORDER BY is built AFTER the cursor pushes (param order
|
|
@@ -2861,10 +2931,40 @@ export class QueryInterface {
|
|
|
2861
2931
|
throw new ValidationError('`distinct` cannot be combined with vector distance ordering.');
|
|
2862
2932
|
}
|
|
2863
2933
|
const userOrder = this.buildOrderBy(args.orderBy, freshParams);
|
|
2864
|
-
|
|
2934
|
+
// The outer level re-orders by the user's columns, which must therefore
|
|
2935
|
+
// exist in the derived table. A NARROWED projection (`select`, `omit`,
|
|
2936
|
+
// or the PII rule, which narrows with no user projection at all) left
|
|
2937
|
+
// them out, so the statement failed with 42703. Every order column the
|
|
2938
|
+
// projection omits is projected into the INNER query under its own
|
|
2939
|
+
// name, and the outer SELECT then lists the projected columns
|
|
2940
|
+
// explicitly instead of `*`, so the extra column orders the result and
|
|
2941
|
+
// never reaches a row (a PII column never even crosses the wire). The
|
|
2942
|
+
// full projection (`columnsList === null`, `"t".*`) and a projection
|
|
2943
|
+
// that already covers the order keeps the `SELECT *` wrapper byte for
|
|
2944
|
+
// byte. The user's order is rendered ONCE (params pushed once, as the
|
|
2945
|
+
// collect path mirrors) and its text reused at both levels.
|
|
2946
|
+
let innerSelect = selectClause;
|
|
2947
|
+
let outerSelect = '*';
|
|
2948
|
+
if (columnsList) {
|
|
2949
|
+
const projected = new Set(columnsList);
|
|
2950
|
+
const extras = [];
|
|
2951
|
+
for (const [key] of orderByEntries(args.orderBy)) {
|
|
2952
|
+
// Relation and vector ordering are refused above, so every key
|
|
2953
|
+
// names a column of this table (a JSON-path entry's key is its
|
|
2954
|
+
// column); buildOrderBy has already validated it.
|
|
2955
|
+
const col = this.toColumn(key);
|
|
2956
|
+
if (!projected.has(col) && !extras.includes(col))
|
|
2957
|
+
extras.push(col);
|
|
2958
|
+
}
|
|
2959
|
+
if (extras.length > 0) {
|
|
2960
|
+
innerSelect = `${selectClause}, ${extras.map((c) => `${qt}.${this.q(c)}`).join(', ')}`;
|
|
2961
|
+
outerSelect = this.distinctOuterSelectList(columnsList, args.with).join(', ');
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
const inner = `SELECT ${distinctPrefix}${innerSelect} FROM ${qt}${tail} ORDER BY ${distinctCols
|
|
2865
2965
|
.map((c) => `${c} ASC`)
|
|
2866
2966
|
.join(', ')}, ${userOrder}`;
|
|
2867
|
-
sql = `SELECT
|
|
2967
|
+
sql = `SELECT ${outerSelect} FROM (${inner}) AS ${this.q(`${this.table}_distinct`)} ORDER BY ${userOrder}`;
|
|
2868
2968
|
}
|
|
2869
2969
|
else {
|
|
2870
2970
|
// Pass freshParams so vector KNN ordering binds its `$n::vector` query
|
|
@@ -2899,11 +2999,11 @@ export class QueryInterface {
|
|
|
2899
2999
|
if (args?.with) {
|
|
2900
3000
|
this.collectWithParams(args.with, params, undefined, flattenPlan);
|
|
2901
3001
|
}
|
|
2902
|
-
// 3. Cursor params,
|
|
3002
|
+
// 3. Cursor params, keyset order, matching cursorFp and the build path
|
|
3003
|
+
// (each value binds ONCE; the expanded predicate re-references its `$n`).
|
|
2903
3004
|
if (args?.cursor) {
|
|
2904
|
-
const
|
|
2905
|
-
|
|
2906
|
-
params.push(v);
|
|
3005
|
+
for (const e of this.cursorSeekEntries(args.cursor, args.orderBy)) {
|
|
3006
|
+
params.push(e.value);
|
|
2907
3007
|
}
|
|
2908
3008
|
}
|
|
2909
3009
|
// 4. ORDER BY params (vector KNN ordering binds a `$n::vector` query vector).
|
|
@@ -3682,6 +3782,36 @@ export class QueryInterface {
|
|
|
3682
3782
|
toSqlColumn(field) {
|
|
3683
3783
|
return this.q(this.toColumn(field));
|
|
3684
3784
|
}
|
|
3785
|
+
/**
|
|
3786
|
+
* The explicit outer SELECT list of the `distinct` + `orderBy` wrapper, used
|
|
3787
|
+
* only when the inner derived table had to carry ORDER BY columns the
|
|
3788
|
+
* projection leaves out (see the distinct branch of {@link buildFindMany}):
|
|
3789
|
+
* the projected base columns, then one column per `with` relation, then one
|
|
3790
|
+
* per counted relation. These are the aliases `buildSelectWithRelations`
|
|
3791
|
+
* (relations.ts) gives the same columns, `AS "<relName>"` per entry in sorted
|
|
3792
|
+
* order and `AS "_count__<rel>"` per counted relation, over the SAME
|
|
3793
|
+
* `resolveCountRelations` it uses, so the two lists name the same columns.
|
|
3794
|
+
* `flatten` never reaches here (the strategy is gated off under `distinct`),
|
|
3795
|
+
* so no prefixed scalar aliases exist to enumerate. A drift between the two
|
|
3796
|
+
* would fail LOUDLY, as a 42703 on the outer list, never as a silently wrong
|
|
3797
|
+
* row, and the live distinct test pins the `with` + `_count` shape.
|
|
3798
|
+
*/
|
|
3799
|
+
distinctOuterSelectList(columnsList, withClause) {
|
|
3800
|
+
const list = columnsList.map((c) => this.q(c));
|
|
3801
|
+
if (!withClause)
|
|
3802
|
+
return list;
|
|
3803
|
+
for (const [relName] of sortedEntries(withClause)) {
|
|
3804
|
+
if (relName === '_count')
|
|
3805
|
+
continue;
|
|
3806
|
+
list.push(this.q(relName));
|
|
3807
|
+
}
|
|
3808
|
+
const countSpec = withClause._count;
|
|
3809
|
+
if (countSpec !== undefined) {
|
|
3810
|
+
for (const rel of resolveCountRelations(this.tableMeta, countSpec))
|
|
3811
|
+
list.push(this.q(`_count__${rel.name}`));
|
|
3812
|
+
}
|
|
3813
|
+
return list;
|
|
3814
|
+
}
|
|
3685
3815
|
// =========================================================================
|
|
3686
3816
|
// Fingerprinting, value-invariant shape keys for SQL cache lookup
|
|
3687
3817
|
// =========================================================================
|
|
@@ -77,7 +77,70 @@ export declare function expandCompoundUniqueWhere(meta: TableMetadata, where: Re
|
|
|
77
77
|
* person cannot take.
|
|
78
78
|
*/
|
|
79
79
|
export declare function assertWhereIdentifiesOneRow(meta: TableMetadata, table: string, where: Record<string, unknown> | undefined): void;
|
|
80
|
+
/**
|
|
81
|
+
* The same refusal for the single-row WRITE methods, `update` and `delete`, and
|
|
82
|
+
* for `upsert`, whose `where` carries the same one-row contract by a different
|
|
83
|
+
* mechanism.
|
|
84
|
+
*
|
|
85
|
+
* `update` / `delete` emit `... WHERE <predicate> RETURNING *` and hand back
|
|
86
|
+
* `rows[0]`, so a `where` that matches many rows mutates EVERY one of them and
|
|
87
|
+
* reports one: the `findUnique` hazard (an arbitrary one of many) with a write
|
|
88
|
+
* attached. `upsert`'s `where` is not a predicate at all, it IS the conflict
|
|
89
|
+
* target, so a non-unique one has no single row to update on conflict and the
|
|
90
|
+
* emitted `ON CONFLICT (...)` names columns no unique index backs. Same rule,
|
|
91
|
+
* same sources of uniqueness, same null policy as
|
|
92
|
+
* {@link assertWhereIdentifiesOneRow}; only the sentence differs, because the
|
|
93
|
+
* consequence and the fix differ per operation. A reader who meant one row
|
|
94
|
+
* names a key; a reader who meant every matching row has `updateMany` /
|
|
95
|
+
* `deleteMany`, which report `{ count }` and never pretend to have touched one
|
|
96
|
+
* row.
|
|
97
|
+
*
|
|
98
|
+
* Called on the CALLER's where, before any global filter is merged in (a
|
|
99
|
+
* tenancy filter narrows, it does not identify). `update` / `delete` skip it
|
|
100
|
+
* under `allowFullTableScan: UNSAFE`, which already says "every row" in so many
|
|
101
|
+
* words, and their message names that hatch: a predicate CAN identify one row
|
|
102
|
+
* through a constraint this schema's metadata does not carry (a stale generated
|
|
103
|
+
* `metadata.ts`, a `defineSchema` that declares fewer uniques than the database
|
|
104
|
+
* has), and a rule with no way past it turns that into an unreachable method.
|
|
105
|
+
* `upsert` has no such option and its message therefore offers none.
|
|
106
|
+
*/
|
|
107
|
+
export declare function assertMutationWhereIdentifiesOneRow(meta: TableMetadata, table: string, where: Record<string, unknown> | undefined, operation: 'update' | 'delete' | 'upsert'): void;
|
|
80
108
|
export declare function uniqueKeyNames(meta: TableMetadata): string[][];
|
|
109
|
+
/**
|
|
110
|
+
* Brands a predicate Turbine wrote ENTIRELY itself to address the one row a
|
|
111
|
+
* DECLARED to-one relation points at, so the single-row write rule accepts it.
|
|
112
|
+
*
|
|
113
|
+
* A nested `disconnect: true` / `delete: true` on a `hasOne`, and a nested
|
|
114
|
+
* `update` on a `belongsTo` (whose argument is `{ data }`, with no `where` at
|
|
115
|
+
* all), give the caller no selector to write. The engine builds the predicate
|
|
116
|
+
* from the relation's own correlation key, and the thing that makes it one row
|
|
117
|
+
* is the relation's declared CARDINALITY, which is not in `primaryKey` /
|
|
118
|
+
* `uniqueColumns` / `indexes` and so is invisible to
|
|
119
|
+
* {@link whereIdentifiesOneRow}. Without this the rule refused those writes
|
|
120
|
+
* with "Name a unique key", advice the caller cannot take because they never
|
|
121
|
+
* wrote a `where`, on every schema whose `hasOne` FK is not ALSO declared
|
|
122
|
+
* unique: the `defineSchema` code-first path, the PowDB path, and any FK backed
|
|
123
|
+
* only by a partial unique index.
|
|
124
|
+
*
|
|
125
|
+
* Turbine believes a declared `hasOne` everywhere else (it reads the relation
|
|
126
|
+
* as an object rather than an array, and emits `LIMIT 1` for it), so believing
|
|
127
|
+
* it here is consistency, not a hole. The brand is applied at the one place the
|
|
128
|
+
* predicate is synthesized (`scopeWhereToParent` / the belongsTo correlation in
|
|
129
|
+
* nested-write.ts) and never to anything a caller supplied.
|
|
130
|
+
*
|
|
131
|
+
* A distinct Symbol rather than a second meaning for `markInternalCombinator`:
|
|
132
|
+
* that brand exists to keep a fixed-arity wrapper on a NAMED prepared
|
|
133
|
+
* statement, and folding "identifies one row" into it would mean any future
|
|
134
|
+
* fixed-arity wrapper silently switched this rule off. `Symbol.for` for the
|
|
135
|
+
* ESM/CJS cross-copy identity reason `INTERNAL_COMBINATOR` documents, and a
|
|
136
|
+
* SYMBOL so the `Object.keys` every where walker enumerates never sees it and
|
|
137
|
+
* no emitted SQL can change.
|
|
138
|
+
*/
|
|
139
|
+
export declare const INTERNAL_ROW_SELECTOR: unique symbol;
|
|
140
|
+
/** Tag `where` as an engine-written single-row selector, and return it. */
|
|
141
|
+
export declare function markInternalRowSelector<T extends object>(where: T): T;
|
|
142
|
+
/** Did Turbine itself write this predicate to address one declared related row? */
|
|
143
|
+
export declare function isInternalRowSelector(where: unknown): boolean;
|
|
81
144
|
/**
|
|
82
145
|
* True when `where` pins every column of at least one unique key to a single
|
|
83
146
|
* value, so the row it names is the row it gets.
|
|
@@ -91,5 +154,18 @@ export declare function uniqueKeyNames(meta: TableMetadata): string[][];
|
|
|
91
154
|
* A NULL is not an identity. `WHERE email IS NULL` matches every row whose
|
|
92
155
|
* email is null, which a UNIQUE constraint permits any number of, so a null
|
|
93
156
|
* value satisfies no key here even on a unique column.
|
|
157
|
+
*
|
|
158
|
+
* The one combinator it does read through is Turbine's OWN: a nested write
|
|
159
|
+
* scopes a child selector to its parent by merging the parent correlation in,
|
|
160
|
+
* and when the two name the same column the merge is `{ AND: [selector,
|
|
161
|
+
* correlation] }` BRANDED via `markInternalCombinator` (query/utils.ts). That
|
|
162
|
+
* brand is a Symbol no request body can produce, the arity is fixed at two,
|
|
163
|
+
* and each branch only narrows the other, so the wrapper identifies a row
|
|
164
|
+
* exactly when one of its branches does. A caller-written `AND` is not read.
|
|
165
|
+
*
|
|
166
|
+
* The other thing it reads is {@link markInternalRowSelector}: a predicate the
|
|
167
|
+
* ENGINE wrote in full, for which there is no caller `where` this rule could be
|
|
168
|
+
* about. See that function for why the declaration, not the metadata, is the
|
|
169
|
+
* uniqueness source there.
|
|
94
170
|
*/
|
|
95
171
|
export declare function whereIdentifiesOneRow(meta: TableMetadata, where: Record<string, unknown>): boolean;
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
import { ValidationError } from '../errors.js';
|
|
42
42
|
import { isArrayFilter, isJsonFilter, isVectorFilter, isWhereOperator } from './filters.js';
|
|
43
|
-
import { ownLookup, resolveColumnName, resolveRelationDef } from './utils.js';
|
|
43
|
+
import { isInternalCombinator, ownLookup, resolveColumnName, resolveRelationDef } from './utils.js';
|
|
44
44
|
const syntheticKeyCache = new WeakMap();
|
|
45
45
|
/**
|
|
46
46
|
* Build (once per table) the map from a synthetic selector name to the ordered
|
|
@@ -239,6 +239,70 @@ export function assertWhereIdentifiesOneRow(meta, table, where) {
|
|
|
239
239
|
throw new ValidationError(`findUnique on "${table}" refused: the \`where\` clause does not identify a single row, ` +
|
|
240
240
|
`so this would return an arbitrary one of the rows that match. ${advice}`);
|
|
241
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* The escape hatch sentence, on `update` / `delete` only.
|
|
244
|
+
*
|
|
245
|
+
* Stated with its cost rather than as an option to reach for: the same flag
|
|
246
|
+
* turns off the empty-`where` guard, so a predicate that is merely NARROW today
|
|
247
|
+
* and becomes EMPTY tomorrow (every value `undefined` on a request that omitted
|
|
248
|
+
* them) writes the whole table instead of being refused.
|
|
249
|
+
*/
|
|
250
|
+
const UNSAFE_ESCAPE = 'If this predicate really does identify one row through a constraint this schema does not describe, ' +
|
|
251
|
+
'`allowFullTableScan: UNSAFE` writes it anyway, at the cost of the empty-`where` guard as well: an ' +
|
|
252
|
+
'all-`undefined` `where` then matches every row instead of being refused.';
|
|
253
|
+
/**
|
|
254
|
+
* The same refusal for the single-row WRITE methods, `update` and `delete`, and
|
|
255
|
+
* for `upsert`, whose `where` carries the same one-row contract by a different
|
|
256
|
+
* mechanism.
|
|
257
|
+
*
|
|
258
|
+
* `update` / `delete` emit `... WHERE <predicate> RETURNING *` and hand back
|
|
259
|
+
* `rows[0]`, so a `where` that matches many rows mutates EVERY one of them and
|
|
260
|
+
* reports one: the `findUnique` hazard (an arbitrary one of many) with a write
|
|
261
|
+
* attached. `upsert`'s `where` is not a predicate at all, it IS the conflict
|
|
262
|
+
* target, so a non-unique one has no single row to update on conflict and the
|
|
263
|
+
* emitted `ON CONFLICT (...)` names columns no unique index backs. Same rule,
|
|
264
|
+
* same sources of uniqueness, same null policy as
|
|
265
|
+
* {@link assertWhereIdentifiesOneRow}; only the sentence differs, because the
|
|
266
|
+
* consequence and the fix differ per operation. A reader who meant one row
|
|
267
|
+
* names a key; a reader who meant every matching row has `updateMany` /
|
|
268
|
+
* `deleteMany`, which report `{ count }` and never pretend to have touched one
|
|
269
|
+
* row.
|
|
270
|
+
*
|
|
271
|
+
* Called on the CALLER's where, before any global filter is merged in (a
|
|
272
|
+
* tenancy filter narrows, it does not identify). `update` / `delete` skip it
|
|
273
|
+
* under `allowFullTableScan: UNSAFE`, which already says "every row" in so many
|
|
274
|
+
* words, and their message names that hatch: a predicate CAN identify one row
|
|
275
|
+
* through a constraint this schema's metadata does not carry (a stale generated
|
|
276
|
+
* `metadata.ts`, a `defineSchema` that declares fewer uniques than the database
|
|
277
|
+
* has), and a rule with no way past it turns that into an unreachable method.
|
|
278
|
+
* `upsert` has no such option and its message therefore offers none.
|
|
279
|
+
*/
|
|
280
|
+
export function assertMutationWhereIdentifiesOneRow(meta, table, where, operation) {
|
|
281
|
+
if (whereIdentifiesOneRow(meta, where ?? {}))
|
|
282
|
+
return;
|
|
283
|
+
const keys = describeUniqueKeys(meta);
|
|
284
|
+
if (operation === 'upsert') {
|
|
285
|
+
const advice = keys.length > 0
|
|
286
|
+
? `Name a unique key (${keys.join(', ')}).`
|
|
287
|
+
: `Table "${table}" declares no primary key and no unique constraint, so no \`where\` can identify one row ` +
|
|
288
|
+
'here and no upsert is possible on it: insert with `create`, or change matching rows with `updateMany`.';
|
|
289
|
+
throw new ValidationError(`upsert on "${table}" refused: the \`where\` clause does not identify a single row. An upsert's \`where\` IS ` +
|
|
290
|
+
'its conflict target, so there is no one row to update on conflict, and the `ON CONFLICT` this would emit ' +
|
|
291
|
+
`names columns no unique constraint backs. ${advice}`);
|
|
292
|
+
}
|
|
293
|
+
const many = operation === 'update' ? 'updateMany' : 'deleteMany';
|
|
294
|
+
const advice = keys.length > 0
|
|
295
|
+
? `Name a unique key (${keys.join(', ')}), or use \`${many}\` if you meant "every row matching a filter".`
|
|
296
|
+
: `Table "${table}" declares no primary key and no unique constraint, so no \`where\` can identify one row ` +
|
|
297
|
+
`here. Use \`${many}\`, which reports how many rows it touched.`;
|
|
298
|
+
throw new ValidationError(`${operation} on "${table}" refused: the \`where\` clause does not identify a single row, ` +
|
|
299
|
+
`so this would ${operation} every row that matches and return only one of them. ${advice} ${UNSAFE_ESCAPE}`);
|
|
300
|
+
}
|
|
301
|
+
/** Each unique key of `meta` rendered in field spelling, for an error message. */
|
|
302
|
+
function describeUniqueKeys(meta) {
|
|
303
|
+
const field = (c) => meta.reverseColumnMap[c] ?? c;
|
|
304
|
+
return uniqueKeyNames(meta).map((cols) => cols.length === 1 ? `\`${field(cols[0])}\`` : `\`{ ${cols.map(field).join(', ')} }\``);
|
|
305
|
+
}
|
|
242
306
|
export function uniqueKeyNames(meta) {
|
|
243
307
|
return dedupeColumnSets(uniqueColumnSets(meta));
|
|
244
308
|
}
|
|
@@ -268,6 +332,49 @@ function uniqueColumnSets(meta) {
|
|
|
268
332
|
}
|
|
269
333
|
return sets;
|
|
270
334
|
}
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// Engine-addressed row selectors
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
/**
|
|
339
|
+
* Brands a predicate Turbine wrote ENTIRELY itself to address the one row a
|
|
340
|
+
* DECLARED to-one relation points at, so the single-row write rule accepts it.
|
|
341
|
+
*
|
|
342
|
+
* A nested `disconnect: true` / `delete: true` on a `hasOne`, and a nested
|
|
343
|
+
* `update` on a `belongsTo` (whose argument is `{ data }`, with no `where` at
|
|
344
|
+
* all), give the caller no selector to write. The engine builds the predicate
|
|
345
|
+
* from the relation's own correlation key, and the thing that makes it one row
|
|
346
|
+
* is the relation's declared CARDINALITY, which is not in `primaryKey` /
|
|
347
|
+
* `uniqueColumns` / `indexes` and so is invisible to
|
|
348
|
+
* {@link whereIdentifiesOneRow}. Without this the rule refused those writes
|
|
349
|
+
* with "Name a unique key", advice the caller cannot take because they never
|
|
350
|
+
* wrote a `where`, on every schema whose `hasOne` FK is not ALSO declared
|
|
351
|
+
* unique: the `defineSchema` code-first path, the PowDB path, and any FK backed
|
|
352
|
+
* only by a partial unique index.
|
|
353
|
+
*
|
|
354
|
+
* Turbine believes a declared `hasOne` everywhere else (it reads the relation
|
|
355
|
+
* as an object rather than an array, and emits `LIMIT 1` for it), so believing
|
|
356
|
+
* it here is consistency, not a hole. The brand is applied at the one place the
|
|
357
|
+
* predicate is synthesized (`scopeWhereToParent` / the belongsTo correlation in
|
|
358
|
+
* nested-write.ts) and never to anything a caller supplied.
|
|
359
|
+
*
|
|
360
|
+
* A distinct Symbol rather than a second meaning for `markInternalCombinator`:
|
|
361
|
+
* that brand exists to keep a fixed-arity wrapper on a NAMED prepared
|
|
362
|
+
* statement, and folding "identifies one row" into it would mean any future
|
|
363
|
+
* fixed-arity wrapper silently switched this rule off. `Symbol.for` for the
|
|
364
|
+
* ESM/CJS cross-copy identity reason `INTERNAL_COMBINATOR` documents, and a
|
|
365
|
+
* SYMBOL so the `Object.keys` every where walker enumerates never sees it and
|
|
366
|
+
* no emitted SQL can change.
|
|
367
|
+
*/
|
|
368
|
+
export const INTERNAL_ROW_SELECTOR = Symbol.for('turbine.internalRowSelector');
|
|
369
|
+
/** Tag `where` as an engine-written single-row selector, and return it. */
|
|
370
|
+
export function markInternalRowSelector(where) {
|
|
371
|
+
Object.defineProperty(where, INTERNAL_ROW_SELECTOR, { value: true, enumerable: false, configurable: true });
|
|
372
|
+
return where;
|
|
373
|
+
}
|
|
374
|
+
/** Did Turbine itself write this predicate to address one declared related row? */
|
|
375
|
+
export function isInternalRowSelector(where) {
|
|
376
|
+
return (typeof where === 'object' && where !== null && where[INTERNAL_ROW_SELECTOR] === true);
|
|
377
|
+
}
|
|
271
378
|
/**
|
|
272
379
|
* True when `where` pins every column of at least one unique key to a single
|
|
273
380
|
* value, so the row it names is the row it gets.
|
|
@@ -281,8 +388,26 @@ function uniqueColumnSets(meta) {
|
|
|
281
388
|
* A NULL is not an identity. `WHERE email IS NULL` matches every row whose
|
|
282
389
|
* email is null, which a UNIQUE constraint permits any number of, so a null
|
|
283
390
|
* value satisfies no key here even on a unique column.
|
|
391
|
+
*
|
|
392
|
+
* The one combinator it does read through is Turbine's OWN: a nested write
|
|
393
|
+
* scopes a child selector to its parent by merging the parent correlation in,
|
|
394
|
+
* and when the two name the same column the merge is `{ AND: [selector,
|
|
395
|
+
* correlation] }` BRANDED via `markInternalCombinator` (query/utils.ts). That
|
|
396
|
+
* brand is a Symbol no request body can produce, the arity is fixed at two,
|
|
397
|
+
* and each branch only narrows the other, so the wrapper identifies a row
|
|
398
|
+
* exactly when one of its branches does. A caller-written `AND` is not read.
|
|
399
|
+
*
|
|
400
|
+
* The other thing it reads is {@link markInternalRowSelector}: a predicate the
|
|
401
|
+
* ENGINE wrote in full, for which there is no caller `where` this rule could be
|
|
402
|
+
* about. See that function for why the declaration, not the metadata, is the
|
|
403
|
+
* uniqueness source there.
|
|
284
404
|
*/
|
|
285
405
|
export function whereIdentifiesOneRow(meta, where) {
|
|
406
|
+
if (isInternalRowSelector(where))
|
|
407
|
+
return true;
|
|
408
|
+
if (isInternalCombinator(where) && Array.isArray(where.AND)) {
|
|
409
|
+
return where.AND.some((branch) => typeof branch === 'object' && branch !== null && whereIdentifiesOneRow(meta, branch));
|
|
410
|
+
}
|
|
286
411
|
const pinned = new Set();
|
|
287
412
|
for (const [key, value] of Object.entries(where)) {
|
|
288
413
|
if (!isPinnedToOneValue(value))
|
package/dist/query/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `import { … } from './query/index.js'` is a drop-in replacement for the
|
|
6
6
|
* former monolithic `import { … } from './query.js'`.
|
|
7
7
|
*/
|
|
8
|
-
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByAggregateSpec, GroupByArgs, GroupByDistinctOn, GroupByResult, HavingClause, JsonEncoding, JsonFilter, JsonPathAggregateTarget, JsonPathGroupKey, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderByObject, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, RelationPickBy, RelationPickOrderBy, ResolvedSkipGlobalFilters, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithOrderByObject, WithResult, } from './types.js';
|
|
8
|
+
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByAggregateSpec, GroupByArgs, GroupByDistinctOn, GroupByResult, HavingClause, JsonEncoding, JsonFilter, JsonPathAggregateTarget, JsonPathGroupKey, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderByObject, OrderBySpec, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, RelationOrderBy, RelationOrderByChain, RelationPickBy, RelationPickOrderBy, ResolvedSkipGlobalFilters, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithOrderByObject, WithResult, } from './types.js';
|
|
9
9
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
11
|
export type { OptionKind, OptionTable } from './option-surface.js';
|