@typed-sql/sqlite 2.0.0-rc.1 → 2.1.0-rc.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.
Files changed (103) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +3 -3
  3. package/dist/packages/sqlite/src/capabilities.d.ts +18 -0
  4. package/dist/packages/sqlite/src/capabilities.d.ts.map +1 -0
  5. package/dist/packages/sqlite/src/capabilities.js +155 -0
  6. package/dist/packages/sqlite/src/capabilities.js.map +1 -0
  7. package/dist/packages/sqlite/src/catalog/coercions.d.ts +6 -0
  8. package/dist/packages/sqlite/src/catalog/coercions.d.ts.map +1 -0
  9. package/dist/packages/sqlite/src/catalog/coercions.js +41 -0
  10. package/dist/packages/sqlite/src/catalog/coercions.js.map +1 -0
  11. package/dist/packages/sqlite/src/catalog/date-time.d.ts +5 -0
  12. package/dist/packages/sqlite/src/catalog/date-time.d.ts.map +1 -0
  13. package/dist/packages/sqlite/src/catalog/date-time.js +20 -0
  14. package/dist/packages/sqlite/src/catalog/date-time.js.map +1 -0
  15. package/dist/packages/sqlite/src/catalog/extensions.d.ts +5 -0
  16. package/dist/packages/sqlite/src/catalog/extensions.d.ts.map +1 -0
  17. package/dist/packages/sqlite/src/catalog/extensions.js +65 -0
  18. package/dist/packages/sqlite/src/catalog/extensions.js.map +1 -0
  19. package/dist/packages/sqlite/src/catalog/functions.d.ts +57 -0
  20. package/dist/packages/sqlite/src/catalog/functions.d.ts.map +1 -0
  21. package/dist/packages/sqlite/src/catalog/functions.js +165 -0
  22. package/dist/packages/sqlite/src/catalog/functions.js.map +1 -0
  23. package/dist/packages/sqlite/src/catalog/index.d.ts +8 -0
  24. package/dist/packages/sqlite/src/catalog/index.d.ts.map +1 -0
  25. package/dist/packages/sqlite/src/catalog/index.js +8 -0
  26. package/dist/packages/sqlite/src/catalog/index.js.map +1 -0
  27. package/dist/packages/sqlite/src/catalog/json.d.ts +5 -0
  28. package/dist/packages/sqlite/src/catalog/json.d.ts.map +1 -0
  29. package/dist/packages/sqlite/src/catalog/json.js +146 -0
  30. package/dist/packages/sqlite/src/catalog/json.js.map +1 -0
  31. package/dist/packages/sqlite/src/catalog/math.d.ts +4 -0
  32. package/dist/packages/sqlite/src/catalog/math.d.ts.map +1 -0
  33. package/dist/packages/sqlite/src/catalog/math.js +44 -0
  34. package/dist/packages/sqlite/src/catalog/math.js.map +1 -0
  35. package/dist/packages/sqlite/src/catalog/operators.d.ts +10 -0
  36. package/dist/packages/sqlite/src/catalog/operators.d.ts.map +1 -0
  37. package/dist/packages/sqlite/src/catalog/operators.js +52 -0
  38. package/dist/packages/sqlite/src/catalog/operators.js.map +1 -0
  39. package/dist/packages/sqlite/src/index.d.ts +5 -2
  40. package/dist/packages/sqlite/src/index.d.ts.map +1 -1
  41. package/dist/packages/sqlite/src/index.js +15 -16
  42. package/dist/packages/sqlite/src/index.js.map +1 -1
  43. package/dist/packages/sqlite/src/node-sqlite.d.ts +18 -3
  44. package/dist/packages/sqlite/src/node-sqlite.d.ts.map +1 -1
  45. package/dist/packages/sqlite/src/node-sqlite.js +136 -25
  46. package/dist/packages/sqlite/src/node-sqlite.js.map +1 -1
  47. package/dist/packages/sqlite/src/parser/index.d.ts +8 -0
  48. package/dist/packages/sqlite/src/parser/index.d.ts.map +1 -0
  49. package/dist/packages/sqlite/src/parser/index.js +4 -0
  50. package/dist/packages/sqlite/src/parser/index.js.map +1 -0
  51. package/dist/packages/sqlite/src/parser/parser.d.ts +14 -0
  52. package/dist/packages/sqlite/src/parser/parser.d.ts.map +1 -0
  53. package/dist/packages/sqlite/src/parser/parser.js +1122 -0
  54. package/dist/packages/sqlite/src/parser/parser.js.map +1 -0
  55. package/dist/packages/sqlite/src/parser/tokenizer.d.ts +14 -0
  56. package/dist/packages/sqlite/src/parser/tokenizer.d.ts.map +1 -0
  57. package/dist/packages/sqlite/src/parser/tokenizer.js +375 -0
  58. package/dist/packages/sqlite/src/parser/tokenizer.js.map +1 -0
  59. package/dist/packages/sqlite/src/parser/types.d.ts +313 -0
  60. package/dist/packages/sqlite/src/parser/types.d.ts.map +1 -0
  61. package/dist/packages/sqlite/src/parser/types.js +9 -0
  62. package/dist/packages/sqlite/src/parser/types.js.map +1 -0
  63. package/dist/packages/sqlite/src/parser/walk.d.ts +13 -0
  64. package/dist/packages/sqlite/src/parser/walk.d.ts.map +1 -0
  65. package/dist/packages/sqlite/src/parser/walk.js +180 -0
  66. package/dist/packages/sqlite/src/parser/walk.js.map +1 -0
  67. package/dist/packages/sqlite/src/prepared.d.ts +4 -3
  68. package/dist/packages/sqlite/src/prepared.d.ts.map +1 -1
  69. package/dist/packages/sqlite/src/prepared.js +7 -14
  70. package/dist/packages/sqlite/src/prepared.js.map +1 -1
  71. package/dist/packages/sqlite/src/provider.d.ts +27 -5
  72. package/dist/packages/sqlite/src/provider.d.ts.map +1 -1
  73. package/dist/packages/sqlite/src/provider.js +345 -14
  74. package/dist/packages/sqlite/src/provider.js.map +1 -1
  75. package/dist/packages/sqlite/src/resolver.d.ts +1 -1
  76. package/dist/packages/sqlite/src/resolver.d.ts.map +1 -1
  77. package/dist/packages/sqlite/src/resolver.js +989 -124
  78. package/dist/packages/sqlite/src/resolver.js.map +1 -1
  79. package/dist/packages/sqlite/src/runtime.d.ts +2 -0
  80. package/dist/packages/sqlite/src/runtime.d.ts.map +1 -1
  81. package/dist/packages/sqlite/src/runtime.js +1 -1
  82. package/dist/packages/sqlite/src/runtime.js.map +1 -1
  83. package/dist/packages/sqlite/src/schema-sql.d.ts +8 -0
  84. package/dist/packages/sqlite/src/schema-sql.d.ts.map +1 -0
  85. package/dist/packages/sqlite/src/schema-sql.js +157 -0
  86. package/dist/packages/sqlite/src/schema-sql.js.map +1 -0
  87. package/dist/packages/sqlite/src/semantics.d.ts +1 -1
  88. package/dist/packages/sqlite/src/semantics.d.ts.map +1 -1
  89. package/dist/packages/sqlite/src/semantics.js +44 -6
  90. package/dist/packages/sqlite/src/semantics.js.map +1 -1
  91. package/dist/packages/sqlite/src/snapshot.d.ts +10 -4
  92. package/dist/packages/sqlite/src/snapshot.d.ts.map +1 -1
  93. package/dist/packages/sqlite/src/snapshot.js +136 -3
  94. package/dist/packages/sqlite/src/snapshot.js.map +1 -1
  95. package/dist/packages/sqlite/src/support.d.ts +19 -0
  96. package/dist/packages/sqlite/src/support.d.ts.map +1 -0
  97. package/dist/packages/sqlite/src/support.js +59 -0
  98. package/dist/packages/sqlite/src/support.js.map +1 -0
  99. package/dist/packages/sqlite/src/type-policy.d.ts +3 -2
  100. package/dist/packages/sqlite/src/type-policy.d.ts.map +1 -1
  101. package/dist/packages/sqlite/src/type-policy.js +2 -12
  102. package/dist/packages/sqlite/src/type-policy.js.map +1 -1
  103. package/package.json +8 -8
@@ -1,46 +1,75 @@
1
- import { ParameterCollector, ResolverSchemaIndex, unionTypeLiterals } from "@typed-sql/core";
1
+ var _a;
2
+ import { ParameterCollector, ResolverSchemaIndex, unionTypeLiterals, } from "@typed-sql/core";
3
+ import { isSqliteAggregateFunction, isSqliteWindowFunction, normalizeSqliteDatabaseType, resolveSqliteBuiltin, SQLITE_CURRENT_TIME_KEYWORDS, SQLITE_EXTENSION_TABLE_FUNCTIONS, SQLITE_JSON_TABLE_FUNCTIONS, SQLITE_MATH_FUNCTIONS, SQLITE_UNSUPPORTED_OPERATORS, sqliteNumericOperands, sqliteOperator, } from "./catalog/index.js";
4
+ import { walkStatement } from "./parser/index.js";
5
+ import { compareSqliteVersions, parseSqliteVersion, sqliteVersionSupport } from "./support.js";
2
6
  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
7
  function name(identifier) {
36
8
  return identifier.quoted ? identifier.name : identifier.name.toLowerCase();
37
9
  }
