@vibeorm/sql 2.0.0-alpha.1
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 +58 -0
- package/dist/ast.d.ts +474 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/capabilities.d.ts +13 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/codecs.d.ts +95 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/dialect.d.ts +103 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialects/mysql.d.ts +15 -0
- package/dist/dialects/mysql.d.ts.map +1 -0
- package/dist/dialects/postgres.d.ts +7 -0
- package/dist/dialects/postgres.d.ts.map +1 -0
- package/dist/dialects/registry.d.ts +14 -0
- package/dist/dialects/registry.d.ts.map +1 -0
- package/dist/dialects/sqlite.d.ts +12 -0
- package/dist/dialects/sqlite.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1030 -0
- package/dist/index.js.map +19 -0
- package/dist/like.d.ts +44 -0
- package/dist/like.d.ts.map +1 -0
- package/dist/params.d.ts +17 -0
- package/dist/params.d.ts.map +1 -0
- package/dist/render.d.ts +46 -0
- package/dist/render.d.ts.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @vibeorm/sql
|
|
2
|
+
|
|
3
|
+
> Part of **[VibeORM](https://github.com/vibeorm/vibeorm)** — a type-safe TypeScript ORM for Bun and Node. Prisma-schema or TypeScript-DSL input, a canonical schema IR, a generated client, and a dialect-aware SQL layer over PostgreSQL, PGlite, SQLite and MySQL.
|
|
4
|
+
|
|
5
|
+
The dialect-aware SQL layer. Every decision about SQL *text* — quoting, placeholders, per-dialect strategy switches, value encoding — lives in this package, so the runtime and migrate build a dialect-neutral AST and never concatenate SQL themselves.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @vibeorm/sql@alpha
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> **Pre-release.** `2.0.0-alpha.x`, published under the `alpha` dist-tag. `npm i vibeorm` still resolves to the stable v1 line.
|
|
12
|
+
|
|
13
|
+
Most users never install this directly — it ships as a dependency of [`vibeorm`](https://www.npmjs.com/package/vibeorm). Reach for it when you are building tooling on the IR or SQL layer.
|
|
14
|
+
|
|
15
|
+
## What it exports
|
|
16
|
+
|
|
17
|
+
- **SQL AST**: `SqlStatement` (`SelectStatement`, `InsertStatement`, `UpdateStatement`, `DeleteStatement`) and `SqlExpr`, plus the supporting node types `ColRef`, `TableRef`, `SelectExpr`, `OrderTerm`, `AggRef`, `AggSelect`, `LateralJoin`, `InnerJoin`, `OnConflictClause`, `SetOperation`, `ListSetOperation`, `RowNumberProjection`. Guards: `isSetOperation`, `isListSetOperation`, `isSqlDefaultCell`, and the `SQL_DEFAULT_CELL` marker.
|
|
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
|
+
- **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
|
+
- **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
|
+
- **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
|
+
- **LIKE helpers**: `escapeLikePattern`, `escapeGlobPattern`, `containsPattern`, `startsWithPattern`, `endsWithPattern`, `strictMatchPattern`.
|
|
23
|
+
|
|
24
|
+
## Rendering an AST
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { postgresDialect, renderStatement, sqliteDialect } from "@vibeorm/sql";
|
|
28
|
+
import type { SqlStatement } from "@vibeorm/sql";
|
|
29
|
+
|
|
30
|
+
const statement: SqlStatement = {
|
|
31
|
+
kind: "select",
|
|
32
|
+
table: { name: "User" },
|
|
33
|
+
columns: [{ name: "id" }, { name: "email" }],
|
|
34
|
+
where: { kind: "cmp", op: "eq", column: { name: "id" }, value: 1 },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
renderStatement({ dialect: postgresDialect, statement });
|
|
38
|
+
// { text: `SELECT "id", "email" FROM "User" WHERE "id" = $1`, values: [1], ... }
|
|
39
|
+
|
|
40
|
+
renderStatement({ dialect: sqliteDialect, statement });
|
|
41
|
+
// { text: `SELECT "id", "email" FROM "User" WHERE "id" = ?`, values: [1], ... }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The `returning` field reports `"native"` when `RETURNING` was rendered, `"omitted"` when it was requested but the dialect lacks it (the caller re-selects), and `"none"` when it was not requested.
|
|
45
|
+
|
|
46
|
+
## Reviewable SQL
|
|
47
|
+
|
|
48
|
+
Every statement shape this package can emit is pinned to its exact SQL text and parameters, per dialect, in [`sql-catalog.md`](https://github.com/vibeorm/vibeorm/blob/master/sql-catalog.md). The file is generated from test-asserted cases (`tests/catalog/cases.ts`), so it cannot drift from the renderers.
|
|
49
|
+
|
|
50
|
+
Adding or changing a rendering means adding or updating a catalog case in the same change; `bun run catalog:check` enforces that in CI. Reviewing the catalog diff is how wrong quoting, bad placeholder order or dialect drift becomes visible without running a database.
|
|
51
|
+
|
|
52
|
+
## Links
|
|
53
|
+
|
|
54
|
+
- [Documentation](https://github.com/vibeorm/vibeorm/tree/master/docs)
|
|
55
|
+
- [Getting started](https://github.com/vibeorm/vibeorm/blob/master/docs/getting-started.md)
|
|
56
|
+
- [Dialect support tables](https://github.com/vibeorm/vibeorm/blob/master/docs/dialects.md)
|
|
57
|
+
- [Repository and issues](https://github.com/vibeorm/vibeorm)
|
|
58
|
+
- MIT licensed
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SQL AST — the small statement language the runtime and migrate packages
|
|
3
|
+
* build and dialects render.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately minimal at P0 (single-table statements, no joins/subqueries);
|
|
6
|
+
* grows with the runtime in P2/P3. Every shape added here needs a catalog case
|
|
7
|
+
* (tests/catalog/cases.ts) in the same change.
|
|
8
|
+
*/
|
|
9
|
+
import type { SqlFragmentSpec } from "@vibeorm/schema";
|
|
10
|
+
/** A column reference, optionally table-qualified. */
|
|
11
|
+
export type ColRef = {
|
|
12
|
+
readonly table?: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/**
|
|
15
|
+
* Projection-only wire transform, postgres lateral joins only (SQL review
|
|
16
|
+
* B3): raw `to_jsonb` would ship BigInt/Decimal as float64 JSON numbers and
|
|
17
|
+
* Bytes as `\x` hex text, so the subselect casts instead — `castText`
|
|
18
|
+
* renders `"col"::text AS "col"`, `encodeBase64` renders
|
|
19
|
+
* `encode("col", 'base64') AS "col"`. Never set on filter/order refs.
|
|
20
|
+
*/
|
|
21
|
+
readonly transform?: "castText" | "encodeBase64";
|
|
22
|
+
/**
|
|
23
|
+
* Comparison/order/aggregate cast, sqlite Decimal only (SQL review B4):
|
|
24
|
+
* Decimal stores as exact TEXT there and text comparison is lexicographic —
|
|
25
|
+
* compare, ORDER BY and aggregate sites render `CAST("col" AS REAL)` (with
|
|
26
|
+
* NUMBER operands) instead. Never set on projections or GROUP BY keys, so
|
|
27
|
+
* storage and read-back stay the exact text.
|
|
28
|
+
*/
|
|
29
|
+
readonly castReal?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Projection alias: renders `ref AS "alias"`. PROJECTION-ONLY — filter,
|
|
32
|
+
* order and correlation refs never set it (the renderer ignores it outside
|
|
33
|
+
* a projection list). Used by the single-statement M2M join to expose the
|
|
34
|
+
* join table's parent-key column under a reserved name (`__vibe_parent`).
|
|
35
|
+
*/
|
|
36
|
+
readonly alias?: string;
|
|
37
|
+
};
|
|
38
|
+
export type TableRef = {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly alias?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Aggregate functions applied to one column. */
|
|
43
|
+
export type AggFn = "count" | "avg" | "sum" | "min" | "max";
|
|
44
|
+
/** Reference to an aggregate expression: `COUNT(*)` or `FN("col")`. */
|
|
45
|
+
export type AggRef = {
|
|
46
|
+
readonly fn: "countAll";
|
|
47
|
+
} | {
|
|
48
|
+
readonly fn: AggFn;
|
|
49
|
+
readonly column: ColRef;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* One aggregate projection: `FN("col") AS "alias"`. The alias is part of the
|
|
53
|
+
* catalog contract — the runtime derives result keys from it deterministically
|
|
54
|
+
* (`_count___all`, `_avg__score`), never by parsing the SQL text back.
|
|
55
|
+
*/
|
|
56
|
+
export type AggSelect = {
|
|
57
|
+
readonly agg: AggRef;
|
|
58
|
+
readonly alias: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* JSON path predicates (Prisma's Json filter surface). `not` is `equals` with
|
|
62
|
+
* `negate` — there is no separate op. Which ops a dialect can express is
|
|
63
|
+
* declared on `SqlDialect.supportedJsonOps` and enforced at render time too.
|
|
64
|
+
*/
|
|
65
|
+
export type JsonFilterOp = "equals" | "string_contains" | "string_starts_with" | "string_ends_with" | "array_contains" | "array_starts_with" | "array_ends_with";
|
|
66
|
+
export type ComparisonOp = "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
|
|
67
|
+
export type SqlExpr = {
|
|
68
|
+
readonly kind: "cmp";
|
|
69
|
+
readonly op: ComparisonOp;
|
|
70
|
+
readonly column: ColRef;
|
|
71
|
+
readonly value: unknown;
|
|
72
|
+
}
|
|
73
|
+
/** Column-to-column comparison — the correlation predicate of EXISTS/lateral subqueries. */
|
|
74
|
+
| {
|
|
75
|
+
readonly kind: "cmpCol";
|
|
76
|
+
readonly op: ComparisonOp;
|
|
77
|
+
readonly left: ColRef;
|
|
78
|
+
readonly right: ColRef;
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: "isNull";
|
|
81
|
+
readonly column: ColRef;
|
|
82
|
+
readonly negate?: boolean;
|
|
83
|
+
} | {
|
|
84
|
+
readonly kind: "inArray";
|
|
85
|
+
readonly column: ColRef;
|
|
86
|
+
readonly values: readonly unknown[];
|
|
87
|
+
readonly negate?: boolean;
|
|
88
|
+
} | {
|
|
89
|
+
readonly kind: "like";
|
|
90
|
+
readonly column: ColRef;
|
|
91
|
+
/** Pre-built pattern (see like.ts helpers for escaping). */
|
|
92
|
+
readonly pattern: string;
|
|
93
|
+
readonly insensitive?: boolean;
|
|
94
|
+
readonly negate?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Case-SENSITIVE string match (`mode: "strict"`, board #20). `value` is the
|
|
98
|
+
* RAW operand — pattern building happens at render time because escaping
|
|
99
|
+
* differs per strategy (LIKE's `%`/`_`/`\` vs GLOB's `*`/`?`/`[`). Renders
|
|
100
|
+
* per `SqlDialect.strictMatchStrategy`: plain `=`/`LIKE` on postgres
|
|
101
|
+
* (already byte-sensitive — a deliberate, documented no-op), `=`/`GLOB` on
|
|
102
|
+
* sqlite, `= CAST(? AS BINARY)`/`LIKE CAST(? AS BINARY)` on mysql. The
|
|
103
|
+
* non-native forms defeat plain indexes; docs/dialect-notes.md carries the
|
|
104
|
+
* cost and the functional/`_bin`-index mitigation.
|
|
105
|
+
*/
|
|
106
|
+
| {
|
|
107
|
+
readonly kind: "strictMatch";
|
|
108
|
+
readonly column: ColRef;
|
|
109
|
+
readonly op: "equals" | "contains" | "startsWith" | "endsWith";
|
|
110
|
+
readonly value: string;
|
|
111
|
+
readonly negate?: boolean;
|
|
112
|
+
} | {
|
|
113
|
+
readonly kind: "and";
|
|
114
|
+
readonly operands: readonly SqlExpr[];
|
|
115
|
+
} | {
|
|
116
|
+
readonly kind: "or";
|
|
117
|
+
readonly operands: readonly SqlExpr[];
|
|
118
|
+
} | {
|
|
119
|
+
readonly kind: "not";
|
|
120
|
+
readonly operand: SqlExpr;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Correlated subquery test: `[NOT] EXISTS (SELECT …)`. The inner select
|
|
124
|
+
* correlates to the outer scope through table-qualified `cmpCol` predicates;
|
|
125
|
+
* its parameters merge into the statement's single parameter sequence in
|
|
126
|
+
* render order. Relation filters (`some`/`every`/`none`/`is`/`isNot`)
|
|
127
|
+
* compile to this shape.
|
|
128
|
+
*/
|
|
129
|
+
| {
|
|
130
|
+
readonly kind: "exists";
|
|
131
|
+
readonly select: SelectStatement;
|
|
132
|
+
readonly negate?: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** Aggregate comparison — the HAVING predicate: `COUNT("col") > $n`. */
|
|
135
|
+
| {
|
|
136
|
+
readonly kind: "aggCmp";
|
|
137
|
+
readonly op: ComparisonOp;
|
|
138
|
+
readonly agg: AggRef;
|
|
139
|
+
readonly value: unknown;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Scalar-list membership: `$n = ANY("col")` — Prisma's `has`. Postgres only
|
|
143
|
+
* (`capabilities.scalarArrays`); other dialects refuse at render time.
|
|
144
|
+
*/
|
|
145
|
+
| {
|
|
146
|
+
readonly kind: "arrayHas";
|
|
147
|
+
readonly column: ColRef;
|
|
148
|
+
readonly value: unknown;
|
|
149
|
+
readonly negate?: boolean;
|
|
150
|
+
}
|
|
151
|
+
/** Scalar-list superset: `"col" @> $n` (one array param) — Prisma's `hasEvery`. Postgres only. */
|
|
152
|
+
| {
|
|
153
|
+
readonly kind: "arrayContainsAll";
|
|
154
|
+
readonly column: ColRef;
|
|
155
|
+
readonly values: readonly unknown[];
|
|
156
|
+
}
|
|
157
|
+
/** Scalar-list overlap: `"col" && $n` (one array param) — Prisma's `hasSome`. Postgres only. */
|
|
158
|
+
| {
|
|
159
|
+
readonly kind: "arrayOverlaps";
|
|
160
|
+
readonly column: ColRef;
|
|
161
|
+
readonly values: readonly unknown[];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Scalar-list emptiness via `array_length("col", 1) IS [NOT] NULL` — the v1
|
|
165
|
+
* semantics kept deliberately: `empty: true` matches NULL **and** empty
|
|
166
|
+
* arrays, `empty: false` matches only rows with at least one element.
|
|
167
|
+
*/
|
|
168
|
+
| {
|
|
169
|
+
readonly kind: "arrayIsEmpty";
|
|
170
|
+
readonly column: ColRef;
|
|
171
|
+
readonly empty: boolean;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* JSON path predicate. The path is always BOUND as a parameter (a `text[]`
|
|
175
|
+
* on postgres, a `$.a.b[0]` path string on sqlite/mysql) — path segments
|
|
176
|
+
* never become SQL text. `value` is the RAW JS operand; each dialect decides
|
|
177
|
+
* its wire form (JSON.stringify for jsonb/json comparisons, LIKE patterns
|
|
178
|
+
* for the string ops). `insensitive` applies to the string ops only.
|
|
179
|
+
*/
|
|
180
|
+
| {
|
|
181
|
+
readonly kind: "jsonFilter";
|
|
182
|
+
readonly column: ColRef;
|
|
183
|
+
readonly path: readonly (string | number)[];
|
|
184
|
+
readonly op: JsonFilterOp;
|
|
185
|
+
readonly value: unknown;
|
|
186
|
+
readonly insensitive?: boolean;
|
|
187
|
+
readonly negate?: boolean;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* A bound extension fragment: literal text, bound parameters and quoted
|
|
191
|
+
* identifiers in order (extensions.md §3 sql plane — trgm operators, rank
|
|
192
|
+
* and distance terms). Parameters flow through the shared ParamCollector,
|
|
193
|
+
* identifiers through dialect.quoteIdent — never hand-written.
|
|
194
|
+
*/
|
|
195
|
+
| {
|
|
196
|
+
readonly kind: "fragment";
|
|
197
|
+
readonly fragment: SqlFragmentSpec;
|
|
198
|
+
}
|
|
199
|
+
/** Escape hatch for dialect-reviewed fragments. Text only — no parameters. */
|
|
200
|
+
| {
|
|
201
|
+
readonly kind: "raw";
|
|
202
|
+
readonly text: string;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* An aggregate projection.
|
|
206
|
+
*
|
|
207
|
+
* `countAll` renders `COUNT(*)`. The alias matters for portability: postgres
|
|
208
|
+
* names the bare column `count`, sqlite and mysql name it `COUNT(*)` — callers
|
|
209
|
+
* that must read the value back always pass an alias. Combined with a
|
|
210
|
+
* non-empty `columns` list (grouped counts, `SELECT "fk", COUNT(*) … GROUP
|
|
211
|
+
* BY "fk"`), it renders AFTER the columns.
|
|
212
|
+
*
|
|
213
|
+
* `one` renders the constant `1` — the conventional EXISTS projection; it
|
|
214
|
+
* cannot be combined with a column list.
|
|
215
|
+
*/
|
|
216
|
+
export type SelectExpr = {
|
|
217
|
+
readonly kind: "countAll";
|
|
218
|
+
readonly alias?: string;
|
|
219
|
+
} | {
|
|
220
|
+
readonly kind: "one";
|
|
221
|
+
};
|
|
222
|
+
/** Arithmetic applied to a column's current value in an UPDATE ... SET. */
|
|
223
|
+
export type SetOperator = "increment" | "decrement" | "multiply" | "divide";
|
|
224
|
+
/**
|
|
225
|
+
* An atomic update expression: `"col" = "col" <op> $n`.
|
|
226
|
+
*
|
|
227
|
+
* Recognised structurally inside `UpdateStatement.set`, so the marker key is
|
|
228
|
+
* deliberately unlikely (`__op`). Three layers keep a user's JSON payload from
|
|
229
|
+
* ever being mistaken for one: the Json codec stringifies JSON values before
|
|
230
|
+
* they reach the AST, the query builder only emits this shape for numeric
|
|
231
|
+
* fields, and {@link isSetOperation} demands an exact two-key object.
|
|
232
|
+
*/
|
|
233
|
+
export type SetOperation = {
|
|
234
|
+
readonly __op: SetOperator;
|
|
235
|
+
readonly value: number;
|
|
236
|
+
};
|
|
237
|
+
/** Narrow an UPDATE set value to an atomic {@link SetOperation}. */
|
|
238
|
+
export declare function isSetOperation(value: unknown): value is SetOperation;
|
|
239
|
+
/**
|
|
240
|
+
* A scalar-list update expression (Prisma's `{ push: … }` on list columns):
|
|
241
|
+
* `append` is one element (`"col" = array_append("col", $n)`), `concat` an
|
|
242
|
+
* array of elements (`"col" = "col" || $n`). Postgres only — the query builder
|
|
243
|
+
* gates on `capabilities.scalarArrays` at build time and the renderer refuses
|
|
244
|
+
* too. Same structural-marker discipline as {@link SetOperation}: the key is
|
|
245
|
+
* unlikely, the shape exact, and Json payloads are stringified before they
|
|
246
|
+
* could ever reach an UPDATE set.
|
|
247
|
+
*/
|
|
248
|
+
export type ListSetOperation = {
|
|
249
|
+
readonly __listOp: "append" | "concat";
|
|
250
|
+
readonly value: unknown;
|
|
251
|
+
};
|
|
252
|
+
/** Narrow an UPDATE set value to a {@link ListSetOperation}. */
|
|
253
|
+
export declare function isListSetOperation(value: unknown): value is ListSetOperation;
|
|
254
|
+
/** ORDER BY a column, with optional NULLS placement (native or emulated per dialect). */
|
|
255
|
+
export type OrderByColumn = {
|
|
256
|
+
readonly column: ColRef;
|
|
257
|
+
readonly direction: "asc" | "desc";
|
|
258
|
+
/** NULLS placement; rendered natively or emulated per dialect. */
|
|
259
|
+
readonly nulls?: "first" | "last";
|
|
260
|
+
};
|
|
261
|
+
/** ORDER BY an aggregate expression — grouped selects ordering by `COUNT("col")`. */
|
|
262
|
+
export type OrderByAggregate = {
|
|
263
|
+
readonly agg: AggRef;
|
|
264
|
+
readonly direction: "asc" | "desc";
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* ORDER BY a correlated scalar subquery — relation orderBy
|
|
268
|
+
* (`orderBy: { posts: { _count: "desc" } }` → `(SELECT COUNT(*) …)` /
|
|
269
|
+
* `orderBy: { author: { email: "asc" } }` → `(SELECT "email" …)`).
|
|
270
|
+
* Portable: all three dialects order by scalar subqueries. The subquery
|
|
271
|
+
* correlates to the outer scope through table-qualified `cmpCol` predicates,
|
|
272
|
+
* exactly like `exists`; its parameters merge into the statement's single
|
|
273
|
+
* sequence in render order. NULLS placement is deliberately unsupported here
|
|
274
|
+
* (emulating it would render the subquery twice and duplicate its params) —
|
|
275
|
+
* missing relations sort at each engine's native NULL position.
|
|
276
|
+
*/
|
|
277
|
+
export type OrderBySubquery = {
|
|
278
|
+
readonly select: SelectStatement;
|
|
279
|
+
readonly direction: "asc" | "desc";
|
|
280
|
+
};
|
|
281
|
+
export type OrderTerm = OrderByColumn | OrderByAggregate | OrderBySubquery;
|
|
282
|
+
/**
|
|
283
|
+
* One `LEFT JOIN LATERAL (…) AS alias ON true` clause aggregating a correlated
|
|
284
|
+
* subquery into a single JSON value per outer row — the "join" relation-loading
|
|
285
|
+
* strategy (postgres/pglite only; gated on `capabilities.lateralJoin`).
|
|
286
|
+
*
|
|
287
|
+
* Renders (agg `jsonArray`):
|
|
288
|
+
* `LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb)
|
|
289
|
+
* AS "columnAlias" FROM (<select>) AS "__sub") AS "alias" ON true`
|
|
290
|
+
* and (agg `jsonObject`):
|
|
291
|
+
* `LEFT JOIN LATERAL (SELECT to_jsonb("__sub".*) AS "columnAlias"
|
|
292
|
+
* FROM (<select>) AS "__sub") AS "alias" ON true`
|
|
293
|
+
*
|
|
294
|
+
* `to_jsonb` over a subselect (not `json_build_object`) sidesteps postgres's
|
|
295
|
+
* 100-argument function limit on wide models (v1 lesson). The inner select's
|
|
296
|
+
* ORDER BY drives the aggregate's element order. Consumers must run the codec
|
|
297
|
+
* table over the parsed JSON children: `to_jsonb` turns timestamps into
|
|
298
|
+
* strings and loses BigInt-ness (LEARNINGS).
|
|
299
|
+
*/
|
|
300
|
+
export type LateralJoin = {
|
|
301
|
+
/** Join alias (`__lat_0`) — the outer projection selects `alias.columnAlias`. */
|
|
302
|
+
readonly alias: string;
|
|
303
|
+
/** Column the aggregated JSON value is exposed under (`__rel_posts`). */
|
|
304
|
+
readonly columnAlias: string;
|
|
305
|
+
/** `jsonArray` for to-many (aggregated array), `jsonObject` for to-one (LIMIT 1 row or NULL). */
|
|
306
|
+
readonly agg: "jsonArray" | "jsonObject";
|
|
307
|
+
/** The correlated child select — may reference outer columns via table-qualified refs. */
|
|
308
|
+
readonly select: SelectStatement;
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* One `INNER JOIN "table" AS alias ON left = right [AND …]` clause — plain
|
|
312
|
+
* equality joins only, portable across all dialects. Added for the
|
|
313
|
+
* single-statement implicit-M2M load (child rows joined to their join-table
|
|
314
|
+
* membership rows); extra join-table columns project through the statement's
|
|
315
|
+
* `columns` list with a table qualifier + `alias`.
|
|
316
|
+
*/
|
|
317
|
+
export type InnerJoin = {
|
|
318
|
+
readonly table: TableRef;
|
|
319
|
+
/** Equality pairs ANDed into the ON clause; never empty. */
|
|
320
|
+
readonly on: readonly {
|
|
321
|
+
readonly left: ColRef;
|
|
322
|
+
readonly right: ColRef;
|
|
323
|
+
}[];
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* A `ROW_NUMBER() OVER (PARTITION BY … [ORDER BY …]) AS "alias"` projection,
|
|
327
|
+
* appended after `columns` — the distinct emulation on dialects without
|
|
328
|
+
* `DISTINCT ON` (sqlite ≥3.25, mysql ≥8.0 both have window functions): the
|
|
329
|
+
* outer statement filters `alias = 1` on the derived table, restoring
|
|
330
|
+
* SQL-side take/skip and bounded transfer (N10).
|
|
331
|
+
*/
|
|
332
|
+
export type RowNumberProjection = {
|
|
333
|
+
readonly partitionBy: readonly ColRef[];
|
|
334
|
+
/** Intra-partition order picking the representative row; may be empty. */
|
|
335
|
+
readonly orderBy: readonly OrderTerm[];
|
|
336
|
+
readonly alias: string;
|
|
337
|
+
};
|
|
338
|
+
export type SelectStatement = {
|
|
339
|
+
readonly kind: "select";
|
|
340
|
+
readonly table: TableRef;
|
|
341
|
+
/** Empty array renders as `SELECT *` (unless `selectExpr` replaces it). */
|
|
342
|
+
readonly columns: readonly ColRef[];
|
|
343
|
+
/**
|
|
344
|
+
* Aggregate projection. Replaces the column list when `columns` is empty;
|
|
345
|
+
* a `countAll` with columns renders after them (grouped counts).
|
|
346
|
+
*/
|
|
347
|
+
readonly selectExpr?: SelectExpr;
|
|
348
|
+
/**
|
|
349
|
+
* Aliased aggregate projections (`AVG("col") AS "_avg__col"`), rendered
|
|
350
|
+
* after `columns` — the aggregate/groupBy/count-select shapes. Cannot be
|
|
351
|
+
* combined with `selectExpr`.
|
|
352
|
+
*/
|
|
353
|
+
readonly aggregates?: readonly AggSelect[];
|
|
354
|
+
/**
|
|
355
|
+
* `SELECT DISTINCT ON (cols)` — postgres only (`capabilities.distinctOn`;
|
|
356
|
+
* other dialects refuse at render time and the runtime emulates client-side
|
|
357
|
+
* instead). ORDER BY must lead with these columns; the query builder
|
|
358
|
+
* guarantees that arrangement.
|
|
359
|
+
*/
|
|
360
|
+
readonly distinctOn?: readonly ColRef[];
|
|
361
|
+
/**
|
|
362
|
+
* Derived-table FROM: renders `FROM (<fromSelect>) AS "<table.name>"` —
|
|
363
|
+
* `table.name` is the subquery ALIAS and `table.alias` must be unset. Used
|
|
364
|
+
* by aggregate-over-a-subset shapes (`aggregate({ take, skip, orderBy })`).
|
|
365
|
+
*/
|
|
366
|
+
readonly fromSelect?: SelectStatement;
|
|
367
|
+
/** Lateral JSON-aggregation joins — postgres-only, see {@link LateralJoin}. */
|
|
368
|
+
readonly lateralJoins?: readonly LateralJoin[];
|
|
369
|
+
/** Plain equality INNER JOINs — portable, see {@link InnerJoin}. */
|
|
370
|
+
readonly innerJoins?: readonly InnerJoin[];
|
|
371
|
+
/** ROW_NUMBER window projection — see {@link RowNumberProjection}. */
|
|
372
|
+
readonly rowNumber?: RowNumberProjection;
|
|
373
|
+
readonly where?: SqlExpr;
|
|
374
|
+
readonly groupBy?: readonly ColRef[];
|
|
375
|
+
/** HAVING predicate — grouped selects; may contain `aggCmp` expressions. */
|
|
376
|
+
readonly having?: SqlExpr;
|
|
377
|
+
readonly orderBy?: readonly OrderTerm[];
|
|
378
|
+
readonly limit?: number;
|
|
379
|
+
readonly offset?: number;
|
|
380
|
+
/**
|
|
381
|
+
* Row-locking read (`SELECT … FOR UPDATE`) — the overlay flows on dialects
|
|
382
|
+
* without RETURNING read a row they are about to write. Rendered only where
|
|
383
|
+
* the dialect names a lock clause (`dialect.selectForUpdate`); sqlite has
|
|
384
|
+
* none and needs none — its write transactions already exclude each other.
|
|
385
|
+
*/
|
|
386
|
+
readonly lockForUpdate?: boolean;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Sentinel VALUES cell: render the `DEFAULT` keyword instead of a placeholder
|
|
390
|
+
* (M4) — a heterogeneous createMany row that omits a field lets the COLUMN
|
|
391
|
+
* default apply (or NULL on a defaultless nullable column), exactly like a
|
|
392
|
+
* single-row insert that omits it. Postgres/mysql only
|
|
393
|
+
* (`dialect.rowDefaultKeyword`); sqlite groups rows instead.
|
|
394
|
+
*/
|
|
395
|
+
export declare const SQL_DEFAULT_CELL: {
|
|
396
|
+
readonly __sqlDefault: true;
|
|
397
|
+
};
|
|
398
|
+
/** Is this VALUES cell the {@link SQL_DEFAULT_CELL} sentinel? */
|
|
399
|
+
export declare function isSqlDefaultCell(value: unknown): boolean;
|
|
400
|
+
/**
|
|
401
|
+
* VALUES/SET cell carrying a Json column's parameter (the codec's JSON text).
|
|
402
|
+
*
|
|
403
|
+
* Why it exists: OID-typed drivers (bun:sql) resolve each parameter's postgres
|
|
404
|
+
* type and re-serialize the JS value AS that type — a pre-stringified Json
|
|
405
|
+
* param bound straight into a bare jsonb slot arrives DOUBLE-ENCODED (a jsonb
|
|
406
|
+
* string holding the document; live-proven on Bun 1.3.14). The postgres
|
|
407
|
+
* renderer pins the parameter's type instead (`$n::text::jsonb`,
|
|
408
|
+
* `SqlDialect.jsonParamCast`), so every driver sends the text through and the
|
|
409
|
+
* SERVER parses it; text-param drivers (node-postgres, pglite) are unaffected.
|
|
410
|
+
* Sqlite/mysql render the bare placeholder — their Json transport is already
|
|
411
|
+
* plain text. The COLLECTED parameter is the plain text either way: this
|
|
412
|
+
* wrapper never crosses the wire.
|
|
413
|
+
*/
|
|
414
|
+
export type JsonParamCell = {
|
|
415
|
+
readonly __jsonParam: true;
|
|
416
|
+
readonly text: string;
|
|
417
|
+
};
|
|
418
|
+
/** Wrap a Json column's encoded (stringified) parameter for cast-site rendering. */
|
|
419
|
+
export declare function jsonParamCell(params: {
|
|
420
|
+
text: string;
|
|
421
|
+
}): JsonParamCell;
|
|
422
|
+
/** Is this VALUES/SET cell a {@link JsonParamCell}? */
|
|
423
|
+
export declare function isJsonParamCell(value: unknown): value is JsonParamCell;
|
|
424
|
+
export type InsertStatement = {
|
|
425
|
+
readonly kind: "insert";
|
|
426
|
+
readonly table: TableRef;
|
|
427
|
+
readonly columns: readonly string[];
|
|
428
|
+
/** Row values aligned to `columns` — deterministic parameter order. */
|
|
429
|
+
readonly rows: readonly (readonly unknown[])[];
|
|
430
|
+
/** Upsert clause: ON CONFLICT (pg/sqlite) or ON DUPLICATE KEY (mysql). */
|
|
431
|
+
readonly onConflict?: OnConflictClause;
|
|
432
|
+
/** Rendered as RETURNING where the dialect supports it, else omitted+flagged. */
|
|
433
|
+
readonly returning?: readonly string[];
|
|
434
|
+
};
|
|
435
|
+
/** Conflicting rows are updated with these column values. */
|
|
436
|
+
export type OnConflictUpdate = {
|
|
437
|
+
/** Conflict target columns (ignored by the mysql form). */
|
|
438
|
+
readonly columns: readonly string[];
|
|
439
|
+
readonly update: Readonly<Record<string, unknown>>;
|
|
440
|
+
/**
|
|
441
|
+
* MySQL only (`upsertForm: "onDuplicateKey"`): append
|
|
442
|
+
* `` `col` = LAST_INSERT_ID(`col`) `` to the update list so the UPDATE arm
|
|
443
|
+
* records the addressed row's key — without it `LAST_INSERT_ID()` is stale
|
|
444
|
+
* on the update arm and the read-back can resolve a wrong, unrelated row
|
|
445
|
+
* (SQL review B2, live-proven on 8.4). Only meaningful for integer keys.
|
|
446
|
+
*/
|
|
447
|
+
readonly lastInsertIdColumn?: string;
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* Conflicting rows are skipped (`createMany({ skipDuplicates: true })`).
|
|
451
|
+
* An empty/absent target means "any conflict" — the mysql form (INSERT IGNORE)
|
|
452
|
+
* can only express that, so it ignores `columns` entirely.
|
|
453
|
+
*/
|
|
454
|
+
export type OnConflictDoNothing = {
|
|
455
|
+
readonly columns?: readonly string[];
|
|
456
|
+
readonly doNothing: true;
|
|
457
|
+
};
|
|
458
|
+
export type OnConflictClause = OnConflictUpdate | OnConflictDoNothing;
|
|
459
|
+
export type UpdateStatement = {
|
|
460
|
+
readonly kind: "update";
|
|
461
|
+
readonly table: TableRef;
|
|
462
|
+
/** Column → parameter value, or an atomic {@link SetOperation}. */
|
|
463
|
+
readonly set: Readonly<Record<string, unknown>>;
|
|
464
|
+
readonly where?: SqlExpr;
|
|
465
|
+
readonly returning?: readonly string[];
|
|
466
|
+
};
|
|
467
|
+
export type DeleteStatement = {
|
|
468
|
+
readonly kind: "delete";
|
|
469
|
+
readonly table: TableRef;
|
|
470
|
+
readonly where?: SqlExpr;
|
|
471
|
+
readonly returning?: readonly string[];
|
|
472
|
+
};
|
|
473
|
+
export type SqlStatement = SelectStatement | InsertStatement | UpdateStatement | DeleteStatement;
|
|
474
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,sDAAsD;AACtD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IACjD;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAIF,iDAAiD;AACjD,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5D,uEAAuE;AACvE,MAAM,MAAM,MAAM,GACd;IAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAIF;;;;GAIG;AACH,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,iBAAiB,CAAC;AAItB,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;AAErE,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACvG,4FAA4F;GAC1F;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/E;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AACH;;;;;;;;;GASG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AACrD;;;;;;GAMG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE;AAC1F,wEAAwE;GACtE;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACvG;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE;AAC5G,kGAAkG;GAChG;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE;AACrG,gGAAgG;GAC9F;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE;AAClG;;;;GAIG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACrF;;;;;;GAMG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AACH;;;;;GAKG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;CAAE;AACnE,8EAA8E;GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAIpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAI7B,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE5E;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAIF,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAWpE;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAIF,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAM5E;AAID,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;IACnC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACnC,CAAC;AAEF,qFAAqF;AACrF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAI3E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iGAAiG;IACjG,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,CAAC;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CAClC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3E,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/C,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE;IAAE,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAA;CAA0C,CAAC;AAEvG,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElF,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAErE;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAMtE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;IAC/C,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IACvC,iFAAiF;IACjF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-dialect capability tables — the frozen feature contract.
|
|
3
|
+
*
|
|
4
|
+
* Mirrored verbatim by tests/capabilities.test.ts: changing a value here forces
|
|
5
|
+
* a reviewed test change. Runtime fallbacks, migrate DDL, generator output and
|
|
6
|
+
* E2E `skipUnlessCapability()` all read these same objects.
|
|
7
|
+
*/
|
|
8
|
+
import type { Dialect, DialectCapabilities } from "@vibeorm/schema";
|
|
9
|
+
export declare const POSTGRES_CAPABILITIES: DialectCapabilities;
|
|
10
|
+
export declare const SQLITE_CAPABILITIES: DialectCapabilities;
|
|
11
|
+
export declare const MYSQL_CAPABILITIES: DialectCapabilities;
|
|
12
|
+
export declare const DIALECT_CAPABILITIES: Readonly<Record<Dialect, DialectCapabilities>>;
|
|
13
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,eAAO,MAAM,qBAAqB,EAAE,mBAanC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,mBAajC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,mBAahC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAI/E,CAAC"}
|
package/dist/codecs.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure codec tables — constitution rule 6.
|
|
3
|
+
*
|
|
4
|
+
* ONE place defines how a JS value becomes a driver parameter (`encode`) and
|
|
5
|
+
* how a driver value becomes a JS value (`decode`), per dialect × scalar type.
|
|
6
|
+
* Nothing else in any package may coerce: v1's BigInt-as-string, enum-array and
|
|
7
|
+
* timestamp bugs all came from coercion scattered across query builders and
|
|
8
|
+
* relation loaders (LEARNINGS.md).
|
|
9
|
+
*
|
|
10
|
+
* These tables are PURE — dialect × scalar type only, no schema/model
|
|
11
|
+
* awareness. The FieldMeta-aware entry points (`getCodec`/`encodeValue`/
|
|
12
|
+
* `decodeValue`/`decodeRow`/`decodeRows`) live in @vibeorm/runtime, which
|
|
13
|
+
* re-exports these tables so existing consumers keep one import site.
|
|
14
|
+
*
|
|
15
|
+
* The two rules that make it work:
|
|
16
|
+
* - EVERY parameter bound by the query builder passes through `encode`.
|
|
17
|
+
* - EVERY row materialized by the client passes through `decode` — including
|
|
18
|
+
* every relation-loaded child row (the v1 lesson).
|
|
19
|
+
*
|
|
20
|
+
* Enum fields ride the String codec: enum values are plain strings on the wire
|
|
21
|
+
* on all three dialects.
|
|
22
|
+
*/
|
|
23
|
+
import type { Dialect, ScalarType } from "@vibeorm/schema";
|
|
24
|
+
/**
|
|
25
|
+
* One scalar type's transport rules for one dialect. Both directions see only
|
|
26
|
+
* non-null values — the runtime's `encodeValue`/`decodeValue` short-circuit
|
|
27
|
+
* null/undefined.
|
|
28
|
+
*/
|
|
29
|
+
export type ScalarCodec = {
|
|
30
|
+
readonly encode: (value: unknown) => unknown;
|
|
31
|
+
readonly decode: (value: unknown) => unknown;
|
|
32
|
+
};
|
|
33
|
+
export type CodecTable = Readonly<Record<ScalarType, ScalarCodec>>;
|
|
34
|
+
/**
|
|
35
|
+
* What a DRIVER actually delivers for NON-LIST **base-table storage** columns
|
|
36
|
+
* on its result path — declared per adapter (`DatabaseAdapter.wire`), verified
|
|
37
|
+
* live per driver, and consumed by the runtime's decode plans to skip decoders
|
|
38
|
+
* that are provably no-ops for that driver. The claims hold because base-table
|
|
39
|
+
* storage types follow the declared field types by construction (migrate's
|
|
40
|
+
* mappings); VIEW columns can hide any expression type under a declared field
|
|
41
|
+
* type (mysql `COUNT(*)` is BIGINT-as-string under an `Int` field), so the
|
|
42
|
+
* runtime exempts views from pruning entirely.
|
|
43
|
+
*
|
|
44
|
+
* Why it exists: the codec tables above are keyed per DIALECT, but wire forms
|
|
45
|
+
* differ per DRIVER (node-postgres hands `Date` objects where a text-mode
|
|
46
|
+
* driver hands strings). Without a declaration every DateTime/Json/Int column
|
|
47
|
+
* pays an idempotency probe per row per read; with one, the decode plan keeps
|
|
48
|
+
* only decoders that do real work. The codec DEFINITIONS stay right here —
|
|
49
|
+
* rule 6 — only plan membership changes, and only under a declaration.
|
|
50
|
+
*
|
|
51
|
+
* An adapter that declares nothing gets the full idempotency-guarded plan
|
|
52
|
+
* (today's behaviour). Declarations are PROMISES: declare only what the
|
|
53
|
+
* driver verifiably does.
|
|
54
|
+
*/
|
|
55
|
+
export type WireFidelity = {
|
|
56
|
+
/** DateTime columns arrive as JS `Date` instances (`"native"`) or as text/number (`"text"`). */
|
|
57
|
+
readonly dateTime?: "native" | "text";
|
|
58
|
+
/** Json columns arrive already parsed (`"parsed"`) or as raw JSON text (`"text"`). */
|
|
59
|
+
readonly json?: "parsed" | "text";
|
|
60
|
+
/** Int/Float columns arrive as JS numbers (`"native"`) or as text (`"text"`). */
|
|
61
|
+
readonly numbers?: "native" | "text";
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Is this scalar type's DECODE a no-op under the declared wire fidelity?
|
|
65
|
+
* Provable against the decode implementations in this file, identically on
|
|
66
|
+
* every dialect table:
|
|
67
|
+
*
|
|
68
|
+
* - `DateTime` + `dateTime: "native"` — every DateTime decode
|
|
69
|
+
* (postgres/sqlite/mysql all share {@link dateNativeCodec}'s decode) returns
|
|
70
|
+
* a `Date` instance unchanged.
|
|
71
|
+
* - `Json` + `json: "parsed"` — {@link jsonCodec}'s decode returns non-strings
|
|
72
|
+
* unchanged. (For a driver that pre-parses, skipping is also the CORRECT
|
|
73
|
+
* reading of a stored top-level JSON **string**: the driver hands a bare JS
|
|
74
|
+
* string, which the guarded decode would wrongly re-parse.)
|
|
75
|
+
* - `Int`/`Float` + `numbers: "native"` — {@link numberCodec}'s decode returns
|
|
76
|
+
* numbers unchanged.
|
|
77
|
+
*
|
|
78
|
+
* Everything else always decodes: BigInt (string→BigInt is real work on every
|
|
79
|
+
* driver that can't send int64), Decimal (mysql pads), Boolean (0/1→boolean),
|
|
80
|
+
* Bytes, and every LIST field (array wire forms differ per driver and are not
|
|
81
|
+
* covered by the declaration).
|
|
82
|
+
*/
|
|
83
|
+
export declare function decodeIsNoop(params: {
|
|
84
|
+
scalarType: ScalarType;
|
|
85
|
+
wire: WireFidelity | undefined;
|
|
86
|
+
}): boolean;
|
|
87
|
+
/** postgres (and pglite — same wire behaviour). */
|
|
88
|
+
export declare const POSTGRES_CODECS: CodecTable;
|
|
89
|
+
/** sqlite — no boolean, date or json types; everything rides TEXT/INTEGER. */
|
|
90
|
+
export declare const SQLITE_CODECS: CodecTable;
|
|
91
|
+
/** mysql — native DATETIME and JSON, booleans stored as TINYINT(1). */
|
|
92
|
+
export declare const MYSQL_CODECS: CodecTable;
|
|
93
|
+
/** The whole table, keyed by dialect. */
|
|
94
|
+
export declare const DIALECT_CODECS: Readonly<Record<Dialect, CodecTable>>;
|
|
95
|
+
//# sourceMappingURL=codecs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../src/codecs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI3D;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAInE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,GAAG,OAAO,CAOxG;AAiKD,mDAAmD;AACnD,eAAO,MAAM,eAAe,EAAE,UAU7B,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,UAU3B,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,UAU1B,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAIhE,CAAC"}
|