@vibeorm/sql 5.9.0 → 5.11.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/dist/ast.d.ts +337 -4
- package/dist/codecs.d.ts +106 -12
- package/dist/composable.d.ts +44 -4
- package/dist/dialect.d.ts +73 -1
- package/dist/dialects/migration-inventory.d.ts +0 -11
- package/dist/dialects/mysql-generated-columns.d.ts +1 -5
- package/dist/dialects/mysql-index-expressions.d.ts +1 -19
- package/dist/dialects/mysql-json-keys.d.ts +26 -0
- 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 -11
- 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 -24
- 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.d.ts +11 -6
- package/dist/index.js +3023 -2064
- package/dist/index.js.map +47 -43
- package/dist/json-patch.d.ts +0 -14
- package/dist/json-path.d.ts +1 -2
- package/dist/key-list.d.ts +38 -21
- package/dist/native-types.d.ts +3 -1
- 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/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/dist/ast.d.ts
CHANGED
|
@@ -307,6 +307,8 @@ export type SqlExpr = {
|
|
|
307
307
|
/** The tested columns, in the subquery's projection order. */
|
|
308
308
|
readonly columns: readonly ColRef[];
|
|
309
309
|
readonly select: SelectStatement;
|
|
310
|
+
/** The subquery yields at most one row: one column, rendered `"fk" = (SELECT …)`. */
|
|
311
|
+
readonly single?: true;
|
|
310
312
|
} | {
|
|
311
313
|
readonly kind: "like";
|
|
312
314
|
readonly column: ColRef;
|
|
@@ -450,12 +452,20 @@ export type SqlExpr = {
|
|
|
450
452
|
*
|
|
451
453
|
* `one` renders the constant `1` — the conventional EXISTS projection; it
|
|
452
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.
|
|
453
460
|
*/
|
|
454
461
|
export type SelectExpr = {
|
|
455
462
|
readonly kind: "countAll";
|
|
456
463
|
readonly alias?: string;
|
|
457
464
|
} | {
|
|
458
465
|
readonly kind: "one";
|
|
466
|
+
} | {
|
|
467
|
+
readonly kind: "exists";
|
|
468
|
+
readonly alias: string;
|
|
459
469
|
};
|
|
460
470
|
/** Arithmetic applied to a column's current value in an UPDATE ... SET. */
|
|
461
471
|
export type SetOperator = "increment" | "decrement" | "multiply" | "divide";
|
|
@@ -574,10 +584,18 @@ export type OrderTerm = OrderByColumn | OrderByAggregate | OrderBySubquery;
|
|
|
574
584
|
export type LateralJoin = {
|
|
575
585
|
/** Join alias (`__lat_0`) — the outer projection selects `alias.columnAlias`. */
|
|
576
586
|
readonly alias: string;
|
|
577
|
-
/**
|
|
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
|
+
*/
|
|
578
592
|
readonly columnAlias: string;
|
|
579
|
-
/**
|
|
580
|
-
|
|
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";
|
|
581
599
|
/**
|
|
582
600
|
* Object output is the SQL-layer default. Positional rows follow the exact
|
|
583
601
|
* select.columns order, using explicit output names (never SELECT *).
|
|
@@ -586,6 +604,20 @@ export type LateralJoin = {
|
|
|
586
604
|
* slot. Consumers must use the SAME projection to decode the payload.
|
|
587
605
|
*/
|
|
588
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[];
|
|
614
|
+
/**
|
|
615
|
+
* `jsonObject` laterals only: output names of `select.columns` the lateral
|
|
616
|
+
* exposes as typed columns of its own beside the JSON value (`"alias"."name"`
|
|
617
|
+
* in the outer scope). The outer statement reads a key of the row from them
|
|
618
|
+
* without parsing the JSON; they are SQL NULL when the lateral finds no row.
|
|
619
|
+
*/
|
|
620
|
+
readonly expose?: readonly string[];
|
|
589
621
|
/** The correlated child select — may reference outer columns via table-qualified refs. */
|
|
590
622
|
readonly select: SelectStatement;
|
|
591
623
|
};
|
|
@@ -752,6 +784,175 @@ export type RowNumberProjection = {
|
|
|
752
784
|
readonly orderBy: readonly OrderTerm[];
|
|
753
785
|
readonly alias: string;
|
|
754
786
|
};
|
|
787
|
+
/**
|
|
788
|
+
* One window column of a select, rendered after `rowNumber`
|
|
789
|
+
* (`SelectStatement.windows`). Together the two kinds let a statement say
|
|
790
|
+
* which of its rows is the first to hold a key:
|
|
791
|
+
*
|
|
792
|
+
* - `ordinal`: `ROW_NUMBER() OVER ([ORDER BY …]) AS "alias"`, the row's
|
|
793
|
+
* position among the rows of its query level, in `orderBy`, or in the order
|
|
794
|
+
* the rows arrive when `orderBy` is empty (`OVER ()`).
|
|
795
|
+
* - `firstOrdinal`: `MIN(<ordinal>) OVER (PARTITION BY …) AS "alias"`, the
|
|
796
|
+
* smallest `ordinal` among the rows that share the partition columns. The
|
|
797
|
+
* database's own equality groups them (NULLs together), whatever the
|
|
798
|
+
* column's type or collation.
|
|
799
|
+
*
|
|
800
|
+
* A window is computed before the `ORDER BY` and `LIMIT` of its own query
|
|
801
|
+
* level: number a page one level outside the select that cuts it.
|
|
802
|
+
*/
|
|
803
|
+
export type WindowColumn = {
|
|
804
|
+
readonly kind: "ordinal";
|
|
805
|
+
readonly alias: string;
|
|
806
|
+
readonly orderBy: readonly OrderTerm[];
|
|
807
|
+
} | {
|
|
808
|
+
readonly kind: "firstOrdinal";
|
|
809
|
+
readonly alias: string;
|
|
810
|
+
readonly ordinal: ColRef;
|
|
811
|
+
readonly partitionBy: readonly ColRef[];
|
|
812
|
+
};
|
|
813
|
+
/**
|
|
814
|
+
* One column of a `SelectStatement.fromRow`: an outer column under the name
|
|
815
|
+
* the row gives it.
|
|
816
|
+
*/
|
|
817
|
+
export type RowSourceColumn = {
|
|
818
|
+
readonly column: ColRef;
|
|
819
|
+
readonly alias: string;
|
|
820
|
+
};
|
|
821
|
+
/**
|
|
822
|
+
* One common table expression of a select (`SelectStatement.with`):
|
|
823
|
+
* `"name" AS [MATERIALIZED] (<select>)`. `materialized` pins the table to one
|
|
824
|
+
* evaluation whatever the number of references: required when two readers
|
|
825
|
+
* must see the same rows of a select whose rows are numbered in arrival
|
|
826
|
+
* order.
|
|
827
|
+
*/
|
|
828
|
+
export type CommonTable = {
|
|
829
|
+
readonly name: string;
|
|
830
|
+
readonly select: SelectStatement;
|
|
831
|
+
readonly materialized?: true;
|
|
832
|
+
};
|
|
833
|
+
/**
|
|
834
|
+
* The place of one value in a positional JSON row ({@link LateralJoin}
|
|
835
|
+
* `rowFormat: "positional"`): its index among the row's `width` values. A row
|
|
836
|
+
* wider than `JSON_TRANSPORT.chunkSize` is an array of chunks, so the renderer
|
|
837
|
+
* needs both numbers to address the value.
|
|
838
|
+
*/
|
|
839
|
+
export type JsonSlot = {
|
|
840
|
+
readonly index: number;
|
|
841
|
+
readonly width: number;
|
|
842
|
+
};
|
|
843
|
+
/**
|
|
844
|
+
* One step of a walk through JSON a statement built: into one value of a
|
|
845
|
+
* positional row, or over every element of a list.
|
|
846
|
+
*/
|
|
847
|
+
export type JsonStep = {
|
|
848
|
+
readonly kind: "slot";
|
|
849
|
+
readonly slot: JsonSlot;
|
|
850
|
+
} | {
|
|
851
|
+
readonly kind: "elements";
|
|
852
|
+
};
|
|
853
|
+
/**
|
|
854
|
+
* One key column of a {@link JsonKeys} source: `token` is the output name of
|
|
855
|
+
* the key's text as the JSON rows hold it (at `slot` of the row the walk
|
|
856
|
+
* ends at), `alias` the output name of the same key cast back to the
|
|
857
|
+
* column's own type (`member`, typed like a key-list member).
|
|
858
|
+
*/
|
|
859
|
+
export type JsonKeyColumn = {
|
|
860
|
+
readonly slot: JsonSlot;
|
|
861
|
+
readonly token: string;
|
|
862
|
+
readonly alias: string;
|
|
863
|
+
readonly member: KeyListMember;
|
|
864
|
+
};
|
|
865
|
+
/**
|
|
866
|
+
* The distinct keys of the rows a JSON walk reaches, as a derived-table FROM
|
|
867
|
+
* (`SelectStatement.fromJsonKeys`). `source` is a JSON column of a common
|
|
868
|
+
* table of the statement (`source.table` names it); `steps` walk from that
|
|
869
|
+
* value to positional rows, each of which holds the text of its key columns
|
|
870
|
+
* in the slots `keys` name. Renders on PostgreSQL as
|
|
871
|
+
*
|
|
872
|
+
* `(SELECT "__vibe_k"."<token>", CAST("__vibe_k"."<token>" AS <type>) AS "<alias>"
|
|
873
|
+
* FROM (SELECT DISTINCT <row> ->> <slot> AS "<token>"
|
|
874
|
+
* FROM json_array_elements((SELECT json_agg(<list>) FROM "<table>" AS "__vibe_s" WHERE <list> IS NOT NULL))
|
|
875
|
+
* AS "__vibe_d0"("value")
|
|
876
|
+
* CROSS JOIN LATERAL json_array_elements("__vibe_d0"."value") AS "__vibe_e0"("value")) AS "__vibe_k")
|
|
877
|
+
* AS "<table.name>"`
|
|
878
|
+
*
|
|
879
|
+
* Each list on the way is read through ONE value that holds that level's
|
|
880
|
+
* lists of every row (an uncorrelated `json_agg`, planned once), and a
|
|
881
|
+
* second list on the way wraps the first walk the same way. PostgreSQL
|
|
882
|
+
* estimates every `json_array_elements` call at 100 rows whatever it reads:
|
|
883
|
+
* walked row by row, the estimate is rows x 100 per list on the way, and two
|
|
884
|
+
* lists over 2 000 rows were priced past the cost where the server's JIT
|
|
885
|
+
* compiler optimizes (measured: 249 ms against 94 ms with JIT off). Read
|
|
886
|
+
* through one value, each list on the way is 100 x 100 rows at any depth and
|
|
887
|
+
* any row count. The price: that one value holds a level's lists of the whole
|
|
888
|
+
* statement, so it is bound by PostgreSQL's 1 GB limit on a single value.
|
|
889
|
+
*
|
|
890
|
+
* The database groups the keys by their text and types each one from that
|
|
891
|
+
* text with the key-list's base types (`postgresKeyListElementType`), so the
|
|
892
|
+
* laterals joined to the derived table compare `child."fk" = "<table.name>"."<alias>"`
|
|
893
|
+
* under the column's own operator and index. A row the walk does not reach
|
|
894
|
+
* (a missing to-one, an empty list) contributes nothing or a NULL key.
|
|
895
|
+
* Needs the `sharedListDictionary` capability; never together with another
|
|
896
|
+
* FROM form, a `table.alias` or a `rowLock`.
|
|
897
|
+
*/
|
|
898
|
+
export type JsonKeys = {
|
|
899
|
+
readonly source: ColRef;
|
|
900
|
+
readonly steps: readonly JsonStep[];
|
|
901
|
+
readonly keys: readonly JsonKeyColumn[];
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* The rows of a common table, read back through arrays, as a FROM
|
|
905
|
+
* (`SelectStatement.fromTableArrays`): `columns` of the table `source`, each
|
|
906
|
+
* under the output name `alias`, in the order of the column `orderBy`.
|
|
907
|
+
* Renders on PostgreSQL as
|
|
908
|
+
*
|
|
909
|
+
* `(SELECT array_agg("<source>"."<name>" ORDER BY "<source>"."<orderBy>") AS "__vibe_a0", …
|
|
910
|
+
* FROM "<source>") AS "__vibe_a"
|
|
911
|
+
* CROSS JOIN LATERAL unnest("__vibe_a"."__vibe_a0", …) AS "<table.name>"("<alias>", …)`
|
|
912
|
+
*
|
|
913
|
+
* It returns the rows a plain read of the table would, typed as the table's
|
|
914
|
+
* columns (no text on the way), and exists for two things the plain read
|
|
915
|
+
* does not give. The estimate: PostgreSQL expects 10 rows from `unnest` of an
|
|
916
|
+
* array it cannot see, while it expects one row per input row from a table
|
|
917
|
+
* grouped by a key it can trace to a unique column, and every lateral joined
|
|
918
|
+
* to the rows is priced once per expected row (measured: 20 000 rows of 10
|
|
919
|
+
* keys priced at 3.1 million against 129 000, which decides whether the
|
|
920
|
+
* server's JIT compiler runs). The order: laterals joined to the rows read
|
|
921
|
+
* their tables in `orderBy` order, not in a hash table's (measured: the same
|
|
922
|
+
* 20 000 index lookups in 440 ms against 1 066 ms). Needs the `carrierRows`
|
|
923
|
+
* capability on a dialect whose `lateralJson.carriedLists` is `"firstRow"`;
|
|
924
|
+
* never together with another FROM form, a `table.alias` or a `rowLock`.
|
|
925
|
+
*/
|
|
926
|
+
export type TableArrays = {
|
|
927
|
+
readonly source: string;
|
|
928
|
+
readonly columns: readonly {
|
|
929
|
+
readonly name: string;
|
|
930
|
+
readonly alias: string;
|
|
931
|
+
}[];
|
|
932
|
+
readonly orderBy: string;
|
|
933
|
+
};
|
|
934
|
+
/**
|
|
935
|
+
* A JSON list a statement returns on its first row only
|
|
936
|
+
* (`SelectStatement.firstRowLists`): `list` describes the list as a
|
|
937
|
+
* `jsonArray` lateral does (its select, row format and column alias; the join
|
|
938
|
+
* alias is unused), `ordinal` is the statement's row ordinal (see
|
|
939
|
+
* {@link WindowColumn} `ordinal`). Renders as a projection:
|
|
940
|
+
*
|
|
941
|
+
* `CASE WHEN <ordinal> = 1 THEN (SELECT COALESCE(json_agg(<row>), '[]'::json)
|
|
942
|
+
* FROM (<list.select>) AS "__sub") END AS "<list.columnAlias>"`
|
|
943
|
+
*
|
|
944
|
+
* The select must not read the statement's own row: an uncorrelated scalar
|
|
945
|
+
* subquery is planned and costed once and runs once, when the first row asks
|
|
946
|
+
* for it. A lateral gated on the ordinal returns the same rows, but
|
|
947
|
+
* PostgreSQL prices it once per row of the statement (measured: 500 rows
|
|
948
|
+
* priced the list 500 times, the estimate passed `jit_above_cost` and the
|
|
949
|
+
* statement spent 100 ms compiling against 5 ms running). Every other row
|
|
950
|
+
* holds SQL NULL. Needs the `sharedListDictionary` capability.
|
|
951
|
+
*/
|
|
952
|
+
export type FirstRowList = {
|
|
953
|
+
readonly ordinal: ColRef;
|
|
954
|
+
readonly list: LateralJoin;
|
|
955
|
+
};
|
|
755
956
|
/**
|
|
756
957
|
* A PER-KEY PAGE of one child table as a derived-table FROM
|
|
757
958
|
* (`SelectStatement.keyPage`): for every listed key tuple, the child rows
|
|
@@ -844,6 +1045,14 @@ export type GroupingSets = {
|
|
|
844
1045
|
};
|
|
845
1046
|
export type SelectStatement = {
|
|
846
1047
|
readonly kind: "select";
|
|
1048
|
+
/**
|
|
1049
|
+
* Common table expressions, rendered ahead of the select (`WITH a AS (…),
|
|
1050
|
+
* b AS (…) SELECT …`) and bound first, in order. The statement and the
|
|
1051
|
+
* tables after it read a table by its name as a plain `table`. Only on the
|
|
1052
|
+
* outermost select of a statement; needs the `sharedListDictionary`
|
|
1053
|
+
* capability. See {@link CommonTable}.
|
|
1054
|
+
*/
|
|
1055
|
+
readonly with?: readonly CommonTable[];
|
|
847
1056
|
readonly table: TableRef;
|
|
848
1057
|
/** Empty array renders as `SELECT *` (unless `selectExpr` replaces it). */
|
|
849
1058
|
readonly columns: readonly ColRef[];
|
|
@@ -893,6 +1102,39 @@ export type SelectStatement = {
|
|
|
893
1102
|
readonly leftJoins?: readonly LeftJoin[];
|
|
894
1103
|
/** ROW_NUMBER window projection — see {@link RowNumberProjection}. */
|
|
895
1104
|
readonly rowNumber?: RowNumberProjection;
|
|
1105
|
+
/** Window columns, rendered after `rowNumber` — see {@link WindowColumn}. */
|
|
1106
|
+
readonly windows?: readonly WindowColumn[];
|
|
1107
|
+
/** JSON lists of the first row, rendered after `windows` — see {@link FirstRowList}. */
|
|
1108
|
+
readonly firstRowLists?: readonly FirstRowList[];
|
|
1109
|
+
/**
|
|
1110
|
+
* Every column of the statement's FROM, ahead of the other projections:
|
|
1111
|
+
* `SELECT "<from>".*, …`. With `fromSelect` and an empty `columns` list (a
|
|
1112
|
+
* level that adds window columns to the level below it re-exposes that
|
|
1113
|
+
* level's columns without naming each one again), or over a common table
|
|
1114
|
+
* of the statement's own `with`, read under `table.alias ?? table.name`,
|
|
1115
|
+
* where the other projections follow it.
|
|
1116
|
+
*/
|
|
1117
|
+
readonly allColumns?: true;
|
|
1118
|
+
/**
|
|
1119
|
+
* Derived-table FROM of exactly ONE row built from outer columns: renders
|
|
1120
|
+
* `FROM (SELECT <column> AS "<alias>", …) AS "<table.name>"`. Inside a
|
|
1121
|
+
* lateral it gives outer values the column names of a table row, so the
|
|
1122
|
+
* laterals joined to it correlate as they would to that table's row
|
|
1123
|
+
* (`child."fk" = "<table.name>"."id"`) and the table is not read again.
|
|
1124
|
+
* Needs the `carrierRows` capability; never together with `fromSelect`,
|
|
1125
|
+
* `keyPage`, `fromUnion`, `fromKeys`, a `table.alias` or a `rowLock`.
|
|
1126
|
+
*/
|
|
1127
|
+
readonly fromRow?: readonly RowSourceColumn[];
|
|
1128
|
+
/**
|
|
1129
|
+
* Derived-table FROM over the distinct, typed keys of a JSON walk, named
|
|
1130
|
+
* `table.name` — see {@link JsonKeys}.
|
|
1131
|
+
*/
|
|
1132
|
+
readonly fromJsonKeys?: JsonKeys;
|
|
1133
|
+
/**
|
|
1134
|
+
* FROM over the rows of a common table read back through arrays, named
|
|
1135
|
+
* `table.name` — see {@link TableArrays}.
|
|
1136
|
+
*/
|
|
1137
|
+
readonly fromTableArrays?: TableArrays;
|
|
896
1138
|
/**
|
|
897
1139
|
* Fix lane Q1 (DB-01): derived-table FROM over a per-key page — renders
|
|
898
1140
|
* `FROM (<per-key page>) AS "<table.name>"` like `fromSelect` (and never
|
|
@@ -1044,7 +1286,18 @@ export type ReturningColumn = string | {
|
|
|
1044
1286
|
} | {
|
|
1045
1287
|
readonly name: string;
|
|
1046
1288
|
readonly transform: "jsonText";
|
|
1047
|
-
} | CaptureReturningColumn | OtherRowReturningColumn;
|
|
1289
|
+
} | CaptureReturningColumn | OtherRowReturningColumn | ServerVersionReturningColumn;
|
|
1290
|
+
/**
|
|
1291
|
+
* The server's `server_version_num` as text, exposed as `alias`:
|
|
1292
|
+
* `pg_catalog.current_setting('server_version_num') AS "alias"`. Rides a write
|
|
1293
|
+
* whose client does not know the server version yet, so the next statement can
|
|
1294
|
+
* take the form that version allows without a read of its own. Dialects with
|
|
1295
|
+
* the `rowCapture` capability only (the version decides whether it applies).
|
|
1296
|
+
*/
|
|
1297
|
+
export type ServerVersionReturningColumn = {
|
|
1298
|
+
readonly serverVersion: true;
|
|
1299
|
+
readonly alias: string;
|
|
1300
|
+
};
|
|
1048
1301
|
/**
|
|
1049
1302
|
* A RETURNING column read from a row OTHER than the one the statement wrote,
|
|
1050
1303
|
* exposed as `alias` and projected like the written row's own column
|
|
@@ -1201,6 +1454,42 @@ export type OnConflictUpdate = {
|
|
|
1201
1454
|
* mysql's ODKU has no WHERE and refuses it (`VIBE_UNSUPPORTED_CAPABILITY`).
|
|
1202
1455
|
*/
|
|
1203
1456
|
readonly updateWhere?: SqlExpr;
|
|
1457
|
+
/**
|
|
1458
|
+
* Skip-unchanged guard: the conflicting row is updated only when at least
|
|
1459
|
+
* one of these columns' stored value differs, NULL counting as a value,
|
|
1460
|
+
* from the value its `update` assignment would write (each column must have
|
|
1461
|
+
* one). Rendered as the first member of the update arm's `WHERE`, AND-ed
|
|
1462
|
+
* with {@link OnConflictUpdate.updateWhere}; a skipped row writes nothing
|
|
1463
|
+
* and returns nothing. On ON DUPLICATE KEY UPDATE (no WHERE) the columns it
|
|
1464
|
+
* leaves out are assigned first, keeping their stored value when nothing
|
|
1465
|
+
* else would change, and the dialect itself skips the unchanged row.
|
|
1466
|
+
* Needs `upsertSkipUnchanged`.
|
|
1467
|
+
*/
|
|
1468
|
+
readonly skipUnchanged?: readonly UnchangedColumn[];
|
|
1469
|
+
/**
|
|
1470
|
+
* The table's native row-security predicate, RAW SQL without parameters
|
|
1471
|
+
* (the managed policy's check, columns qualified by the target table's
|
|
1472
|
+
* name). With a guard on the arm ({@link OnConflictUpdate.updateWhere} or
|
|
1473
|
+
* {@link OnConflictUpdate.skipUnchanged}) the arm's `WHERE` becomes
|
|
1474
|
+
* `(<policy>) IS NOT TRUE OR (<guard>)`: PostgreSQL evaluates that `WHERE`
|
|
1475
|
+
* before its row-security check of the conflicting row, so a row the policy
|
|
1476
|
+
* hides must always take the update path and fail that check, whatever its
|
|
1477
|
+
* values. Ignored when the arm has no guard; refused by a dialect without
|
|
1478
|
+
* `nativeRowLevelSecurity`.
|
|
1479
|
+
*/
|
|
1480
|
+
readonly rowPolicy?: string;
|
|
1481
|
+
/**
|
|
1482
|
+
* MySQL only, a single-row statement: the duplicate arm also runs
|
|
1483
|
+
* `` `column` = IF(LAST_INSERT_ID(flag), `t`.`column`, `t`.`column`) `` (a
|
|
1484
|
+
* self-assignment, rendered last), so the statement reports `flag` as its
|
|
1485
|
+
* insert id exactly when the row took the update arm — the one signal that
|
|
1486
|
+
* tells an inserted row from an unchanged one when the driver reports no
|
|
1487
|
+
* `Duplicates:` info. `flag` is a decimal literal.
|
|
1488
|
+
*/
|
|
1489
|
+
readonly duplicateFlag?: {
|
|
1490
|
+
readonly column: string;
|
|
1491
|
+
readonly flag: string;
|
|
1492
|
+
};
|
|
1204
1493
|
/**
|
|
1205
1494
|
* MySQL only (`upsertForm: "onDuplicateKey"`): append
|
|
1206
1495
|
* `` `col` = LAST_INSERT_ID(`col`) `` to the update list so the UPDATE arm
|
|
@@ -1210,6 +1499,41 @@ export type OnConflictUpdate = {
|
|
|
1210
1499
|
*/
|
|
1211
1500
|
readonly lastInsertIdColumn?: string;
|
|
1212
1501
|
};
|
|
1502
|
+
/**
|
|
1503
|
+
* One column a skip-unchanged conflict arm compares. `json`: a Json column,
|
|
1504
|
+
* compared as `jsonb` on postgres (its `json` type has no equality operator;
|
|
1505
|
+
* `"list"` for a Json list).
|
|
1506
|
+
*/
|
|
1507
|
+
export type UnchangedColumn = {
|
|
1508
|
+
readonly column: string;
|
|
1509
|
+
readonly json?: "scalar" | "list";
|
|
1510
|
+
/**
|
|
1511
|
+
* A column whose SQL equality can be coarser than "the stored value would
|
|
1512
|
+
* not change"; the renderer compares it exactly where the dialect needs it.
|
|
1513
|
+
* See {@link UnchangedExactness}.
|
|
1514
|
+
*/
|
|
1515
|
+
readonly bytes?: UnchangedExactness;
|
|
1516
|
+
/**
|
|
1517
|
+
* A text column the schema declares with a folding type or collation
|
|
1518
|
+
* (`citext`, a `@collation` that is not byte-ordered): its equality may call
|
|
1519
|
+
* `'x'` and `'X'` equal on any dialect, not only under MySQL's default.
|
|
1520
|
+
*/
|
|
1521
|
+
readonly folds?: true;
|
|
1522
|
+
/** The column is a list of `bytes`'s kind (a postgres array). */
|
|
1523
|
+
readonly list?: true;
|
|
1524
|
+
};
|
|
1525
|
+
/**
|
|
1526
|
+
* Why a compared column's own equality may call two different stored values
|
|
1527
|
+
* equal:
|
|
1528
|
+
* - `"text"`: a text column; MySQL's default `_ai_ci` collation folds case
|
|
1529
|
+
* and accents (and see `folds`).
|
|
1530
|
+
* - `"decimal"`: a Decimal column without a declared scale (PostgreSQL's bare
|
|
1531
|
+
* `numeric` keeps `1.50` and `1.5` apart, its equality does not).
|
|
1532
|
+
* - `"float"`: a float column (`-0` equals `0`).
|
|
1533
|
+
* - `"other"`: a Json or list column, or a text-like column of another
|
|
1534
|
+
* declared type, compared by its bytes on MySQL.
|
|
1535
|
+
*/
|
|
1536
|
+
export type UnchangedExactness = "text" | "decimal" | "float" | "other";
|
|
1213
1537
|
/**
|
|
1214
1538
|
* Conflicting rows are skipped (`createMany({ skipDuplicates: true })`).
|
|
1215
1539
|
* An empty/absent target means "any conflict" — the mysql form
|
|
@@ -1528,6 +1852,15 @@ export type ChainResult = {
|
|
|
1528
1852
|
* guard, so it is evaluated exactly once, on the one guaranteed result row.
|
|
1529
1853
|
*/
|
|
1530
1854
|
readonly veto?: readonly ChainVeto[];
|
|
1855
|
+
/**
|
|
1856
|
+
* A step whose rows every veto depends on: when it returned no row, no veto
|
|
1857
|
+
* is checked and the statement succeeds, answering the one guaranteed row
|
|
1858
|
+
* with NULL columns and the tally (rendered as the first CASE arm, `WHEN NOT
|
|
1859
|
+
* EXISTS (SELECT 1 FROM "step") THEN <tally>`). A nested write whose
|
|
1860
|
+
* missing parent is an answer, not an error, gates its children's vetoes on
|
|
1861
|
+
* the parent step this way. Requires `tally` and at least one veto.
|
|
1862
|
+
*/
|
|
1863
|
+
readonly vetoGate?: string;
|
|
1531
1864
|
/**
|
|
1532
1865
|
* One column `alias` on every result row: the number of rows the listed
|
|
1533
1866
|
* data-modifying steps returned (their RETURNING rows = their direct
|
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
|
package/dist/composable.d.ts
CHANGED
|
@@ -21,10 +21,22 @@ export type SqlTag = {
|
|
|
21
21
|
value: NoInfer<T>;
|
|
22
22
|
decoder?: ResultDecoder<T>;
|
|
23
23
|
}) => SqlFragment;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A list: fragments spliced, anything else bound as one value each, joined
|
|
26
|
+
* by `separator` (default `, `). `sql.join(items, separator)` and
|
|
27
|
+
* `sql.join({ items, separator })` are the same call.
|
|
28
|
+
*/
|
|
29
|
+
readonly join: {
|
|
30
|
+
(items: readonly unknown[], separator?: SqlFragment): SqlFragment;
|
|
31
|
+
(params: {
|
|
32
|
+
items: readonly unknown[];
|
|
33
|
+
separator?: SqlFragment;
|
|
34
|
+
}): SqlFragment;
|
|
35
|
+
};
|
|
36
|
+
/** Every condition must hold: `(a) AND (b)`, each part in parentheses; `undefined` parts are skipped, and none left is `TRUE`. */
|
|
37
|
+
readonly and: (parts: readonly (SqlFragment | undefined)[]) => SqlFragment;
|
|
38
|
+
/** Any condition may hold: `(a) OR (b)`, each part in parentheses; `undefined` parts are skipped, and none left is `FALSE`. */
|
|
39
|
+
readonly or: (parts: readonly (SqlFragment | undefined)[]) => SqlFragment;
|
|
28
40
|
readonly existing: (params: {
|
|
29
41
|
column: string;
|
|
30
42
|
}) => SqlFragment;
|
|
@@ -62,6 +74,34 @@ export declare function compileSql(params: {
|
|
|
62
74
|
text: string;
|
|
63
75
|
values: unknown[];
|
|
64
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* A tagged template whose every interpolated value binds as one plain
|
|
79
|
+
* parameter (no fragment, no typed array or JSON-list parameter), captured
|
|
80
|
+
* without building a fragment. Its values are snapshotted when captured, as
|
|
81
|
+
* `sql` snapshots them; `compileSqlTemplate` renders it.
|
|
82
|
+
*/
|
|
83
|
+
export declare class PlainSqlTemplate {
|
|
84
|
+
readonly strings: TemplateStringsArray;
|
|
85
|
+
readonly values: readonly unknown[];
|
|
86
|
+
constructor(strings: TemplateStringsArray, values: readonly unknown[]);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* What `sql` builds for a template, except that a template whose
|
|
90
|
+
* values all bind as plain parameters comes back as a `PlainSqlTemplate`:
|
|
91
|
+
* the same text and values once compiled, without a fragment per call.
|
|
92
|
+
*/
|
|
93
|
+
export declare function captureSqlTemplate(params: {
|
|
94
|
+
strings: TemplateStringsArray;
|
|
95
|
+
values: readonly unknown[];
|
|
96
|
+
}): SqlFragment | PlainSqlTemplate;
|
|
97
|
+
/** `compileSql` over what `captureSqlTemplate` returned. */
|
|
98
|
+
export declare function compileSqlTemplate(params: {
|
|
99
|
+
dialect: SqlDialect;
|
|
100
|
+
query: SqlFragment | PlainSqlTemplate;
|
|
101
|
+
}): {
|
|
102
|
+
text: string;
|
|
103
|
+
values: unknown[];
|
|
104
|
+
};
|
|
65
105
|
/** Whether a nested SQL fragment reads the incoming row (MySQL alias declaration). */
|
|
66
106
|
export declare function sqlUsesIncoming(params: {
|
|
67
107
|
fragment: SqlFragment;
|