@typed-sql/sqlite 2.0.0-rc.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +34 -0
  4. package/dist/packages/sqlite/src/index.d.ts +17 -0
  5. package/dist/packages/sqlite/src/index.d.ts.map +1 -0
  6. package/dist/packages/sqlite/src/index.js +72 -0
  7. package/dist/packages/sqlite/src/index.js.map +1 -0
  8. package/dist/packages/sqlite/src/node-sqlite.d.ts +44 -0
  9. package/dist/packages/sqlite/src/node-sqlite.d.ts.map +1 -0
  10. package/dist/packages/sqlite/src/node-sqlite.js +125 -0
  11. package/dist/packages/sqlite/src/node-sqlite.js.map +1 -0
  12. package/dist/packages/sqlite/src/prepared.d.ts +20 -0
  13. package/dist/packages/sqlite/src/prepared.d.ts.map +1 -0
  14. package/dist/packages/sqlite/src/prepared.js +47 -0
  15. package/dist/packages/sqlite/src/prepared.js.map +1 -0
  16. package/dist/packages/sqlite/src/provider.d.ts +21 -0
  17. package/dist/packages/sqlite/src/provider.d.ts.map +1 -0
  18. package/dist/packages/sqlite/src/provider.js +120 -0
  19. package/dist/packages/sqlite/src/provider.js.map +1 -0
  20. package/dist/packages/sqlite/src/resolver.d.ts +26 -0
  21. package/dist/packages/sqlite/src/resolver.d.ts.map +1 -0
  22. package/dist/packages/sqlite/src/resolver.js +664 -0
  23. package/dist/packages/sqlite/src/resolver.js.map +1 -0
  24. package/dist/packages/sqlite/src/runtime.d.ts +27 -0
  25. package/dist/packages/sqlite/src/runtime.d.ts.map +1 -0
  26. package/dist/packages/sqlite/src/runtime.js +288 -0
  27. package/dist/packages/sqlite/src/runtime.js.map +1 -0
  28. package/dist/packages/sqlite/src/semantics.d.ts +5 -0
  29. package/dist/packages/sqlite/src/semantics.d.ts.map +1 -0
  30. package/dist/packages/sqlite/src/semantics.js +206 -0
  31. package/dist/packages/sqlite/src/semantics.js.map +1 -0
  32. package/dist/packages/sqlite/src/snapshot.d.ts +40 -0
  33. package/dist/packages/sqlite/src/snapshot.d.ts.map +1 -0
  34. package/dist/packages/sqlite/src/snapshot.js +131 -0
  35. package/dist/packages/sqlite/src/snapshot.js.map +1 -0
  36. package/dist/packages/sqlite/src/type-policy.d.ts +18 -0
  37. package/dist/packages/sqlite/src/type-policy.d.ts.map +1 -0
  38. package/dist/packages/sqlite/src/type-policy.js +63 -0
  39. package/dist/packages/sqlite/src/type-policy.js.map +1 -0
  40. package/dist/packages/sqlite/src/version.d.ts +2 -0
  41. package/dist/packages/sqlite/src/version.d.ts.map +1 -0
  42. package/dist/packages/sqlite/src/version.js +2 -0
  43. package/dist/packages/sqlite/src/version.js.map +1 -0
  44. package/package.json +53 -0
