@typed-sql/mysql 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 (89) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +12 -0
  3. package/dist/packages/mysql/src/bulk.d.ts.map +1 -1
  4. package/dist/packages/mysql/src/bulk.js +9 -3
  5. package/dist/packages/mysql/src/bulk.js.map +1 -1
  6. package/dist/packages/mysql/src/capabilities.d.ts +30 -0
  7. package/dist/packages/mysql/src/capabilities.d.ts.map +1 -0
  8. package/dist/packages/mysql/src/capabilities.js +233 -0
  9. package/dist/packages/mysql/src/capabilities.js.map +1 -0
  10. package/dist/packages/mysql/src/catalog/generated/26.7.d.ts +4 -0
  11. package/dist/packages/mysql/src/catalog/generated/26.7.d.ts.map +1 -0
  12. package/dist/packages/mysql/src/catalog/generated/26.7.js +3 -0
  13. package/dist/packages/mysql/src/catalog/generated/26.7.js.map +1 -0
  14. package/dist/packages/mysql/src/catalog/generated/8.4.d.ts +4 -0
  15. package/dist/packages/mysql/src/catalog/generated/8.4.d.ts.map +1 -0
  16. package/dist/packages/mysql/src/catalog/generated/8.4.js +3 -0
  17. package/dist/packages/mysql/src/catalog/generated/8.4.js.map +1 -0
  18. package/dist/packages/mysql/src/catalog/generated/9.7.d.ts +4 -0
  19. package/dist/packages/mysql/src/catalog/generated/9.7.d.ts.map +1 -0
  20. package/dist/packages/mysql/src/catalog/generated/9.7.js +3 -0
  21. package/dist/packages/mysql/src/catalog/generated/9.7.js.map +1 -0
  22. package/dist/packages/mysql/src/catalog/index.d.ts +15 -0
  23. package/dist/packages/mysql/src/catalog/index.d.ts.map +1 -0
  24. package/dist/packages/mysql/src/catalog/index.js +129 -0
  25. package/dist/packages/mysql/src/catalog/index.js.map +1 -0
  26. package/dist/packages/mysql/src/catalog/types.d.ts +50 -0
  27. package/dist/packages/mysql/src/catalog/types.d.ts.map +1 -0
  28. package/dist/packages/mysql/src/catalog/types.js +2 -0
  29. package/dist/packages/mysql/src/catalog/types.js.map +1 -0
  30. package/dist/packages/mysql/src/index.d.ts +9 -1
  31. package/dist/packages/mysql/src/index.d.ts.map +1 -1
  32. package/dist/packages/mysql/src/index.js +37 -15
  33. package/dist/packages/mysql/src/index.js.map +1 -1
  34. package/dist/packages/mysql/src/mysql2.d.ts +9 -1
  35. package/dist/packages/mysql/src/mysql2.d.ts.map +1 -1
  36. package/dist/packages/mysql/src/mysql2.js +86 -3
  37. package/dist/packages/mysql/src/mysql2.js.map +1 -1
  38. package/dist/packages/mysql/src/parser/index.d.ts +8 -0
  39. package/dist/packages/mysql/src/parser/index.d.ts.map +1 -0
  40. package/dist/packages/mysql/src/parser/index.js +4 -0
  41. package/dist/packages/mysql/src/parser/index.js.map +1 -0
  42. package/dist/packages/mysql/src/parser/parser.d.ts +14 -0
  43. package/dist/packages/mysql/src/parser/parser.d.ts.map +1 -0
  44. package/dist/packages/mysql/src/parser/parser.js +1276 -0
  45. package/dist/packages/mysql/src/parser/parser.js.map +1 -0
  46. package/dist/packages/mysql/src/parser/tokenizer.d.ts +18 -0
  47. package/dist/packages/mysql/src/parser/tokenizer.d.ts.map +1 -0
  48. package/dist/packages/mysql/src/parser/tokenizer.js +385 -0
  49. package/dist/packages/mysql/src/parser/tokenizer.js.map +1 -0
  50. package/dist/packages/mysql/src/parser/types.d.ts +313 -0
  51. package/dist/packages/mysql/src/parser/types.d.ts.map +1 -0
  52. package/dist/packages/mysql/src/parser/types.js +9 -0
  53. package/dist/packages/mysql/src/parser/types.js.map +1 -0
  54. package/dist/packages/mysql/src/parser/walk.d.ts +13 -0
  55. package/dist/packages/mysql/src/parser/walk.d.ts.map +1 -0
  56. package/dist/packages/mysql/src/parser/walk.js +190 -0
  57. package/dist/packages/mysql/src/parser/walk.js.map +1 -0
  58. package/dist/packages/mysql/src/prepared.d.ts +6 -3
  59. package/dist/packages/mysql/src/prepared.d.ts.map +1 -1
  60. package/dist/packages/mysql/src/prepared.js +15 -14
  61. package/dist/packages/mysql/src/prepared.js.map +1 -1
  62. package/dist/packages/mysql/src/provider.d.ts +21 -5
  63. package/dist/packages/mysql/src/provider.d.ts.map +1 -1
  64. package/dist/packages/mysql/src/provider.js +647 -44
  65. package/dist/packages/mysql/src/provider.js.map +1 -1
  66. package/dist/packages/mysql/src/resolver.d.ts +5 -1
  67. package/dist/packages/mysql/src/resolver.d.ts.map +1 -1
  68. package/dist/packages/mysql/src/resolver.js +1207 -95
  69. package/dist/packages/mysql/src/resolver.js.map +1 -1
  70. package/dist/packages/mysql/src/routing.d.ts.map +1 -1
  71. package/dist/packages/mysql/src/routing.js +3 -2
  72. package/dist/packages/mysql/src/routing.js.map +1 -1
  73. package/dist/packages/mysql/src/runtime.d.ts +33 -0
  74. package/dist/packages/mysql/src/runtime.d.ts.map +1 -1
  75. package/dist/packages/mysql/src/runtime.js +166 -12
  76. package/dist/packages/mysql/src/runtime.js.map +1 -1
  77. package/dist/packages/mysql/src/semantics.d.ts +1 -1
  78. package/dist/packages/mysql/src/semantics.d.ts.map +1 -1
  79. package/dist/packages/mysql/src/semantics.js +27 -3
  80. package/dist/packages/mysql/src/semantics.js.map +1 -1
  81. package/dist/packages/mysql/src/support.d.ts +43 -0
  82. package/dist/packages/mysql/src/support.d.ts.map +1 -0
  83. package/dist/packages/mysql/src/support.js +102 -0
  84. package/dist/packages/mysql/src/support.js.map +1 -0
  85. package/dist/packages/mysql/src/type-policy.d.ts +2 -0
  86. package/dist/packages/mysql/src/type-policy.d.ts.map +1 -1
  87. package/dist/packages/mysql/src/type-policy.js +24 -50
  88. package/dist/packages/mysql/src/type-policy.js.map +1 -1
  89. package/package.json +8 -5
