@vibeorm/sql 5.8.0 → 5.10.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 +1 -0
- package/dist/ast.d.ts +293 -9
- package/dist/codecs.d.ts +106 -12
- package/dist/composable.d.ts +44 -4
- package/dist/dialect.d.ts +30 -2
- package/dist/dialects/diagnostics.d.ts +1 -1
- package/dist/dialects/migration-inventory.d.ts +0 -11
- package/dist/dialects/migration-shadow-marker.d.ts +65 -0
- package/dist/dialects/mysql-generated-columns.d.ts +1 -5
- package/dist/dialects/mysql-index-expressions.d.ts +1 -19
- package/dist/dialects/mysql-row-rules.d.ts +1 -7
- package/dist/dialects/postgres-generated-columns.d.ts +0 -5
- package/dist/dialects/postgres-index-expressions.d.ts +1 -9
- package/dist/dialects/postgres-rls-readiness.d.ts +0 -2
- package/dist/dialects/postgres-rls.d.ts +7 -1
- package/dist/dialects/postgres-row-rules.d.ts +1 -24
- package/dist/dialects/sqlite-fts.d.ts +2 -2
- package/dist/dialects/sqlite-generated-columns.d.ts +1 -12
- package/dist/dialects/sqlite-index-expressions.d.ts +1 -22
- package/dist/dialects/sqlite-json-equality.d.ts +1 -14
- package/dist/dialects/sqlite-row-rules.d.ts +1 -7
- package/dist/dialects/sqlite-table-definition.d.ts +0 -19
- package/dist/dialects/test-databases.d.ts +96 -0
- package/dist/expr-fragment.d.ts +13 -0
- package/dist/identifiers.d.ts +1 -4
- package/dist/index-expressions.d.ts +8 -2
- package/dist/index.d.ts +16 -7
- package/dist/index.js +4148 -2762
- package/dist/index.js.map +49 -42
- package/dist/json-patch.d.ts +82 -0
- package/dist/json-path.d.ts +2 -0
- package/dist/key-list.d.ts +0 -21
- package/dist/native-types.d.ts +72 -0
- package/dist/params.d.ts +0 -2
- package/dist/query/expressions.d.ts +1 -33
- package/dist/query/index.js.map +7 -7
- package/dist/query/mutations.d.ts +1 -14
- package/dist/query/reusable.d.ts +1 -16
- package/dist/query/select.d.ts +1 -10
- package/dist/query/sources.d.ts +1 -36
- package/dist/query/state.d.ts +1 -77
- package/dist/query-plan.d.ts +2 -2
- package/dist/render.d.ts +9 -0
- package/dist/result-decoder.d.ts +21 -1
- package/dist/row-rules.d.ts +1 -19
- package/dist/sql-bindings.d.ts +7 -43
- package/dist/sql-handles.d.ts +1 -3
- package/dist/sql-input.d.ts +8 -24
- package/dist/timestamp-identity.d.ts +6 -12
- package/dist/timestamp-text.d.ts +36 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Most users never install this directly — it ships as a dependency of [`vibeorm
|
|
|
18
18
|
- **Dialects**: `postgresDialect`, `sqliteDialect`, `mysqlDialect`, the `SqlDialect` type, and the registry `SQL_DIALECTS` / `getSqlDialect`. A dialect carries `quoteIdent` plus the strategy switches: `= ANY` versus `IN` expansion, `ILIKE` versus `LOWER(...) LIKE LOWER(...)`, the upsert form, `ESCAPE` clause presence, `RETURNING` availability.
|
|
19
19
|
- **Capability tables**: `DIALECT_CAPABILITIES`, `POSTGRES_CAPABILITIES`, `SQLITE_CAPABILITIES`, `MYSQL_CAPABILITIES` — frozen objects consumed by the runtime, migrate, the generator and the E2E harness. A feature a dialect cannot express throws `VIBE_UNSUPPORTED_CAPABILITY` rather than degrading.
|
|
20
20
|
- **Codec tables**: `DIALECT_CODECS`, `POSTGRES_CODECS`, `SQLITE_CODECS`, `MYSQL_CODECS`, `decodeIsNoop`, and the `CodecTable` / `ScalarCodec` / `WireFidelity` types. All per-dialect value encode and decode lives here, in one place, instead of scattered through query builders.
|
|
21
|
+
- **Column types**: `canonicalColumnType({ dialect, scalar, nativeType })` names the column type migrate creates for a field, in the one spelling migrate compares column types by (`"integer"` for `Int`, `@db.Int4` and `@db.Integer` on PostgreSQL); `normalizePostgresNativeType` / `normalizeMysqlNativeType`, the tables `POSTGRES_SCALAR_COLUMN_TYPES`, `MYSQL_SCALAR_COLUMN_TYPES`, `SQLITE_SCALAR_STORAGE_CLASSES` and `POSTGRES_EXACT_EQUIVALENTS` are the pieces. `describeSchema` (`@vibeorm/runtime`) reports each field's `nativeType` with it.
|
|
21
22
|
- **Rendering**: `renderStatement({ dialect, statement })` returns `RenderedQuery` (`{ text, values, returning }`); `renderExpr` and `renderFragment` render sub-parts against a shared `ParamCollector`. Placeholders always come from `ParamCollector`, never from hand-written `$1` or `?`.
|
|
22
23
|
- **LIKE helpers**: `escapeLikePattern`, `escapeGlobPattern`, `containsPattern`, `startsWithPattern`, `endsWithPattern`, `strictMatchPattern`.
|
|
23
24
|
|
package/dist/ast.d.ts
CHANGED
|
@@ -66,6 +66,25 @@ export type ColRef = {
|
|
|
66
66
|
* without the `dateTruncation` capability refuse it when rendering.
|
|
67
67
|
*/
|
|
68
68
|
readonly truncate?: DateTruncation;
|
|
69
|
+
/**
|
|
70
|
+
* A `groupBy` expression key read from the column ({@link GroupKeyExpression}):
|
|
71
|
+
* the text at a JSON path, or a string's leading characters. Like
|
|
72
|
+
* {@link ColRef.truncate} it is set on the projection (with `alias`, the
|
|
73
|
+
* key's result name), GROUP BY and ORDER BY refs of one key, which repeat
|
|
74
|
+
* the identical text: every operand is a validated literal, never a bound
|
|
75
|
+
* parameter, so PostgreSQL and MySQL match the GROUP BY expression.
|
|
76
|
+
*/
|
|
77
|
+
readonly groupKey?: GroupKeyExpression;
|
|
78
|
+
/**
|
|
79
|
+
* The first non-NULL of these references — `COALESCE("a", "b")` — instead
|
|
80
|
+
* of the column `name`: the value of an `orderBy: { _coalesce: … }` term.
|
|
81
|
+
* Each member renders as its own reference, casts included (a sqlite
|
|
82
|
+
* Decimal member as `CAST(… AS REAL)`, a mysql native enum as its member
|
|
83
|
+
* index). Set on ORDER BY and comparison refs only. `name` is then
|
|
84
|
+
* {@link COALESCE_REF_NAME}, never a column of a table, so a reader that
|
|
85
|
+
* looks a column up by name finds none.
|
|
86
|
+
*/
|
|
87
|
+
readonly coalesce?: readonly ColRef[];
|
|
69
88
|
/**
|
|
70
89
|
* Projection alias: renders `ref AS "alias"`. PROJECTION-ONLY — filter,
|
|
71
90
|
* order and correlation refs never set it (the renderer ignores it outside
|
|
@@ -74,6 +93,15 @@ export type ColRef = {
|
|
|
74
93
|
*/
|
|
75
94
|
readonly alias?: string;
|
|
76
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* The `name` of a {@link ColRef.coalesce} reference. The `__vibe_` column
|
|
98
|
+
* prefix is reserved by schema validation, so no model column carries it.
|
|
99
|
+
*/
|
|
100
|
+
export declare const COALESCE_REF_NAME: string;
|
|
101
|
+
/** A {@link ColRef.coalesce} reference over `members`. */
|
|
102
|
+
export declare function coalesceRef(params: {
|
|
103
|
+
members: readonly ColRef[];
|
|
104
|
+
}): ColRef;
|
|
77
105
|
/** A {@link ColRef.truncate} request: the calendar unit, the zone that defines the calendar, and the column's storage form. */
|
|
78
106
|
export type DateTruncation = {
|
|
79
107
|
readonly unit: DateTruncationUnit;
|
|
@@ -81,6 +109,24 @@ export type DateTruncation = {
|
|
|
81
109
|
readonly timeZone: string;
|
|
82
110
|
readonly storage: DateTruncationStorage;
|
|
83
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* A {@link ColRef.groupKey} expression.
|
|
114
|
+
*
|
|
115
|
+
* - `jsonPathText`: the text at an object-key path of a JSON column — a JSON
|
|
116
|
+
* string unquoted, a number or boolean in its JSON spelling, JSON `null` and
|
|
117
|
+
* a missing path SQL NULL, an object or array its JSON text. Each step reads
|
|
118
|
+
* an object member only. `storage: "json"` marks a postgres `json` column,
|
|
119
|
+
* read through jsonb like the JSON filters.
|
|
120
|
+
* - `prefix`: the first `length` characters of a string column.
|
|
121
|
+
*/
|
|
122
|
+
export type GroupKeyExpression = {
|
|
123
|
+
readonly kind: "jsonPathText";
|
|
124
|
+
readonly path: readonly string[];
|
|
125
|
+
readonly storage?: "json";
|
|
126
|
+
} | {
|
|
127
|
+
readonly kind: "prefix";
|
|
128
|
+
readonly length: number;
|
|
129
|
+
};
|
|
84
130
|
export type TableRef = {
|
|
85
131
|
readonly name: string;
|
|
86
132
|
/** Explicit fixed PostgreSQL placement; absence preserves existing SQL. */
|
|
@@ -92,11 +138,19 @@ export type AggFn = "count" | "avg" | "sum" | "min" | "max";
|
|
|
92
138
|
/** Reference to an aggregate expression: `COUNT(*)` or `FN("col")`. */
|
|
93
139
|
export type AggRef = {
|
|
94
140
|
readonly fn: "countAll";
|
|
141
|
+
readonly filter?: SqlExpr;
|
|
95
142
|
} | {
|
|
96
143
|
readonly fn: AggFn;
|
|
97
144
|
readonly column: ColRef;
|
|
98
145
|
/** Native enum MIN/MAX: projections and HAVING return the database label; order keys keep its ordinal. */
|
|
99
146
|
readonly enumOrder?: "value" | "ordinal";
|
|
147
|
+
/**
|
|
148
|
+
* Aggregate only the rows the predicate admits: `AGG(x) FILTER (WHERE …)`
|
|
149
|
+
* or `AGG(CASE WHEN … THEN x END)` per the dialect's
|
|
150
|
+
* `filteredAggregates` capability. Its parameters bind each time the
|
|
151
|
+
* aggregate renders (projection, HAVING, ORDER BY), in text order.
|
|
152
|
+
*/
|
|
153
|
+
readonly filter?: SqlExpr;
|
|
100
154
|
};
|
|
101
155
|
/**
|
|
102
156
|
* One aggregate projection: `FN("col") AS "alias"`. The alias is part of the
|
|
@@ -111,10 +165,13 @@ export type AggSelect = {
|
|
|
111
165
|
};
|
|
112
166
|
/**
|
|
113
167
|
* JSON path predicates (Prisma's Json filter surface). `not` is `equals` with
|
|
114
|
-
* `negate` — there is no separate op.
|
|
168
|
+
* `negate` — there is no separate op. `exists` takes a boolean `value`: the
|
|
169
|
+
* member at the path (object keys only) is present (its value may be JSON
|
|
170
|
+
* `null`) or, for `false`, the document is present and the member is not; a
|
|
171
|
+
* SQL NULL column is unknown for both. Which ops a dialect can express is
|
|
115
172
|
* declared on `SqlDialect.supportedJsonOps` and enforced at render time too.
|
|
116
173
|
*/
|
|
117
|
-
export type JsonFilterOp = "equals" | "string_contains" | "string_starts_with" | "string_ends_with" | "array_contains" | "array_starts_with" | "array_ends_with";
|
|
174
|
+
export type JsonFilterOp = "equals" | "exists" | "string_contains" | "string_starts_with" | "string_ends_with" | "array_contains" | "array_starts_with" | "array_ends_with";
|
|
118
175
|
export type ComparisonOp = "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
|
|
119
176
|
/**
|
|
120
177
|
* The column facts one member of a {@link SqlExpr} `keyList` tuple needs to
|
|
@@ -250,6 +307,8 @@ export type SqlExpr = {
|
|
|
250
307
|
/** The tested columns, in the subquery's projection order. */
|
|
251
308
|
readonly columns: readonly ColRef[];
|
|
252
309
|
readonly select: SelectStatement;
|
|
310
|
+
/** The subquery yields at most one row: one column, rendered `"fk" = (SELECT …)`. */
|
|
311
|
+
readonly single?: true;
|
|
253
312
|
} | {
|
|
254
313
|
readonly kind: "like";
|
|
255
314
|
readonly column: ColRef;
|
|
@@ -393,12 +452,20 @@ export type SqlExpr = {
|
|
|
393
452
|
*
|
|
394
453
|
* `one` renders the constant `1` — the conventional EXISTS projection; it
|
|
395
454
|
* cannot be combined with a column list.
|
|
455
|
+
*
|
|
456
|
+
* `exists` asks whether the statement's own FROM and WHERE match a row:
|
|
457
|
+
* `SELECT EXISTS (SELECT 1 FROM … WHERE …) AS "alias"` — one row, one
|
|
458
|
+
* boolean-valued column (PostgreSQL `boolean`, SQLite and MySQL `0`/`1`). It
|
|
459
|
+
* takes no column list, aggregate, grouping, order, paging, lock or join.
|
|
396
460
|
*/
|
|
397
461
|
export type SelectExpr = {
|
|
398
462
|
readonly kind: "countAll";
|
|
399
463
|
readonly alias?: string;
|
|
400
464
|
} | {
|
|
401
465
|
readonly kind: "one";
|
|
466
|
+
} | {
|
|
467
|
+
readonly kind: "exists";
|
|
468
|
+
readonly alias: string;
|
|
402
469
|
};
|
|
403
470
|
/** Arithmetic applied to a column's current value in an UPDATE ... SET. */
|
|
404
471
|
export type SetOperator = "increment" | "decrement" | "multiply" | "divide";
|
|
@@ -517,10 +584,18 @@ export type OrderTerm = OrderByColumn | OrderByAggregate | OrderBySubquery;
|
|
|
517
584
|
export type LateralJoin = {
|
|
518
585
|
/** Join alias (`__lat_0`) — the outer projection selects `alias.columnAlias`. */
|
|
519
586
|
readonly alias: string;
|
|
520
|
-
/**
|
|
587
|
+
/**
|
|
588
|
+
* Column the aggregated JSON value is exposed under (`__rel_posts`). An
|
|
589
|
+
* `aggregate` lateral exposes one column per entry of its select's
|
|
590
|
+
* `aggregates` instead, under each entry's alias.
|
|
591
|
+
*/
|
|
521
592
|
readonly columnAlias: string;
|
|
522
|
-
/**
|
|
523
|
-
|
|
593
|
+
/**
|
|
594
|
+
* jsonArray: many rows; jsonObject: one row or NULL; count: a scalar COUNT(*)
|
|
595
|
+
* lateral; aggregate: one ungrouped row of `aggregates` (no columns), which
|
|
596
|
+
* an aggregate without GROUP BY returns even over zero rows.
|
|
597
|
+
*/
|
|
598
|
+
readonly agg: "jsonArray" | "jsonObject" | "count" | "aggregate";
|
|
524
599
|
/**
|
|
525
600
|
* Object output is the SQL-layer default. Positional rows follow the exact
|
|
526
601
|
* select.columns order, using explicit output names (never SELECT *).
|
|
@@ -529,6 +604,13 @@ export type LateralJoin = {
|
|
|
529
604
|
* slot. Consumers must use the SAME projection to decode the payload.
|
|
530
605
|
*/
|
|
531
606
|
readonly rowFormat?: "object" | "positional";
|
|
607
|
+
/**
|
|
608
|
+
* Positional rows only: output names of `select.columns` the subselect
|
|
609
|
+
* projects for the statement's own use (an order key the aggregate's ORDER
|
|
610
|
+
* BY reads, a key a nested lateral correlates on) but the JSON row leaves
|
|
611
|
+
* out. The row is the remaining columns in `select.columns` order.
|
|
612
|
+
*/
|
|
613
|
+
readonly hidden?: readonly string[];
|
|
532
614
|
/** The correlated child select — may reference outer columns via table-qualified refs. */
|
|
533
615
|
readonly select: SelectStatement;
|
|
534
616
|
};
|
|
@@ -771,6 +853,20 @@ export type KeyRows = {
|
|
|
771
853
|
/** Encoded tuples; never empty, each exactly `columns.length` long. */
|
|
772
854
|
readonly rows: readonly (readonly unknown[])[];
|
|
773
855
|
};
|
|
856
|
+
/**
|
|
857
|
+
* `GROUP BY GROUPING SETS (…)` over a select's `groupBy` columns. Each set
|
|
858
|
+
* lists indexes into `groupBy`; `()` is the grand total. The projection gains
|
|
859
|
+
* `CASE GROUPING(<every groupBy column>) WHEN <mask> THEN <set index> … END AS
|
|
860
|
+
* "<indexAlias>"` after the aggregates, so a row names the set that produced
|
|
861
|
+
* it, and an ORDER BY term may name `indexAlias` as a bare column (an output
|
|
862
|
+
* column). Dialect `groupingSets` capability: `"groupingSets"` renders any
|
|
863
|
+
* list, `"rollup"` only the rollup chain of `groupBy` in order (`GROUP BY a,
|
|
864
|
+
* b WITH ROLLUP`), `"none"` refuses.
|
|
865
|
+
*/
|
|
866
|
+
export type GroupingSets = {
|
|
867
|
+
readonly sets: readonly (readonly number[])[];
|
|
868
|
+
readonly indexAlias: string;
|
|
869
|
+
};
|
|
774
870
|
export type SelectStatement = {
|
|
775
871
|
readonly kind: "select";
|
|
776
872
|
readonly table: TableRef;
|
|
@@ -850,6 +946,8 @@ export type SelectStatement = {
|
|
|
850
946
|
readonly fromKeys?: KeyRows;
|
|
851
947
|
readonly where?: SqlExpr;
|
|
852
948
|
readonly groupBy?: readonly ColRef[];
|
|
949
|
+
/** Grouping sets over `groupBy` — see {@link GroupingSets}. */
|
|
950
|
+
readonly groupingSets?: GroupingSets;
|
|
853
951
|
/** HAVING predicate — grouped selects; may contain `aggCmp` expressions. */
|
|
854
952
|
readonly having?: SqlExpr;
|
|
855
953
|
readonly orderBy?: readonly OrderTerm[];
|
|
@@ -971,7 +1069,37 @@ export type ReturningColumn = string | {
|
|
|
971
1069
|
} | {
|
|
972
1070
|
readonly name: string;
|
|
973
1071
|
readonly transform: "jsonText";
|
|
974
|
-
} | CaptureReturningColumn;
|
|
1072
|
+
} | CaptureReturningColumn | OtherRowReturningColumn | ServerVersionReturningColumn;
|
|
1073
|
+
/**
|
|
1074
|
+
* The server's `server_version_num` as text, exposed as `alias`:
|
|
1075
|
+
* `pg_catalog.current_setting('server_version_num') AS "alias"`. Rides a write
|
|
1076
|
+
* whose client does not know the server version yet, so the next statement can
|
|
1077
|
+
* take the form that version allows without a read of its own. Dialects with
|
|
1078
|
+
* the `rowCapture` capability only (the version decides whether it applies).
|
|
1079
|
+
*/
|
|
1080
|
+
export type ServerVersionReturningColumn = {
|
|
1081
|
+
readonly serverVersion: true;
|
|
1082
|
+
readonly alias: string;
|
|
1083
|
+
};
|
|
1084
|
+
/**
|
|
1085
|
+
* A RETURNING column read from a row OTHER than the one the statement wrote,
|
|
1086
|
+
* exposed as `alias` and projected like the written row's own column
|
|
1087
|
+
* (`projection`), so one codec decodes both:
|
|
1088
|
+
*
|
|
1089
|
+
* - `from: "old"` — the row as it was before the statement (capability
|
|
1090
|
+
* `rowCapture`, PostgreSQL 18): `old."col" AS "alias"`, NULL in every column
|
|
1091
|
+
* for a row the statement inserted;
|
|
1092
|
+
* - `from: { source }` — the joined source of an `UPDATE … FROM` by its alias
|
|
1093
|
+
* (capability `returningReadsJoinedSource`): `"source"."col" AS "alias"`.
|
|
1094
|
+
*/
|
|
1095
|
+
export type OtherRowReturningColumn = {
|
|
1096
|
+
readonly from: "old" | {
|
|
1097
|
+
readonly source: string;
|
|
1098
|
+
};
|
|
1099
|
+
readonly name: string;
|
|
1100
|
+
readonly alias: string;
|
|
1101
|
+
readonly projection?: "castText" | "jsonText";
|
|
1102
|
+
};
|
|
975
1103
|
/**
|
|
976
1104
|
* A row-capture RETURNING column (`$transaction(…, { capture })`, dialects with
|
|
977
1105
|
* the `rowCapture` capability: PostgreSQL 18, where RETURNING names the row
|
|
@@ -982,15 +1110,71 @@ export type ReturningColumn = string | {
|
|
|
982
1110
|
* inserted row, `new` for a deleted one);
|
|
983
1111
|
* - `"inserted"`: `(old."col" IS NULL) AS "alias"` — the arm of an `INSERT …
|
|
984
1112
|
* ON CONFLICT DO UPDATE` row, true when it was inserted (`col` must be a
|
|
985
|
-
* column no stored row holds NULL in: a primary-key column)
|
|
1113
|
+
* column no stored row holds NULL in: a primary-key column);
|
|
1114
|
+
* - `"old"` / `"new"` with `hop`: the related row's column, read in the same
|
|
1115
|
+
* statement — `(SELECT "__vibe_h"."col"::text FROM "Related" AS "__vibe_h"
|
|
1116
|
+
* WHERE "__vibe_h"."id" = new."relatedId") AS "alias"` (see {@link CaptureHop});
|
|
1117
|
+
* - `"key"`: `old."col" AS "alias"` in the column's own type, for a chain's
|
|
1118
|
+
* {@link CaptureCascade} to match child rows on;
|
|
1119
|
+
* - `"cascade"` ({@link CaptureCascadeReturningColumn}): the images of the
|
|
1120
|
+
* child rows a referential action of this row changes.
|
|
986
1121
|
*/
|
|
987
1122
|
export type CaptureReturningColumn = {
|
|
988
|
-
readonly capture: "old" | "new" | "inserted";
|
|
1123
|
+
readonly capture: "old" | "new" | "inserted" | "key";
|
|
989
1124
|
readonly name: string;
|
|
990
1125
|
readonly alias: string;
|
|
1126
|
+
/** With `"old"` / `"new"`: read `name` from the related row instead of this one. */
|
|
1127
|
+
readonly hop?: CaptureHop;
|
|
1128
|
+
} | CaptureCascadeReturningColumn;
|
|
1129
|
+
/**
|
|
1130
|
+
* A to-one hop of a capture cell: the row of `table` whose `on[i].target`
|
|
1131
|
+
* columns equal the reading row's `on[i].source` columns. The target columns
|
|
1132
|
+
* must be a unique key (a scalar subquery that found two rows would fail the
|
|
1133
|
+
* statement); no row, or a NULL source column, reads NULL.
|
|
1134
|
+
*/
|
|
1135
|
+
export type CaptureHop = {
|
|
1136
|
+
readonly table: TableRef;
|
|
1137
|
+
readonly on: readonly {
|
|
1138
|
+
readonly target: string;
|
|
1139
|
+
readonly source: string;
|
|
1140
|
+
}[];
|
|
1141
|
+
};
|
|
1142
|
+
/** One cell of a {@link CaptureCascade} image: a child column, or a column of its related row. */
|
|
1143
|
+
export type CaptureCell = {
|
|
1144
|
+
readonly name: string;
|
|
1145
|
+
readonly hop?: CaptureHop;
|
|
1146
|
+
};
|
|
1147
|
+
/**
|
|
1148
|
+
* The distinct images of the child rows of `table` whose `on[i].child` columns
|
|
1149
|
+
* equal a written parent row's `on[i].parent` columns: `(SELECT
|
|
1150
|
+
* array_agg("__vibe_i")::text FROM (SELECT DISTINCT ARRAY[<cells>] AS "__vibe_i"
|
|
1151
|
+
* FROM "Child" AS "__vibe_c" WHERE <match> LIMIT $n) AS "__vibe_d")`, NULL when
|
|
1152
|
+
* no child matched. Read inside the parent's statement, whose snapshot still
|
|
1153
|
+
* holds the rows its foreign keys' referential actions remove or rewrite at
|
|
1154
|
+
* the statement's end. `limit` is bound as a parameter.
|
|
1155
|
+
*/
|
|
1156
|
+
export type CaptureCascade = {
|
|
1157
|
+
readonly table: TableRef;
|
|
1158
|
+
readonly on: readonly {
|
|
1159
|
+
readonly child: string;
|
|
1160
|
+
readonly parent: string;
|
|
1161
|
+
}[];
|
|
1162
|
+
readonly cells: readonly CaptureCell[];
|
|
1163
|
+
readonly limit: number;
|
|
1164
|
+
};
|
|
1165
|
+
/**
|
|
1166
|
+
* A per-row {@link CaptureCascade} in RETURNING: `parent` columns are read from
|
|
1167
|
+
* `old.` (`"__vibe_c"."child" = old."parent"`).
|
|
1168
|
+
*/
|
|
1169
|
+
export type CaptureCascadeReturningColumn = {
|
|
1170
|
+
readonly capture: "cascade";
|
|
1171
|
+
readonly alias: string;
|
|
1172
|
+
readonly cascade: CaptureCascade;
|
|
991
1173
|
};
|
|
992
1174
|
/** The column name a {@link ReturningColumn} exposes in the result row. */
|
|
993
1175
|
export declare function returningColumnName(column: ReturningColumn): string;
|
|
1176
|
+
/** Is this RETURNING column read from another row ({@link OtherRowReturningColumn})? */
|
|
1177
|
+
export declare function isOtherRowReturningColumn(column: ReturningColumn): column is OtherRowReturningColumn;
|
|
994
1178
|
/** Is this RETURNING column a row-capture column ({@link CaptureReturningColumn})? */
|
|
995
1179
|
export declare function isCaptureReturningColumn(column: ReturningColumn): column is CaptureReturningColumn;
|
|
996
1180
|
export type InsertStatement = {
|
|
@@ -1053,6 +1237,42 @@ export type OnConflictUpdate = {
|
|
|
1053
1237
|
* mysql's ODKU has no WHERE and refuses it (`VIBE_UNSUPPORTED_CAPABILITY`).
|
|
1054
1238
|
*/
|
|
1055
1239
|
readonly updateWhere?: SqlExpr;
|
|
1240
|
+
/**
|
|
1241
|
+
* Skip-unchanged guard: the conflicting row is updated only when at least
|
|
1242
|
+
* one of these columns' stored value differs, NULL counting as a value,
|
|
1243
|
+
* from the value its `update` assignment would write (each column must have
|
|
1244
|
+
* one). Rendered as the first member of the update arm's `WHERE`, AND-ed
|
|
1245
|
+
* with {@link OnConflictUpdate.updateWhere}; a skipped row writes nothing
|
|
1246
|
+
* and returns nothing. On ON DUPLICATE KEY UPDATE (no WHERE) the columns it
|
|
1247
|
+
* leaves out are assigned first, keeping their stored value when nothing
|
|
1248
|
+
* else would change, and the dialect itself skips the unchanged row.
|
|
1249
|
+
* Needs `upsertSkipUnchanged`.
|
|
1250
|
+
*/
|
|
1251
|
+
readonly skipUnchanged?: readonly UnchangedColumn[];
|
|
1252
|
+
/**
|
|
1253
|
+
* The table's native row-security predicate, RAW SQL without parameters
|
|
1254
|
+
* (the managed policy's check, columns qualified by the target table's
|
|
1255
|
+
* name). With a guard on the arm ({@link OnConflictUpdate.updateWhere} or
|
|
1256
|
+
* {@link OnConflictUpdate.skipUnchanged}) the arm's `WHERE` becomes
|
|
1257
|
+
* `(<policy>) IS NOT TRUE OR (<guard>)`: PostgreSQL evaluates that `WHERE`
|
|
1258
|
+
* before its row-security check of the conflicting row, so a row the policy
|
|
1259
|
+
* hides must always take the update path and fail that check, whatever its
|
|
1260
|
+
* values. Ignored when the arm has no guard; refused by a dialect without
|
|
1261
|
+
* `nativeRowLevelSecurity`.
|
|
1262
|
+
*/
|
|
1263
|
+
readonly rowPolicy?: string;
|
|
1264
|
+
/**
|
|
1265
|
+
* MySQL only, a single-row statement: the duplicate arm also runs
|
|
1266
|
+
* `` `column` = IF(LAST_INSERT_ID(flag), `t`.`column`, `t`.`column`) `` (a
|
|
1267
|
+
* self-assignment, rendered last), so the statement reports `flag` as its
|
|
1268
|
+
* insert id exactly when the row took the update arm — the one signal that
|
|
1269
|
+
* tells an inserted row from an unchanged one when the driver reports no
|
|
1270
|
+
* `Duplicates:` info. `flag` is a decimal literal.
|
|
1271
|
+
*/
|
|
1272
|
+
readonly duplicateFlag?: {
|
|
1273
|
+
readonly column: string;
|
|
1274
|
+
readonly flag: string;
|
|
1275
|
+
};
|
|
1056
1276
|
/**
|
|
1057
1277
|
* MySQL only (`upsertForm: "onDuplicateKey"`): append
|
|
1058
1278
|
* `` `col` = LAST_INSERT_ID(`col`) `` to the update list so the UPDATE arm
|
|
@@ -1062,6 +1282,41 @@ export type OnConflictUpdate = {
|
|
|
1062
1282
|
*/
|
|
1063
1283
|
readonly lastInsertIdColumn?: string;
|
|
1064
1284
|
};
|
|
1285
|
+
/**
|
|
1286
|
+
* One column a skip-unchanged conflict arm compares. `json`: a Json column,
|
|
1287
|
+
* compared as `jsonb` on postgres (its `json` type has no equality operator;
|
|
1288
|
+
* `"list"` for a Json list).
|
|
1289
|
+
*/
|
|
1290
|
+
export type UnchangedColumn = {
|
|
1291
|
+
readonly column: string;
|
|
1292
|
+
readonly json?: "scalar" | "list";
|
|
1293
|
+
/**
|
|
1294
|
+
* A column whose SQL equality can be coarser than "the stored value would
|
|
1295
|
+
* not change"; the renderer compares it exactly where the dialect needs it.
|
|
1296
|
+
* See {@link UnchangedExactness}.
|
|
1297
|
+
*/
|
|
1298
|
+
readonly bytes?: UnchangedExactness;
|
|
1299
|
+
/**
|
|
1300
|
+
* A text column the schema declares with a folding type or collation
|
|
1301
|
+
* (`citext`, a `@collation` that is not byte-ordered): its equality may call
|
|
1302
|
+
* `'x'` and `'X'` equal on any dialect, not only under MySQL's default.
|
|
1303
|
+
*/
|
|
1304
|
+
readonly folds?: true;
|
|
1305
|
+
/** The column is a list of `bytes`'s kind (a postgres array). */
|
|
1306
|
+
readonly list?: true;
|
|
1307
|
+
};
|
|
1308
|
+
/**
|
|
1309
|
+
* Why a compared column's own equality may call two different stored values
|
|
1310
|
+
* equal:
|
|
1311
|
+
* - `"text"`: a text column; MySQL's default `_ai_ci` collation folds case
|
|
1312
|
+
* and accents (and see `folds`).
|
|
1313
|
+
* - `"decimal"`: a Decimal column without a declared scale (PostgreSQL's bare
|
|
1314
|
+
* `numeric` keeps `1.50` and `1.5` apart, its equality does not).
|
|
1315
|
+
* - `"float"`: a float column (`-0` equals `0`).
|
|
1316
|
+
* - `"other"`: a Json or list column, or a text-like column of another
|
|
1317
|
+
* declared type, compared by its bytes on MySQL.
|
|
1318
|
+
*/
|
|
1319
|
+
export type UnchangedExactness = "text" | "decimal" | "float" | "other";
|
|
1065
1320
|
/**
|
|
1066
1321
|
* Conflicting rows are skipped (`createMany({ skipDuplicates: true })`).
|
|
1067
1322
|
* An empty/absent target means "any conflict" — the mysql form
|
|
@@ -1236,11 +1491,24 @@ export declare function stepColumnRef(params: {
|
|
|
1236
1491
|
column: string;
|
|
1237
1492
|
}): StepColumnRef;
|
|
1238
1493
|
/** The joined relation of an {@link UpdateStatement}: its table, alias and join condition. */
|
|
1239
|
-
export type UpdateSource =
|
|
1494
|
+
export type UpdateSource = UpdateTableSource | UpdateSelectSource;
|
|
1495
|
+
/** A joined table: `FROM "table" AS alias` (`updateMany({ from })`). */
|
|
1496
|
+
export type UpdateTableSource = {
|
|
1240
1497
|
readonly table: TableRef;
|
|
1241
1498
|
readonly alias: string;
|
|
1242
1499
|
readonly on: SqlExpr;
|
|
1243
1500
|
};
|
|
1501
|
+
/**
|
|
1502
|
+
* A joined subquery: `FROM (SELECT …) AS alias`, PostgreSQL only. With a
|
|
1503
|
+
* locking read of the target table it is the row as it was before the write,
|
|
1504
|
+
* and an {@link OtherRowReturningColumn} `from: { source: alias }` returns its
|
|
1505
|
+
* values (`update({ previous })`, capability `returningReadsJoinedSource`).
|
|
1506
|
+
*/
|
|
1507
|
+
export type UpdateSelectSource = {
|
|
1508
|
+
readonly select: SelectStatement;
|
|
1509
|
+
readonly alias: string;
|
|
1510
|
+
readonly on: SqlExpr;
|
|
1511
|
+
};
|
|
1244
1512
|
/** An UPDATE set value read from the joined source's `column` (`UPDATE … FROM`). */
|
|
1245
1513
|
export type SourceColumnRef = {
|
|
1246
1514
|
readonly __sourceColumn: true;
|
|
@@ -1367,6 +1635,15 @@ export type ChainResult = {
|
|
|
1367
1635
|
* guard, so it is evaluated exactly once, on the one guaranteed result row.
|
|
1368
1636
|
*/
|
|
1369
1637
|
readonly veto?: readonly ChainVeto[];
|
|
1638
|
+
/**
|
|
1639
|
+
* A step whose rows every veto depends on: when it returned no row, no veto
|
|
1640
|
+
* is checked and the statement succeeds, answering the one guaranteed row
|
|
1641
|
+
* with NULL columns and the tally (rendered as the first CASE arm, `WHEN NOT
|
|
1642
|
+
* EXISTS (SELECT 1 FROM "step") THEN <tally>`). A nested write whose
|
|
1643
|
+
* missing parent is an answer, not an error, gates its children's vetoes on
|
|
1644
|
+
* the parent step this way. Requires `tally` and at least one veto.
|
|
1645
|
+
*/
|
|
1646
|
+
readonly vetoGate?: string;
|
|
1370
1647
|
/**
|
|
1371
1648
|
* One column `alias` on every result row: the number of rows the listed
|
|
1372
1649
|
* data-modifying steps returned (their RETURNING rows = their direct
|
|
@@ -1412,6 +1689,13 @@ export type ChainCapture = {
|
|
|
1412
1689
|
readonly after?: readonly string[];
|
|
1413
1690
|
/** The most distinct images one image column returns: a positive safe integer, bound as a parameter. */
|
|
1414
1691
|
readonly limit: number;
|
|
1692
|
+
/**
|
|
1693
|
+
* Child images of the step's rows, one result column `<alias>_c<j>` each: a
|
|
1694
|
+
* {@link CaptureCascade} whose `parent` columns name columns the step
|
|
1695
|
+
* RETURNS, matched by `EXISTS (SELECT 1 FROM "step" AS "__vibe_k" WHERE
|
|
1696
|
+
* "__vibe_k"."parent" = "__vibe_c"."child")`.
|
|
1697
|
+
*/
|
|
1698
|
+
readonly cascades?: readonly CaptureCascade[];
|
|
1415
1699
|
};
|
|
1416
1700
|
/**
|
|
1417
1701
|
* Fail the statement when `step` returned no row; `token` names the veto in the
|
package/dist/codecs.d.ts
CHANGED
|
@@ -76,15 +76,30 @@ export type WireFidelity = {
|
|
|
76
76
|
*/
|
|
77
77
|
readonly bytes?: "buffer";
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
79
|
+
* The zone the driver reads zone-less date-time text in (MySQL DATETIME /
|
|
80
|
+
* TIMESTAMP / DATE): `"utc"` (mysql2 with `timezone: "Z"`), a fixed offset
|
|
81
|
+
* in minutes east of UTC, or `"local"` (the process zone, read the way
|
|
82
|
+
* mysql2 builds a local `Date`). The MySQL DateTime decoders read the same
|
|
83
|
+
* text in the same zone wherever it arrives as text (the join strategy's
|
|
84
|
+
* JSON, a page key, `exactDates`), so one stored cell is one instant on every
|
|
85
|
+
* path; the adapter writes a bound parameter in it. Absent reads as UTC.
|
|
86
|
+
* Never prunes a decoder. `"utc"` alone also lets a dialect default of
|
|
87
|
+
* `"join"` apply (`lateralJson.requiresUtcDateTimeWire`): it is declared only
|
|
88
|
+
* where the zone is known to be UTC.
|
|
85
89
|
*/
|
|
86
|
-
readonly zoneLessDateTime?:
|
|
90
|
+
readonly zoneLessDateTime?: DateTimeZone;
|
|
87
91
|
};
|
|
92
|
+
/** A zone zone-less date-time text is read in: UTC, a fixed offset in minutes east of UTC, or the process's local zone. */
|
|
93
|
+
export type DateTimeZone = "utc" | "local" | {
|
|
94
|
+
readonly offsetMinutes: number;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* The cache-key fragment of a wire's date-time zone: `""` when zone-less text
|
|
98
|
+
* reads as UTC, so every UTC plan keeps its key; else `"local"` or the offset.
|
|
99
|
+
*/
|
|
100
|
+
export declare function dateTimeZoneKey(params: {
|
|
101
|
+
wire: WireFidelity | undefined;
|
|
102
|
+
}): string;
|
|
88
103
|
/**
|
|
89
104
|
* Is this scalar type's DECODE a no-op under the declared wire fidelity?
|
|
90
105
|
* Provable against the decode implementations in this file, identically on
|
|
@@ -115,7 +130,8 @@ export declare function decodeIsNoop(params: {
|
|
|
115
130
|
wire: WireFidelity | undefined;
|
|
116
131
|
}): boolean;
|
|
117
132
|
/**
|
|
118
|
-
* Parse date-time text into a `Date`, reading ZONE-LESS text as **UTC
|
|
133
|
+
* Parse date-time text into a `Date`, reading ZONE-LESS text as **UTC** — for
|
|
134
|
+
* text a CALLER wrote (a filter or data value, a validator's check).
|
|
119
135
|
*
|
|
120
136
|
* ECMA-262 reads `2026-01-15T12:00:00` as process-LOCAL time, so the same
|
|
121
137
|
* stored value would decode to a different instant on every host time zone,
|
|
@@ -124,9 +140,12 @@ export declare function decodeIsNoop(params: {
|
|
|
124
140
|
* that yields ONE instant everywhere (MYSQL-2, live-proven; the postgres
|
|
125
141
|
* `timestamp` case is proven by apps/tests `timestamp-zones.e2e`). Text with
|
|
126
142
|
* an explicit `Z`/offset and date-only text parse as ECMA-262 already reads
|
|
127
|
-
* them (as written / UTC midnight);
|
|
128
|
-
*
|
|
129
|
-
*
|
|
143
|
+
* them (as written / UTC midnight); any other form the engine reads
|
|
144
|
+
* (`Jan 15 2026 12:00 GMT`) is accepted as the engine reads it, and
|
|
145
|
+
* anything unparseable is `undefined` so the caller keeps its own fallback.
|
|
146
|
+
* Fractions beyond milliseconds are truncated — `Date` has no finer
|
|
147
|
+
* resolution and engines differ on longer fractions. Text from the DATABASE
|
|
148
|
+
* never takes that last, guessing step ({@link parseDatabaseDateTime}).
|
|
130
149
|
*/
|
|
131
150
|
export declare function parseDateTimeText(params: {
|
|
132
151
|
text: string;
|
|
@@ -144,11 +163,14 @@ export declare function parseDateTimeText(params: {
|
|
|
144
163
|
* milliseconds, an offset with seconds applied exactly.
|
|
145
164
|
*
|
|
146
165
|
* `undefined` for every other text — a 4-digit AD value keeps the parse it
|
|
147
|
-
* had
|
|
166
|
+
* had, unless `anyYear` (the database-text parser, after its other forms
|
|
167
|
+
* declined: an offset with seconds) — and for an instant outside the `Date`
|
|
168
|
+
* range. Exported for the adapters
|
|
148
169
|
* whose zone-less driver parsers fall back to it (one rule, rule 6).
|
|
149
170
|
*/
|
|
150
171
|
export declare function parseExtendedDateTimeText(params: {
|
|
151
172
|
text: string;
|
|
173
|
+
anyYear?: boolean;
|
|
152
174
|
}): Date | undefined;
|
|
153
175
|
/**
|
|
154
176
|
* The decode refusal of a non-finite or unparseable DateTime — one error for
|
|
@@ -157,6 +179,78 @@ export declare function parseExtendedDateTimeText(params: {
|
|
|
157
179
|
* reports exactly this error.
|
|
158
180
|
*/
|
|
159
181
|
export declare function decodedDateTimeRefusal(): VibeError;
|
|
182
|
+
/**
|
|
183
|
+
* The refusal of database date-time text no decoder reads, by its cause —
|
|
184
|
+
* each with its own `meta.reason`, so the message names the right remedy:
|
|
185
|
+
*
|
|
186
|
+
* - `"zero-date"`: a MySQL zero date or a zero month or day, which has no
|
|
187
|
+
* instant (mysql2 renders a binary zero `DATETIME` as `undefined 00:00:00`);
|
|
188
|
+
* - `"datetime-out-of-range"`: ISO-shaped text with no `Date` (a year past
|
|
189
|
+
* ±275760, `294276-12-31` in PostgreSQL, or a day the calendar lacks);
|
|
190
|
+
* - `"datetime-text-layout"`: any other layout; on PostgreSQL its cause is a
|
|
191
|
+
* `DateStyle` other than ISO, and the error names that remedy
|
|
192
|
+
* ({@link dateTimeTextLayoutError}).
|
|
193
|
+
*
|
|
194
|
+
* The text itself is never included.
|
|
195
|
+
*/
|
|
196
|
+
export declare function undecodableDateTimeText(params: {
|
|
197
|
+
text: string;
|
|
198
|
+
dialect: Dialect;
|
|
199
|
+
}): VibeError;
|
|
200
|
+
/**
|
|
201
|
+
* The refusal for database DateTime text outside the ISO layouts. On
|
|
202
|
+
* PostgreSQL its cause is a `DateStyle` other than ISO, set by the server,
|
|
203
|
+
* the database or the role; the message and `meta.fix` name the remedies.
|
|
204
|
+
* The text itself is never included.
|
|
205
|
+
*/
|
|
206
|
+
export declare function dateTimeTextLayoutError(): VibeError;
|
|
207
|
+
/**
|
|
208
|
+
* The codec of a DateTime field whose native type is a date-only column on
|
|
209
|
+
* `dialect`, or `undefined` (the DateTime table row applies): MySQL's
|
|
210
|
+
* `@db.Date` ({@link mysqlDateOnlyCodec}). PostgreSQL's `date` input and
|
|
211
|
+
* SQLite's text need none.
|
|
212
|
+
*/
|
|
213
|
+
export declare function dateOnlyCodec(params: {
|
|
214
|
+
dialect: Dialect;
|
|
215
|
+
nativeType: string | undefined;
|
|
216
|
+
}): ScalarCodec | undefined;
|
|
217
|
+
/**
|
|
218
|
+
* The instant of zone-less date-time text a MySQL server printed (`DATETIME`,
|
|
219
|
+
* `DATE`, a `TIMESTAMP` in the session zone), read in `zone`, the zone mysql2
|
|
220
|
+
* reads the same cell in; the years 0-99 keep their year. `undefined` for any
|
|
221
|
+
* other text. For the adapter's raw path; ORM reads take the codec table.
|
|
222
|
+
*/
|
|
223
|
+
export declare function parseZoneLessDateTime(params: {
|
|
224
|
+
text: string;
|
|
225
|
+
zone: DateTimeZone | undefined;
|
|
226
|
+
}): Date | undefined;
|
|
227
|
+
/**
|
|
228
|
+
* `codec` as it reads under the adapter's declared zone
|
|
229
|
+
* (`wire.zoneLessDateTime`): a MySQL DateTime or date-only codec reads
|
|
230
|
+
* zone-less text in that zone when it is not UTC; every other codec, and every
|
|
231
|
+
* codec under UTC, is returned unchanged. Encode is never changed: a `Date`
|
|
232
|
+
* goes to the driver, which writes it in the same zone.
|
|
233
|
+
*/
|
|
234
|
+
export declare function zonedCodec(params: {
|
|
235
|
+
dialect: Dialect;
|
|
236
|
+
codec: ScalarCodec;
|
|
237
|
+
wire: WireFidelity | undefined;
|
|
238
|
+
}): ScalarCodec;
|
|
239
|
+
/**
|
|
240
|
+
* The text a MySQL adapter writes for a parameter the DateTime codec bound
|
|
241
|
+
* exactly (a `Date` read from the server's text, which carries more fraction
|
|
242
|
+
* digits than a `Date` holds): the wall clock it was read from when that text,
|
|
243
|
+
* read in `zone`, is this instant (so a wall clock in a daylight-saving gap
|
|
244
|
+
* under `"local"` addresses its own cell); else the instant's wall clock in
|
|
245
|
+
* `zone` (text with an offset, or read in another zone). Every fraction digit
|
|
246
|
+
* is kept, and at least three are written. `undefined` for every other value,
|
|
247
|
+
* which the driver writes as it always did. A keyset position over such a
|
|
248
|
+
* value carries the same text.
|
|
249
|
+
*/
|
|
250
|
+
export declare function mysqlBoundTimestampText(params: {
|
|
251
|
+
value: unknown;
|
|
252
|
+
zone: DateTimeZone | undefined;
|
|
253
|
+
}): string | undefined;
|
|
160
254
|
/**
|
|
161
255
|
* The time-of-day codec for a DateTime field's native type, or `undefined`
|
|
162
256
|
* when the native type is not `time`/`timetz` (the DateTime table row
|