38
- function normalized(databaseType) {
39
- return databaseType
40
- .trim()
41
- .toLowerCase()
42
- .replace(/\s+unsigned$/u, "")
43
- .replace(/\(.*/u, "");
10
+ function unionSqliteTypes(types) {
11
+ const members = types.flatMap((type) => {
12
+ const parts = [];
13
+ let depth = 0;
14
+ let quote;
15
+ let start = 0;
16
+ for (let index = 0; index < type.length; index += 1) {
17
+ const character = type[index];
18
+ if (quote !== undefined) {
19
+ if (character === quote && type[index - 1] !== "\\")
20
+ quote = undefined;
21
+ continue;
22
+ }
23
+ if (character === "'" || character === '"' || character === "`")
24
+ quote = character;
25
+ else if (character === "(" || character === "[" || character === "{")
26
+ depth += 1;
27
+ else if (character === ")" || character === "]" || character === "}")
28
+ depth -= 1;
29
+ else if (depth === 0 && type.slice(index, index + 3) === " | ") {
30
+ parts.push(type.slice(start, index));
31
+ start = index + 3;
32
+ index += 2;
33
+ }
34
+ }
35
+ parts.push(type.slice(start));
36
+ return parts;
37
+ });
38
+ return unionTypeLiterals(members);
39
+ }
40
+ function containsWindowFunction(expression) {
41
+ if (expression.kind === "call") {
42
+ return (expression.over !== undefined ||
43
+ expression.arguments.some(containsWindowFunction) ||
44
+ (expression.filter !== undefined && containsWindowFunction(expression.filter)));
45
+ }
46
+ if (expression.kind === "array" || expression.kind === "row")
47
+ return expression.elements.some(containsWindowFunction);
48
+ if (expression.kind === "cast" || expression.kind === "unary")
49
+ return containsWindowFunction(expression.expression);
50
+ if (expression.kind === "binary")
51
+ return containsWindowFunction(expression.left) || containsWindowFunction(expression.right);
52
+ if (expression.kind === "case")
53
+ return ((expression.operand !== undefined && containsWindowFunction(expression.operand)) ||
54
+ expression.branches.some((branch) => containsWindowFunction(branch.when) || containsWindowFunction(branch.then)) ||
55
+ (expression.elseExpression !== undefined && containsWindowFunction(expression.elseExpression)));
56
+ if (expression.kind === "in")
57
+ return (containsWindowFunction(expression.expression) ||
58
+ (!("kind" in expression.values) && expression.values.some(containsWindowFunction)));
59
+ if (expression.kind === "between")
60
+ return (containsWindowFunction(expression.expression) ||
61
+ containsWindowFunction(expression.lower) ||
62
+ containsWindowFunction(expression.upper));
63
+ return false;
64
+ }
65
+ function isConstantFrameExpression(expression) {
66
+ if (expression.kind === "literal" || expression.kind === "parameter")
67
+ return true;
68
+ if (expression.kind === "unary")
69
+ return isConstantFrameExpression(expression.expression);
70
+ if (expression.kind === "binary")
71
+ return isConstantFrameExpression(expression.left) && isConstantFrameExpression(expression.right);
72
+ return false;
44
73
  }
45
74
  class Resolver {
46
75
  #schema;
@@ -49,6 +78,7 @@ class Resolver {
49
78
  #diagnostics = [];
50
79
  #parameters = new ParameterCollector();
51
80
  #index;
81
+ #activeWindows = new Map();
52
82
  constructor(schema, options) {
53
83
  this.#schema = schema;
54
84
  this.#index = ResolverSchemaIndex.for(schema);
@@ -71,13 +101,30 @@ class Resolver {
71
101
  return { columns: this.#select(statement, outer, ctes), resultKind: "rows" };
72
102
  const scope = { relations: [], usingColumns: new Map(), ...(outer === undefined ? {} : { outer }) };
73
103
  const target = this.#relation(statement.table, false, scope, ctes);
104
+ const targetScope = {
105
+ relations: target === undefined ? [] : [target],
106
+ usingColumns: new Map(),
107
+ ...(outer === undefined ? {} : { outer }),
108
+ };
74
109
  if (statement.kind === "insert") {
75
110
  const targets = statement.columns.length === 0
76
- ? Object.values(target?.table.columns ?? {}).filter((column) => !this.#generated(column))
111
+ ? Object.values(target?.table.columns ?? {}).filter((column) => target === undefined || this.#insertable(target.table, column))
77
112
  : 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);
113
+ if (target !== undefined && statement.columns.length > 0) {
114
+ statement.columns.forEach((identifier, index) => {
115
+ const column = targets[index];
116
+ if (column !== undefined && !this.#insertable(target.table, column)) {
117
+ this.#diagnostic("TSQ218", `Cannot INSERT into non-insertable column ${column.name}`, identifier.range);
118
+ }
119
+ });
120
+ const supplied = new Set(statement.columns.map((column) => name(column).toLowerCase()));
121
+ const required = this.#index.requiredInsertColumns(target.table);
122
+ if (required !== "unknown") {
123
+ for (const column of required) {
124
+ if (!supplied.has(column.name.toLowerCase())) {
125
+ this.#diagnostic("TSQ219", `INSERT omits required column ${column.name}`, statement.table.range);
126
+ }
127
+ }
81
128
  }
82
129
  }
83
130
  if (statement.source.kind === "values") {
@@ -94,15 +141,16 @@ class Resolver {
94
141
  if (selected.columns.length !== targets.length)
95
142
  this.#diagnostic("TSQ214", `INSERT has ${targets.length} target columns but SELECT returns ${selected.columns.length}`, statement.source.range);
96
143
  }
144
+ this.#upserts(statement, target, targetScope, ctes);
97
145
  return statement.returning.length === 0
98
146
  ? { columns: [], resultKind: "command" }
99
- : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
147
+ : { columns: this.#returning(statement.returning, targetScope, ctes), resultKind: "rows" };
100
148
  }
101
149
  if (statement.kind === "update") {
102
150
  for (const assignment of statement.assignments) {
103
151
  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);
152
+ if (target !== undefined && column !== undefined && !this.#updatable(target.table, column)) {
153
+ this.#diagnostic("TSQ218", `Cannot UPDATE non-updatable column ${column.name}`, assignment.column.range);
106
154
  }
107
155
  this.#expression(assignment.value, scope, ctes, this.#snapshotType(column));
108
156
  }
@@ -114,7 +162,7 @@ class Resolver {
114
162
  this.#expression(statement.where, scope, ctes, this.#databaseType("boolean", false));
115
163
  return statement.returning.length === 0
116
164
  ? { columns: [], resultKind: "command" }
117
- : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
165
+ : { columns: this.#returning(statement.returning, targetScope, ctes), resultKind: "rows" };
118
166
  }
119
167
  if (statement.using.length > 0) {
120
168
  this.#unsupported("SQLite does not support DELETE USING", statement.using[0].range);
@@ -125,22 +173,132 @@ class Resolver {
125
173
  this.#expression(statement.where, scope, ctes, this.#databaseType("boolean", false));
126
174
  return statement.returning.length === 0
127
175
  ? { columns: [], resultKind: "command" }
128
- : { columns: this.#items(statement.returning, scope, ctes), resultKind: "rows" };
176
+ : { columns: this.#returning(statement.returning, targetScope, ctes), resultKind: "rows" };
177
+ }
178
+ #upserts(statement, target, scope, ctes) {
179
+ if (statement.upserts.length === 0)
180
+ return;
181
+ if (statement.source.kind === "select" &&
182
+ statement.source.from !== undefined &&
183
+ statement.source.where === undefined) {
184
+ this.#diagnostic("TSQ224", "INSERT SELECT with UPSERT requires a WHERE clause to avoid SQLite's ON parsing ambiguity", statement.source.range, "Add WHERE TRUE before ON CONFLICT.");
185
+ }
186
+ const excluded = target === undefined ? undefined : { alias: "excluded", table: target.table, nullable: false };
187
+ const actionScope = {
188
+ relations: [...scope.relations, ...(excluded === undefined ? [] : [excluded])],
189
+ usingColumns: new Map(),
190
+ ...(scope.outer === undefined ? {} : { outer: scope.outer }),
191
+ };
192
+ statement.upserts.forEach((upsert, upsertIndex) => {
193
+ if (upsert.target.length === 0 && upsertIndex !== statement.upserts.length - 1)
194
+ this.#diagnostic("TSQ224", "Only the final ON CONFLICT clause may omit its conflict target", upsert.range);
195
+ if (upsert.target.length > 0 && target !== undefined)
196
+ this.#validateConflictTarget(target.table, upsert.target, upsert.targetWhere !== undefined, upsert.range);
197
+ for (const term of upsert.target)
198
+ this.#expression(term.expression, scope, ctes);
199
+ if (upsert.targetWhere !== undefined)
200
+ this.#expression(upsert.targetWhere, scope, ctes, this.#databaseType("integer", false));
201
+ if (upsert.action.kind === "nothing")
202
+ return;
203
+ for (const assignment of upsert.action.assignments) {
204
+ const column = this.#findColumn(target?.table, assignment.column);
205
+ if (target !== undefined && column !== undefined && !this.#updatable(target.table, column))
206
+ this.#diagnostic("TSQ218", `Cannot UPDATE non-updatable column ${column.name}`, assignment.column.range);
207
+ this.#expression(assignment.value, actionScope, ctes, this.#snapshotType(column));
208
+ }
209
+ if (upsert.action.where !== undefined)
210
+ this.#expression(upsert.action.where, actionScope, ctes, this.#databaseType("integer", false));
211
+ });
212
+ }
213
+ #validateConflictTarget(table, terms, partial, range) {
214
+ const sqliteTable = table;
215
+ if (partial) {
216
+ this.#diagnostic("TSQ402", "Exact partial conflict-target matching requires normalized predicate evidence", range);
217
+ return;
218
+ }
219
+ const columns = [];
220
+ for (const term of terms) {
221
+ if (term.expression.kind !== "column" || term.expression.relation !== undefined) {
222
+ this.#diagnostic("TSQ402", "Exact expression conflict-target matching requires normalized index-expression evidence", term.range);
223
+ return;
224
+ }
225
+ columns.push({
226
+ name: name(term.expression.column),
227
+ ...(term.collation === undefined ? {} : { collation: name(term.collation) }),
228
+ });
229
+ }
230
+ const primaryColumns = Object.values(table.columns)
231
+ .map((column) => column)
232
+ .filter((column) => column.primaryKeyPosition !== undefined)
233
+ .sort((left, right) => left.primaryKeyPosition - right.primaryKeyPosition)
234
+ .map((column) => column.name.toLowerCase());
235
+ const primaryMatch = !partial &&
236
+ primaryColumns.length === columns.length &&
237
+ primaryColumns.every((value, index) => value === columns[index]?.name) &&
238
+ columns.every(({ collation }) => collation === undefined);
239
+ const indexMatch = (sqliteTable.indexes ?? []).some((index) => index.unique &&
240
+ !index.partial &&
241
+ index.columns.length === columns.length &&
242
+ index.columns.every((column, columnIndex) => {
243
+ const requested = columns[columnIndex];
244
+ return (!column.expression &&
245
+ column.name?.toLowerCase() === requested.name &&
246
+ (requested.collation === undefined || column.collation?.toLowerCase() === requested.collation));
247
+ }));
248
+ if (!primaryMatch && !indexMatch)
249
+ this.#diagnostic("TSQ226", "Conflict target does not match a declared UNIQUE or PRIMARY KEY constraint", range);
250
+ }
251
+ #returning(items, scope, ctes) {
252
+ for (const item of items) {
253
+ if (containsWindowFunction(item.expression) || this.#containsTopLevelAggregate(item.expression))
254
+ this.#diagnostic("TSQ225", "SQLite RETURNING cannot contain top-level aggregate or window functions", item.expression.range);
255
+ }
256
+ return this.#items(items, scope, ctes);
257
+ }
258
+ #containsTopLevelAggregate(expression) {
259
+ if (expression.kind === "call")
260
+ return (isSqliteAggregateFunction(expression.name.name) ||
261
+ expression.arguments.some((argument) => this.#containsTopLevelAggregate(argument)) ||
262
+ (expression.filter !== undefined && this.#containsTopLevelAggregate(expression.filter)));
263
+ if (expression.kind === "array" || expression.kind === "row")
264
+ return expression.elements.some((element) => this.#containsTopLevelAggregate(element));
265
+ if (expression.kind === "cast" || expression.kind === "unary")
266
+ return this.#containsTopLevelAggregate(expression.expression);
267
+ if (expression.kind === "binary")
268
+ return this.#containsTopLevelAggregate(expression.left) || this.#containsTopLevelAggregate(expression.right);
269
+ if (expression.kind === "case")
270
+ return ((expression.operand !== undefined && this.#containsTopLevelAggregate(expression.operand)) ||
271
+ expression.branches.some(({ when, then }) => this.#containsTopLevelAggregate(when) || this.#containsTopLevelAggregate(then)) ||
272
+ (expression.elseExpression !== undefined && this.#containsTopLevelAggregate(expression.elseExpression)));
273
+ if (expression.kind === "in")
274
+ return (this.#containsTopLevelAggregate(expression.expression) ||
275
+ (!("kind" in expression.values) && expression.values.some((value) => this.#containsTopLevelAggregate(value))));
276
+ if (expression.kind === "between")
277
+ return (this.#containsTopLevelAggregate(expression.expression) ||
278
+ this.#containsTopLevelAggregate(expression.lower) ||
279
+ this.#containsTopLevelAggregate(expression.upper));
280
+ return false;
129
281
  }
130
282
  #with(withClause, outer, inherited) {
131
283
  const ctes = new Map(inherited);
132
284
  if (withClause === undefined)
133
285
  return ctes;
134
- if (withClause.recursive)
135
- this.#unsupported("Recursive CTE inference is not supported safely", withClause.range);
136
286
  for (const query of withClause.queries) {
137
287
  const key = name(query.name);
138
288
  if (ctes.has(key))
139
289
  this.#diagnostic("TSQ211", `Duplicate CTE ${query.name.name}`, query.name.range);
140
- const result = this.#statement(query.statement, outer, ctes);
141
290
  if (query.statement.kind !== "select") {
142
291
  this.#unsupported("SQLite CTE bodies must be SELECT statements", query.statement.range);
143
292
  }
293
+ const recursive = query.statement.kind === "select" && this.#isRecursiveCte(query);
294
+ const result = query.statement.kind === "select"
295
+ ? recursive
296
+ ? this.#recursiveCte(query, outer, ctes)
297
+ : {
298
+ columns: this.#select(query.statement, outer, ctes, query.columns.length === 0),
299
+ resultKind: "rows",
300
+ }
301
+ : this.#statement(query.statement, outer, ctes);
144
302
  if (result.resultKind === "command")
145
303
  this.#diagnostic("TSQ212", `CTE ${query.name.name} does not return rows`, query.range);
146
304
  if (query.columns.length > 0 && query.columns.length !== result.columns.length)
@@ -159,10 +317,174 @@ class Resolver {
159
317
  }
160
318
  return ctes;
161
319
  }
162
- #select(statement, outer, ctes) {
320
+ #isRecursiveCte(query) {
321
+ if (query.statement.kind !== "select")
322
+ return false;
323
+ return this.#selectArms(query.statement).some(({ statement }) => this.#selfReferences(statement, query.name) > 0);
324
+ }
325
+ #recursiveCte(query, outer, inherited) {
326
+ const arms = this.#selectArms(query.statement);
327
+ const references = arms.map(({ statement }) => ({
328
+ direct: this.#directSelfReferences(statement, query.name),
329
+ total: this.#selfReferences(statement, query.name),
330
+ }));
331
+ const firstRecursive = references.findIndex(({ total }) => total > 0);
332
+ let valid = true;
333
+ const invalid = (message, range) => {
334
+ valid = false;
335
+ this.#diagnostic("TSQ220", message, range);
336
+ };
337
+ if (arms.length < 2)
338
+ invalid(`Recursive CTE ${query.name.name} must be a compound SELECT`, query.range);
339
+ if (firstRecursive <= 0)
340
+ invalid(`Recursive CTE ${query.name.name} requires a non-recursive SELECT before its recursive member`, query.range);
341
+ let recursiveAll;
342
+ for (let index = 0; index < arms.length; index += 1) {
343
+ const arm = arms[index];
344
+ const reference = references[index];
345
+ if (reference.total === 0) {
346
+ if (firstRecursive >= 0 && index > firstRecursive)
347
+ invalid(`Non-recursive members of ${query.name.name} must precede recursive members`, arm.statement.range);
348
+ continue;
349
+ }
350
+ if (reference.direct !== 1 || reference.total !== 1) {
351
+ invalid(`Each recursive member of ${query.name.name} must reference itself exactly once in its top-level FROM clause`, arm.statement.range);
352
+ }
353
+ if (arm.operator !== "union") {
354
+ invalid(`Recursive members of ${query.name.name} must be joined with UNION or UNION ALL`, arm.range);
355
+ }
356
+ else if (recursiveAll === undefined)
357
+ recursiveAll = arm.all ?? false;
358
+ else if ((arm.all ?? false) !== recursiveAll) {
359
+ invalid(`All recursive members of ${query.name.name} must use the same UNION form`, arm.range);
360
+ }
361
+ if (this.#hasAggregateOrWindow(arm.statement)) {
362
+ this.#diagnostic("TSQ221", `Recursive member of ${query.name.name} cannot use aggregate or window functions`, arm.statement.range);
363
+ valid = false;
364
+ }
365
+ }
366
+ const seedColumns = this.#mergeCteSeedColumns(arms.slice(0, Math.max(firstRecursive, 0)).map(({ statement }) => statement), outer, inherited);
367
+ const provisionalColumns = {};
368
+ const width = Math.max(query.columns.length, seedColumns.length);
369
+ for (let index = 0; index < width; index += 1) {
370
+ const seed = seedColumns[index];
371
+ const columnName = query.columns[index] === undefined ? seed?.name : name(query.columns[index]);
372
+ if (columnName === undefined)
373
+ continue;
374
+ provisionalColumns[columnName] = {
375
+ name: columnName,
376
+ databaseType: seed?.databaseType ?? "unknown",
377
+ tsType: seed?.tsType ?? "unknown",
378
+ nullable: seed?.nullable ?? true,
379
+ };
380
+ }
381
+ const recursiveScope = new Map(inherited);
382
+ recursiveScope.set(name(query.name), { name: name(query.name), columns: provisionalColumns });
383
+ const columns = this.#select(query.statement, outer, recursiveScope, query.columns.length === 0);
384
+ if (!valid && columns.length === 0) {
385
+ return {
386
+ columns: Object.values(provisionalColumns).map((column) => ({
387
+ name: column.name,
388
+ tsType: "unknown",
389
+ nullable: true,
390
+ databaseType: "unknown",
391
+ range: query.range,
392
+ })),
393
+ resultKind: "rows",
394
+ };
395
+ }
396
+ return { columns, resultKind: "rows" };
397
+ }
398
+ #selectArms(statement) {
399
+ const { compounds, ...simple } = statement;
400
+ const arms = [{ statement: { ...simple, compounds: [] }, range: statement.range }];
401
+ for (const compound of compounds) {
402
+ const nested = this.#selectArms(compound.statement);
403
+ const [first, ...rest] = nested;
404
+ if (first !== undefined)
405
+ arms.push({
406
+ statement: first.statement,
407
+ operator: compound.operator,
408
+ all: compound.all,
409
+ range: compound.range,
410
+ });
411
+ arms.push(...rest);
412
+ }
413
+ return arms;
414
+ }
415
+ #directSelfReferences(statement, identifier) {
416
+ const references = [statement.from, ...statement.joins.map(({ table }) => table)];
417
+ return references.filter((reference) => reference?.kind === "table" && reference.schema === undefined && name(reference.name) === name(identifier)).length;
418
+ }
419
+ #selfReferences(statement, identifier) {
420
+ let references = 0;
421
+ walkStatement(statement, {
422
+ table(reference) {
423
+ if (reference.kind === "table" && reference.schema === undefined && name(reference.name) === name(identifier)) {
424
+ references += 1;
425
+ }
426
+ },
427
+ });
428
+ return references;
429
+ }
430
+ #hasAggregateOrWindow(statement) {
431
+ let prohibited = statement.windows.length > 0;
432
+ walkStatement(statement, {
433
+ expression(expression) {
434
+ if (expression.kind === "call" &&
435
+ (expression.over !== undefined ||
436
+ expression.filter !== undefined ||
437
+ isSqliteAggregateFunction(expression.name.name))) {
438
+ prohibited = true;
439
+ }
440
+ },
441
+ });
442
+ return prohibited;
443
+ }
444
+ #mergeCteSeedColumns(statements, outer, inherited) {
445
+ const merged = [];
446
+ for (const statement of statements) {
447
+ const probe = new _a(this.#schema, { typePolicy: this.#policy, strictExpressions: false });
448
+ const columns = probe.#select(statement, outer, inherited, false);
449
+ if (merged.length === 0) {
450
+ merged.push(...columns);
451
+ continue;
452
+ }
453
+ for (let index = 0; index < Math.min(merged.length, columns.length); index += 1) {
454
+ const current = merged[index];
455
+ const candidate = columns[index];
456
+ const sameDatabaseType = current.databaseType === candidate.databaseType && current.databaseType !== undefined;
457
+ merged[index] = {
458
+ name: current.name,
459
+ tsType: unionSqliteTypes([current.tsType, candidate.tsType]),
460
+ nullable: current.nullable || candidate.nullable,
461
+ ...(sameDatabaseType ? { databaseType: current.databaseType } : {}),
462
+ range: current.range,
463
+ };
464
+ }
465
+ }
466
+ return merged;
467
+ }
468
+ #select(statement, outer, ctes, requireOutputNames = true) {
469
+ const previousWindows = this.#activeWindows;
470
+ this.#activeWindows = this.#windows(statement);
163
471
  const scope = { relations: [], usingColumns: new Map(), ...(outer === undefined ? {} : { outer }) };
164
472
  if (statement.distinctOn.length > 0)
165
473
  this.#unsupported("SQLite does not support DISTINCT ON", statement.distinctOn[0].range);
474
+ const prohibitedWindowExpressions = [
475
+ statement.where,
476
+ statement.having,
477
+ ...statement.groupBy,
478
+ ...statement.joins.map(({ on }) => on),
479
+ ...statement.windows.flatMap(({ specification }) => [
480
+ ...specification.partitionBy,
481
+ ...specification.orderBy.map(({ expression }) => expression),
482
+ ]),
483
+ ].filter((expression) => expression !== undefined);
484
+ for (const expression of prohibitedWindowExpressions) {
485
+ if (containsWindowFunction(expression))
486
+ this.#diagnostic("TSQ223", "Window functions are only allowed in SELECT results and ORDER BY", expression.range);
487
+ }
166
488
  if (statement.from !== undefined)
167
489
  this.#relation(statement.from, false, scope, ctes);
168
490
  for (const join of statement.joins)
@@ -180,8 +502,9 @@ class Resolver {
180
502
  this.#expression(value, scope, ctes);
181
503
  for (const item of window.specification.orderBy)
182
504
  this.#expression(item.expression, scope, ctes);
505
+ this.#windowFrame(window.specification, scope, ctes);
183
506
  }
184
- const columns = [...this.#items(statement.columns, scope, ctes)];
507
+ const columns = [...this.#items(statement.columns, scope, ctes, requireOutputNames)];
185
508
  const outputAliases = new Set(columns.map((column) => column.name));
186
509
  for (const compound of statement.compounds) {
187
510
  for (const output of this.#compoundOutputNames(compound.statement))
@@ -198,7 +521,7 @@ class Resolver {
198
521
  if (statement.offset !== undefined)
199
522
  this.#expression(statement.offset, scope, ctes, this.#databaseType("int", false));
200
523
  for (const compound of statement.compounds) {
201
- const right = this.#select(compound.statement, outer, ctes);
524
+ const right = this.#select(compound.statement, outer, ctes, false);
202
525
  if (right.length !== columns.length) {
203
526
  this.#diagnostic("TSQ214", `Compound SELECT has ${columns.length} columns on the left and ${right.length} on the right`, compound.range);
204
527
  continue;
@@ -209,7 +532,7 @@ class Resolver {
209
532
  const { databaseType: _databaseType, ...leftWithoutDatabaseType } = left;
210
533
  columns[index] = {
211
534
  ...leftWithoutDatabaseType,
212
- tsType: unionTypeLiterals([left.tsType, candidate.tsType]),
535
+ tsType: unionSqliteTypes([left.tsType, candidate.tsType]),
213
536
  nullable: left.nullable || candidate.nullable,
214
537
  ...(left.databaseType === candidate.databaseType && left.databaseType !== undefined
215
538
  ? { databaseType: left.databaseType }
@@ -217,8 +540,74 @@ class Resolver {
217
540
  };
218
541
  }
219
542
  }
543
+ this.#activeWindows = previousWindows;
220
544
  return columns;
221
545
  }
546
+ #windows(statement) {
547
+ const windows = new Map();
548
+ for (const window of statement.windows) {
549
+ const key = name(window.name);
550
+ if (windows.has(key)) {
551
+ this.#diagnostic("TSQ222", `Duplicate window ${window.name.name}`, window.name.range);
552
+ continue;
553
+ }
554
+ const specification = this.#effectiveWindow(window.specification, windows);
555
+ windows.set(key, specification);
556
+ }
557
+ return windows;
558
+ }
559
+ #effectiveWindow(specification, windows = this.#activeWindows) {
560
+ if (specification.base === undefined) {
561
+ this.#validateWindowFrame(specification);
562
+ return specification;
563
+ }
564
+ const base = windows.get(name(specification.base));
565
+ if (base === undefined) {
566
+ this.#diagnostic("TSQ222", `Unknown base window ${specification.base.name}`, specification.base.range);
567
+ return specification;
568
+ }
569
+ if (specification.partitionBy.length > 0)
570
+ this.#diagnostic("TSQ222", "A chained window cannot replace PARTITION BY", specification.range);
571
+ if (base.orderBy.length > 0 && specification.orderBy.length > 0)
572
+ this.#diagnostic("TSQ222", "A chained window cannot replace ORDER BY", specification.range);
573
+ if (base.frame !== undefined)
574
+ this.#diagnostic("TSQ222", "A framed window cannot be used as a chaining base", specification.base.range);
575
+ const effective = {
576
+ ...specification,
577
+ partitionBy: base.partitionBy,
578
+ orderBy: base.orderBy.length > 0 ? base.orderBy : specification.orderBy,
579
+ };
580
+ this.#validateWindowFrame(effective);
581
+ return effective;
582
+ }
583
+ #validateWindowFrame(specification) {
584
+ const frame = specification.frame;
585
+ if (frame?.unit !== "range")
586
+ return;
587
+ const hasOffset = [frame.start, frame.end].some(({ kind }) => kind === "preceding" || kind === "following");
588
+ if (hasOffset && specification.orderBy.length !== 1) {
589
+ this.#diagnostic("TSQ222", "A RANGE offset frame requires exactly one ORDER BY expression", frame.range);
590
+ }
591
+ }
592
+ #windowFrame(specification, scope, ctes) {
593
+ const frame = specification.frame;
594
+ if (frame === undefined)
595
+ return;
596
+ for (const boundary of [frame.start, frame.end]) {
597
+ if (boundary.kind === "preceding" || boundary.kind === "following") {
598
+ if (!isConstantFrameExpression(boundary.expression))
599
+ this.#diagnostic("TSQ222", "A window-frame offset must be a constant numeric expression", boundary.range);
600
+ if ((frame.unit === "rows" || frame.unit === "groups") &&
601
+ boundary.expression.kind === "literal" &&
602
+ (typeof boundary.expression.value !== "number" ||
603
+ !Number.isInteger(boundary.expression.value) ||
604
+ boundary.expression.value < 0)) {
605
+ this.#diagnostic("TSQ222", `${frame.unit.toUpperCase()} offsets must be non-negative integers`, boundary.range);
606
+ }
607
+ this.#expression(boundary.expression, scope, ctes, this.#databaseType("numeric", false));
608
+ }
609
+ }
610
+ }
222
611
  #compoundOutputNames(statement) {
223
612
  const outputs = new Set();
224
613
  for (const item of statement.columns) {
@@ -253,7 +642,7 @@ class Resolver {
253
642
  const left = this.#columnType(leftMatches[0], this.#column(leftMatches[0].table, columnName));
254
643
  const rightType = this.#columnType(relation, right);
255
644
  scope.usingColumns.set(columnName, {
256
- tsType: unionTypeLiterals([left.tsType, rightType.tsType]),
645
+ tsType: unionSqliteTypes([left.tsType, rightType.tsType]),
257
646
  nullable: join.kind === "left"
258
647
  ? left.nullable
259
648
  : join.kind === "right"
@@ -268,7 +657,85 @@ class Resolver {
268
657
  #relation(reference, nullable, scope, ctes) {
269
658
  let table;
270
659
  let alias;
271
- if (reference.kind === "subquery") {
660
+ if (reference.kind === "table-function") {
661
+ if (reference.lateral)
662
+ this.#unsupported("SQLite does not support the LATERAL keyword", reference.range);
663
+ for (const argument of reference.arguments)
664
+ this.#expression(argument, scope, ctes);
665
+ const functionName = reference.name.name.toUpperCase();
666
+ const definition = [...SQLITE_JSON_TABLE_FUNCTIONS, ...SQLITE_EXTENSION_TABLE_FUNCTIONS].find(({ name }) => name === functionName);
667
+ if (definition === undefined) {
668
+ this.#diagnostic("TSQ100", `Unknown table-valued function ${reference.name.name}`, reference.name.range);
669
+ return undefined;
670
+ }
671
+ if (reference.arguments.length < definition.arguments[0] ||
672
+ reference.arguments.length > definition.arguments[1]) {
673
+ this.#diagnostic("TSQ227", `${reference.name.name} expects ${this.#arity(definition.arguments)} arguments, received ${reference.arguments.length}`, reference.range);
674
+ }
675
+ if (!this.#tableFunctionAvailable(definition, reference.range))
676
+ return undefined;
677
+ if (definition.result === "carray") {
678
+ if (reference.arguments[1]?.kind === "parameter")
679
+ this.#expression(reference.arguments[1], scope, ctes, this.#databaseType("integer", false));
680
+ if (reference.arguments[2]?.kind === "parameter")
681
+ this.#expression(reference.arguments[2], scope, ctes, this.#databaseType("text", false));
682
+ }
683
+ else {
684
+ const jsonInput = {
685
+ tsType: sqliteFlexibleType(this.#policy),
686
+ nullable: true,
687
+ databaseType: "json",
688
+ };
689
+ const rootPath = { tsType: "string", nullable: true, databaseType: "text" };
690
+ if (reference.arguments[0]?.kind === "parameter")
691
+ this.#expression(reference.arguments[0], scope, ctes, jsonInput);
692
+ if (reference.arguments[1]?.kind === "parameter")
693
+ this.#expression(reference.arguments[1], scope, ctes, rootPath);
694
+ }
695
+ const flexible = sqliteFlexibleType(this.#policy);
696
+ table =
697
+ definition.result === "carray"
698
+ ? {
699
+ name: name(reference.alias ?? reference.name),
700
+ columns: {
701
+ value: { name: "value", databaseType: "ANY", tsType: flexible, nullable: true },
702
+ },
703
+ }
704
+ : {
705
+ name: name(reference.alias ?? reference.name),
706
+ columns: {
707
+ key: {
708
+ name: "key",
709
+ databaseType: "ANY",
710
+ tsType: unionSqliteTypes([this.#policy.integer, "string"]),
711
+ nullable: true,
712
+ },
713
+ value: {
714
+ name: "value",
715
+ databaseType: "ANY",
716
+ tsType: definition.result === "jsonb"
717
+ ? unionSqliteTypes([this.#policy.integer, "number", "string", "Uint8Array"])
718
+ : unionSqliteTypes([this.#policy.integer, "number", "string"]),
719
+ nullable: true,
720
+ },
721
+ type: { name: "type", databaseType: "TEXT", tsType: "string", nullable: false },
722
+ atom: {
723
+ name: "atom",
724
+ databaseType: "ANY",
725
+ tsType: unionSqliteTypes([this.#policy.integer, "number", "string"]),
726
+ nullable: true,
727
+ },
728
+ id: { name: "id", databaseType: "INTEGER", tsType: this.#policy.integer, nullable: false },
729
+ parent: { name: "parent", databaseType: "INTEGER", tsType: this.#policy.integer, nullable: true },
730
+ fullkey: { name: "fullkey", databaseType: "TEXT", tsType: "string", nullable: false },
731
+ path: { name: "path", databaseType: "TEXT", tsType: "string", nullable: false },
732
+ json: { name: "json", databaseType: "JSON", tsType: "string", nullable: true, hidden: true },
733
+ root: { name: "root", databaseType: "TEXT", tsType: "string", nullable: true, hidden: true },
734
+ },
735
+ };
736
+ alias = name(reference.alias ?? reference.name);
737
+ }
738
+ else if (reference.kind === "subquery") {
272
739
  if (reference.lateral)
273
740
  this.#unsupported("SQLite does not support LATERAL subqueries", reference.range);
274
741
  const result = this.#statement(reference.query, reference.lateral ? scope : scope.outer, ctes);
@@ -310,7 +777,7 @@ class Resolver {
310
777
  scope.relations.push(relation);
311
778
  return relation;
312
779
  }
313
- #items(items, scope, ctes) {
780
+ #items(items, scope, ctes, requireOutputNames = true) {
314
781
  const columns = [];
315
782
  const names = new Set();
316
783
  const add = (column) => {
@@ -340,6 +807,8 @@ class Resolver {
340
807
  for (const column of Object.values(relation.table.columns)) {
341
808
  if (star.relation === undefined && scope.usingColumns.has(column.name.toLowerCase()))
342
809
  continue;
810
+ if ("hidden" in column && column.hidden === true)
811
+ continue;
343
812
  add({ name: column.name, ...this.#columnType(relation, column), range: item.range });
344
813
  }
345
814
  continue;
@@ -347,11 +816,12 @@ class Resolver {
347
816
  const type = this.#expression(item.expression, scope, ctes);
348
817
  const output = item.alias === undefined ? this.#outputName(item.expression) : name(item.alias);
349
818
  if (output === undefined) {
350
- if (this.#strict)
819
+ if (this.#strict && requireOutputNames)
351
820
  this.#diagnostic("TSQ104", "Expressions in SELECT require an explicit alias", item.range, "Add AS <name>.");
352
- continue;
821
+ if (requireOutputNames)
822
+ continue;
353
823
  }
354
- add({ name: output, ...type, range: item.range });
824
+ add({ name: output ?? `column${columns.length + 1}`, ...type, range: item.range });
355
825
  }
356
826
  return columns;
357
827
  }
@@ -359,6 +829,10 @@ class Resolver {
359
829
  if (expression.kind === "column") {
360
830
  if (expression.relation === undefined && expression.column.name === "DEFAULT")
361
831
  return { tsType: "unknown", nullable: true };
832
+ if (expression.relation === undefined &&
833
+ !expression.column.quoted &&
834
+ SQLITE_CURRENT_TIME_KEYWORDS.has(expression.column.name.toUpperCase()))
835
+ return { tsType: "string", nullable: false, databaseType: "text" };
362
836
  return this.#resolveColumn(expression.relation, expression.column, scope);
363
837
  }
364
838
  if (expression.kind === "literal") {
@@ -396,7 +870,7 @@ class Resolver {
396
870
  return {
397
871
  tsType: mapSqliteCastType(expression.databaseType.name, this.#policy),
398
872
  nullable: source.nullable,
399
- databaseType: normalized(expression.databaseType.name),
873
+ databaseType: normalizeSqliteDatabaseType(expression.databaseType.name),
400
874
  };
401
875
  }
402
876
  if (expression.kind === "unary") {
@@ -418,7 +892,7 @@ class Resolver {
418
892
  if (expression.elseExpression !== undefined)
419
893
  values.push(this.#expression(expression.elseExpression, scope, ctes));
420
894
  return {
421
- tsType: unionTypeLiterals(values.map((value) => value.tsType)),
895
+ tsType: unionSqliteTypes(values.map((value) => value.tsType)),
422
896
  nullable: expression.elseExpression === undefined || values.some((value) => value.nullable),
423
897
  };
424
898
  }
@@ -442,10 +916,21 @@ class Resolver {
442
916
  if (expression.kind === "in") {
443
917
  const subject = this.#expression(expression.expression, scope, ctes);
444
918
  let nullable = subject.nullable;
445
- if (Array.isArray(expression.values))
919
+ if (!("kind" in expression.values))
446
920
  nullable ||= expression.values
447
921
  .map((value) => this.#expression(value, scope, ctes, subject))
448
922
  .some((value) => value.nullable);
923
+ else if (expression.values.kind !== "select") {
924
+ const tableScope = { relations: [], usingColumns: new Map(), outer: scope };
925
+ const relation = this.#relation(expression.values, false, tableScope, ctes);
926
+ const columns = Object.values(relation?.table.columns ?? {}).filter((column) => !("hidden" in column && column.hidden === true));
927
+ if (columns.length !== 1)
928
+ this.#diagnostic("TSQ217", `IN table expression returns ${columns.length} columns instead of one`, expression.range);
929
+ const column = columns[0];
930
+ if (column !== undefined && expression.expression.kind === "parameter")
931
+ this.#expression(expression.expression, scope, ctes, this.#snapshotType(column));
932
+ nullable ||= column?.nullable ?? true;
933
+ }
449
934
  else {
450
935
  const result = this.#statement(expression.values, scope, ctes);
451
936
  if (result.columns.length !== 1)
@@ -467,7 +952,7 @@ class Resolver {
467
952
  };
468
953
  }
469
954
  #binary(expression, scope, ctes) {
470
- if (["ILIKE", "SIMILAR TO", "~", "~*", "!~", "!~*", "@>", "<@", "?", "?|", "?&", "&&"].includes(expression.operator)) {
955
+ if (SQLITE_UNSUPPORTED_OPERATORS.has(expression.operator)) {
471
956
  this.#unsupported(`SQLite does not support operator ${expression.operator}`, expression.range);
472
957
  }
473
958
  let left;
@@ -480,30 +965,31 @@ class Resolver {
480
965
  left = this.#expression(expression.left, scope, ctes);
481
966
  right = this.#expression(expression.right, scope, ctes, expression.right.kind === "parameter" ? left : undefined);
482
967
  }
483
- if (comparisonOperators.has(expression.operator))
968
+ const operator = sqliteOperator(expression.operator);
969
+ if (operator?.result === "json-text" || operator?.result === "json-flexible") {
970
+ const availability = resolveSqliteBuiltin("JSON", 1, this.#schema.server?.versionKey ?? this.#schema.version, this.#schema.server?.features);
971
+ if (!this.#catalogAvailable(availability, `operator ${expression.operator}`, expression.range))
972
+ return { tsType: "unknown", nullable: true };
973
+ }
974
+ if (operator?.result === "comparison")
484
975
  return {
485
976
  tsType: this.#policy.integer,
486
- nullable: expression.operator.startsWith("IS") ? false : left.nullable || right.nullable,
977
+ nullable: operator.nullSafe === true ? false : left.nullable || right.nullable,
487
978
  databaseType: "integer",
488
979
  };
489
- if (expression.operator === "||") {
980
+ if (operator?.result === "text") {
490
981
  return { tsType: "string", nullable: left.nullable || right.nullable, databaseType: "text" };
491
982
  }
492
- if (expression.operator === "->")
983
+ if (operator?.result === "json-text")
493
984
  return { tsType: "string", nullable: true, databaseType: "text" };
494
- if (expression.operator === "->>")
985
+ if (operator?.result === "json-flexible")
495
986
  return {
496
987
  tsType: sqliteFlexibleType(this.#policy),
497
988
  nullable: true,
498
989
  };
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)) {
990
+ if (operator?.result === "numeric" && sqliteNumericOperands(left.databaseType, right.databaseType)) {
505
991
  return {
506
- tsType: unionTypeLiterals([this.#policy.integer, "number"]),
992
+ tsType: unionSqliteTypes([this.#policy.integer, "number"]),
507
993
  nullable: left.nullable || right.nullable,
508
994
  databaseType: "numeric",
509
995
  };
@@ -518,63 +1004,408 @@ class Resolver {
518
1004
  if (expression.filter !== undefined)
519
1005
  this.#expression(expression.filter, scope, ctes, this.#databaseType("integer", false));
520
1006
  if (expression.over !== undefined && "partitionBy" in expression.over) {
521
- for (const value of expression.over.partitionBy)
1007
+ const window = this.#effectiveWindow(expression.over);
1008
+ for (const value of window.partitionBy)
522
1009
  this.#expression(value, scope, ctes);
523
- for (const item of expression.over.orderBy)
1010
+ for (const item of window.orderBy)
524
1011
  this.#expression(item.expression, scope, ctes);
1012
+ this.#windowFrame(window, scope, ctes);
1013
+ }
1014
+ else if (expression.over !== undefined && !this.#activeWindows.has(name(expression.over))) {
1015
+ this.#diagnostic("TSQ222", `Unknown window ${expression.over.name}`, expression.over.range);
525
1016
  }
526
1017
  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]);
1018
+ const routineCandidates = this.#index.routineOverloads(name(expression.name), values.length, expression.schema === undefined ? undefined : name(expression.schema));
1019
+ if (routineCandidates.length > 0) {
1020
+ if (routineCandidates.length > 1) {
1021
+ this.#diagnostic("TSQ204", `Ambiguous function ${expression.name.name}`, expression.range);
1022
+ return { tsType: "unknown", nullable: true };
552
1023
  }
553
- return {
554
- ...(values[0] ?? { tsType: "unknown" }),
555
- nullable: functionName === "NULLIF" || values.every((value) => value.nullable),
556
- };
1024
+ return this.#applicationRoutine(expression, routineCandidates[0], values, scope, ctes);
557
1025
  }
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) {
1026
+ const declaredCandidates = this.#index.functions(name(expression.name), values.length, expression.schema === undefined ? undefined : name(expression.schema));
1027
+ if (declaredCandidates.length > 0) {
1028
+ if (declaredCandidates.length > 1) {
1029
+ this.#diagnostic("TSQ204", `Ambiguous function ${expression.name.name}`, expression.range);
1030
+ return { tsType: "unknown", nullable: true };
1031
+ }
1032
+ if (expression.over !== undefined || expression.filter !== undefined)
1033
+ this.#diagnostic("TSQ227", `Legacy function ${expression.name.name} has no aggregate or window kind evidence`, expression.range);
1034
+ const selected = declaredCandidates[0];
569
1035
  expression.arguments.forEach((argument, index) => {
570
1036
  if (argument.kind === "parameter")
571
1037
  this.#expression(argument, scope, ctes, this.#databaseType(selected.argumentTypes[index], true));
572
1038
  });
573
1039
  return this.#function(selected);
574
1040
  }
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");
1041
+ const builtinWindow = isSqliteWindowFunction(functionName);
1042
+ if (builtinWindow && expression.over === undefined)
1043
+ this.#diagnostic("TSQ223", `${expression.name.name} requires an OVER clause`, expression.range);
1044
+ if (expression.over !== undefined && (expression.distinct || (builtinWindow && expression.filter !== undefined))) {
1045
+ this.#diagnostic("TSQ223", expression.distinct
1046
+ ? "SQLite window functions cannot use DISTINCT"
1047
+ : `SQLite built-in window function ${expression.name.name} cannot use FILTER`, expression.range);
1048
+ }
1049
+ const builtin = resolveSqliteBuiltin(functionName, values.length, this.#schema.server?.versionKey ?? this.#schema.version, this.#schema.server?.features);
1050
+ if (builtin.status === "exact") {
1051
+ if (expression.over !== undefined && builtin.definition.kind === "scalar")
1052
+ this.#diagnostic("TSQ223", `${expression.name.name} is not a window or aggregate function`, expression.range);
1053
+ if (expression.filter !== undefined && builtin.definition.kind === "scalar")
1054
+ this.#diagnostic("TSQ227", `${expression.name.name} cannot use FILTER`, expression.range);
1055
+ if (builtin.definition.result === "coalesce" || builtin.definition.result === "nullif") {
1056
+ const firstArgument = expression.arguments[0];
1057
+ const secondArgument = expression.arguments[1];
1058
+ if (firstArgument?.kind === "parameter" && values[1] !== undefined) {
1059
+ this.#expression(firstArgument, scope, ctes, values[1]);
1060
+ }
1061
+ if (secondArgument?.kind === "parameter" && values[0] !== undefined) {
1062
+ this.#expression(secondArgument, scope, ctes, values[0]);
1063
+ }
1064
+ }
1065
+ this.#builtinParameters(functionName, expression, scope, ctes);
1066
+ this.#dateTimeArguments(functionName, expression);
1067
+ return this.#builtinResult(builtin.definition, values, expression.arguments);
1068
+ }
1069
+ if (builtin.status === "arity") {
1070
+ const ranges = [...new Set(builtin.definitions.map(({ arguments: range }) => this.#arity(range)))].join(" or ");
1071
+ this.#diagnostic(builtinWindow ? "TSQ223" : "TSQ227", `${expression.name.name} expects ${ranges} arguments, received ${values.length}`, expression.range);
1072
+ return { tsType: "unknown", nullable: true };
1073
+ }
1074
+ if (builtin.status === "evidence-required") {
1075
+ this.#diagnostic("TSQ402", `SQLite ${builtin.since} server-version evidence is required to resolve ${expression.name.name}`, expression.range);
1076
+ return { tsType: "unknown", nullable: true };
1077
+ }
1078
+ if (builtin.status === "outside-supported-version") {
1079
+ this.#diagnostic("TSQ403", `SQLite ${builtin.status === "outside-supported-version" ? builtin.version : "version"} is outside the tested built-in catalog range`, expression.range);
1080
+ return { tsType: "unknown", nullable: true };
1081
+ }
1082
+ if (builtin.status === "unavailable") {
1083
+ this.#diagnostic("TSQ404", `${expression.name.name} requires SQLite ${builtin.since} or newer`, expression.range);
1084
+ return { tsType: "unknown", nullable: true };
1085
+ }
1086
+ if (builtin.status === "compile-evidence-required") {
1087
+ this.#diagnostic("TSQ402", `SQLite compile-option evidence is required to resolve ${expression.name.name} (${builtin.option})`, expression.range);
1088
+ return { tsType: "unknown", nullable: true };
1089
+ }
1090
+ if (builtin.status === "compile-option-unavailable") {
1091
+ this.#diagnostic("TSQ406", `${expression.name.name} is unavailable under the recorded SQLite compile options (${builtin.option})`, expression.range);
1092
+ return { tsType: "unknown", nullable: true };
1093
+ }
1094
+ this.#diagnostic("TSQ202", `Unknown function ${expression.name.name}`, expression.range, undefined, "warning");
1095
+ return { tsType: "unknown", nullable: true };
1096
+ }
1097
+ #applicationRoutine(expression, routine, _values, scope, ctes) {
1098
+ const inputs = routine.arguments.filter(({ mode }) => mode !== "out");
1099
+ expression.arguments.forEach((argument, index) => {
1100
+ const expected = inputs[index];
1101
+ if (argument.kind === "parameter" && expected !== undefined)
1102
+ this.#expression(argument, scope, ctes, {
1103
+ tsType: expected.tsType,
1104
+ nullable: true,
1105
+ databaseType: expected.databaseType,
1106
+ });
1107
+ });
1108
+ if (routine.kind === "window" && expression.over === undefined)
1109
+ this.#diagnostic("TSQ223", `${expression.name.name} requires an OVER clause`, expression.range);
1110
+ if (routine.kind === "function" && expression.over !== undefined)
1111
+ this.#diagnostic("TSQ223", `${expression.name.name} is not a window or aggregate function`, expression.range);
1112
+ if (routine.kind === "function" && expression.filter !== undefined)
1113
+ this.#diagnostic("TSQ227", `${expression.name.name} cannot use FILTER`, expression.range);
1114
+ if (routine.kind === "window" && expression.distinct)
1115
+ this.#diagnostic("TSQ223", "SQLite window functions cannot use DISTINCT", expression.range);
1116
+ const result = routine.result;
1117
+ if (result.kind === "scalar")
1118
+ return { tsType: result.tsType, nullable: result.nullable, databaseType: result.databaseType };
1119
+ this.#diagnostic("TSQ227", `${expression.name.name} does not have a scalar SQL result`, expression.range);
576
1120
  return { tsType: "unknown", nullable: true };
577
1121
  }
1122
+ #catalogAvailable(resolution, displayName, range) {
1123
+ if (resolution.status === "exact")
1124
+ return true;
1125
+ if (resolution.status === "evidence-required")
1126
+ this.#diagnostic("TSQ402", `SQLite ${resolution.since} server-version evidence is required to resolve ${displayName}`, range);
1127
+ else if (resolution.status === "outside-supported-version")
1128
+ this.#diagnostic("TSQ403", `SQLite ${resolution.version} is outside the tested built-in catalog range`, range);
1129
+ else if (resolution.status === "unavailable")
1130
+ this.#diagnostic("TSQ404", `${displayName} requires SQLite ${resolution.since} or newer`, range);
1131
+ else if (resolution.status === "compile-evidence-required")
1132
+ this.#diagnostic("TSQ402", `SQLite compile-option evidence is required to resolve ${displayName} (${resolution.option})`, range);
1133
+ else if (resolution.status === "compile-option-unavailable")
1134
+ this.#diagnostic("TSQ406", `${displayName} is unavailable under the recorded SQLite compile options (${resolution.option})`, range);
1135
+ else
1136
+ this.#diagnostic(resolution.status === "arity" ? "TSQ227" : "TSQ202", resolution.status === "arity" ? `Invalid invocation of ${displayName}` : `Unknown function ${displayName}`, range);
1137
+ return false;
1138
+ }
1139
+ #tableFunctionAvailable(definition, range) {
1140
+ const version = this.#schema.server?.versionKey ?? this.#schema.version;
1141
+ const parsed = version === undefined ? undefined : parseSqliteVersion(version);
1142
+ if (parsed === undefined) {
1143
+ this.#diagnostic("TSQ402", `SQLite ${definition.availableSince} server-version evidence is required to resolve ${definition.name}`, range);
1144
+ return false;
1145
+ }
1146
+ const support = sqliteVersionSupport(version);
1147
+ if (support === "below-supported" || support === "newer-than-tested" || support === "prerelease") {
1148
+ this.#diagnostic("TSQ403", `SQLite ${version} is outside the tested table-function catalog range`, range);
1149
+ return false;
1150
+ }
1151
+ if (compareSqliteVersions(parsed, parseSqliteVersion(definition.availableSince)) < 0) {
1152
+ this.#diagnostic("TSQ404", `${definition.name} requires SQLite ${definition.availableSince} or newer`, range);
1153
+ return false;
1154
+ }
1155
+ const option = definition.requiredCompileOption ?? definition.omittedByCompileOption;
1156
+ const features = this.#schema.server?.features;
1157
+ if (features === undefined) {
1158
+ this.#diagnostic("TSQ402", `SQLite compile-option evidence is required to resolve ${definition.name} (${option})`, range);
1159
+ return false;
1160
+ }
1161
+ const has = (candidate) => features.some((feature) => feature === candidate || feature.startsWith(`${candidate}=`));
1162
+ if ((definition.requiredCompileOption !== undefined && !has(definition.requiredCompileOption)) ||
1163
+ (definition.omittedByCompileOption !== undefined && has(definition.omittedByCompileOption))) {
1164
+ this.#diagnostic("TSQ406", `${definition.name} is unavailable under the recorded SQLite compile options (${option})`, range);
1165
+ return false;
1166
+ }
1167
+ return true;
1168
+ }
1169
+ #dateTimeArguments(functionName, expression) {
1170
+ if (!["DATE", "DATETIME", "JULIANDAY", "STRFTIME", "TIME", "UNIXEPOCH"].includes(functionName))
1171
+ return;
1172
+ const modifierStart = functionName === "STRFTIME" ? 2 : 1;
1173
+ const argumentsToCheck = [...expression.arguments.slice(modifierStart)];
1174
+ const first = expression.arguments[0];
1175
+ if (first?.kind === "literal" &&
1176
+ typeof first.value === "string" &&
1177
+ ["subsec", "subsecond"].includes(first.value.toLowerCase()))
1178
+ argumentsToCheck.push(first);
1179
+ for (const argument of argumentsToCheck) {
1180
+ if (argument.kind !== "literal" || typeof argument.value !== "string")
1181
+ continue;
1182
+ const modifier = argument.value.toLowerCase();
1183
+ if (modifier === "subsec" || modifier === "subsecond")
1184
+ this.#dateTimeVersion("3.42.0", `date/time modifier ${argument.value}`, argument.range);
1185
+ if (modifier === "ceiling" || modifier === "floor")
1186
+ this.#dateTimeVersion("3.46.0", `date/time modifier ${argument.value}`, argument.range);
1187
+ }
1188
+ if (functionName === "STRFTIME") {
1189
+ const format = expression.arguments[0];
1190
+ if (format?.kind === "literal" && typeof format.value === "string" && /%(?:G|g|U|V)/u.test(format.value))
1191
+ this.#dateTimeVersion("3.46.0", "the requested strftime substitution", format.range);
1192
+ }
1193
+ }
1194
+ #builtinParameters(functionName, expression, scope, ctes) {
1195
+ const numeric = {
1196
+ tsType: unionSqliteTypes([this.#policy.integer, "number"]),
1197
+ nullable: true,
1198
+ databaseType: "numeric",
1199
+ };
1200
+ const text = { tsType: "string", nullable: true, databaseType: "text" };
1201
+ const json = { tsType: sqliteFlexibleType(this.#policy), nullable: true, databaseType: "json" };
1202
+ const jsonValue = { tsType: sqliteFlexibleType(this.#policy), nullable: true };
1203
+ const timeValue = {
1204
+ tsType: unionSqliteTypes([this.#policy.integer, "number", "string"]),
1205
+ nullable: true,
1206
+ databaseType: "numeric",
1207
+ };
1208
+ const expect = (index, expected) => {
1209
+ const argument = expression.arguments[index];
1210
+ if (argument?.kind === "parameter")
1211
+ this.#expression(argument, scope, ctes, expected);
1212
+ };
1213
+ if (SQLITE_MATH_FUNCTIONS.some(({ name }) => name === functionName) ||
1214
+ ["MEDIAN", "PERCENTILE", "PERCENTILE_CONT", "PERCENTILE_DISC"].includes(functionName)) {
1215
+ expression.arguments.forEach((_, index) => {
1216
+ expect(index, numeric);
1217
+ });
1218
+ return;
1219
+ }
1220
+ if (functionName === "SOUNDEX") {
1221
+ expect(0, text);
1222
+ return;
1223
+ }
1224
+ if (["DATE", "DATETIME", "JULIANDAY", "TIME", "UNIXEPOCH"].includes(functionName)) {
1225
+ expect(0, timeValue);
1226
+ for (let index = 1; index < expression.arguments.length; index += 1)
1227
+ expect(index, text);
1228
+ return;
1229
+ }
1230
+ if (functionName === "STRFTIME") {
1231
+ expect(0, text);
1232
+ expect(1, timeValue);
1233
+ for (let index = 2; index < expression.arguments.length; index += 1)
1234
+ expect(index, text);
1235
+ return;
1236
+ }
1237
+ if (functionName === "TIMEDIFF") {
1238
+ expect(0, timeValue);
1239
+ expect(1, timeValue);
1240
+ return;
1241
+ }
1242
+ if (!functionName.startsWith("JSON"))
1243
+ return;
1244
+ if (["JSON_ARRAY", "JSONB_ARRAY"].includes(functionName)) {
1245
+ expression.arguments.forEach((_, index) => {
1246
+ expect(index, jsonValue);
1247
+ });
1248
+ return;
1249
+ }
1250
+ if (["JSON_GROUP_ARRAY", "JSONB_GROUP_ARRAY", "JSON_QUOTE"].includes(functionName)) {
1251
+ expect(0, jsonValue);
1252
+ return;
1253
+ }
1254
+ if (["JSON_OBJECT", "JSONB_OBJECT"].includes(functionName)) {
1255
+ for (let index = 0; index < expression.arguments.length; index += 2) {
1256
+ expect(index, text);
1257
+ expect(index + 1, jsonValue);
1258
+ }
1259
+ return;
1260
+ }
1261
+ if (["JSON_GROUP_OBJECT", "JSONB_GROUP_OBJECT"].includes(functionName)) {
1262
+ expect(0, text);
1263
+ return;
1264
+ }
1265
+ expect(0, json);
1266
+ if (["JSON_ARRAY_LENGTH", "JSON_EXTRACT", "JSONB_EXTRACT", "JSON_REMOVE", "JSONB_REMOVE", "JSON_TYPE"].includes(functionName)) {
1267
+ for (let index = 1; index < expression.arguments.length; index += 1)
1268
+ expect(index, text);
1269
+ return;
1270
+ }
1271
+ if ([
1272
+ "JSON_ARRAY_INSERT",
1273
+ "JSON_INSERT",
1274
+ "JSON_REPLACE",
1275
+ "JSON_SET",
1276
+ "JSONB_ARRAY_INSERT",
1277
+ "JSONB_INSERT",
1278
+ "JSONB_REPLACE",
1279
+ "JSONB_SET",
1280
+ ].includes(functionName)) {
1281
+ for (let index = 1; index < expression.arguments.length; index += 2)
1282
+ expect(index, text);
1283
+ for (let index = 2; index < expression.arguments.length; index += 2)
1284
+ expect(index, jsonValue);
1285
+ return;
1286
+ }
1287
+ if (functionName === "JSON_VALID")
1288
+ expect(1, numeric);
1289
+ if (functionName === "JSON_PRETTY")
1290
+ expect(1, text);
1291
+ }
1292
+ #dateTimeVersion(minimum, feature, range) {
1293
+ const version = this.#schema.server?.versionKey ?? this.#schema.version;
1294
+ if (version === undefined || parseSqliteVersion(version) === undefined) {
1295
+ this.#diagnostic("TSQ402", `SQLite ${minimum} server-version evidence is required for ${feature}`, range);
1296
+ return;
1297
+ }
1298
+ if (compareSqliteVersions(parseSqliteVersion(version), parseSqliteVersion(minimum)) < 0)
1299
+ this.#diagnostic("TSQ404", `${feature} requires SQLite ${minimum} or newer`, range);
1300
+ }
1301
+ #arity(range) {
1302
+ if (range[0] === range[1])
1303
+ return String(range[0]);
1304
+ if (!Number.isFinite(range[1]))
1305
+ return `at least ${range[0]}`;
1306
+ return `${range[0]} to ${range[1]}`;
1307
+ }
1308
+ #builtinResult(definition, values, arguments_) {
1309
+ const first = values[0] ?? { tsType: "unknown", nullable: true };
1310
+ switch (definition.result) {
1311
+ case "blob":
1312
+ return { tsType: "Uint8Array", nullable: false, databaseType: "blob" };
1313
+ case "blob-nullable":
1314
+ return { tsType: "Uint8Array", nullable: true, databaseType: "blob" };
1315
+ case "coalesce":
1316
+ return {
1317
+ tsType: unionSqliteTypes(values.map((value) => value.tsType)),
1318
+ nullable: values.every((value) => value.nullable),
1319
+ };
1320
+ case "concat":
1321
+ return { tsType: "string", nullable: false, databaseType: "text" };
1322
+ case "concat-ws":
1323
+ return { tsType: "string", nullable: first.nullable, databaseType: "text" };
1324
+ case "first-argument":
1325
+ return first;
1326
+ case "first-argument-nullable":
1327
+ return { ...first, nullable: true };
1328
+ case "flexible-nullable":
1329
+ return {
1330
+ tsType: unionSqliteTypes(values.map((value) => value.tsType)),
1331
+ nullable: values.some((value) => value.nullable),
1332
+ };
1333
+ case "iif": {
1334
+ const branches = values.filter((_, index) => index % 2 === 1 || (values.length % 2 === 1 && index === values.length - 1));
1335
+ return {
1336
+ tsType: unionSqliteTypes(branches.map((value) => value.tsType)),
1337
+ nullable: values.length % 2 === 0 || branches.some((value) => value.nullable),
1338
+ };
1339
+ }
1340
+ case "integer":
1341
+ return { tsType: this.#policy.integer, nullable: false, databaseType: "integer" };
1342
+ case "integer-always-nullable":
1343
+ return { tsType: this.#policy.integer, nullable: true, databaseType: "integer" };
1344
+ case "integer-nullable":
1345
+ return {
1346
+ tsType: this.#policy.integer,
1347
+ nullable: values.some((value) => value.nullable),
1348
+ databaseType: "integer",
1349
+ };
1350
+ case "json-extract":
1351
+ return values.length === 2
1352
+ ? { tsType: sqliteFlexibleType(this.#policy), nullable: true }
1353
+ : { tsType: "string", nullable: true, databaseType: "text" };
1354
+ case "jsonb-extract":
1355
+ return values.length === 2
1356
+ ? { tsType: sqliteFlexibleType(this.#policy), nullable: true }
1357
+ : { tsType: "Uint8Array", nullable: true, databaseType: "blob" };
1358
+ case "lag-lead": {
1359
+ const fallback = values[2];
1360
+ return {
1361
+ ...first,
1362
+ tsType: fallback === undefined ? first.tsType : unionSqliteTypes([first.tsType, fallback.tsType]),
1363
+ nullable: first.nullable || fallback === undefined || fallback.nullable,
1364
+ };
1365
+ }
1366
+ case "nullif":
1367
+ return { ...first, nullable: true };
1368
+ case "numeric-from-arguments":
1369
+ return {
1370
+ tsType: unionSqliteTypes([this.#policy.integer, "number"]),
1371
+ nullable: values.some((value) => value.nullable),
1372
+ databaseType: "numeric",
1373
+ };
1374
+ case "numeric-nullable":
1375
+ return {
1376
+ tsType: unionSqliteTypes([this.#policy.integer, "number"]),
1377
+ nullable: true,
1378
+ databaseType: "numeric",
1379
+ };
1380
+ case "real":
1381
+ return { tsType: "number", nullable: false, databaseType: "real" };
1382
+ case "real-nullable":
1383
+ return { tsType: "number", nullable: true, databaseType: "real" };
1384
+ case "text":
1385
+ return { tsType: "string", nullable: false, databaseType: "text" };
1386
+ case "text-always-nullable":
1387
+ return { tsType: "string", nullable: true, databaseType: "text" };
1388
+ case "text-nullable":
1389
+ return { tsType: "string", nullable: values.some((value) => value.nullable), databaseType: "text" };
1390
+ case "unixepoch": {
1391
+ const dynamic = arguments_.some((argument) => argument.kind !== "literal" || typeof argument.value !== "string");
1392
+ const subsecond = arguments_.some((argument) => argument.kind === "literal" &&
1393
+ typeof argument.value === "string" &&
1394
+ ["subsec", "subsecond"].includes(argument.value.toLowerCase()));
1395
+ return {
1396
+ tsType: subsecond
1397
+ ? "number"
1398
+ : dynamic
1399
+ ? unionSqliteTypes([this.#policy.integer, "number"])
1400
+ : this.#policy.integer,
1401
+ nullable: true,
1402
+ databaseType: subsecond ? "real" : dynamic ? "numeric" : "integer",
1403
+ };
1404
+ }
1405
+ case "unknown-nullable":
1406
+ return { tsType: "unknown", nullable: true };
1407
+ }
1408
+ }
578
1409
  #resolveColumn(relationIdentifier, columnIdentifier, scope) {
579
1410
  const columnName = name(columnIdentifier);
580
1411
  if (relationIdentifier === undefined) {
@@ -587,7 +1418,11 @@ class Resolver {
587
1418
  : scope.relations.filter((relation) => relation.alias === name(relationIdentifier));
588
1419
  const matches = relations.flatMap((relation) => {
589
1420
  const column = this.#column(relation.table, columnName);
590
- return column === undefined ? [] : [[relation, column]];
1421
+ if (column !== undefined)
1422
+ return [[relation, this.#columnType(relation, column)]];
1423
+ return this.#hasImplicitRowid(relation.table, columnName)
1424
+ ? [[relation, { tsType: this.#policy.integer, nullable: relation.nullable, databaseType: "integer" }]]
1425
+ : [];
591
1426
  });
592
1427
  if (matches.length > 1) {
593
1428
  this.#diagnostic("TSQ102", `Ambiguous column ${columnIdentifier.name}`, columnIdentifier.range);
@@ -601,13 +1436,23 @@ class Resolver {
601
1436
  : `Unknown relation or column ${relationIdentifier.name}.${columnIdentifier.name}`, columnIdentifier.range);
602
1437
  return { tsType: "unknown", nullable: true };
603
1438
  }
604
- return this.#columnType(matches[0][0], matches[0][1]);
1439
+ return matches[0][1];
605
1440
  }
606
1441
  #column(table, columnName) {
607
1442
  return this.#index.column(table, columnName);
608
1443
  }
609
1444
  #findColumn(table, identifier) {
610
- const column = table === undefined ? undefined : this.#column(table, name(identifier));
1445
+ const column = table === undefined
1446
+ ? undefined
1447
+ : (this.#column(table, name(identifier)) ??
1448
+ (this.#hasImplicitRowid(table, name(identifier))
1449
+ ? {
1450
+ name: identifier.name,
1451
+ databaseType: "INTEGER",
1452
+ tsType: this.#policy.integer,
1453
+ nullable: false,
1454
+ }
1455
+ : undefined));
611
1456
  if (column === undefined)
612
1457
  this.#diagnostic("TSQ101", `Unknown column ${identifier.name}`, identifier.range);
613
1458
  return column;
@@ -615,6 +1460,14 @@ class Resolver {
615
1460
  #columnType(relation, column) {
616
1461
  return { tsType: column.tsType, nullable: column.nullable || relation.nullable, databaseType: column.databaseType };
617
1462
  }
1463
+ #hasImplicitRowid(table, columnName) {
1464
+ if (!["rowid", "oid", "_rowid_"].includes(columnName.toLowerCase()))
1465
+ return false;
1466
+ if (this.#column(table, columnName) !== undefined)
1467
+ return false;
1468
+ const sqliteTable = table;
1469
+ return sqliteTable.withoutRowid !== true && (sqliteTable.kind === "table" || sqliteTable.kind === "shadow");
1470
+ }
618
1471
  #outputName(expression) {
619
1472
  if (expression.kind === "column")
620
1473
  return name(expression.column);
@@ -641,11 +1494,22 @@ class Resolver {
641
1494
  #generated(column) {
642
1495
  return "generated" in column && (column.generated === "virtual" || column.generated === "stored");
643
1496
  }
1497
+ #insertable(table, column) {
1498
+ const evidence = this.#index.columnEligibility(table, column, "insert");
1499
+ return evidence === "unknown" ? !this.#generated(column) && !this.#hidden(column) : evidence;
1500
+ }
1501
+ #updatable(table, column) {
1502
+ const evidence = this.#index.columnEligibility(table, column, "update");
1503
+ return evidence === "unknown" ? !this.#generated(column) && !this.#hidden(column) : evidence;
1504
+ }
1505
+ #hidden(column) {
1506
+ return "hidden" in column && column.hidden === true;
1507
+ }
644
1508
  #databaseType(databaseType, nullable) {
645
1509
  return {
646
1510
  tsType: mapSqliteCastType(databaseType, this.#policy),
647
1511
  nullable,
648
- databaseType: normalized(databaseType),
1512
+ databaseType: normalizeSqliteDatabaseType(databaseType),
649
1513
  };
650
1514
  }
651
1515
  #recordParameter(index, expected) {
@@ -658,6 +1522,7 @@ class Resolver {
658
1522
  this.#diagnostics.push({ code, message, range, severity, ...(suggestion === undefined ? {} : { suggestion }) });
659
1523
  }
660
1524
  }
1525
+ _a = Resolver;
661
1526
  export function resolveSqliteStatement(statement, schema, options = {}) {
662
1527
  return new Resolver(schema, options).resolve(statement);
663
1528
  }