@@ -0,0 +1,1276 @@
1
+ import { SqlToolkitError, TokenCursor } from "@typed-sql/ast/toolkit";
2
+ import { SqlTokenizeError, tokenize } from "./tokenizer.js";
3
+ import { mergeRanges, } from "./types.js";
4
+ export const DEFAULT_MAX_PARSE_DEPTH = 128;
5
+ export class SqlParseError extends Error {
6
+ code;
7
+ range;
8
+ constructor(message, range, code = "TSQ001") {
9
+ super(message);
10
+ this.name = "SqlParseError";
11
+ this.code = code;
12
+ this.range = range;
13
+ }
14
+ }
15
+ const precedence = new Map([
16
+ ["OR", 1],
17
+ ["XOR", 2],
18
+ ["AND", 3],
19
+ ["&&", 3],
20
+ ["=", 4],
21
+ ["!=", 4],
22
+ ["<>", 4],
23
+ ["<", 4],
24
+ ["<=", 4],
25
+ [">", 4],
26
+ [">=", 4],
27
+ ["<=>", 4],
28
+ ["IS", 4],
29
+ ["LIKE", 4],
30
+ ["|", 5],
31
+ ["&", 6],
32
+ ["<<", 7],
33
+ [">>", 7],
34
+ ["||", 8],
35
+ ["+", 9],
36
+ ["-", 9],
37
+ ["*", 10],
38
+ ["/", 10],
39
+ ["%", 10],
40
+ ["^", 11],
41
+ ["->", 12],
42
+ ["->>", 12],
43
+ ]);
44
+ const typeContinuationWords = new Set([
45
+ "BIT",
46
+ "CHARACTER",
47
+ "DOUBLE",
48
+ "INTERVAL",
49
+ "PRECISION",
50
+ "TIME",
51
+ "TIMESTAMP",
52
+ "VARYING",
53
+ "WITH",
54
+ "WITHOUT",
55
+ "ZONE",
56
+ ]);
57
+ class Parser {
58
+ #source;
59
+ #cursor;
60
+ constructor(source, options) {
61
+ this.#source = source;
62
+ this.#cursor = new TokenCursor(tokenize(source, options), {
63
+ maxDepth: options.maxDepth ?? DEFAULT_MAX_PARSE_DEPTH,
64
+ });
65
+ }
66
+ parse() {
67
+ const statement = this.#parseStatement();
68
+ this.#matchPunctuation(";");
69
+ this.#expect("eof", "end of query");
70
+ return statement;
71
+ }
72
+ #parseStatement() {
73
+ return this.#nested(() => {
74
+ const withClause = this.#current().value === "WITH" ? this.#parseWithClause() : undefined;
75
+ if (this.#current().value === "SELECT")
76
+ return this.#parseSelect(withClause);
77
+ if (this.#current().value === "TABLE")
78
+ return this.#parseTableQuery(withClause);
79
+ if (this.#current().value === "VALUES")
80
+ return this.#parseValuesQuery(withClause);
81
+ if (withClause === undefined && this.#current().value === "(") {
82
+ const statement = this.#parseParenthesizedSelect();
83
+ return this.#parseSelectTail(this.#parseCompoundExpression(statement, 0));
84
+ }
85
+ if (this.#current().value === "INSERT" || this.#current().value === "REPLACE") {
86
+ if (withClause !== undefined) {
87
+ throw this.#error("MySQL INSERT and REPLACE place WITH immediately before their query source", withClause.range, "TSQ401");
88
+ }
89
+ return this.#parseInsert();
90
+ }
91
+ if (this.#current().value === "UPDATE")
92
+ return this.#parseUpdate(withClause);
93
+ if (this.#current().value === "DELETE")
94
+ return this.#parseDelete(withClause);
95
+ throw this.#error(`Expected SELECT, INSERT, REPLACE, UPDATE, or DELETE, found ${this.#current().text || "end of query"}`, this.#current().range);
96
+ });
97
+ }
98
+ #parseWithClause() {
99
+ const start = this.#expectKeyword("WITH").range;
100
+ const recursive = this.#matchKeyword("RECURSIVE");
101
+ const queries = [];
102
+ do {
103
+ const name = this.#parseIdentifier();
104
+ const columns = [];
105
+ if (this.#matchPunctuation("(")) {
106
+ if (!this.#matchPunctuation(")")) {
107
+ do
108
+ columns.push(this.#parseIdentifier());
109
+ while (this.#matchPunctuation(","));
110
+ this.#expectPunctuation(")");
111
+ }
112
+ }
113
+ this.#expectKeyword("AS");
114
+ this.#expectPunctuation("(");
115
+ const statement = this.#parseStatement();
116
+ const close = this.#expectPunctuation(")");
117
+ queries.push({ name, columns, statement, range: mergeRanges(name.range, close.range) });
118
+ } while (this.#matchPunctuation(","));
119
+ return { recursive, queries, range: mergeRanges(start, queries.at(-1)?.range ?? start) };
120
+ }
121
+ #parseSelect(withClause, minimumCompoundPrecedence = 0) {
122
+ const start = withClause?.range ?? this.#current().range;
123
+ this.#expectKeyword("SELECT");
124
+ let distinct = false;
125
+ const distinctOn = [];
126
+ if (this.#matchKeyword("DISTINCT")) {
127
+ distinct = true;
128
+ if (this.#matchKeyword("ON")) {
129
+ this.#expectPunctuation("(");
130
+ distinctOn.push(...this.#parseExpressionList());
131
+ this.#expectPunctuation(")");
132
+ }
133
+ }
134
+ else
135
+ this.#matchKeyword("ALL");
136
+ const columns = this.#parseSelectList();
137
+ let from;
138
+ const joins = [];
139
+ let where;
140
+ const groupBy = [];
141
+ let groupRollup = false;
142
+ let having;
143
+ const windows = [];
144
+ if (this.#matchKeyword("FROM")) {
145
+ from = this.#parseTableReference();
146
+ while (true) {
147
+ if (this.#matchPunctuation(",")) {
148
+ const table = this.#parseTableReference();
149
+ joins.push({ kind: "cross", table, range: table.range });
150
+ }
151
+ else if (this.#isJoinStart())
152
+ joins.push(this.#parseJoin());
153
+ else
154
+ break;
155
+ }
156
+ }
157
+ if (this.#matchKeyword("WHERE"))
158
+ where = this.#parseExpression();
159
+ if (this.#matchKeyword("GROUP")) {
160
+ this.#expectKeyword("BY");
161
+ if (this.#matchKeyword("ROLLUP")) {
162
+ groupRollup = true;
163
+ this.#expectPunctuation("(");
164
+ groupBy.push(...this.#parseExpressionList());
165
+ this.#expectPunctuation(")");
166
+ }
167
+ else
168
+ groupBy.push(...this.#parseExpressionList());
169
+ if (!groupRollup && this.#matchKeyword("WITH")) {
170
+ this.#expectKeyword("ROLLUP");
171
+ groupRollup = true;
172
+ }
173
+ }
174
+ if (this.#matchKeyword("HAVING"))
175
+ having = this.#parseExpression();
176
+ if (this.#matchKeyword("WINDOW")) {
177
+ do {
178
+ const name = this.#parseIdentifier();
179
+ this.#expectKeyword("AS");
180
+ const specification = this.#parseWindowSpecification();
181
+ windows.push({ name, specification, range: mergeRanges(name.range, specification.range) });
182
+ } while (this.#matchPunctuation(","));
183
+ }
184
+ const end = this.#previous().range;
185
+ const core = {
186
+ kind: "select",
187
+ ...(withClause === undefined ? {} : { with: withClause }),
188
+ distinct,
189
+ distinctOn,
190
+ columns,
191
+ ...(from === undefined ? {} : { from }),
192
+ joins,
193
+ ...(where === undefined ? {} : { where }),
194
+ groupBy,
195
+ ...(groupRollup ? { groupRollup: true } : {}),
196
+ ...(having === undefined ? {} : { having }),
197
+ windows,
198
+ orderBy: [],
199
+ locking: [],
200
+ compounds: [],
201
+ range: mergeRanges(start, end),
202
+ };
203
+ const compound = this.#parseCompoundExpression(core, minimumCompoundPrecedence);
204
+ return minimumCompoundPrecedence === 0 ? this.#parseSelectTail(compound) : compound;
205
+ }
206
+ #parseParenthesizedSelect() {
207
+ const open = this.#expectPunctuation("(");
208
+ const statement = this.#parseStatement();
209
+ if (statement.kind !== "select")
210
+ throw this.#error("A compound query operand must be a SELECT", statement.range);
211
+ const close = this.#expectPunctuation(")");
212
+ return { ...statement, parenthesized: true, range: mergeRanges(open.range, close.range) };
213
+ }
214
+ #parseTableQuery(withClause, minimumCompoundPrecedence = 0) {
215
+ const start = withClause?.range ?? this.#expectKeyword("TABLE").range;
216
+ if (withClause !== undefined)
217
+ this.#expectKeyword("TABLE");
218
+ const table = this.#parseNamedTableReference(false);
219
+ const star = { kind: "star", range: table.range };
220
+ const core = {
221
+ kind: "select",
222
+ ...(withClause === undefined ? {} : { with: withClause }),
223
+ distinct: false,
224
+ distinctOn: [],
225
+ columns: [{ expression: star, range: star.range }],
226
+ from: table,
227
+ joins: [],
228
+ groupBy: [],
229
+ windows: [],
230
+ orderBy: [],
231
+ locking: [],
232
+ compounds: [],
233
+ range: mergeRanges(start, table.range),
234
+ };
235
+ const compound = this.#parseCompoundExpression(core, minimumCompoundPrecedence);
236
+ return minimumCompoundPrecedence === 0 ? this.#parseSelectTail(compound) : compound;
237
+ }
238
+ #parseValuesQuery(withClause, minimumCompoundPrecedence = 0) {
239
+ const start = withClause?.range ?? this.#expectKeyword("VALUES").range;
240
+ if (withClause !== undefined)
241
+ this.#expectKeyword("VALUES");
242
+ const rows = [];
243
+ do {
244
+ this.#expectKeyword("ROW");
245
+ this.#expectPunctuation("(");
246
+ const row = this.#matchPunctuation(")") ? [] : [...this.#parseExpressionList()];
247
+ if (this.#previous().value !== ")")
248
+ this.#expectPunctuation(")");
249
+ rows.push(row);
250
+ } while (this.#matchPunctuation(","));
251
+ const queryValues = {
252
+ kind: "values",
253
+ rows,
254
+ range: mergeRanges(start, this.#previous().range),
255
+ };
256
+ const core = {
257
+ kind: "select",
258
+ ...(withClause === undefined ? {} : { with: withClause }),
259
+ queryValues,
260
+ distinct: false,
261
+ distinctOn: [],
262
+ columns: [],
263
+ joins: [],
264
+ groupBy: [],
265
+ windows: [],
266
+ orderBy: [],
267
+ locking: [],
268
+ compounds: [],
269
+ range: queryValues.range,
270
+ };
271
+ const compound = this.#parseCompoundExpression(core, minimumCompoundPrecedence);
272
+ return minimumCompoundPrecedence === 0 ? this.#parseSelectTail(compound) : compound;
273
+ }
274
+ #parseCompoundExpression(initial, minimumPrecedence) {
275
+ let statement = initial;
276
+ while (["UNION", "INTERSECT", "EXCEPT"].includes(this.#current().value)) {
277
+ const compoundPrecedence = this.#current().value === "INTERSECT" ? 2 : 1;
278
+ if (compoundPrecedence < minimumPrecedence)
279
+ break;
280
+ const operatorToken = this.#current();
281
+ this.#advance();
282
+ const all = this.#matchKeyword("ALL");
283
+ if (!all)
284
+ this.#matchKeyword("DISTINCT");
285
+ const right = this.#current().value === "("
286
+ ? this.#parseParenthesizedSelect()
287
+ : this.#current().value === "TABLE"
288
+ ? this.#parseTableQuery(undefined, compoundPrecedence + 1)
289
+ : this.#current().value === "VALUES"
290
+ ? this.#parseValuesQuery(undefined, compoundPrecedence + 1)
291
+ : this.#parseSelect(undefined, compoundPrecedence + 1);
292
+ const compound = {
293
+ operator: operatorToken.value.toLowerCase(),
294
+ all,
295
+ statement: right,
296
+ range: mergeRanges(operatorToken.range, right.range),
297
+ };
298
+ statement = {
299
+ ...statement,
300
+ compounds: [...statement.compounds, compound],
301
+ range: mergeRanges(statement.range, right.range),
302
+ };
303
+ }
304
+ return statement;
305
+ }
306
+ #parseSelectTail(statement) {
307
+ let orderBy = [];
308
+ let limit;
309
+ let offset;
310
+ const locking = [];
311
+ if (this.#matchKeyword("ORDER")) {
312
+ this.#expectKeyword("BY");
313
+ orderBy = this.#parseOrderByList();
314
+ }
315
+ if (this.#matchKeyword("LIMIT")) {
316
+ limit = this.#parseExpression();
317
+ if (this.#matchPunctuation(",")) {
318
+ offset = limit;
319
+ limit = this.#parseExpression();
320
+ }
321
+ }
322
+ if (this.#matchKeyword("OFFSET"))
323
+ offset = this.#parseExpression();
324
+ while (this.#current().value === "FOR")
325
+ locking.push(this.#parseSelectLockingClause());
326
+ if (this.#current().value === "LOCK") {
327
+ if (locking.length > 0) {
328
+ throw this.#error("LOCK IN SHARE MODE cannot follow another MySQL locking clause", this.#current().range);
329
+ }
330
+ const lockStart = this.#expectKeyword("LOCK").range;
331
+ this.#expectKeyword("IN");
332
+ this.#expectKeyword("SHARE");
333
+ const lockEnd = this.#expectKeyword("MODE").range;
334
+ locking.push({ strength: "share", relations: [], range: mergeRanges(lockStart, lockEnd) });
335
+ }
336
+ return {
337
+ ...statement,
338
+ orderBy,
339
+ ...(limit === undefined ? {} : { limit }),
340
+ ...(offset === undefined ? {} : { offset }),
341
+ locking,
342
+ range: mergeRanges(statement.range, this.#previous().range),
343
+ };
344
+ }
345
+ #parseSelectLockingClause() {
346
+ const start = this.#expectKeyword("FOR").range;
347
+ let strength;
348
+ if (this.#matchKeyword("UPDATE"))
349
+ strength = "update";
350
+ else if (this.#matchKeyword("SHARE"))
351
+ strength = "share";
352
+ else if (this.#matchKeyword("NO")) {
353
+ throw this.#error("MySQL does not support FOR NO KEY UPDATE", start);
354
+ }
355
+ else if (this.#matchKeyword("KEY")) {
356
+ throw this.#error("MySQL does not support FOR KEY SHARE", start);
357
+ }
358
+ else {
359
+ throw this.#error("Expected UPDATE, NO KEY UPDATE, SHARE, or KEY SHARE after FOR", this.#current().range);
360
+ }
361
+ const relations = [];
362
+ if (this.#matchKeyword("OF")) {
363
+ do
364
+ relations.push(this.#parseIdentifier());
365
+ while (this.#matchPunctuation(","));
366
+ }
367
+ let wait;
368
+ if (this.#matchKeyword("NOWAIT"))
369
+ wait = "nowait";
370
+ else if (this.#matchKeyword("SKIP")) {
371
+ this.#expectKeyword("LOCKED");
372
+ wait = "skip-locked";
373
+ }
374
+ return {
375
+ strength,
376
+ relations,
377
+ ...(wait === undefined ? {} : { wait }),
378
+ range: mergeRanges(start, this.#previous().range),
379
+ };
380
+ }
381
+ #parseInsert(withClause) {
382
+ const operation = this.#current().value === "REPLACE" ? "replace" : "insert";
383
+ const start = withClause?.range ?? this.#expectKeyword(operation.toUpperCase()).range;
384
+ if (withClause !== undefined)
385
+ this.#expectKeyword(operation.toUpperCase());
386
+ const priority = this.#parseDmlPriority();
387
+ const ignore = operation === "insert" && this.#matchKeyword("IGNORE");
388
+ this.#matchKeyword("INTO");
389
+ const table = this.#parseNamedTableReference(false);
390
+ const columns = [];
391
+ const columnList = this.#matchPunctuation("(");
392
+ if (columnList) {
393
+ if (!this.#matchPunctuation(")")) {
394
+ do
395
+ columns.push(this.#parseIdentifier());
396
+ while (this.#matchPunctuation(","));
397
+ this.#expectPunctuation(")");
398
+ }
399
+ }
400
+ let source;
401
+ let valuesRowConstructor;
402
+ if (this.#matchKeyword("DEFAULT")) {
403
+ const sourceStart = this.#previous().range;
404
+ const end = this.#expectKeyword("VALUES").range;
405
+ source = { kind: "default-values", range: mergeRanges(sourceStart, end) };
406
+ }
407
+ else if (this.#matchKeyword("VALUES") || this.#matchContextWord("VALUE")) {
408
+ const sourceStart = this.#previous().range;
409
+ const rows = [];
410
+ do {
411
+ const rowConstructor = this.#matchKeyword("ROW");
412
+ if (valuesRowConstructor !== undefined && valuesRowConstructor !== rowConstructor) {
413
+ throw this.#error("VALUES rows must use one consistent constructor form", this.#current().range, "TSQ224");
414
+ }
415
+ valuesRowConstructor = rowConstructor;
416
+ this.#expectPunctuation("(");
417
+ const row = this.#matchPunctuation(")") ? [] : [...this.#parseExpressionList()];
418
+ if (this.#previous().value !== ")")
419
+ this.#expectPunctuation(")");
420
+ rows.push(row);
421
+ } while (this.#matchPunctuation(","));
422
+ source = { kind: "values", rows, range: mergeRanges(sourceStart, this.#previous().range) };
423
+ }
424
+ else if (this.#matchKeyword("SET")) {
425
+ const sourceStart = this.#previous().range;
426
+ const assignments = this.#parseAssignments();
427
+ source = {
428
+ kind: "set",
429
+ assignments,
430
+ range: mergeRanges(sourceStart, assignments.at(-1)?.range ?? sourceStart),
431
+ };
432
+ }
433
+ else if (["SELECT", "TABLE", "WITH"].includes(this.#current().value)) {
434
+ const statement = this.#parseStatement();
435
+ if (statement.kind !== "select")
436
+ throw this.#error("INSERT source must be SELECT or VALUES", statement.range);
437
+ source = statement;
438
+ }
439
+ else
440
+ throw this.#error("Expected VALUES, SET, DEFAULT VALUES, SELECT, or TABLE after DML target", this.#current().range);
441
+ if (operation === "replace" && priority === "high") {
442
+ throw this.#error("REPLACE does not support HIGH_PRIORITY", start, "TSQ224");
443
+ }
444
+ if (priority === "delayed" && source.kind === "select") {
445
+ throw this.#error(`${operation.toUpperCase()} DELAYED does not support a query source`, source.range, "TSQ224");
446
+ }
447
+ let rowAlias;
448
+ const columnAliases = [];
449
+ if (this.#matchKeyword("AS")) {
450
+ if ((source.kind !== "values" && source.kind !== "set") || valuesRowConstructor === true) {
451
+ throw this.#error("Inserted-row aliases apply only to VALUES or SET sources", this.#previous().range, "TSQ224");
452
+ }
453
+ rowAlias = this.#parseIdentifier();
454
+ if (this.#matchPunctuation("(")) {
455
+ if (this.#matchPunctuation(")")) {
456
+ throw this.#error("An inserted-row column alias list cannot be empty", this.#previous().range, "TSQ224");
457
+ }
458
+ do
459
+ columnAliases.push(this.#parseIdentifier());
460
+ while (this.#matchPunctuation(","));
461
+ this.#expectPunctuation(")");
462
+ }
463
+ }
464
+ let duplicateKey = [];
465
+ if (this.#matchKeyword("ON")) {
466
+ if (operation === "replace") {
467
+ throw this.#error("REPLACE cannot use ON DUPLICATE KEY UPDATE", this.#previous().range, "TSQ224");
468
+ }
469
+ this.#expectKeyword("DUPLICATE");
470
+ this.#expectKeyword("KEY");
471
+ this.#expectKeyword("UPDATE");
472
+ duplicateKey = this.#parseAssignments();
473
+ }
474
+ const returning = this.#matchKeyword("RETURNING") ? this.#parseSelectList() : [];
475
+ return {
476
+ kind: "insert",
477
+ operation,
478
+ ...(withClause === undefined ? {} : { with: withClause }),
479
+ table,
480
+ ...(priority === undefined ? {} : { priority }),
481
+ ignore,
482
+ columnList,
483
+ columns,
484
+ source,
485
+ ...(rowAlias === undefined ? {} : { rowAlias }),
486
+ columnAliases,
487
+ duplicateKey,
488
+ returning,
489
+ range: mergeRanges(start, this.#previous().range),
490
+ };
491
+ }
492
+ #parseUpdate(withClause) {
493
+ const start = withClause?.range ?? this.#expectKeyword("UPDATE").range;
494
+ if (withClause !== undefined)
495
+ this.#expectKeyword("UPDATE");
496
+ const priority = this.#matchKeyword("LOW_PRIORITY") ? "low" : undefined;
497
+ const ignore = this.#matchKeyword("IGNORE");
498
+ const table = this.#parseTableReference();
499
+ const joins = this.#parseFollowingTableReferences();
500
+ this.#expectKeyword("SET");
501
+ const assignments = this.#parseAssignments();
502
+ if (this.#current().value === "FROM") {
503
+ throw this.#error("MySQL uses joined table references before SET instead of UPDATE FROM", this.#current().range, "TSQ401");
504
+ }
505
+ const where = this.#matchKeyword("WHERE") ? this.#parseExpression() : undefined;
506
+ let orderBy = [];
507
+ if (this.#matchKeyword("ORDER")) {
508
+ this.#expectKeyword("BY");
509
+ orderBy = this.#parseOrderByList();
510
+ }
511
+ const limit = this.#matchKeyword("LIMIT") ? this.#parseExpression() : undefined;
512
+ const returning = this.#matchKeyword("RETURNING") ? this.#parseSelectList() : [];
513
+ return {
514
+ kind: "update",
515
+ ...(withClause === undefined ? {} : { with: withClause }),
516
+ table,
517
+ ...(priority === undefined ? {} : { priority }),
518
+ ignore,
519
+ assignments,
520
+ joins,
521
+ ...(where === undefined ? {} : { where }),
522
+ orderBy,
523
+ ...(limit === undefined ? {} : { limit }),
524
+ returning,
525
+ range: mergeRanges(start, this.#previous().range),
526
+ };
527
+ }
528
+ #parseDelete(withClause) {
529
+ const start = withClause?.range ?? this.#expectKeyword("DELETE").range;
530
+ if (withClause !== undefined)
531
+ this.#expectKeyword("DELETE");
532
+ const priority = this.#matchKeyword("LOW_PRIORITY") ? "low" : undefined;
533
+ const quick = this.#matchKeyword("QUICK");
534
+ const ignore = this.#matchKeyword("IGNORE");
535
+ const targets = [];
536
+ let table;
537
+ let joins = [];
538
+ let multiTable = false;
539
+ if (this.#matchKeyword("FROM")) {
540
+ let first = this.#parseNamedTableReference(true);
541
+ if (this.#current().value === "USING" || this.#current().value === ",") {
542
+ targets.push(first.name);
543
+ while (this.#matchPunctuation(","))
544
+ targets.push(this.#parseDeleteTarget());
545
+ this.#expectKeyword("USING");
546
+ table = this.#parseTableReference();
547
+ joins = this.#parseFollowingTableReferences();
548
+ multiTable = true;
549
+ }
550
+ else {
551
+ if (this.#current().value === "PARTITION") {
552
+ first = { ...first, partitions: this.#parsePartitions() };
553
+ }
554
+ table = first;
555
+ targets.push(first.alias ?? first.name);
556
+ }
557
+ }
558
+ else {
559
+ do
560
+ targets.push(this.#parseDeleteTarget());
561
+ while (this.#matchPunctuation(","));
562
+ this.#expectKeyword("FROM");
563
+ table = this.#parseTableReference();
564
+ joins = this.#parseFollowingTableReferences();
565
+ multiTable = true;
566
+ }
567
+ const where = this.#matchKeyword("WHERE") ? this.#parseExpression() : undefined;
568
+ let orderBy = [];
569
+ if (this.#matchKeyword("ORDER")) {
570
+ this.#expectKeyword("BY");
571
+ orderBy = this.#parseOrderByList();
572
+ }
573
+ const limit = this.#matchKeyword("LIMIT") ? this.#parseExpression() : undefined;
574
+ const returning = this.#matchKeyword("RETURNING") ? this.#parseSelectList() : [];
575
+ return {
576
+ kind: "delete",
577
+ ...(withClause === undefined ? {} : { with: withClause }),
578
+ table,
579
+ targets,
580
+ ...(priority === undefined ? {} : { priority }),
581
+ quick,
582
+ ignore,
583
+ multiTable,
584
+ joins,
585
+ ...(where === undefined ? {} : { where }),
586
+ orderBy,
587
+ ...(limit === undefined ? {} : { limit }),
588
+ returning,
589
+ range: mergeRanges(start, this.#previous().range),
590
+ };
591
+ }
592
+ #parseDmlPriority() {
593
+ if (this.#matchKeyword("LOW_PRIORITY"))
594
+ return "low";
595
+ if (this.#matchKeyword("DELAYED"))
596
+ return "delayed";
597
+ if (this.#matchKeyword("HIGH_PRIORITY"))
598
+ return "high";
599
+ return undefined;
600
+ }
601
+ #parseAssignmentColumn() {
602
+ const first = this.#parseIdentifier();
603
+ if (!this.#matchPunctuation("."))
604
+ return { kind: "column", column: first, range: first.range };
605
+ const column = this.#parseIdentifier();
606
+ return { kind: "column", relation: first, column, range: mergeRanges(first.range, column.range) };
607
+ }
608
+ #parseAssignments() {
609
+ const assignments = [];
610
+ do {
611
+ const column = this.#parseAssignmentColumn();
612
+ this.#expectOperator("=");
613
+ const value = this.#parseExpression();
614
+ assignments.push({ column, value, range: mergeRanges(column.range, value.range) });
615
+ } while (this.#matchPunctuation(","));
616
+ return assignments;
617
+ }
618
+ #parseFollowingTableReferences() {
619
+ const joins = [];
620
+ while (true) {
621
+ if (this.#matchPunctuation(",")) {
622
+ const table = this.#parseTableReference();
623
+ joins.push({ kind: "cross", table, range: table.range });
624
+ }
625
+ else if (this.#isJoinStart())
626
+ joins.push(this.#parseJoin());
627
+ else
628
+ break;
629
+ }
630
+ return joins;
631
+ }
632
+ #parseDeleteTarget() {
633
+ const target = this.#parseIdentifier();
634
+ if (this.#matchPunctuation("."))
635
+ this.#expectOperator("*");
636
+ return target;
637
+ }
638
+ #parseSelectList() {
639
+ const items = [];
640
+ do {
641
+ const expression = this.#parseExpression();
642
+ let alias;
643
+ if (this.#matchKeyword("AS"))
644
+ alias = this.#parseIdentifier();
645
+ else if (this.#current().kind === "identifier" || this.#current().kind === "quoted-identifier")
646
+ alias = this.#parseIdentifier();
647
+ items.push({
648
+ expression,
649
+ ...(alias === undefined ? {} : { alias }),
650
+ range: mergeRanges(expression.range, alias?.range ?? expression.range),
651
+ });
652
+ } while (this.#matchPunctuation(","));
653
+ return items;
654
+ }
655
+ #parseTableReference() {
656
+ const lateral = this.#matchKeyword("LATERAL");
657
+ const start = lateral ? this.#previous().range : this.#current().range;
658
+ if (this.#matchPunctuation("(")) {
659
+ if (!this.#isStatementStart()) {
660
+ throw this.#error("A parenthesized FROM item must be a SELECT subquery", this.#current().range);
661
+ }
662
+ const statement = this.#parseStatement();
663
+ if (statement.kind !== "select")
664
+ throw this.#error("FROM subquery must be SELECT", statement.range);
665
+ this.#expectPunctuation(")");
666
+ this.#matchKeyword("AS");
667
+ const alias = this.#parseIdentifier();
668
+ return { kind: "subquery", query: statement, alias, lateral, range: mergeRanges(start, alias.range) };
669
+ }
670
+ if (lateral) {
671
+ throw this.#error("MySQL LATERAL applies only to derived-table subqueries", start, "TSQ401");
672
+ }
673
+ return this.#parseNamedTableReference(true, lateral, start);
674
+ }
675
+ #parseNamedTableReference(allowAlias, lateral = false, start = this.#current().range) {
676
+ const first = this.#parseIdentifier();
677
+ let schema;
678
+ let name = first;
679
+ if (this.#matchPunctuation(".")) {
680
+ schema = first;
681
+ name = this.#parseIdentifier();
682
+ }
683
+ const partitions = this.#current().value === "PARTITION" ? this.#parsePartitions() : [];
684
+ let alias;
685
+ if (allowAlias && this.#matchKeyword("AS"))
686
+ alias = this.#parseIdentifier();
687
+ else if (allowAlias && (this.#current().kind === "identifier" || this.#current().kind === "quoted-identifier"))
688
+ alias = this.#parseIdentifier();
689
+ return {
690
+ kind: "table",
691
+ name,
692
+ ...(schema === undefined ? {} : { schema }),
693
+ ...(partitions.length === 0 ? {} : { partitions }),
694
+ ...(alias === undefined ? {} : { alias }),
695
+ lateral,
696
+ range: mergeRanges(start, alias?.range ?? partitions.at(-1)?.range ?? name.range),
697
+ };
698
+ }
699
+ #parsePartitions() {
700
+ this.#expectKeyword("PARTITION");
701
+ this.#expectPunctuation("(");
702
+ const partitions = [];
703
+ do
704
+ partitions.push(this.#parseIdentifier());
705
+ while (this.#matchPunctuation(","));
706
+ this.#expectPunctuation(")");
707
+ return partitions;
708
+ }
709
+ #isJoinStart() {
710
+ return ["JOIN", "INNER", "LEFT", "RIGHT", "FULL", "CROSS"].includes(this.#current().value);
711
+ }
712
+ #parseJoin() {
713
+ const start = this.#current().range;
714
+ let kind = "inner";
715
+ if (this.#matchKeyword("INNER"))
716
+ kind = "inner";
717
+ else if (this.#matchKeyword("LEFT"))
718
+ kind = "left";
719
+ else if (this.#matchKeyword("RIGHT"))
720
+ kind = "right";
721
+ else if (this.#matchKeyword("FULL"))
722
+ kind = "full";
723
+ else if (this.#matchKeyword("CROSS"))
724
+ kind = "cross";
725
+ this.#matchKeyword("OUTER");
726
+ this.#expectKeyword("JOIN");
727
+ const table = this.#parseTableReference();
728
+ if (kind === "cross")
729
+ return { kind, table, range: mergeRanges(start, table.range) };
730
+ if (this.#matchKeyword("ON")) {
731
+ const on = this.#parseExpression();
732
+ return { kind, table, on, range: mergeRanges(start, on.range) };
733
+ }
734
+ if (this.#matchKeyword("USING")) {
735
+ this.#expectPunctuation("(");
736
+ const using = [];
737
+ do
738
+ using.push(this.#parseIdentifier());
739
+ while (this.#matchPunctuation(","));
740
+ const close = this.#expectPunctuation(")");
741
+ return { kind, table, using, range: mergeRanges(start, close.range) };
742
+ }
743
+ throw this.#error("JOIN requires ON or USING", this.#current().range);
744
+ }
745
+ #parseExpressionList() {
746
+ const expressions = [];
747
+ do
748
+ expressions.push(this.#parseExpression());
749
+ while (this.#matchPunctuation(","));
750
+ return expressions;
751
+ }
752
+ #parseOrderByList() {
753
+ const orderBy = [];
754
+ do {
755
+ const expression = this.#parseExpression();
756
+ const direction = this.#matchKeyword("ASC") ? "asc" : this.#matchKeyword("DESC") ? "desc" : undefined;
757
+ let nulls;
758
+ if (this.#matchKeyword("NULLS")) {
759
+ if (this.#matchKeyword("FIRST"))
760
+ nulls = "first";
761
+ else {
762
+ this.#expectKeyword("LAST");
763
+ nulls = "last";
764
+ }
765
+ }
766
+ orderBy.push({
767
+ expression,
768
+ ...(direction === undefined ? {} : { direction }),
769
+ ...(nulls === undefined ? {} : { nulls }),
770
+ range: mergeRanges(expression.range, this.#previous().range),
771
+ });
772
+ } while (this.#matchPunctuation(","));
773
+ return orderBy;
774
+ }
775
+ #parseExpression(minimum = 0) {
776
+ return this.#nested(() => {
777
+ let left = this.#parseUnary();
778
+ while (true) {
779
+ const negated = this.#current().value === "NOT" && ["IN", "BETWEEN", "LIKE"].includes(this.#peekToken(1).value);
780
+ const special = negated ? this.#peekToken(1).value : this.#current().value;
781
+ if (special === "IN") {
782
+ const strength = 4;
783
+ if (strength < minimum)
784
+ break;
785
+ if (negated)
786
+ this.#advance();
787
+ this.#advance();
788
+ const start = left.range;
789
+ this.#expectPunctuation("(");
790
+ let values;
791
+ if (this.#isStatementStart()) {
792
+ const statement = this.#parseStatement();
793
+ if (statement.kind !== "select")
794
+ throw this.#error("IN subquery must be SELECT", statement.range);
795
+ values = statement;
796
+ }
797
+ else {
798
+ if (this.#current().value === ")")
799
+ throw this.#error("IN requires at least one value", this.#current().range);
800
+ values = this.#parseExpressionList();
801
+ }
802
+ const close = this.#previous().value === ")" ? this.#previous() : this.#expectPunctuation(")");
803
+ left = { kind: "in", expression: left, values, negated, range: mergeRanges(start, close.range) };
804
+ continue;
805
+ }
806
+ if (special === "BETWEEN") {
807
+ const strength = 4;
808
+ if (strength < minimum)
809
+ break;
810
+ if (negated)
811
+ this.#advance();
812
+ this.#advance();
813
+ const lower = this.#parseExpression(strength + 1);
814
+ this.#expectKeyword("AND");
815
+ const upper = this.#parseExpression(strength + 1);
816
+ left = {
817
+ kind: "between",
818
+ expression: left,
819
+ lower,
820
+ upper,
821
+ negated,
822
+ range: mergeRanges(left.range, upper.range),
823
+ };
824
+ continue;
825
+ }
826
+ let operator = this.#current().value.toUpperCase();
827
+ if (negated)
828
+ operator = `NOT ${special}`;
829
+ const strength = precedence.get(operator.replace(/^NOT /u, ""));
830
+ if (strength === undefined || strength < minimum)
831
+ break;
832
+ if (negated)
833
+ this.#advance();
834
+ this.#advance();
835
+ if (operator === "IS") {
836
+ if (this.#matchKeyword("NOT"))
837
+ operator = "IS NOT";
838
+ }
839
+ const right = this.#parseExpression(strength + 1);
840
+ left = { kind: "binary", left, operator, right, range: mergeRanges(left.range, right.range) };
841
+ }
842
+ return left;
843
+ });
844
+ }
845
+ #parseUnary() {
846
+ const token = this.#current();
847
+ if (this.#matchKeyword("NOT") ||
848
+ this.#matchOperator("!") ||
849
+ this.#matchOperator("+") ||
850
+ this.#matchOperator("-") ||
851
+ this.#matchOperator("~")) {
852
+ const expression = this.#parseUnary();
853
+ return {
854
+ kind: "unary",
855
+ operator: token.value === "!" ? "NOT" : token.value.toUpperCase(),
856
+ expression,
857
+ range: mergeRanges(token.range, expression.range),
858
+ };
859
+ }
860
+ if (this.#matchKeyword("EXISTS")) {
861
+ this.#expectPunctuation("(");
862
+ const statement = this.#parseStatement();
863
+ if (statement.kind !== "select")
864
+ throw this.#error("EXISTS requires a SELECT subquery", statement.range);
865
+ const close = this.#expectPunctuation(")");
866
+ return { kind: "exists", query: statement, range: mergeRanges(token.range, close.range) };
867
+ }
868
+ let expression = this.#parsePrimary();
869
+ while (this.#matchKeyword("COLLATE")) {
870
+ const collation = this.#parseIdentifier(true);
871
+ expression = {
872
+ kind: "collate",
873
+ expression,
874
+ collation,
875
+ range: mergeRanges(expression.range, collation.range),
876
+ };
877
+ }
878
+ return expression;
879
+ }
880
+ #parsePrimary() {
881
+ const token = this.#current();
882
+ if (this.#matchPunctuation("(")) {
883
+ if (this.#isStatementStart()) {
884
+ const statement = this.#parseStatement();
885
+ if (statement.kind !== "select")
886
+ throw this.#error("Scalar subquery must be SELECT", statement.range);
887
+ const close = this.#expectPunctuation(")");
888
+ return { kind: "subquery", query: statement, range: mergeRanges(token.range, close.range) };
889
+ }
890
+ const first = this.#parseExpression();
891
+ if (this.#matchPunctuation(",")) {
892
+ const elements = [first];
893
+ do
894
+ elements.push(this.#parseExpression());
895
+ while (this.#matchPunctuation(","));
896
+ const close = this.#expectPunctuation(")");
897
+ return { kind: "row", elements, range: mergeRanges(token.range, close.range) };
898
+ }
899
+ this.#expectPunctuation(")");
900
+ return first;
901
+ }
902
+ if (this.#matchKeyword("ARRAY")) {
903
+ this.#expectPunctuation("[");
904
+ const elements = this.#matchPunctuation("]") ? [] : [...this.#parseExpressionList()];
905
+ const close = this.#previous().value === "]" ? this.#previous() : this.#expectPunctuation("]");
906
+ return { kind: "array", elements, range: mergeRanges(token.range, close.range) };
907
+ }
908
+ if (this.#matchKeyword("ROW")) {
909
+ this.#expectPunctuation("(");
910
+ const elements = this.#matchPunctuation(")") ? [] : [...this.#parseExpressionList()];
911
+ const close = this.#previous().value === ")" ? this.#previous() : this.#expectPunctuation(")");
912
+ return { kind: "row", elements, range: mergeRanges(token.range, close.range) };
913
+ }
914
+ if (this.#matchKeyword("CAST"))
915
+ return this.#parseCast(token.range);
916
+ if (this.#matchKeyword("CASE"))
917
+ return this.#parseCase(token.range);
918
+ if (this.#matchKeyword("NULL"))
919
+ return { kind: "literal", value: null, range: token.range };
920
+ if (this.#matchKeyword("TRUE"))
921
+ return { kind: "literal", value: true, range: token.range };
922
+ if (this.#matchKeyword("FALSE"))
923
+ return { kind: "literal", value: false, range: token.range };
924
+ if (this.#matchKeyword("DEFAULT"))
925
+ return { kind: "column", column: { name: "DEFAULT", quoted: false, range: token.range }, range: token.range };
926
+ if (token.kind === "number") {
927
+ this.#advance();
928
+ return {
929
+ kind: "literal",
930
+ value: Number(token.value),
931
+ range: token.range,
932
+ };
933
+ }
934
+ if (token.kind === "string") {
935
+ this.#advance();
936
+ return { kind: "literal", value: token.value, range: token.range };
937
+ }
938
+ if (token.kind === "parameter") {
939
+ this.#advance();
940
+ return { kind: "parameter", index: Number(token.value), range: token.range };
941
+ }
942
+ if (this.#matchOperator("*"))
943
+ return { kind: "star", range: token.range };
944
+ if (token.kind === "identifier" && token.value.startsWith("_") && this.#peekToken(1).kind === "string") {
945
+ const characterSet = this.#parseIdentifier();
946
+ const value = this.#current();
947
+ this.#advance();
948
+ return {
949
+ kind: "literal",
950
+ value: value.value,
951
+ characterSet,
952
+ range: mergeRanges(characterSet.range, value.range),
953
+ };
954
+ }
955
+ if (this.#isIdentifierLike(token)) {
956
+ const first = this.#parseIdentifier(true);
957
+ if (this.#matchPunctuation("("))
958
+ return this.#parseCall(undefined, first);
959
+ if (this.#matchPunctuation(".")) {
960
+ if (this.#matchOperator("*"))
961
+ return { kind: "star", relation: first, range: mergeRanges(first.range, this.#previous().range) };
962
+ const second = this.#parseIdentifier(true);
963
+ if (this.#matchPunctuation("("))
964
+ return this.#parseCall(first, second);
965
+ return { kind: "column", relation: first, column: second, range: mergeRanges(first.range, second.range) };
966
+ }
967
+ return { kind: "column", column: first, range: first.range };
968
+ }
969
+ throw this.#error(`Expected expression, found ${token.text || "end of query"}`, token.range);
970
+ }
971
+ #parseCall(schema, name) {
972
+ const distinct = this.#matchKeyword("DISTINCT");
973
+ const args = [];
974
+ let close;
975
+ if (this.#matchPunctuation(")"))
976
+ close = this.#previous();
977
+ else {
978
+ do
979
+ args.push(this.#parseExpression());
980
+ while (this.#matchPunctuation(","));
981
+ close = this.#expectPunctuation(")");
982
+ }
983
+ const filter = undefined;
984
+ if (this.#current().value === "FROM" && ["FIRST", "LAST"].includes(this.#peekToken(1).value)) {
985
+ this.#advance();
986
+ if (this.#matchKeyword("LAST")) {
987
+ throw this.#error("MySQL does not support FROM LAST for window functions", this.#previous().range, "TSQ401");
988
+ }
989
+ this.#expectKeyword("FIRST");
990
+ }
991
+ if (this.#matchKeyword("IGNORE")) {
992
+ const nulls = this.#expectKeyword("NULLS");
993
+ throw this.#error("MySQL supports only RESPECT NULLS for window functions", nulls.range, "TSQ401");
994
+ }
995
+ if (this.#matchKeyword("RESPECT"))
996
+ this.#expectKeyword("NULLS");
997
+ let over;
998
+ if (this.#matchKeyword("OVER")) {
999
+ if (this.#current().kind === "identifier" || this.#current().kind === "quoted-identifier")
1000
+ over = this.#parseIdentifier();
1001
+ else
1002
+ over = this.#parseWindowSpecification();
1003
+ close = { ...close, range: over.range };
1004
+ }
1005
+ return {
1006
+ kind: "call",
1007
+ name,
1008
+ ...(schema === undefined ? {} : { schema }),
1009
+ arguments: args,
1010
+ distinct,
1011
+ ...(filter === undefined ? {} : { filter }),
1012
+ ...(over === undefined ? {} : { over }),
1013
+ range: mergeRanges(schema?.range ?? name.range, close.range),
1014
+ };
1015
+ }
1016
+ #parseWindowSpecification() {
1017
+ const start = this.#expectPunctuation("(").range;
1018
+ const base = this.#isIdentifierLike(this.#current()) &&
1019
+ !["PARTITION", "ORDER", "ROWS", "RANGE"].includes(this.#current().value)
1020
+ ? this.#parseIdentifier()
1021
+ : undefined;
1022
+ const partitionBy = [];
1023
+ let orderBy = [];
1024
+ let frame;
1025
+ if (this.#matchKeyword("PARTITION")) {
1026
+ this.#expectKeyword("BY");
1027
+ partitionBy.push(...this.#parseExpressionList());
1028
+ }
1029
+ if (this.#matchKeyword("ORDER")) {
1030
+ this.#expectKeyword("BY");
1031
+ orderBy = this.#parseOrderByList();
1032
+ }
1033
+ if (this.#current().value === "ROWS" || this.#current().value === "RANGE") {
1034
+ const frameStart = this.#current();
1035
+ this.#advance();
1036
+ const unit = frameStart.value.toLowerCase();
1037
+ let first;
1038
+ let end;
1039
+ if (this.#matchKeyword("BETWEEN")) {
1040
+ first = this.#parseWindowFrameBoundary();
1041
+ this.#expectKeyword("AND");
1042
+ end = this.#parseWindowFrameBoundary();
1043
+ }
1044
+ else
1045
+ first = this.#parseWindowFrameBoundary();
1046
+ frame = {
1047
+ unit,
1048
+ start: first,
1049
+ ...(end === undefined ? {} : { end }),
1050
+ range: mergeRanges(frameStart.range, end?.range ?? first.range),
1051
+ };
1052
+ }
1053
+ if (this.#current().value === "GROUPS" || this.#current().value === "EXCLUDE") {
1054
+ throw this.#error(`MySQL does not support ${this.#current().value} window frames`, this.#current().range, "TSQ401");
1055
+ }
1056
+ const close = this.#expectPunctuation(")");
1057
+ return {
1058
+ ...(base === undefined ? {} : { base }),
1059
+ partitionBy,
1060
+ orderBy,
1061
+ ...(frame === undefined ? {} : { frame }),
1062
+ range: mergeRanges(start, close.range),
1063
+ };
1064
+ }
1065
+ #parseWindowFrameBoundary() {
1066
+ const start = this.#current().range;
1067
+ if (this.#matchKeyword("UNBOUNDED")) {
1068
+ if (this.#matchKeyword("PRECEDING")) {
1069
+ return { kind: "unbounded-preceding", range: mergeRanges(start, this.#previous().range) };
1070
+ }
1071
+ this.#expectKeyword("FOLLOWING");
1072
+ return { kind: "unbounded-following", range: mergeRanges(start, this.#previous().range) };
1073
+ }
1074
+ if (this.#matchKeyword("CURRENT")) {
1075
+ const end = this.#expectKeyword("ROW").range;
1076
+ return { kind: "current-row", range: mergeRanges(start, end) };
1077
+ }
1078
+ const expression = this.#parseExpression(11);
1079
+ if (this.#matchKeyword("PRECEDING")) {
1080
+ return { kind: "preceding", expression, range: mergeRanges(start, this.#previous().range) };
1081
+ }
1082
+ this.#expectKeyword("FOLLOWING");
1083
+ return { kind: "following", expression, range: mergeRanges(start, this.#previous().range) };
1084
+ }
1085
+ #parseCast(start) {
1086
+ this.#expectPunctuation("(");
1087
+ const expression = this.#parseExpression();
1088
+ this.#expectKeyword("AS");
1089
+ const databaseType = this.#parseTypeName(true);
1090
+ const close = this.#expectPunctuation(")");
1091
+ return { kind: "cast", expression, databaseType, syntax: "cast", range: mergeRanges(start, close.range) };
1092
+ }
1093
+ #parseTypeName(stopAtClose) {
1094
+ const start = this.#current();
1095
+ if (!this.#isIdentifierLike(start))
1096
+ throw this.#error(`Expected identifier, found ${start.text || "end of query"}`, start.range);
1097
+ this.#advance();
1098
+ let end = start;
1099
+ let schemaSeparatorAllowed = true;
1100
+ while (true) {
1101
+ if (schemaSeparatorAllowed && this.#matchPunctuation(".")) {
1102
+ this.#parseIdentifier(true);
1103
+ end = this.#previous();
1104
+ schemaSeparatorAllowed = false;
1105
+ continue;
1106
+ }
1107
+ schemaSeparatorAllowed = false;
1108
+ if (this.#matchPunctuation("(")) {
1109
+ let depth = 1;
1110
+ while (depth > 0) {
1111
+ const token = this.#current();
1112
+ if (token.kind === "eof")
1113
+ throw this.#error("Unterminated type modifier", token.range);
1114
+ this.#advance();
1115
+ if (token.value === "(")
1116
+ depth += 1;
1117
+ else if (token.value === ")")
1118
+ depth -= 1;
1119
+ end = token;
1120
+ }
1121
+ continue;
1122
+ }
1123
+ if (this.#isIdentifierLike(this.#current()) && typeContinuationWords.has(this.#current().value.toUpperCase())) {
1124
+ end = this.#advance();
1125
+ continue;
1126
+ }
1127
+ if (this.#matchPunctuation("[")) {
1128
+ end = this.#expectPunctuation("]");
1129
+ continue;
1130
+ }
1131
+ break;
1132
+ }
1133
+ if (stopAtClose && this.#current().value !== ")")
1134
+ throw this.#error(`Expected ) after type name, found ${this.#current().text}`, this.#current().range);
1135
+ return {
1136
+ name: this.#source.slice(start.range.start, end.range.end).trim(),
1137
+ range: mergeRanges(start.range, end.range),
1138
+ };
1139
+ }
1140
+ #parseCase(start) {
1141
+ const operand = this.#current().value === "WHEN" ? undefined : this.#parseExpression();
1142
+ const branches = [];
1143
+ while (this.#matchKeyword("WHEN")) {
1144
+ const whenStart = this.#previous().range;
1145
+ const when = this.#parseExpression();
1146
+ this.#expectKeyword("THEN");
1147
+ const then = this.#parseExpression();
1148
+ branches.push({ when, then, range: mergeRanges(whenStart, then.range) });
1149
+ }
1150
+ if (branches.length === 0)
1151
+ throw this.#error("CASE requires at least one WHEN branch", this.#current().range);
1152
+ const elseExpression = this.#matchKeyword("ELSE") ? this.#parseExpression() : undefined;
1153
+ const end = this.#expectKeyword("END");
1154
+ return {
1155
+ kind: "case",
1156
+ ...(operand === undefined ? {} : { operand }),
1157
+ branches,
1158
+ ...(elseExpression === undefined ? {} : { elseExpression }),
1159
+ range: mergeRanges(start, end.range),
1160
+ };
1161
+ }
1162
+ #parseIdentifier(allowKeyword = false) {
1163
+ const token = this.#current();
1164
+ if (!this.#isIdentifierLike(token) || (token.kind === "keyword" && !allowKeyword)) {
1165
+ throw this.#error(`Expected identifier, found ${token.text || "end of query"}`, token.range);
1166
+ }
1167
+ this.#advance();
1168
+ return { name: token.value, quoted: token.kind === "quoted-identifier", range: token.range };
1169
+ }
1170
+ #isIdentifierLike(token) {
1171
+ return token.kind === "identifier" || token.kind === "quoted-identifier" || token.kind === "keyword";
1172
+ }
1173
+ #isStatementStart() {
1174
+ return ["SELECT", "TABLE", "VALUES", "INSERT", "REPLACE", "UPDATE", "DELETE", "WITH"].includes(this.#current().value);
1175
+ }
1176
+ #current() {
1177
+ return this.#cursor.current();
1178
+ }
1179
+ #peekToken(offset) {
1180
+ return this.#cursor.peek(offset);
1181
+ }
1182
+ #previous() {
1183
+ return this.#cursor.previous();
1184
+ }
1185
+ #advance() {
1186
+ return this.#cursor.advance();
1187
+ }
1188
+ #matchKeyword(keyword) {
1189
+ if (this.#current().kind === "keyword" && this.#current().value === keyword) {
1190
+ this.#advance();
1191
+ return true;
1192
+ }
1193
+ return false;
1194
+ }
1195
+ #matchContextWord(word) {
1196
+ if (this.#current().value !== word)
1197
+ return false;
1198
+ this.#advance();
1199
+ return true;
1200
+ }
1201
+ #matchOperator(operator) {
1202
+ if (this.#current().kind === "operator" && this.#current().value === operator) {
1203
+ this.#advance();
1204
+ return true;
1205
+ }
1206
+ return false;
1207
+ }
1208
+ #matchPunctuation(value) {
1209
+ if (this.#current().kind === "punctuation" && this.#current().value === value) {
1210
+ this.#advance();
1211
+ return true;
1212
+ }
1213
+ return false;
1214
+ }
1215
+ #expectKeyword(keyword) {
1216
+ const token = this.#current();
1217
+ if (!this.#matchKeyword(keyword))
1218
+ throw this.#error(`Expected ${keyword}, found ${token.text || "end of query"}`, token.range);
1219
+ return token;
1220
+ }
1221
+ #expectOperator(operator) {
1222
+ const token = this.#current();
1223
+ if (!this.#matchOperator(operator))
1224
+ throw this.#error(`Expected ${operator}, found ${token.text || "end of query"}`, token.range);
1225
+ return token;
1226
+ }
1227
+ #expectPunctuation(value) {
1228
+ const token = this.#current();
1229
+ if (!this.#matchPunctuation(value))
1230
+ throw this.#error(`Expected ${value}, found ${token.text || "end of query"}`, token.range);
1231
+ return token;
1232
+ }
1233
+ #expect(kind, label) {
1234
+ const token = this.#current();
1235
+ if (token.kind !== kind)
1236
+ throw this.#error(`Expected ${label}, found ${token.text || "end of query"}`, token.range);
1237
+ this.#advance();
1238
+ return token;
1239
+ }
1240
+ #nested(fn) {
1241
+ return this.#cursor.nested(fn);
1242
+ }
1243
+ #error(message, range, code = "TSQ001") {
1244
+ return new SqlParseError(message, range, code);
1245
+ }
1246
+ }
1247
+ function deepFreeze(value, seen = new Set()) {
1248
+ if (typeof value !== "object" || value === null || seen.has(value))
1249
+ return value;
1250
+ seen.add(value);
1251
+ for (const nested of Object.values(value))
1252
+ deepFreeze(nested, seen);
1253
+ return Object.freeze(value);
1254
+ }
1255
+ function parse(source, options) {
1256
+ try {
1257
+ return deepFreeze(new Parser(source, options).parse());
1258
+ }
1259
+ catch (error) {
1260
+ if (error instanceof SqlTokenizeError)
1261
+ throw new SqlParseError(error.message, error.range, error.code);
1262
+ if (error instanceof SqlToolkitError)
1263
+ throw new SqlParseError(error.message, error.range, error.code);
1264
+ throw error;
1265
+ }
1266
+ }
1267
+ export function parseStatement(source, options = {}) {
1268
+ return parse(source, options);
1269
+ }
1270
+ export function parseSelect(source, options = {}) {
1271
+ const statement = parse(source, options);
1272
+ if (statement.kind !== "select")
1273
+ throw new SqlParseError(`Expected SELECT, found ${statement.kind.toUpperCase()}`, statement.range);
1274
+ return statement;
1275
+ }
1276
+ //# sourceMappingURL=parser.js.map