@vibeorm/sql 2.5.0 → 4.0.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/LICENSE +21 -0
- package/dist/ast.d.ts +339 -12
- package/dist/codecs.d.ts +57 -0
- package/dist/dialect.d.ts +54 -0
- package/dist/dialects/postgres-index-keys.d.ts +1 -0
- package/dist/dialects/postgres-physical-catalog.d.ts +7 -1
- package/dist/dialects/sqlite-fts.d.ts +28 -0
- package/dist/index.d.ts +19 -7
- package/dist/index.js +2100 -983
- package/dist/index.js.map +26 -20
- package/dist/json-list-parameter.d.ts +37 -0
- package/dist/parameter-snapshot.d.ts +6 -0
- package/dist/params.d.ts +7 -8
- package/dist/pg-array.d.ts +13 -0
- package/dist/query/expressions.d.ts +70 -0
- package/dist/query/index.d.ts +6 -0
- package/dist/query/index.js +1078 -0
- package/dist/query/index.js.map +16 -0
- package/dist/query/mutations.d.ts +15 -0
- package/dist/query/reusable.d.ts +17 -0
- package/dist/query/select.d.ts +11 -0
- package/dist/query/sources.d.ts +37 -0
- package/dist/query/state.d.ts +73 -0
- package/dist/query/types.d.ts +188 -0
- package/dist/query-plan.d.ts +97 -0
- package/dist/render.d.ts +11 -1
- package/dist/result-decoder.d.ts +41 -10
- package/dist/sql-bindings.d.ts +84 -0
- package/dist/sql-input.d.ts +98 -0
- package/package.json +9 -3
- package/dist/ast.d.ts.map +0 -1
- package/dist/capabilities.d.ts.map +0 -1
- package/dist/codecs.d.ts.map +0 -1
- package/dist/composable.d.ts.map +0 -1
- package/dist/dialect.d.ts.map +0 -1
- package/dist/dialects/constraint-owned-index.d.ts.map +0 -1
- package/dist/dialects/diagnostics.d.ts.map +0 -1
- package/dist/dialects/external-dependents.d.ts.map +0 -1
- package/dist/dialects/external-ownership.d.ts.map +0 -1
- package/dist/dialects/migration-history.d.ts.map +0 -1
- package/dist/dialects/migration-inventory.d.ts.map +0 -1
- package/dist/dialects/migration-objects.d.ts.map +0 -1
- package/dist/dialects/migration-replay-coverage.d.ts.map +0 -1
- package/dist/dialects/migration-shadow.d.ts.map +0 -1
- package/dist/dialects/migration-visibility.d.ts.map +0 -1
- package/dist/dialects/module-dependency.d.ts.map +0 -1
- package/dist/dialects/mysql-check-probe.d.ts.map +0 -1
- package/dist/dialects/mysql.d.ts.map +0 -1
- package/dist/dialects/postgres-default-identity.d.ts.map +0 -1
- package/dist/dialects/postgres-diagnostics.d.ts.map +0 -1
- package/dist/dialects/postgres-index-keys.d.ts.map +0 -1
- package/dist/dialects/postgres-migration.d.ts.map +0 -1
- package/dist/dialects/postgres-physical-catalog.d.ts.map +0 -1
- package/dist/dialects/postgres-rls-readiness.d.ts.map +0 -1
- package/dist/dialects/postgres-rls.d.ts.map +0 -1
- package/dist/dialects/postgres-type-identity.d.ts.map +0 -1
- package/dist/dialects/postgres.d.ts.map +0 -1
- package/dist/dialects/registry.d.ts.map +0 -1
- package/dist/dialects/sqlite-constraints.d.ts.map +0 -1
- package/dist/dialects/sqlite-session.d.ts.map +0 -1
- package/dist/dialects/sqlite-table-definition.d.ts.map +0 -1
- package/dist/dialects/sqlite.d.ts.map +0 -1
- package/dist/exclusion.d.ts.map +0 -1
- package/dist/identifiers.d.ts.map +0 -1
- package/dist/index-columns.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/json-transport.d.ts.map +0 -1
- package/dist/like.d.ts.map +0 -1
- package/dist/locks.d.ts.map +0 -1
- package/dist/module-journal.d.ts.map +0 -1
- package/dist/native-enum-parameter.d.ts.map +0 -1
- package/dist/params.d.ts.map +0 -1
- package/dist/pg-array.d.ts.map +0 -1
- package/dist/predicate.d.ts.map +0 -1
- package/dist/render.d.ts.map +0 -1
- package/dist/result-decoder.d.ts.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VibeORM contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/ast.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { NativeEnumArrayParameter } from "./native-enum-parameter.ts";
|
|
2
|
+
import type { JsonListParameter } from "./json-list-parameter.ts";
|
|
2
3
|
import type { SqlFragment } from "./composable.ts";
|
|
3
4
|
import type { RlsReadinessQuery } from "./dialects/postgres-rls-readiness.ts";
|
|
4
5
|
/**
|
|
@@ -15,13 +16,20 @@ export type ColRef = {
|
|
|
15
16
|
readonly table?: string;
|
|
16
17
|
readonly name: string;
|
|
17
18
|
/**
|
|
18
|
-
* Projection-only wire transform
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
19
|
+
* Projection-only wire transform. `jsonText` uses the dialect's JSON text
|
|
20
|
+
* projection to preserve SQL NULL versus the JSON document `null` before
|
|
21
|
+
* driver parsing, without converting a postgres `json` column to `jsonb`.
|
|
22
|
+
* The other transforms are postgres only. `castText` renders
|
|
23
|
+
* `"col"::text AS "col"`, `encodeBase64` renders
|
|
24
|
+
* `encode("col", 'base64') AS "col"`. Two users: the lateral subselect (SQL
|
|
25
|
+
* review B3 — raw `to_jsonb` would ship BigInt/Decimal as float64 JSON
|
|
26
|
+
* numbers and Bytes as `\x` hex text), and every row projection of a
|
|
27
|
+
* `Float[]`/`Json[]` column (bun:sql's array parser refuses float arrays
|
|
28
|
+
* printed with an exponent and json arrays holding integers beyond int32;
|
|
29
|
+
* the runtime parses the array literal text itself). Never set on
|
|
30
|
+
* filter/order refs.
|
|
23
31
|
*/
|
|
24
|
-
readonly transform?: "castText" | "encodeBase64";
|
|
32
|
+
readonly transform?: "castText" | "encodeBase64" | "jsonText";
|
|
25
33
|
/**
|
|
26
34
|
* Comparison/order/aggregate cast, sqlite Decimal only (SQL review B4):
|
|
27
35
|
* Decimal stores as exact TEXT there and text comparison is lexicographic —
|
|
@@ -30,6 +38,14 @@ export type ColRef = {
|
|
|
30
38
|
* storage and read-back stay the exact text.
|
|
31
39
|
*/
|
|
32
40
|
readonly castReal?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Order/distinct/compare cast, postgres `json` columns only (`Json @db.Json`,
|
|
43
|
+
* release-3.0 D3): `json` has no ordering or equality operator, so ORDER BY,
|
|
44
|
+
* DISTINCT ON and comparison sites render `"col"::jsonb` (`"col"::jsonb[]`
|
|
45
|
+
* for a `json[]` list column) — the order and distinctness of a jsonb
|
|
46
|
+
* column. Never set on projections, so read-back keeps the stored text.
|
|
47
|
+
*/
|
|
48
|
+
readonly castJsonb?: "jsonb" | "jsonb[]";
|
|
33
49
|
/**
|
|
34
50
|
* Projection alias: renders `ref AS "alias"`. PROJECTION-ONLY — filter,
|
|
35
51
|
* order and correlation refs never set it (the renderer ignores it outside
|
|
@@ -93,6 +109,11 @@ export type SqlExpr = {
|
|
|
93
109
|
readonly column: ColRef;
|
|
94
110
|
readonly values: readonly unknown[];
|
|
95
111
|
readonly negate?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The column holds a time of day (`@db.Time(n)` / `@db.Timetz(n)`): the
|
|
114
|
+
* dialect renders the membership per `SqlDialect.timeOfDayInStrategy`.
|
|
115
|
+
*/
|
|
116
|
+
readonly timeOfDay?: boolean;
|
|
96
117
|
}
|
|
97
118
|
/**
|
|
98
119
|
* ORDERED-TUPLE membership: `("a", "b") IN ((?, ?), (?, ?))`. The composite
|
|
@@ -183,13 +204,13 @@ export type SqlExpr = {
|
|
|
183
204
|
| {
|
|
184
205
|
readonly kind: "arrayContainsAll";
|
|
185
206
|
readonly column: ColRef;
|
|
186
|
-
readonly values: readonly unknown[] | string | NativeEnumArrayParameter;
|
|
207
|
+
readonly values: readonly unknown[] | string | NativeEnumArrayParameter | JsonListParameter;
|
|
187
208
|
}
|
|
188
209
|
/** Scalar-list overlap: `"col" && $n` (one array param) — Prisma's `hasSome`. Postgres only. */
|
|
189
210
|
| {
|
|
190
211
|
readonly kind: "arrayOverlaps";
|
|
191
212
|
readonly column: ColRef;
|
|
192
|
-
readonly values: readonly unknown[] | string | NativeEnumArrayParameter;
|
|
213
|
+
readonly values: readonly unknown[] | string | NativeEnumArrayParameter | JsonListParameter;
|
|
193
214
|
}
|
|
194
215
|
/**
|
|
195
216
|
* Scalar-list emptiness via `array_length("col", 1) IS [NOT] NULL` — the v1
|
|
@@ -216,6 +237,15 @@ export type SqlExpr = {
|
|
|
216
237
|
readonly value: unknown;
|
|
217
238
|
readonly insensitive?: boolean;
|
|
218
239
|
readonly negate?: boolean;
|
|
240
|
+
/**
|
|
241
|
+
* The column's postgres type: absent (or `jsonb`) for the default
|
|
242
|
+
* `jsonb`, `json` for `Json @db.Json`. Postgres defines `=`, `<>` and
|
|
243
|
+
* `@>` for jsonb only, so on a `json` column every op casts the
|
|
244
|
+
* EXTRACTED value to jsonb (the string ops then read it as text), which
|
|
245
|
+
* gives a jsonb column's answer. Sqlite and mysql have one JSON type
|
|
246
|
+
* and ignore it.
|
|
247
|
+
*/
|
|
248
|
+
readonly storage?: "jsonb" | "json";
|
|
219
249
|
}
|
|
220
250
|
/**
|
|
221
251
|
* A bound extension fragment: literal text, bound parameters and quoted
|
|
@@ -369,6 +399,24 @@ export type LateralJoin = {
|
|
|
369
399
|
/** The correlated child select — may reference outer columns via table-qualified refs. */
|
|
370
400
|
readonly select: SelectStatement;
|
|
371
401
|
};
|
|
402
|
+
/**
|
|
403
|
+
* One correlated scalar `COUNT(*)` projected into a select list:
|
|
404
|
+
* `(SELECT COUNT(*) FROM … WHERE <correlation>) AS "alias"` — the relation
|
|
405
|
+
* `_count` of the round-trip campaign's R-03b, which saves the separate grouped
|
|
406
|
+
* count statement. Portable: postgres, sqlite and mysql all evaluate a
|
|
407
|
+
* correlated scalar subquery once per output row; the runtime wraps a paged
|
|
408
|
+
* root in a derived table first, so the subqueries run only for the page.
|
|
409
|
+
* The select must be ONE ungrouped `COUNT(*)` (`columns: []`, `selectExpr`
|
|
410
|
+
* countAll, no grouping, paging, distinct or lock) — anything else is refused
|
|
411
|
+
* at render. Its parameters join the statement's single sequence in text order
|
|
412
|
+
* (the select list renders before FROM).
|
|
413
|
+
*/
|
|
414
|
+
export type CountSubquery = {
|
|
415
|
+
/** Output column the count is exposed under (`__vibe_count_0`). */
|
|
416
|
+
readonly alias: string;
|
|
417
|
+
/** The correlated count select — references outer columns via table-qualified refs. */
|
|
418
|
+
readonly select: SelectStatement;
|
|
419
|
+
};
|
|
372
420
|
/**
|
|
373
421
|
* One `INNER JOIN "table" AS alias ON left = right [AND …]` clause — plain
|
|
374
422
|
* equality joins only, portable across all dialects. Added for the
|
|
@@ -384,6 +432,75 @@ export type InnerJoin = {
|
|
|
384
432
|
readonly right: ColRef;
|
|
385
433
|
}[];
|
|
386
434
|
};
|
|
435
|
+
/**
|
|
436
|
+
* How one member of a {@link KeyTableJoin} tuple binds inside the derived
|
|
437
|
+
* table. `value` binds the parameter as it is: it stays COERCIBLE, so a text
|
|
438
|
+
* column's OWN collation decides `=` — the equality `col IN (?, …)` uses.
|
|
439
|
+
* `integer` and `decimal` cast the parameter into the column's numeric family:
|
|
440
|
+
* BigInt and Decimal values travel as strings, and a derived-table string
|
|
441
|
+
* compared with a numeric column goes through a double (measured on mysql:
|
|
442
|
+
* `9007199254740993` matched `9007199254740992`).
|
|
443
|
+
*
|
|
444
|
+
* `integer` renders `CAST(? AS SIGNED)`. `decimal` renders
|
|
445
|
+
* `CAST(? AS DECIMAL(65, scale))` with the tested column's own scale: 65 is
|
|
446
|
+
* MySQL's maximum precision, so the cast holds every value a
|
|
447
|
+
* `DECIMAL(p, scale)` column can hold. A fixed `DECIMAL(65,30)` held only 35
|
|
448
|
+
* integer digits and clipped a `Decimal(65, 0)` key onto its maximum, which
|
|
449
|
+
* matched nothing (review finding 1); the column's own precision would clip a
|
|
450
|
+
* wider value onto the column's maximum and pair it with a row it does not
|
|
451
|
+
* equal (measured: `CAST('100000' AS DECIMAL(5,0))` is `99999`). `scale` is
|
|
452
|
+
* 0–30, MySQL's range.
|
|
453
|
+
*
|
|
454
|
+
* `citext` (postgres only) renders `CAST($n AS citext)`: a postgres `citext`
|
|
455
|
+
* key compares case-insensitively, and a bare VALUES parameter would be
|
|
456
|
+
* `text`, whose `=` against a citext column is the case-sensitive text
|
|
457
|
+
* operator. A VALUES cell has no column to infer a type from, so every member
|
|
458
|
+
* of a postgres key table binds typed: `citext`, `text` (`CAST($n AS text)`,
|
|
459
|
+
* a text or varchar column) or `integer` (`CAST($n AS bigint)`, exact against
|
|
460
|
+
* any integer column) — any other member refuses on postgres. `text` is
|
|
461
|
+
* postgres-only; mysql binds text members as `value`.
|
|
462
|
+
*/
|
|
463
|
+
export type KeyTableMember = {
|
|
464
|
+
readonly kind: "value";
|
|
465
|
+
} | {
|
|
466
|
+
readonly kind: "integer";
|
|
467
|
+
} | {
|
|
468
|
+
readonly kind: "decimal";
|
|
469
|
+
readonly scale: number;
|
|
470
|
+
} | {
|
|
471
|
+
readonly kind: "citext";
|
|
472
|
+
} | {
|
|
473
|
+
readonly kind: "text";
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* The batch's key tuples as a derived table, each tagged with its ORDINAL
|
|
477
|
+
* (row `i` carries `i`), INNER JOINed on the tested columns — mysql:
|
|
478
|
+
* `INNER JOIN (VALUES ROW(?, 0), ROW(?, 1)) AS \`alias\` (\`__vibe_k0\`, \`ordinal\`)
|
|
479
|
+
* ON col = \`alias\`.\`__vibe_k0\``; postgres only over `citext` members
|
|
480
|
+
* (`(VALUES (CAST($1 AS citext), 0), …)`, release 3.0 nested writes).
|
|
481
|
+
* Member columns are named `__vibe_k<i>` (a reserved prefix, so unqualified
|
|
482
|
+
* child columns never collide with them).
|
|
483
|
+
*
|
|
484
|
+
* Why it exists (EPIC R): a relation load used to stitch children to parents
|
|
485
|
+
* by comparing decoded keys in JavaScript, while mysql's collations equate
|
|
486
|
+
* spellings (`acme` = `Acme` = `ACME`, `cafe` = `Café`, `x ` = `x`) that
|
|
487
|
+
* JavaScript keeps apart — the foreign key and the `IN` filter accepted the
|
|
488
|
+
* child, the stitch dropped it. Joined this way the DATABASE pairs every child
|
|
489
|
+
* row with every batch key it considers equal, and the projected ordinal names
|
|
490
|
+
* the key; the join is also the membership filter.
|
|
491
|
+
*/
|
|
492
|
+
export type KeyTableJoin = {
|
|
493
|
+
/** Derived-table alias (`__vibe_keys`); the ordinal projects as `alias.ordinalAlias`. */
|
|
494
|
+
readonly alias: string;
|
|
495
|
+
/** The statement's columns, compared member by member (`=`), in tuple order. */
|
|
496
|
+
readonly columns: readonly ColRef[];
|
|
497
|
+
/** How each member binds, in tuple order — see {@link KeyTableMember}. */
|
|
498
|
+
readonly members: readonly KeyTableMember[];
|
|
499
|
+
/** Encoded key tuples; never empty, each as long as `columns`. */
|
|
500
|
+
readonly rows: readonly (readonly unknown[])[];
|
|
501
|
+
/** Output name of the ordinal column. */
|
|
502
|
+
readonly ordinalAlias: string;
|
|
503
|
+
};
|
|
387
504
|
/**
|
|
388
505
|
* A `ROW_NUMBER() OVER (PARTITION BY … [ORDER BY …]) AS "alias"` projection,
|
|
389
506
|
* appended after `columns` — the distinct emulation on dialects without
|
|
@@ -413,6 +530,8 @@ export type SelectStatement = {
|
|
|
413
530
|
* combined with `selectExpr`.
|
|
414
531
|
*/
|
|
415
532
|
readonly aggregates?: readonly AggSelect[];
|
|
533
|
+
/** Correlated `COUNT(*)` subqueries, rendered after `aggregates` — see {@link CountSubquery}. */
|
|
534
|
+
readonly countSubqueries?: readonly CountSubquery[];
|
|
416
535
|
/**
|
|
417
536
|
* `SELECT DISTINCT ON (cols)` — postgres only (`capabilities.distinctOn`;
|
|
418
537
|
* other dialects refuse at render time and the runtime emulates client-side
|
|
@@ -430,6 +549,8 @@ export type SelectStatement = {
|
|
|
430
549
|
readonly lateralJoins?: readonly LateralJoin[];
|
|
431
550
|
/** Plain equality INNER JOINs — portable, see {@link InnerJoin}. */
|
|
432
551
|
readonly innerJoins?: readonly InnerJoin[];
|
|
552
|
+
/** Key-table join, rendered after `innerJoins` — mysql only, see {@link KeyTableJoin}. */
|
|
553
|
+
readonly keyTable?: KeyTableJoin;
|
|
433
554
|
/** ROW_NUMBER window projection — see {@link RowNumberProjection}. */
|
|
434
555
|
readonly rowNumber?: RowNumberProjection;
|
|
435
556
|
readonly where?: SqlExpr;
|
|
@@ -482,14 +603,21 @@ export declare function isSqlDefaultCell(value: unknown): boolean;
|
|
|
482
603
|
* Sqlite/mysql render the bare placeholder — their Json transport is already
|
|
483
604
|
* plain text. The COLLECTED parameter is the plain text either way: this
|
|
484
605
|
* wrapper never crosses the wire.
|
|
606
|
+
*
|
|
607
|
+
* `storage: "json"` marks a postgres `json` column (`Json @db.Json`): the cell
|
|
608
|
+
* renders `$n::text::json`, so the column stores the text as written (key
|
|
609
|
+
* order and spelling kept) instead of jsonb's normalized text (release-3.0
|
|
610
|
+
* D3). Absent = jsonb; dialects without a param cast ignore it.
|
|
485
611
|
*/
|
|
486
612
|
export type JsonParamCell = {
|
|
487
613
|
readonly __jsonParam: true;
|
|
488
614
|
readonly text: string;
|
|
615
|
+
readonly storage?: "json";
|
|
489
616
|
};
|
|
490
617
|
/** Wrap a Json column's encoded (stringified) parameter for cast-site rendering. */
|
|
491
618
|
export declare function jsonParamCell(params: {
|
|
492
619
|
text: string;
|
|
620
|
+
storage?: "json";
|
|
493
621
|
}): JsonParamCell;
|
|
494
622
|
/** Is this VALUES/SET cell a {@link JsonParamCell}? */
|
|
495
623
|
export declare function isJsonParamCell(value: unknown): value is JsonParamCell;
|
|
@@ -511,6 +639,21 @@ export declare function excludedRef(params: {
|
|
|
511
639
|
}): ExcludedRef;
|
|
512
640
|
/** Is this SET cell an {@link ExcludedRef}? */
|
|
513
641
|
export declare function isExcludedRef(value: unknown): value is ExcludedRef;
|
|
642
|
+
/**
|
|
643
|
+
* One RETURNING column: a bare column name renders `"col"`; `castText` renders
|
|
644
|
+
* `"col"::text AS "col"` (postgres only) for `Float[]`/`Json[]`. `jsonText`
|
|
645
|
+
* applies the dialect's JSON text projection, as {@link ColRef.transform} does
|
|
646
|
+
* for SELECT. Dialects without RETURNING still omit the clause and flag it.
|
|
647
|
+
*/
|
|
648
|
+
export type ReturningColumn = string | {
|
|
649
|
+
readonly name: string;
|
|
650
|
+
readonly castText: true;
|
|
651
|
+
} | {
|
|
652
|
+
readonly name: string;
|
|
653
|
+
readonly transform: "jsonText";
|
|
654
|
+
};
|
|
655
|
+
/** The column name a {@link ReturningColumn} exposes in the result row. */
|
|
656
|
+
export declare function returningColumnName(column: ReturningColumn): string;
|
|
514
657
|
export type InsertStatement = {
|
|
515
658
|
readonly kind: "insert";
|
|
516
659
|
readonly table: TableRef;
|
|
@@ -520,7 +663,7 @@ export type InsertStatement = {
|
|
|
520
663
|
/** Upsert clause: ON CONFLICT (pg/sqlite) or ON DUPLICATE KEY (mysql). */
|
|
521
664
|
readonly onConflict?: OnConflictClause;
|
|
522
665
|
/** Rendered as RETURNING where the dialect supports it, else omitted+flagged. */
|
|
523
|
-
readonly returning?: readonly
|
|
666
|
+
readonly returning?: readonly ReturningColumn[];
|
|
524
667
|
};
|
|
525
668
|
/** Conflicting rows are updated with these column values. */
|
|
526
669
|
export type OnConflictUpdate = {
|
|
@@ -577,13 +720,13 @@ export type UpdateStatement = {
|
|
|
577
720
|
/** Column → parameter value, or an atomic {@link SetOperation}. */
|
|
578
721
|
readonly set: Readonly<Record<string, unknown>>;
|
|
579
722
|
readonly where?: SqlExpr;
|
|
580
|
-
readonly returning?: readonly
|
|
723
|
+
readonly returning?: readonly ReturningColumn[];
|
|
581
724
|
};
|
|
582
725
|
export type DeleteStatement = {
|
|
583
726
|
readonly kind: "delete";
|
|
584
727
|
readonly table: TableRef;
|
|
585
728
|
readonly where?: SqlExpr;
|
|
586
|
-
readonly returning?: readonly
|
|
729
|
+
readonly returning?: readonly ReturningColumn[];
|
|
587
730
|
};
|
|
588
731
|
/** Native RLS DDL, kept distinct from ordinary row-query expressions. */
|
|
589
732
|
export type RlsEnableStatement = {
|
|
@@ -663,7 +806,191 @@ export type AdvisoryLockStatement = {
|
|
|
663
806
|
};
|
|
664
807
|
/** The column an advisory try-lock projects its verdict into. */
|
|
665
808
|
export declare const ADVISORY_LOCK_RESULT_COLUMN: string;
|
|
666
|
-
|
|
809
|
+
/**
|
|
810
|
+
* A cell of an {@link InsertSelectStatement} that reads a column of an EARLIER
|
|
811
|
+
* chain step: `"step"."column"`. A column reference, not a parameter — the
|
|
812
|
+
* value comes from the step's rows inside the same statement (a child row
|
|
813
|
+
* taking its parent's key from the parent step, round-trip campaign EPIC F).
|
|
814
|
+
* The step must be named in the insert's `from` list.
|
|
815
|
+
*/
|
|
816
|
+
export type StepColumnRef = {
|
|
817
|
+
readonly __stepColumn: true;
|
|
818
|
+
readonly step: string;
|
|
819
|
+
readonly column: string;
|
|
820
|
+
};
|
|
821
|
+
/** Reference `column` of the earlier chain step `step` from an INSERT … SELECT cell. */
|
|
822
|
+
export declare function stepColumnRef(params: {
|
|
823
|
+
step: string;
|
|
824
|
+
column: string;
|
|
825
|
+
}): StepColumnRef;
|
|
826
|
+
/** Is this INSERT … SELECT cell a {@link StepColumnRef}? */
|
|
827
|
+
export declare function isStepColumnRef(value: unknown): value is StepColumnRef;
|
|
828
|
+
/**
|
|
829
|
+
* `INSERT INTO t (cols) SELECT <cells> [FROM "step", …] [WHERE <expr>]
|
|
830
|
+
* [RETURNING …]` — an INSERT whose ONE row is a SELECT list instead of a
|
|
831
|
+
* VALUES row, so a WHERE can veto the write inside the statement and cells can
|
|
832
|
+
* read earlier steps. Valid ONLY as a {@link ChainStep} statement.
|
|
833
|
+
*
|
|
834
|
+
* Cells take the same forms as a VALUES cell — a codec-encoded value, a
|
|
835
|
+
* {@link JsonParamCell}, a {@link DbNowMarker} — plus {@link StepColumnRef}.
|
|
836
|
+
* The {@link SQL_DEFAULT_CELL} is refused: `DEFAULT` is not allowed in a SELECT
|
|
837
|
+
* list; an omitted column takes its default by being absent from `columns`.
|
|
838
|
+
*
|
|
839
|
+
* Parameter typing (O-03, probed on PostgreSQL): a bare parameter in this
|
|
840
|
+
* one-row SELECT list takes the TARGET column's type, exactly as in VALUES.
|
|
841
|
+
*/
|
|
842
|
+
export type InsertSelectStatement = {
|
|
843
|
+
readonly kind: "insertSelect";
|
|
844
|
+
readonly table: TableRef;
|
|
845
|
+
readonly columns: readonly string[];
|
|
846
|
+
/** One cell per column, aligned to `columns` — deterministic parameter order. */
|
|
847
|
+
readonly cells: readonly unknown[];
|
|
848
|
+
/** Earlier chain steps the SELECT reads FROM, in order (absent: a one-row SELECT without FROM). */
|
|
849
|
+
readonly from?: readonly string[];
|
|
850
|
+
/**
|
|
851
|
+
* Several source rows (round-trip campaign EPIC F): the SELECT reads them
|
|
852
|
+
* from a derived table `alias`, joined after the `from` steps, and the
|
|
853
|
+
* `cells` read its columns through `stepColumnRef({ step: alias, column })`.
|
|
854
|
+
* See {@link ChainRowSource} for how the rows are typed.
|
|
855
|
+
*/
|
|
856
|
+
readonly source?: ChainRowSource;
|
|
857
|
+
readonly where?: SqlExpr;
|
|
858
|
+
readonly returning?: readonly ReturningColumn[];
|
|
859
|
+
};
|
|
860
|
+
/**
|
|
861
|
+
* Rows an {@link InsertSelectStatement} inserts, as ONE derived table:
|
|
862
|
+
* `(SELECT <cols> FROM (VALUES (<typed NULL row>), ($1, $2), …) AS "alias"
|
|
863
|
+
* (<cols>) OFFSET 1) AS "alias"`.
|
|
864
|
+
*
|
|
865
|
+
* Why the typed NULL row (review O-03): a bare parameter outside the INSERT's
|
|
866
|
+
* own VALUES has no type, and PostgreSQL resolves an all-parameter column of a
|
|
867
|
+
* VALUES list or UNION in FROM as `text` ("column is of type integer but
|
|
868
|
+
* expression is of type text"). The first row is one `(SELECT "col" FROM
|
|
869
|
+
* <typedBy> WHERE FALSE)` per column — a NULL of the TARGET column's exact
|
|
870
|
+
* type (enums, `numeric(p,s)`, `varchar(n)`, domains and arrays included) —
|
|
871
|
+
* so every parameter below takes that type, with no cast map anywhere;
|
|
872
|
+
* `OFFSET 1` drops the row. (A `UNION ALL` of one-row SELECTs types the same
|
|
873
|
+
* way but costs ~10× the planning time at 1000 rows — EPIC F shape gate.)
|
|
874
|
+
*
|
|
875
|
+
* Cells take the VALUES forms: a codec-encoded value, a {@link JsonParamCell}
|
|
876
|
+
* or a {@link DbNowMarker}; the DEFAULT cell is refused (a homogeneous row set
|
|
877
|
+
* omits the column instead). A cell carrying its OWN type (the Json cast, the
|
|
878
|
+
* clock) must agree with the column's type under VALUES' common-type rules,
|
|
879
|
+
* which are stricter than INSERT's assignment casts — the builder keeps such
|
|
880
|
+
* columns out (EPIC F eligibility). Rows are read in list order, which is the
|
|
881
|
+
* order the INSERT consumes them.
|
|
882
|
+
*/
|
|
883
|
+
export type ChainRowSource = {
|
|
884
|
+
readonly alias: string;
|
|
885
|
+
/** The table whose columns type the rows — normally the INSERT's own table. */
|
|
886
|
+
readonly typedBy: TableRef;
|
|
887
|
+
readonly columns: readonly string[];
|
|
888
|
+
/** At least one row, each aligned to `columns`. */
|
|
889
|
+
readonly rows: readonly (readonly unknown[])[];
|
|
890
|
+
};
|
|
891
|
+
/** What one chain step may be: a read, or a data-modifying statement with RETURNING. */
|
|
892
|
+
export type ChainStepStatement = SelectStatement | InsertStatement | InsertSelectStatement | UpdateStatement | DeleteStatement;
|
|
893
|
+
/**
|
|
894
|
+
* One named step: rendered `"name" AS (<statement>)`. Later steps and the
|
|
895
|
+
* result read it as a table (`FROM "name"`, `EXISTS (SELECT 1 FROM "name" …)`,
|
|
896
|
+
* a {@link StepColumnRef}). A data-modifying step contributes the rows its
|
|
897
|
+
* RETURNING list names.
|
|
898
|
+
*/
|
|
899
|
+
export type ChainStep = {
|
|
900
|
+
readonly name: string;
|
|
901
|
+
readonly statement: ChainStepStatement;
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* The chain's final SELECT: the rows of `rowsFrom` (in that order, `UNION
|
|
905
|
+
* ALL`), each projecting `columns`.
|
|
906
|
+
*
|
|
907
|
+
* `verdict` adds one column `alias` = `expr` to every row, AND guarantees a
|
|
908
|
+
* row: when the `rowsFrom` steps return nothing, the result is exactly ONE
|
|
909
|
+
* verdict row whose `columns` are all NULL. A caller recognizes it by NULL in
|
|
910
|
+
* a column the steps never return as NULL (a primary key). Accounting
|
|
911
|
+
* consequence: such a statement returns one row per written row OR one
|
|
912
|
+
* verdict row — a caller that counts returned rows as row changes must undo a
|
|
913
|
+
* verdict-row outcome (the upsert fold always rolls it back).
|
|
914
|
+
*/
|
|
915
|
+
export type ChainResult = {
|
|
916
|
+
readonly rowsFrom: readonly string[];
|
|
917
|
+
readonly columns: readonly string[];
|
|
918
|
+
readonly verdict?: {
|
|
919
|
+
readonly alias: string;
|
|
920
|
+
readonly expr: SqlExpr;
|
|
921
|
+
};
|
|
922
|
+
/**
|
|
923
|
+
* In-statement vetoes (round-trip campaign EPIC F), checked in order: when
|
|
924
|
+
* `step` returned no row (fewer than `minRows`, EPIC W), the STATEMENT FAILS — so the database undoes every
|
|
925
|
+
* effect of every step, trigger writes included — with an error whose
|
|
926
|
+
* message carries `__vibe_veto:<token>:<nonce>:` ({@link CHAIN_VETO_PREFIX},
|
|
927
|
+
* {@link chainVetoToken}). Requires `tally`: the veto is the tally column's
|
|
928
|
+
* guard, so it is evaluated exactly once, on the one guaranteed result row.
|
|
929
|
+
*/
|
|
930
|
+
readonly veto?: readonly ChainVeto[];
|
|
931
|
+
/**
|
|
932
|
+
* One column `alias` on every result row: the number of rows the listed
|
|
933
|
+
* data-modifying steps returned (their RETURNING rows = their direct
|
|
934
|
+
* effects). Guarantees one row like `verdict`. Row-change accounting reads
|
|
935
|
+
* it instead of counting result rows (effect `writeTally`).
|
|
936
|
+
*/
|
|
937
|
+
readonly tally?: ChainTally;
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* Fail the statement when `step` returned no row; `token` names the veto in the
|
|
941
|
+
* error. `nonce` is bound as a PARAMETER into the marker (never SQL text): a
|
|
942
|
+
* per-call random value the caller requires back in the error, so a user value
|
|
943
|
+
* that merely looks like a marker is never read as a veto.
|
|
944
|
+
*
|
|
945
|
+
* `minRows` (EPIC W) makes the veto count-sensitive: the statement fails when
|
|
946
|
+
* `step` returned FEWER rows than that (a multi-row INSERT a trigger shortened).
|
|
947
|
+
* Omitted or 1 = "no row". A positive safe integer, rendered as SQL text.
|
|
948
|
+
*/
|
|
949
|
+
export type ChainVeto = {
|
|
950
|
+
readonly step: string;
|
|
951
|
+
readonly token: string;
|
|
952
|
+
readonly nonce: string;
|
|
953
|
+
readonly minRows?: number;
|
|
954
|
+
};
|
|
955
|
+
/** Count the RETURNING rows of `steps` into the result column `alias`. */
|
|
956
|
+
export type ChainTally = {
|
|
957
|
+
readonly alias: string;
|
|
958
|
+
readonly steps: readonly string[];
|
|
959
|
+
};
|
|
960
|
+
/** Marker every chain veto error message carries, followed by `<token>:<nonce>:`. */
|
|
961
|
+
export declare const CHAIN_VETO_PREFIX: string;
|
|
962
|
+
/** Allowed veto tokens: lower-case words — they are rendered into SQL text. */
|
|
963
|
+
export declare const CHAIN_VETO_TOKEN_PATTERN: RegExp;
|
|
964
|
+
/**
|
|
965
|
+
* The veto token a database error message carries for this `nonce`, or
|
|
966
|
+
* `undefined`. Matches the marker anywhere in the text, so a localized server
|
|
967
|
+
* message (`lc_messages`) still yields it: the marker is the invalid VALUE the
|
|
968
|
+
* message quotes. A marker without the caller's nonce is not a veto.
|
|
969
|
+
*/
|
|
970
|
+
export declare function chainVetoToken(params: {
|
|
971
|
+
message: string;
|
|
972
|
+
nonce: string;
|
|
973
|
+
}): string | undefined;
|
|
974
|
+
/**
|
|
975
|
+
* ONE statement made of an ordered chain of named steps — reads (a locked
|
|
976
|
+
* probe) and data-modifying statements with RETURNING — ending in a final
|
|
977
|
+
* SELECT over them (round-trip campaign EPIC 4, reused by EPIC F). Renders as
|
|
978
|
+
* `WITH "a" AS (…), "b" AS (…) SELECT …` on a dialect whose
|
|
979
|
+
* `statementChain` strategy names the form (postgres); every other dialect
|
|
980
|
+
* refuses at render time (`VIBE_UNSUPPORTED_CAPABILITY`) — callers gate on
|
|
981
|
+
* the strategy first and keep their multi-statement path there.
|
|
982
|
+
*
|
|
983
|
+
* PostgreSQL semantics the builder relies on: every step sees ONE snapshot
|
|
984
|
+
* (a step never sees another step's writes — only its RETURNING rows), steps
|
|
985
|
+
* that reference a step run after it, and the statement is atomic: an error in
|
|
986
|
+
* any step undoes every step's effects, trigger writes included.
|
|
987
|
+
*/
|
|
988
|
+
export type ChainStatement = {
|
|
989
|
+
readonly kind: "chain";
|
|
990
|
+
readonly steps: readonly ChainStep[];
|
|
991
|
+
readonly result: ChainResult;
|
|
992
|
+
};
|
|
993
|
+
export type SqlStatement = SelectStatement | InsertStatement | UpdateStatement | DeleteStatement | RlsEnableStatement | RlsForceStatement | RlsDisableStatement | RlsNoForceStatement | RlsDropPolicyStatement | RlsPolicyStatement | RlsContextStatement | RlsRoleStatement | AdvisoryLockStatement | ChainStatement;
|
|
667
994
|
/** Readiness statements remain distinct from application queries. */
|
|
668
995
|
export type RlsReadinessStatement = {
|
|
669
996
|
readonly kind: "rlsReadiness";
|
package/dist/codecs.d.ts
CHANGED
|
@@ -113,6 +113,63 @@ export declare function decodeIsNoop(params: {
|
|
|
113
113
|
export declare function parseDateTimeText(params: {
|
|
114
114
|
text: string;
|
|
115
115
|
}): Date | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* The time-of-day codec for a DateTime field's native type, or `undefined`
|
|
118
|
+
* when the native type is not `time`/`timetz` (the DateTime table row
|
|
119
|
+
* applies). The one native-type rule is `timeOfDayType` in @vibeorm/schema,
|
|
120
|
+
* shared with the dialect refusal in schema validation.
|
|
121
|
+
*/
|
|
122
|
+
export declare function timeOfDayCodec(params: {
|
|
123
|
+
nativeType: string | undefined;
|
|
124
|
+
}): ScalarCodec | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Parse stored JSON text at the centralized codec boundary. A supplied context
|
|
127
|
+
* marks a bound field: its error contains only that context, never the document
|
|
128
|
+
* or the native parser cause. Without context, legacy codec errors are unchanged.
|
|
129
|
+
*/
|
|
130
|
+
export declare function parseJsonText(params: {
|
|
131
|
+
text: string;
|
|
132
|
+
context?: Readonly<Record<string, unknown>>;
|
|
133
|
+
}): unknown;
|
|
134
|
+
/**
|
|
135
|
+
* The SHORTEST decimal that round-trips through float32 to the same value as
|
|
136
|
+
* `value` — what postgres itself prints for a `real` (`0.1`, `3.4028235e+38`).
|
|
137
|
+
*
|
|
138
|
+
* A driver that reads a `real` in BINARY format (bun:sql with named
|
|
139
|
+
* statements) hands back the float32 widened to float64:
|
|
140
|
+
* `0.10000000149011612` for a stored `0.1`. Text-format drivers already give
|
|
141
|
+
* the short form, and for those this is the identity — the shortest form of a
|
|
142
|
+
* short value is itself — so it is safe on every driver. Smallest `p` in 1..9
|
|
143
|
+
* with `fround(toPrecision(p)) === fround(value)`; nine significant digits
|
|
144
|
+
* always identify a float32. `±0`, NaN and ±Infinity pass through.
|
|
145
|
+
*/
|
|
146
|
+
export declare function shortestFloat32(params: {
|
|
147
|
+
value: number;
|
|
148
|
+
}): number;
|
|
149
|
+
/**
|
|
150
|
+
* `Float @db.Real` — a refinement of the Float codec keyed by the native type,
|
|
151
|
+
* not by the scalar type, so it is not a table row: the runtime routes a
|
|
152
|
+
* field with `nativeType: "Real"` here. Decode = {@link numberCodec} then
|
|
153
|
+
* {@link shortestFloat32}. Real aggregates that postgres returns as `real`
|
|
154
|
+
* (`min`/`max`/`sum`) use it too; `avg` returns double precision and must not.
|
|
155
|
+
*/
|
|
156
|
+
export declare const float32Codec: ScalarCodec;
|
|
157
|
+
/**
|
|
158
|
+
* Element decoders for a postgres array that arrived as its TEXT literal
|
|
159
|
+
* (`{1e-07,NULL}`, `{"{\"a\": 1}"}`) — the runtime projects `Float[]`/`Json[]`
|
|
160
|
+
* as `::text`, and drivers return arrays whose type OID they do not know
|
|
161
|
+
* (`uuid[]` on bun:sql; `xml[]`, `bit[]`, `citext[]` on node-postgres) as the
|
|
162
|
+
* literal. Each element is postgres's text output for its type:
|
|
163
|
+
*
|
|
164
|
+
* - Boolean `t`/`f`, Bytes `\x` hex and Json text need their own conversion;
|
|
165
|
+
* - every other scalar's regular postgres codec already accepts the text form
|
|
166
|
+
* (`Number("1e-07")`, `Number("NaN")`, `Number("-0")`, BigInt/Decimal text,
|
|
167
|
+
* `parseDateTimeText` for `2024-06-01 12:34:56.789+00`), so `null` means
|
|
168
|
+
* "decode with the field's own codec".
|
|
169
|
+
*/
|
|
170
|
+
export declare function postgresArrayElementFromText(params: {
|
|
171
|
+
scalarType: ScalarType;
|
|
172
|
+
}): ((text: string) => unknown) | null;
|
|
116
173
|
/** postgres (and pglite — same wire behaviour). */
|
|
117
174
|
export declare const POSTGRES_CODECS: CodecTable;
|
|
118
175
|
/** sqlite — no boolean, date or json types; everything rides TEXT/INTEGER. */
|
package/dist/dialect.d.ts
CHANGED
|
@@ -72,6 +72,14 @@ export type ConflictKeyEquality = {
|
|
|
72
72
|
* created otherwise, and sqlite's default collating sequence is BINARY);
|
|
73
73
|
* mysql does not, because its server default `utf8mb4_0900_ai_ci` ignores
|
|
74
74
|
* case and accents.
|
|
75
|
+
*
|
|
76
|
+
* Consumers: the batched upsert's duplicate-key check
|
|
77
|
+
* (packages/runtime/src/bulk-upsert.ts) and the relation loader
|
|
78
|
+
* (packages/runtime/src/relation-loader.ts, `associatesInDatabase`). Where
|
|
79
|
+
* this is false, a relation key with a `String` member is paired with its
|
|
80
|
+
* parents by the DATABASE through a key table (`SelectStatement.keyTable`,
|
|
81
|
+
* mysql only) instead of by JavaScript string equality, which would drop
|
|
82
|
+
* children whose key the collation equates (`acme` under `Acme`).
|
|
75
83
|
*/
|
|
76
84
|
readonly textIsBinaryByDefault: boolean;
|
|
77
85
|
/** Character capacity of the default String column, if bounded. Also consumed by migrate. */
|
|
@@ -100,6 +108,15 @@ export type SqlDialect = {
|
|
|
100
108
|
* member, so composite membership always chunks against `paramBudget`.
|
|
101
109
|
*/
|
|
102
110
|
readonly tupleInStrategy: "rowValue" | "expand";
|
|
111
|
+
/**
|
|
112
|
+
* Membership on a time-of-day column (`inArray` with `timeOfDay`): `in`
|
|
113
|
+
* renders like any other membership; `equalities` renders an OR of `=`
|
|
114
|
+
* comparisons. MySQL 8.4 types a `?` inside a multi-value IN list against a
|
|
115
|
+
* TIME(n) column as TIME(6) and matches nothing unless n is 6 (live on
|
|
116
|
+
* 8.4.11; 26.7 matches); `=` converts the parameter to the column's TIME on
|
|
117
|
+
* every version.
|
|
118
|
+
*/
|
|
119
|
+
readonly timeOfDayInStrategy: "in" | "equalities";
|
|
103
120
|
/** Upsert clause form. */
|
|
104
121
|
readonly upsertForm: "onConflict" | "onDuplicateKey";
|
|
105
122
|
/** Which equality decides a duplicate conflict key here (batched upsert). */
|
|
@@ -136,6 +153,20 @@ export type SqlDialect = {
|
|
|
136
153
|
* non-native dialects — documented in docs/dialect-notes.md.
|
|
137
154
|
*/
|
|
138
155
|
readonly strictMatchStrategy: "native" | "glob" | "binary";
|
|
156
|
+
/**
|
|
157
|
+
* `sql.greatest` (NULL-ignoring two-operand maximum): `native` renders
|
|
158
|
+
* `GREATEST(l, r)`, whose PostgreSQL semantics already ignore NULL; `frame`
|
|
159
|
+
* binds both operands once in a correlated derived table and compares the
|
|
160
|
+
* bound columns with CASE (MySQL's GREATEST returns NULL on any NULL, and
|
|
161
|
+
* SQLite has none). `wrapped-frame` is the frame with each operand in its own
|
|
162
|
+
* scalar subquery: MySQL 8.4 refuses a derived-table select list that holds a
|
|
163
|
+
* nested frame beside a direct outer column reference (ER_ILLEGAL_REFERENCE
|
|
164
|
+
* 1247). In both frames a bare NULL value operand renders as the NULL
|
|
165
|
+
* literal. Only a node with a nested greatest operand uses a frame; a node
|
|
166
|
+
* without one keeps the 2.x CASE form (2.x semantics, types and aggregate
|
|
167
|
+
* scope), which repeats its two operands, so nesting stays linear.
|
|
168
|
+
*/
|
|
169
|
+
readonly greatestForm: "native" | "frame" | "wrapped-frame";
|
|
139
170
|
/** ORDER BY NULLS FIRST/LAST: native clause or `col IS NULL` emulation term. */
|
|
140
171
|
readonly orderByNullsMode: "native" | "emulate";
|
|
141
172
|
/** Dialects where OFFSET requires a LIMIT clause declare the filler literal. */
|
|
@@ -167,6 +198,8 @@ export type SqlDialect = {
|
|
|
167
198
|
* {@link JsonParamCell} cells and the json-filter operands.
|
|
168
199
|
*/
|
|
169
200
|
readonly jsonParamCast: string;
|
|
201
|
+
/** Physical types for standalone validated scalar values, without column inference. */
|
|
202
|
+
readonly valueCastTypes: Readonly<Record<ScalarType, string>>;
|
|
170
203
|
/**
|
|
171
204
|
* Statement that reads back the id generated by the previous INSERT, for
|
|
172
205
|
* dialects without RETURNING. `null` where RETURNING covers it. The single
|
|
@@ -197,6 +230,19 @@ export type SqlDialect = {
|
|
|
197
230
|
* rows are grouped by column set into separate statements there (M4).
|
|
198
231
|
*/
|
|
199
232
|
readonly rowDefaultKeyword: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* How a {@link ChainStatement} (an ordered chain of read and data-modifying
|
|
235
|
+
* steps ending in one SELECT — round-trip campaign EPIC 4/F) renders, or
|
|
236
|
+
* `null` where the dialect cannot run one as a single statement.
|
|
237
|
+
* `dataModifyingCte` is PostgreSQL's `WITH "step" AS (INSERT/UPDATE/DELETE …
|
|
238
|
+
* RETURNING …) SELECT …` (PGlite included). SQLite and MySQL accept only
|
|
239
|
+
* SELECT inside WITH, so they are `null`: the runtime keeps its
|
|
240
|
+
* multi-statement path there (an optimisation, not a feature — plan D4) and
|
|
241
|
+
* the renderer refuses a chain with `VIBE_UNSUPPORTED_CAPABILITY`. An
|
|
242
|
+
* internal SQL-shape strategy, deliberately NOT in the public capability
|
|
243
|
+
* table (review O-11).
|
|
244
|
+
*/
|
|
245
|
+
readonly statementChain: "dataModifyingCte" | null;
|
|
200
246
|
/**
|
|
201
247
|
* Conservative bound-parameter budget per statement (M7). Hard limits:
|
|
202
248
|
* sqlite errors past SQLITE_MAX_VARIABLE_NUMBER (32766 by default), mysql
|
|
@@ -239,6 +285,14 @@ export type SqlDialect = {
|
|
|
239
285
|
placeholder(params: {
|
|
240
286
|
index: number;
|
|
241
287
|
}): string;
|
|
288
|
+
/**
|
|
289
|
+
* Project a JSON document as text while preserving SQL NULL. PostgreSQL
|
|
290
|
+
* casts to text, MySQL casts to CHAR and SQLite keeps its stored text. The
|
|
291
|
+
* expression is already rendered SQL, not an identifier or a bound value.
|
|
292
|
+
*/
|
|
293
|
+
jsonTextProjection(params: {
|
|
294
|
+
expression: string;
|
|
295
|
+
}): string;
|
|
242
296
|
/**
|
|
243
297
|
* The database-clock expression for `DbNowMarker` sites: `now()` on
|
|
244
298
|
* postgres, `NOW(6)` on mysql, and on sqlite an ISO-8601 `strftime` whose
|