@@ -0,0 +1,664 @@
1
+ import { ParameterCollector, ResolverSchemaIndex, unionTypeLiterals } from "@typed-sql/core";
2
+ import { defaultSqliteTypePolicy, isKnownSqliteType, mapSqliteCastType, sqliteFlexibleType, } from "./type-policy.js";
3
+ const numericTypes = new Set([
4
+ "tinyint",
5
+ "smallint",
6
+ "mediumint",
7
+ "int",
8
+ "integer",
9
+ "bigint",
10
+ "decimal",
11
+ "numeric",
12
+ "float",
13
+ "double",
14
+ "real",
15
+ "bit",
16
+ "year",
17
+ ]);
18
+ const comparisonOperators = new Set([
19
+ "=",
20
+ "!=",
21
+ "<>",
22
+ "<",
23
+ "<=",
24
+ ">",
25
+ ">=",
26
+ "IS",
27
+ "IS NOT",
28
+ "IS DISTINCT FROM",
29
+ "IS NOT DISTINCT FROM",
30
+ "LIKE",
31
+ "NOT LIKE",
32
+ "AND",
33
+ "OR",
34
+ ]);
35
+ function name(identifier) {
36
+ return identifier.quoted ? identifier.name : identifier.name.toLowerCase();
37
+ }
38
+ function normalized(databaseType) {
39
+ return databaseType
40
+ .trim()
41
+ .toLowerCase()
42
+ .replace(/\s+unsigned$/u, "")
43
+ .replace(/\(.*/u, "");
44
+ }
45
+ class Resolver {
46
+ #schema;
47
+ #policy;
48
+ #strict;
49
+ #diagnostics = [];
50
+ #parameters = new ParameterCollector();
51
+ #index;
52
+ constructor(schema, options) {
53
+ this.#schema = schema;
54
+ this.#index = ResolverSchemaIndex.for(schema);
55
+ this.#policy = options.typePolicy ?? defaultSqliteTypePolicy;
56
+ this.#strict = options.strictExpressions ?? true;
57
+ }
58
+ resolve(statement) {
59
+ if (this.#schema.dialect !== "sqlite")
60
+ this.#diagnostic("TSQ007", `SQLite resolver cannot analyze ${this.#schema.dialect}`, statement.range);
61
+ const result = this.#statement(statement, undefined, new Map());
62
+ return {
63
+ ...result,
64
+ parameters: this.#parameters.values(),
65
+ diagnostics: this.#diagnostics,
66
+ };
67
+ }
68
+ #statement(statement, outer, inherited) {
69
+ const ctes = this.#with(statement.with, outer, inherited);
70
+ if (statement.kind === "select")
71
+ return { columns: this.#select(statement, outer, ctes), resultKind: "rows" };
72
+ const scope = { relations: [], usingColumns: new Map(), ...(outer === undefined ? {} : { outer }) };
73
+ const target = this.#relation(statement.table, false, scope, ctes);
74
+ if (statement.kind === "insert") {
75
+ const targets = statement.columns.length === 0
76
+ ? Object.values(target?.table.columns ?? {}).filter((column) => !this.#generated(column))
77
+ : statement.columns.map((column) => this.#findColumn(target?.table, column));
78
+ for (const column of targets) {
79
+ if (column !== undefined && this.#generated(column)) {
80
+ this.#diagnostic("TSQ401", `Cannot INSERT into generated column ${column.name}`, statement.range);
81
+ }
82
+ }
83
+ if (statement.source.kind === "values") {
84
+ for (const row of statement.source.rows) {
85
+ if (row.length !== targets.length)
86
+ this.#diagnostic("TSQ214", `INSERT has ${targets.length} target columns but ${row.length} values`, statement.source.range);
87
+ row.forEach((value, index) => {
88
+ this.#expression(value, scope, ctes, this.#snapshotType(targets[index]));
89
+ });
90
+ }
91
+ }
92
+ else if (statement.source.kind === "select") {
93
+ const selected = this.#statement(statement.source, outer, ctes);
94
+ if (selected.columns.length !== targets.length)
95
+ this.#diagnostic("TSQ214", `INSERT has ${targets.length} target columns but SELECT returns ${selected.columns.length}`, statement.source.range);
96
+ }
97
+ return statement.returning.length === 0
98
+ ? { columns: [], resultKind: "command" }
99
+ : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
100
+ }
101
+ if (statement.kind === "update") {
102
+ for (const assignment of statement.assignments) {
103
+ const column = this.#findColumn(target?.table, assignment.column);
104
+ if (column !== undefined && this.#generated(column)) {
105
+ this.#diagnostic("TSQ401", `Cannot UPDATE generated column ${column.name}`, assignment.range);
106
+ }
107
+ this.#expression(assignment.value, scope, ctes, this.#snapshotType(column));
108
+ }
109
+ if (statement.from !== undefined)
110
+ this.#relation(statement.from, false, scope, ctes);
111
+ for (const join of statement.joins)
112
+ this.#join(join, scope, ctes);
113
+ if (statement.where !== undefined)
114
+ this.#expression(statement.where, scope, ctes, this.#databaseType("boolean", false));
115
+ return statement.returning.length === 0
116
+ ? { columns: [], resultKind: "command" }
117
+ : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
118
+ }
119
+ if (statement.using.length > 0) {
120
+ this.#unsupported("SQLite does not support DELETE USING", statement.using[0].range);
121
+ }
122
+ for (const reference of statement.using)
123
+ this.#relation(reference, false, scope, ctes);
124
+ if (statement.where !== undefined)
125
+ this.#expression(statement.where, scope, ctes, this.#databaseType("boolean", false));
126
+ return statement.returning.length === 0
127
+ ? { columns: [], resultKind: "command" }
128
+ : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
129
+ }
130
+ #with(withClause, outer, inherited) {
131
+ const ctes = new Map(inherited);
132
+ if (withClause === undefined)
133
+ return ctes;
134
+ if (withClause.recursive)
135
+ this.#unsupported("Recursive CTE inference is not supported safely", withClause.range);
136
+ for (const query of withClause.queries) {
137
+ const key = name(query.name);
138
+ if (ctes.has(key))
139
+ this.#diagnostic("TSQ211", `Duplicate CTE ${query.name.name}`, query.name.range);
140
+ const result = this.#statement(query.statement, outer, ctes);
141
+ if (query.statement.kind !== "select") {
142
+ this.#unsupported("SQLite CTE bodies must be SELECT statements", query.statement.range);
143
+ }
144
+ if (result.resultKind === "command")
145
+ this.#diagnostic("TSQ212", `CTE ${query.name.name} does not return rows`, query.range);
146
+ if (query.columns.length > 0 && query.columns.length !== result.columns.length)
147
+ this.#diagnostic("TSQ213", `CTE ${query.name.name} column count does not match its query`, query.range);
148
+ const columns = {};
149
+ result.columns.forEach((column, index) => {
150
+ const columnName = query.columns[index] === undefined ? column.name : name(query.columns[index]);
151
+ columns[columnName] = {
152
+ name: columnName,
153
+ databaseType: column.databaseType ?? "unknown",
154
+ tsType: column.tsType,
155
+ nullable: column.nullable,
156
+ };
157
+ });
158
+ ctes.set(key, { name: key, columns });
159
+ }
160
+ return ctes;
161
+ }
162
+ #select(statement, outer, ctes) {
163
+ const scope = { relations: [], usingColumns: new Map(), ...(outer === undefined ? {} : { outer }) };
164
+ if (statement.distinctOn.length > 0)
165
+ this.#unsupported("SQLite does not support DISTINCT ON", statement.distinctOn[0].range);
166
+ if (statement.from !== undefined)
167
+ this.#relation(statement.from, false, scope, ctes);
168
+ for (const join of statement.joins)
169
+ this.#join(join, scope, ctes);
170
+ for (const clause of statement.locking)
171
+ this.#unsupported("SQLite does not support SELECT locking clauses", clause.range);
172
+ if (statement.where !== undefined)
173
+ this.#expression(statement.where, scope, ctes, this.#databaseType("boolean", false));
174
+ for (const value of statement.groupBy)
175
+ this.#expression(value, scope, ctes);
176
+ if (statement.having !== undefined)
177
+ this.#expression(statement.having, scope, ctes, this.#databaseType("boolean", false));
178
+ for (const window of statement.windows) {
179
+ for (const value of window.specification.partitionBy)
180
+ this.#expression(value, scope, ctes);
181
+ for (const item of window.specification.orderBy)
182
+ this.#expression(item.expression, scope, ctes);
183
+ }
184
+ const columns = [...this.#items(statement.columns, scope, ctes)];
185
+ const outputAliases = new Set(columns.map((column) => column.name));
186
+ for (const compound of statement.compounds) {
187
+ for (const output of this.#compoundOutputNames(compound.statement))
188
+ outputAliases.add(output);
189
+ }
190
+ for (const item of statement.orderBy) {
191
+ const expression = item.expression;
192
+ const outputAlias = expression.kind === "column" && expression.relation === undefined && outputAliases.has(name(expression.column));
193
+ if (!outputAlias)
194
+ this.#expression(expression, scope, ctes);
195
+ }
196
+ if (statement.limit !== undefined)
197
+ this.#expression(statement.limit, scope, ctes, this.#databaseType("int", false));
198
+ if (statement.offset !== undefined)
199
+ this.#expression(statement.offset, scope, ctes, this.#databaseType("int", false));
200
+ for (const compound of statement.compounds) {
201
+ const right = this.#select(compound.statement, outer, ctes);
202
+ if (right.length !== columns.length) {
203
+ this.#diagnostic("TSQ214", `Compound SELECT has ${columns.length} columns on the left and ${right.length} on the right`, compound.range);
204
+ continue;
205
+ }
206
+ for (let index = 0; index < columns.length; index += 1) {
207
+ const left = columns[index];
208
+ const candidate = right[index];
209
+ const { databaseType: _databaseType, ...leftWithoutDatabaseType } = left;
210
+ columns[index] = {
211
+ ...leftWithoutDatabaseType,
212
+ tsType: unionTypeLiterals([left.tsType, candidate.tsType]),
213
+ nullable: left.nullable || candidate.nullable,
214
+ ...(left.databaseType === candidate.databaseType && left.databaseType !== undefined
215
+ ? { databaseType: left.databaseType }
216
+ : {}),
217
+ };
218
+ }
219
+ }
220
+ return columns;
221
+ }
222
+ #compoundOutputNames(statement) {
223
+ const outputs = new Set();
224
+ for (const item of statement.columns) {
225
+ const output = item.alias === undefined ? this.#outputName(item.expression) : name(item.alias);
226
+ if (output !== undefined)
227
+ outputs.add(output);
228
+ }
229
+ for (const compound of statement.compounds) {
230
+ for (const output of this.#compoundOutputNames(compound.statement))
231
+ outputs.add(output);
232
+ }
233
+ return outputs;
234
+ }
235
+ #join(join, scope, ctes) {
236
+ const previous = [...scope.relations];
237
+ if (join.kind === "right" || join.kind === "full")
238
+ for (const relation of previous)
239
+ relation.nullable = true;
240
+ const relation = this.#relation(join.table, join.kind === "left" || join.kind === "full", scope, ctes);
241
+ if (join.on !== undefined)
242
+ this.#expression(join.on, scope, ctes, this.#databaseType("boolean", false));
243
+ if (join.using === undefined || relation === undefined)
244
+ return;
245
+ for (const identifier of join.using) {
246
+ const columnName = name(identifier);
247
+ const leftMatches = previous.filter((candidate) => this.#column(candidate.table, columnName) !== undefined);
248
+ const right = this.#column(relation.table, columnName);
249
+ if (leftMatches.length !== 1 || right === undefined) {
250
+ this.#diagnostic("TSQ215", `JOIN USING column ${identifier.name} must exist once on both sides`, identifier.range);
251
+ continue;
252
+ }
253
+ const left = this.#columnType(leftMatches[0], this.#column(leftMatches[0].table, columnName));
254
+ const rightType = this.#columnType(relation, right);
255
+ scope.usingColumns.set(columnName, {
256
+ tsType: unionTypeLiterals([left.tsType, rightType.tsType]),
257
+ nullable: join.kind === "left"
258
+ ? left.nullable
259
+ : join.kind === "right"
260
+ ? rightType.nullable
261
+ : left.nullable || rightType.nullable,
262
+ ...(left.databaseType === rightType.databaseType && left.databaseType !== undefined
263
+ ? { databaseType: left.databaseType }
264
+ : {}),
265
+ });
266
+ }
267
+ }
268
+ #relation(reference, nullable, scope, ctes) {
269
+ let table;
270
+ let alias;
271
+ if (reference.kind === "subquery") {
272
+ if (reference.lateral)
273
+ this.#unsupported("SQLite does not support LATERAL subqueries", reference.range);
274
+ const result = this.#statement(reference.query, reference.lateral ? scope : scope.outer, ctes);
275
+ const columns = {};
276
+ for (const column of result.columns)
277
+ columns[column.name] = {
278
+ name: column.name,
279
+ databaseType: column.databaseType ?? "unknown",
280
+ tsType: column.tsType,
281
+ nullable: column.nullable,
282
+ };
283
+ table = { name: name(reference.alias), columns };
284
+ alias = name(reference.alias);
285
+ }
286
+ else {
287
+ const requested = name(reference.name);
288
+ const requestedSchema = reference.schema === undefined ? undefined : name(reference.schema);
289
+ if (requestedSchema === undefined)
290
+ table = ctes.get(requested);
291
+ if (table === undefined) {
292
+ const matches = this.#index.tables(requested, requestedSchema);
293
+ if (matches.length === 0) {
294
+ this.#diagnostic("TSQ100", `Unknown table ${reference.name.name}`, reference.name.range);
295
+ return undefined;
296
+ }
297
+ if (requestedSchema === undefined && matches.length > 1) {
298
+ this.#diagnostic("TSQ107", `Ambiguous table ${reference.name.name}`, reference.name.range, "Qualify the table with a database name.");
299
+ return undefined;
300
+ }
301
+ table = matches[0].table;
302
+ }
303
+ alias = reference.alias === undefined ? requested : name(reference.alias);
304
+ }
305
+ if (scope.relations.some((candidate) => candidate.alias === alias)) {
306
+ this.#diagnostic("TSQ108", `Duplicate relation alias ${alias}`, reference.range);
307
+ return undefined;
308
+ }
309
+ const relation = { alias, table, nullable };
310
+ scope.relations.push(relation);
311
+ return relation;
312
+ }
313
+ #items(items, scope, ctes) {
314
+ const columns = [];
315
+ const names = new Set();
316
+ const add = (column) => {
317
+ if (names.has(column.name))
318
+ this.#diagnostic("TSQ105", `Duplicate output property ${column.name}`, column.range);
319
+ else {
320
+ names.add(column.name);
321
+ columns.push(column);
322
+ }
323
+ };
324
+ for (const item of items) {
325
+ if (item.expression.kind === "star") {
326
+ const star = item.expression;
327
+ const relations = star.relation === undefined
328
+ ? scope.relations
329
+ : scope.relations.filter((relation) => relation.alias === name(star.relation));
330
+ if (relations.length === 0) {
331
+ this.#diagnostic("TSQ103", star.relation === undefined
332
+ ? "SELECT * requires a FROM relation"
333
+ : `Unknown relation alias ${star.relation.name}`, item.range);
334
+ continue;
335
+ }
336
+ if (star.relation === undefined)
337
+ for (const [columnName, type] of scope.usingColumns)
338
+ add({ name: columnName, ...type, range: item.range });
339
+ for (const relation of relations)
340
+ for (const column of Object.values(relation.table.columns)) {
341
+ if (star.relation === undefined && scope.usingColumns.has(column.name.toLowerCase()))
342
+ continue;
343
+ add({ name: column.name, ...this.#columnType(relation, column), range: item.range });
344
+ }
345
+ continue;
346
+ }
347
+ const type = this.#expression(item.expression, scope, ctes);
348
+ const output = item.alias === undefined ? this.#outputName(item.expression) : name(item.alias);
349
+ if (output === undefined) {
350
+ if (this.#strict)
351
+ this.#diagnostic("TSQ104", "Expressions in SELECT require an explicit alias", item.range, "Add AS <name>.");
352
+ continue;
353
+ }
354
+ add({ name: output, ...type, range: item.range });
355
+ }
356
+ return columns;
357
+ }
358
+ #expression(expression, scope, ctes, expected) {
359
+ if (expression.kind === "column") {
360
+ if (expression.relation === undefined && expression.column.name === "DEFAULT")
361
+ return { tsType: "unknown", nullable: true };
362
+ return this.#resolveColumn(expression.relation, expression.column, scope);
363
+ }
364
+ if (expression.kind === "literal") {
365
+ if (expression.value === null)
366
+ return { tsType: "unknown", nullable: true };
367
+ if (typeof expression.value === "boolean")
368
+ return { tsType: this.#policy.integer, nullable: false, databaseType: "integer" };
369
+ if (typeof expression.value === "number")
370
+ return {
371
+ tsType: Number.isInteger(expression.value) ? this.#policy.integer : "number",
372
+ nullable: false,
373
+ databaseType: Number.isInteger(expression.value) ? "integer" : "real",
374
+ };
375
+ return { tsType: "string", nullable: false, databaseType: "varchar" };
376
+ }
377
+ if (expression.kind === "parameter")
378
+ return this.#recordParameter(expression.index, expected);
379
+ if (expression.kind === "star")
380
+ return { tsType: "unknown", nullable: false };
381
+ if (expression.kind === "array") {
382
+ this.#unsupported("SQLite does not support ARRAY constructors", expression.range);
383
+ return { tsType: "unknown", nullable: true };
384
+ }
385
+ if (expression.kind === "row") {
386
+ const values = expression.elements.map((value) => this.#expression(value, scope, ctes));
387
+ return {
388
+ tsType: `readonly [${values.map((value) => `${value.tsType}${value.nullable ? " | null" : ""}`).join(", ")}]`,
389
+ nullable: false,
390
+ };
391
+ }
392
+ if (expression.kind === "cast") {
393
+ const source = this.#expression(expression.expression, scope, ctes, this.#databaseType(expression.databaseType.name, true));
394
+ if (!isKnownSqliteType(expression.databaseType.name))
395
+ this.#diagnostic("TSQ106", `Invalid or unknown SQLite cast type ${expression.databaseType.name}`, expression.databaseType.range);
396
+ return {
397
+ tsType: mapSqliteCastType(expression.databaseType.name, this.#policy),
398
+ nullable: source.nullable,
399
+ databaseType: normalized(expression.databaseType.name),
400
+ };
401
+ }
402
+ if (expression.kind === "unary") {
403
+ const operand = this.#expression(expression.expression, scope, ctes);
404
+ return expression.operator === "NOT"
405
+ ? { tsType: this.#policy.integer, nullable: operand.nullable, databaseType: "integer" }
406
+ : operand;
407
+ }
408
+ if (expression.kind === "binary")
409
+ return this.#binary(expression, scope, ctes);
410
+ if (expression.kind === "call")
411
+ return this.#call(expression, scope, ctes);
412
+ if (expression.kind === "case") {
413
+ if (expression.operand !== undefined)
414
+ this.#expression(expression.operand, scope, ctes);
415
+ for (const branch of expression.branches)
416
+ this.#expression(branch.when, scope, ctes);
417
+ const values = expression.branches.map((branch) => this.#expression(branch.then, scope, ctes));
418
+ if (expression.elseExpression !== undefined)
419
+ values.push(this.#expression(expression.elseExpression, scope, ctes));
420
+ return {
421
+ tsType: unionTypeLiterals(values.map((value) => value.tsType)),
422
+ nullable: expression.elseExpression === undefined || values.some((value) => value.nullable),
423
+ };
424
+ }
425
+ if (expression.kind === "subquery") {
426
+ const result = this.#statement(expression.query, scope, ctes);
427
+ if (result.columns.length !== 1) {
428
+ this.#diagnostic("TSQ216", `Scalar subquery returns ${result.columns.length} columns instead of one`, expression.range);
429
+ return { tsType: "unknown", nullable: true };
430
+ }
431
+ const column = result.columns[0];
432
+ return {
433
+ tsType: column.tsType,
434
+ nullable: true,
435
+ ...(column.databaseType === undefined ? {} : { databaseType: column.databaseType }),
436
+ };
437
+ }
438
+ if (expression.kind === "exists") {
439
+ this.#statement(expression.query, scope, ctes);
440
+ return { tsType: this.#policy.integer, nullable: false, databaseType: "integer" };
441
+ }
442
+ if (expression.kind === "in") {
443
+ const subject = this.#expression(expression.expression, scope, ctes);
444
+ let nullable = subject.nullable;
445
+ if (Array.isArray(expression.values))
446
+ nullable ||= expression.values
447
+ .map((value) => this.#expression(value, scope, ctes, subject))
448
+ .some((value) => value.nullable);
449
+ else {
450
+ const result = this.#statement(expression.values, scope, ctes);
451
+ if (result.columns.length !== 1)
452
+ this.#diagnostic("TSQ217", `IN subquery returns ${result.columns.length} columns instead of one`, expression.range);
453
+ nullable ||= result.columns[0]?.nullable ?? true;
454
+ }
455
+ return { tsType: this.#policy.integer, nullable, databaseType: "integer" };
456
+ }
457
+ const subject = this.#expression(expression.expression, scope, ctes);
458
+ const values = [
459
+ subject,
460
+ this.#expression(expression.lower, scope, ctes, subject),
461
+ this.#expression(expression.upper, scope, ctes, subject),
462
+ ];
463
+ return {
464
+ tsType: this.#policy.integer,
465
+ nullable: values.some((value) => value.nullable),
466
+ databaseType: "integer",
467
+ };
468
+ }
469
+ #binary(expression, scope, ctes) {
470
+ if (["ILIKE", "SIMILAR TO", "~", "~*", "!~", "!~*", "@>", "<@", "?", "?|", "?&", "&&"].includes(expression.operator)) {
471
+ this.#unsupported(`SQLite does not support operator ${expression.operator}`, expression.range);
472
+ }
473
+ let left;
474
+ let right;
475
+ if (expression.left.kind === "parameter" && expression.right.kind !== "parameter") {
476
+ right = this.#expression(expression.right, scope, ctes);
477
+ left = this.#expression(expression.left, scope, ctes, right);
478
+ }
479
+ else {
480
+ left = this.#expression(expression.left, scope, ctes);
481
+ right = this.#expression(expression.right, scope, ctes, expression.right.kind === "parameter" ? left : undefined);
482
+ }
483
+ if (comparisonOperators.has(expression.operator))
484
+ return {
485
+ tsType: this.#policy.integer,
486
+ nullable: expression.operator.startsWith("IS") ? false : left.nullable || right.nullable,
487
+ databaseType: "integer",
488
+ };
489
+ if (expression.operator === "||") {
490
+ return { tsType: "string", nullable: left.nullable || right.nullable, databaseType: "text" };
491
+ }
492
+ if (expression.operator === "->")
493
+ return { tsType: "string", nullable: true, databaseType: "text" };
494
+ if (expression.operator === "->>")
495
+ return {
496
+ tsType: sqliteFlexibleType(this.#policy),
497
+ nullable: true,
498
+ };
499
+ const leftType = left.databaseType === undefined ? undefined : normalized(left.databaseType);
500
+ const rightType = right.databaseType === undefined ? undefined : normalized(right.databaseType);
501
+ if (leftType !== undefined &&
502
+ rightType !== undefined &&
503
+ numericTypes.has(leftType) &&
504
+ numericTypes.has(rightType)) {
505
+ return {
506
+ tsType: unionTypeLiterals([this.#policy.integer, "number"]),
507
+ nullable: left.nullable || right.nullable,
508
+ databaseType: "numeric",
509
+ };
510
+ }
511
+ if (left.tsType === "unknown" || right.tsType === "unknown")
512
+ return { tsType: "unknown", nullable: left.nullable || right.nullable };
513
+ this.#diagnostic("TSQ203", `Cannot safely infer SQLite operator ${expression.operator}`, expression.range);
514
+ return { tsType: "unknown", nullable: true };
515
+ }
516
+ #call(expression, scope, ctes) {
517
+ const values = expression.arguments.map((argument) => this.#expression(argument, scope, ctes));
518
+ if (expression.filter !== undefined)
519
+ this.#expression(expression.filter, scope, ctes, this.#databaseType("integer", false));
520
+ if (expression.over !== undefined && "partitionBy" in expression.over) {
521
+ for (const value of expression.over.partitionBy)
522
+ this.#expression(value, scope, ctes);
523
+ for (const item of expression.over.orderBy)
524
+ this.#expression(item.expression, scope, ctes);
525
+ }
526
+ const functionName = expression.name.name.toUpperCase();
527
+ if (functionName === "COUNT")
528
+ return { tsType: this.#policy.integer, nullable: false, databaseType: "integer" };
529
+ if (functionName === "SUM")
530
+ return {
531
+ tsType: unionTypeLiterals([this.#policy.integer, "number"]),
532
+ nullable: true,
533
+ databaseType: "numeric",
534
+ };
535
+ if (functionName === "AVG")
536
+ return { tsType: "number", nullable: true, databaseType: "real" };
537
+ if (["MIN", "MAX"].includes(functionName))
538
+ return { ...(values[0] ?? { tsType: "unknown" }), nullable: true };
539
+ if (functionName === "COALESCE")
540
+ return {
541
+ tsType: unionTypeLiterals(values.map((value) => value.tsType)),
542
+ nullable: values.every((value) => value.nullable),
543
+ };
544
+ if (functionName === "IFNULL" || functionName === "NULLIF") {
545
+ const firstArgument = expression.arguments[0];
546
+ const secondArgument = expression.arguments[1];
547
+ if (firstArgument?.kind === "parameter" && values[1] !== undefined) {
548
+ this.#expression(firstArgument, scope, ctes, values[1]);
549
+ }
550
+ if (secondArgument?.kind === "parameter" && values[0] !== undefined) {
551
+ this.#expression(secondArgument, scope, ctes, values[0]);
552
+ }
553
+ return {
554
+ ...(values[0] ?? { tsType: "unknown" }),
555
+ nullable: functionName === "NULLIF" || values.every((value) => value.nullable),
556
+ };
557
+ }
558
+ if (functionName === "GROUP_CONCAT")
559
+ return { tsType: "string", nullable: true, databaseType: "text" };
560
+ if (["JSON", "JSON_ARRAY", "JSON_OBJECT", "JSON_GROUP_ARRAY", "JSON_GROUP_OBJECT"].includes(functionName))
561
+ return { tsType: "string", nullable: true, databaseType: "text" };
562
+ if (functionName === "RANDOM")
563
+ return { tsType: this.#policy.integer, nullable: false, databaseType: "integer" };
564
+ if (functionName === "LENGTH")
565
+ return { tsType: this.#policy.integer, nullable: true, databaseType: "integer" };
566
+ const candidates = this.#index.functions(name(expression.name), values.length, expression.schema === undefined ? undefined : name(expression.schema));
567
+ const selected = candidates.length === 1 ? candidates[0] : undefined;
568
+ if (selected !== undefined) {
569
+ expression.arguments.forEach((argument, index) => {
570
+ if (argument.kind === "parameter")
571
+ this.#expression(argument, scope, ctes, this.#databaseType(selected.argumentTypes[index], true));
572
+ });
573
+ return this.#function(selected);
574
+ }
575
+ this.#diagnostic(candidates.length > 1 ? "TSQ204" : "TSQ202", candidates.length > 1 ? `Ambiguous function ${expression.name.name}` : `Unknown function ${expression.name.name}`, expression.range, undefined, candidates.length > 1 ? "error" : "warning");
576
+ return { tsType: "unknown", nullable: true };
577
+ }
578
+ #resolveColumn(relationIdentifier, columnIdentifier, scope) {
579
+ const columnName = name(columnIdentifier);
580
+ if (relationIdentifier === undefined) {
581
+ const using = scope.usingColumns.get(columnName);
582
+ if (using !== undefined)
583
+ return using;
584
+ }
585
+ const relations = relationIdentifier === undefined
586
+ ? scope.relations
587
+ : scope.relations.filter((relation) => relation.alias === name(relationIdentifier));
588
+ const matches = relations.flatMap((relation) => {
589
+ const column = this.#column(relation.table, columnName);
590
+ return column === undefined ? [] : [[relation, column]];
591
+ });
592
+ if (matches.length > 1) {
593
+ this.#diagnostic("TSQ102", `Ambiguous column ${columnIdentifier.name}`, columnIdentifier.range);
594
+ return { tsType: "unknown", nullable: true };
595
+ }
596
+ if (matches.length === 0) {
597
+ if (scope.outer !== undefined)
598
+ return this.#resolveColumn(relationIdentifier, columnIdentifier, scope.outer);
599
+ this.#diagnostic(relationIdentifier === undefined ? "TSQ101" : "TSQ103", relationIdentifier === undefined
600
+ ? `Unknown column ${columnIdentifier.name}`
601
+ : `Unknown relation or column ${relationIdentifier.name}.${columnIdentifier.name}`, columnIdentifier.range);
602
+ return { tsType: "unknown", nullable: true };
603
+ }
604
+ return this.#columnType(matches[0][0], matches[0][1]);
605
+ }
606
+ #column(table, columnName) {
607
+ return this.#index.column(table, columnName);
608
+ }
609
+ #findColumn(table, identifier) {
610
+ const column = table === undefined ? undefined : this.#column(table, name(identifier));
611
+ if (column === undefined)
612
+ this.#diagnostic("TSQ101", `Unknown column ${identifier.name}`, identifier.range);
613
+ return column;
614
+ }
615
+ #columnType(relation, column) {
616
+ return { tsType: column.tsType, nullable: column.nullable || relation.nullable, databaseType: column.databaseType };
617
+ }
618
+ #outputName(expression) {
619
+ if (expression.kind === "column")
620
+ return name(expression.column);
621
+ if (expression.kind === "cast")
622
+ return this.#outputName(expression.expression);
623
+ if (expression.kind === "call")
624
+ return name(expression.name);
625
+ if (expression.kind === "case")
626
+ return "case";
627
+ return undefined;
628
+ }
629
+ #function(value) {
630
+ return {
631
+ tsType: value.returnType,
632
+ nullable: value.nullable,
633
+ ...(value.databaseReturnType === undefined ? {} : { databaseType: value.databaseReturnType }),
634
+ };
635
+ }
636
+ #snapshotType(column) {
637
+ if (column === undefined)
638
+ return undefined;
639
+ return { tsType: column.tsType, nullable: column.nullable, databaseType: column.databaseType };
640
+ }
641
+ #generated(column) {
642
+ return "generated" in column && (column.generated === "virtual" || column.generated === "stored");
643
+ }
644
+ #databaseType(databaseType, nullable) {
645
+ return {
646
+ tsType: mapSqliteCastType(databaseType, this.#policy),
647
+ nullable,
648
+ databaseType: normalized(databaseType),
649
+ };
650
+ }
651
+ #recordParameter(index, expected) {
652
+ return this.#parameters.record(index, expected);
653
+ }
654
+ #unsupported(message, range) {
655
+ this.#diagnostic("TSQ401", message, range);
656
+ }
657
+ #diagnostic(code, message, range, suggestion, severity = "error") {
658
+ this.#diagnostics.push({ code, message, range, severity, ...(suggestion === undefined ? {} : { suggestion }) });
659
+ }
660
+ }
661
+ export function resolveSqliteStatement(statement, schema, options = {}) {
662
+ return new Resolver(schema, options).resolve(statement);
663
+ }
664
+ //# sourceMappingURL=resolver.js.map