@foul11/awesome-db 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/BitFields.d.ts +29 -0
  3. package/dist/BitFields.d.ts.map +1 -0
  4. package/dist/Error.d.ts +21 -0
  5. package/dist/Error.d.ts.map +1 -0
  6. package/dist/ORM.d.ts +14 -0
  7. package/dist/ORM.d.ts.map +1 -0
  8. package/dist/SQLParser.d.ts +1394 -0
  9. package/dist/SQLParser.d.ts.map +1 -0
  10. package/dist/WebpackFileProvider.d.ts +12 -0
  11. package/dist/WebpackFileProvider.d.ts.map +1 -0
  12. package/dist/alter/column_add.d.ts +7 -0
  13. package/dist/alter/column_add.d.ts.map +1 -0
  14. package/dist/alter/column_drop.d.ts +6 -0
  15. package/dist/alter/column_drop.d.ts.map +1 -0
  16. package/dist/alter/column_rename.d.ts +6 -0
  17. package/dist/alter/column_rename.d.ts.map +1 -0
  18. package/dist/alter/column_update.d.ts +7 -0
  19. package/dist/alter/column_update.d.ts.map +1 -0
  20. package/dist/alter/columns_order.d.ts +6 -0
  21. package/dist/alter/columns_order.d.ts.map +1 -0
  22. package/dist/alter/index.d.ts +7 -0
  23. package/dist/alter/index.d.ts.map +1 -0
  24. package/dist/alter/pragma.d.ts +4 -0
  25. package/dist/alter/pragma.d.ts.map +1 -0
  26. package/dist/alter/utils.d.ts +6 -0
  27. package/dist/alter/utils.d.ts.map +1 -0
  28. package/dist/defaults.d.ts +2 -0
  29. package/dist/defaults.d.ts.map +1 -0
  30. package/dist/index.d.ts +36 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.mjs +1540 -0
  33. package/dist/index.mjs.map +1 -0
  34. package/dist/indexer.d.ts +12 -0
  35. package/dist/indexer.d.ts.map +1 -0
  36. package/dist/log/access_log.d.ts +7 -0
  37. package/dist/log/access_log.d.ts.map +1 -0
  38. package/dist/log/db.d.ts +6 -0
  39. package/dist/log/db.d.ts.map +1 -0
  40. package/dist/log/index.d.ts +3 -0
  41. package/dist/log/index.d.ts.map +1 -0
  42. package/dist/tables/AccessLog/index.d.ts +79 -0
  43. package/dist/tables/AccessLog/index.d.ts.map +1 -0
  44. package/dist/tables/AccessLog/schema.d.ts +17 -0
  45. package/dist/tables/AccessLog/schema.d.ts.map +1 -0
  46. package/dist/tables/Permission/index.d.ts +43 -0
  47. package/dist/tables/Permission/index.d.ts.map +1 -0
  48. package/dist/tables/Permission/schema.d.ts +12 -0
  49. package/dist/tables/Permission/schema.d.ts.map +1 -0
  50. package/dist/tables/SetString/index.d.ts +10 -0
  51. package/dist/tables/SetString/index.d.ts.map +1 -0
  52. package/dist/tables/SetString/schema.d.ts +7 -0
  53. package/dist/tables/SetString/schema.d.ts.map +1 -0
  54. package/dist/tables/Settings/index.d.ts +42 -0
  55. package/dist/tables/Settings/index.d.ts.map +1 -0
  56. package/dist/tables/Settings/schema.d.ts +8 -0
  57. package/dist/tables/Settings/schema.d.ts.map +1 -0
  58. package/dist/tables/Transaction/index.d.ts +90 -0
  59. package/dist/tables/Transaction/index.d.ts.map +1 -0
  60. package/dist/tables/Transaction/schema.d.ts +16 -0
  61. package/dist/tables/Transaction/schema.d.ts.map +1 -0
  62. package/dist/types/index.d.ts +12 -0
  63. package/dist/types/index.d.ts.map +1 -0
  64. package/dist/utils.d.ts +42 -0
  65. package/dist/utils.d.ts.map +1 -0
  66. package/eslint.config.js +7 -0
  67. package/package.json +54 -0
  68. package/src/BitFields.ts +160 -0
  69. package/src/Error.ts +13 -0
  70. package/src/ORM.ts +49 -0
  71. package/src/SQLParser.js +1204 -0
  72. package/src/WebpackFileProvider.ts +63 -0
  73. package/src/alter/column_add.ts +79 -0
  74. package/src/alter/column_drop.ts +54 -0
  75. package/src/alter/column_rename.ts +55 -0
  76. package/src/alter/column_update.ts +92 -0
  77. package/src/alter/columns_order.ts +60 -0
  78. package/src/alter/index.ts +6 -0
  79. package/src/alter/pragma.ts +10 -0
  80. package/src/alter/utils.ts +70 -0
  81. package/src/defaults.ts +3 -0
  82. package/src/index.ts +227 -0
  83. package/src/indexer.ts +75 -0
  84. package/src/log/access_log.ts +29 -0
  85. package/src/log/db.ts +28 -0
  86. package/src/log/index.ts +2 -0
  87. package/src/tables/AccessLog/index.ts +252 -0
  88. package/src/tables/AccessLog/schema.ts +20 -0
  89. package/src/tables/Permission/index.ts +220 -0
  90. package/src/tables/Permission/schema.ts +13 -0
  91. package/src/tables/SetString/index.ts +45 -0
  92. package/src/tables/SetString/schema.ts +7 -0
  93. package/src/tables/Settings/index.ts +135 -0
  94. package/src/tables/Settings/schema.ts +8 -0
  95. package/src/tables/Transaction/index.ts +343 -0
  96. package/src/tables/Transaction/schema.ts +20 -0
  97. package/src/types/index.ts +33 -0
  98. package/src/utils.ts +48 -0
  99. package/test/sqliteExtExpert.test.ts +39 -0
  100. package/tsconfig.build.json +17 -0
  101. package/tsconfig.json +16 -0
@@ -0,0 +1,1204 @@
1
+ /* eslint-disable no-useless-assignment, jsdoc/reject-any-type, @stylistic/function-call-spacing */
2
+ // spell-checker: words sqlv AUTOINCREMENT SAVEPOINT sqlited
3
+ import { Object_entries, Object_keys, regex } from '@foul11/awesome';
4
+
5
+ /** @param {string | TemplateStringsArray} str */
6
+ export function sqlv(str) {
7
+ if (Array.isArray(str))
8
+ str = str[0];
9
+
10
+ const arr = str.toString().split(/\r?\n/);
11
+
12
+ for (let i = 0; i < arr.length; i++) {
13
+ switch (true) {
14
+ case arr[i].trim().startsWith('INSERT INTO'):
15
+ case arr[i].trim().startsWith('ALTER TABLE'):
16
+ case arr[i].trim().startsWith('DROP TABLE'):
17
+ case arr[i].trim().startsWith('PRAGMA "main".foreign_key_check'):
18
+ arr[i] = arr[i] + ';';
19
+ break;
20
+ }
21
+ }
22
+
23
+ return arr.join('\n');
24
+ }
25
+
26
+ export class SQLParser {
27
+ /**
28
+ * @typedef {string | number | null} sqlite_value
29
+ *
30
+ * @typedef {{
31
+ * scheme_parts: string[],
32
+ * name: string,
33
+ * }} sqlite_schema
34
+ *
35
+ * @typedef {{
36
+ * name: string,
37
+ * as: string?,
38
+ * sort: 'ASC' | 'DESC' | null,
39
+ * }} sqlite_column
40
+ *
41
+ * @typedef {{
42
+ * type: 'PRAGMA',
43
+ * name: sqlite_schema,
44
+ * value: sqlite_value,
45
+ * }} sqlite_PRAGMA
46
+ *
47
+ * @typedef {{
48
+ * type: 'SAVEPOINT',
49
+ * savepoint_name: string?,
50
+ * }} sqlite_SAVEPOINT
51
+ *
52
+ * @typedef {{
53
+ * type: 'RELEASE',
54
+ * savepoint_name: string?,
55
+ * }} sqlite_RELEASE
56
+ *
57
+ * @typedef {{
58
+ * type: 'SELECT',
59
+ * distinct: boolean,
60
+ * columns: Exclude<sqlite_column, 'sort'>[],
61
+ * from: sqlite_schema[],
62
+ * where: string?,
63
+ * group_by: string?,
64
+ * order_by: string?,
65
+ * limit: number?,
66
+ * offset: number?,
67
+ * }} sqlite_SELECT
68
+ *
69
+ * @typedef {{
70
+ * table: sqlite_schema,
71
+ * columns: string[],
72
+ * constraints: string?,
73
+ * }} sqlite_references
74
+ *
75
+ * @typedef {'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'DATETIME'} sqlite_types
76
+ *
77
+ * @typedef {{
78
+ * name: string,
79
+ * type: sqlite_types,
80
+ * primary: boolean,
81
+ * auto_increment: boolean,
82
+ * not_null: boolean,
83
+ * unique: boolean,
84
+ * default: sqlite_value,
85
+ * references: sqlite_references?,
86
+ * }} sqlite_column_def
87
+ *
88
+ * @typedef {Omit<Omit<sqlite_column_def, 'name'>, 'type'>} sqlite_constraint
89
+ *
90
+ * @typedef {{
91
+ * type: 'CREATE_TABLE',
92
+ * table: sqlite_schema,
93
+ * if_not_exists: boolean,
94
+ * columns: sqlite_column_def[],
95
+ * }} sqlite_CREATE_TABLE
96
+ *
97
+ * @typedef {{
98
+ * type: 'CREATE_INDEX',
99
+ * name: sqlite_schema,
100
+ * table_name: string,
101
+ * if_not_exists: boolean,
102
+ * unique: boolean,
103
+ * columns: Omit<sqlite_column, 'as'>[],
104
+ * }} sqlite_CREATE_INDEX
105
+ *
106
+ * @typedef {{
107
+ * type: 'VALUES',
108
+ * values: (number | string)[],
109
+ * }} sqlite_VALUES
110
+ *
111
+ * @typedef {{
112
+ * type: 'INSERT',
113
+ * table: sqlite_schema,
114
+ * columns: Omit<Omit<sqlite_column, 'as'>, 'sort'>[],
115
+ * expr: sqlite_VALUES | sqlite_SELECT,
116
+ * }} sqlite_INSERT
117
+ *
118
+ * @typedef {{
119
+ * type: 'DROP_TABLE',
120
+ * table: sqlite_schema,
121
+ * if_not_exists: boolean,
122
+ * }} sqlite_DROP_TABLE
123
+ *
124
+ * @typedef {{
125
+ * type: 'DROP_INDEX',
126
+ * name: sqlite_schema,
127
+ * if_not_exists: boolean,
128
+ * }} sqlite_DROP_INDEX
129
+ *
130
+ * @typedef {{
131
+ * type: 'ALTER_RENAME_TO',
132
+ * table: sqlite_schema,
133
+ * to: string,
134
+ * }} sqlite_ALTER_RENAME_TO
135
+ *
136
+ * @typedef {{
137
+ * type: 'ALTER_RENAME_COLUMN',
138
+ * table: sqlite_schema,
139
+ * from: string,
140
+ * to: string,
141
+ * }} sqlite_ALTER_RENAME_COLUMN
142
+ *
143
+ * @typedef {{
144
+ * type: 'ALTER_ADD',
145
+ * table: sqlite_schema,
146
+ * column: sqlite_column_def,
147
+ * }} sqlite_ALTER_ADD
148
+ *
149
+ * @typedef {{
150
+ * type: 'ALTER_DROP',
151
+ * table: sqlite_schema,
152
+ * column_name: string,
153
+ * }} sqlite_ALTER_DROP
154
+ *
155
+ * @typedef {sqlite_PRAGMA |
156
+ * sqlite_SAVEPOINT |
157
+ * sqlite_RELEASE |
158
+ * sqlite_SELECT |
159
+ * sqlite_CREATE_TABLE |
160
+ * sqlite_CREATE_INDEX |
161
+ * sqlite_VALUES |
162
+ * sqlite_INSERT |
163
+ * sqlite_DROP_TABLE |
164
+ * sqlite_DROP_INDEX |
165
+ * sqlite_ALTER_RENAME_TO |
166
+ * sqlite_ALTER_RENAME_COLUMN |
167
+ * sqlite_ALTER_ADD |
168
+ * sqlite_ALTER_DROP
169
+ * } sqlite_EXPRS
170
+ */
171
+
172
+ /**
173
+ * @param {string} sql
174
+ * @param {string} char
175
+ */
176
+ static splitQStrByChar(sql, char) {
177
+ const re = new RegExp(`((?=["'])(?:"[^"\\\\]*(?:\\\\[\\s\\S][^"\\\\]*)*"|'[^'\\\\]*(?:\\\\[\\s\\S][^'\\\\]*)*')|${char})`, 'g');
178
+ const out = /** @type {string[]} */ ([]);
179
+ const acc = /** @type {string[]} */ ([]);
180
+
181
+ /** @type {string[]} */
182
+ const res = sql.split(re);
183
+
184
+ for (const str of res) {
185
+ if (!str)
186
+ continue;
187
+
188
+ acc.push(str);
189
+
190
+ if (str === char) {
191
+ out.push(acc.join(''));
192
+ acc.length = 0;
193
+ }
194
+ }
195
+
196
+ if (acc.length)
197
+ out.push(acc.join(''));
198
+
199
+ return out;
200
+ }
201
+
202
+ /**
203
+ * @param {string} input
204
+ * @returns {sqlite_schema}
205
+ */
206
+ static sqliteSchema(input) {
207
+ const out = /** @type {string[]} */ ([]);
208
+
209
+ // Регулярка объявлена вне regex функции, потому что на "\\" она багуется и не компилируется
210
+ const res = input.matchAll(/(?:(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]*(?:\\[\s\S][^'\\]*)*')|\.|[^.]+)/ig);
211
+
212
+ for (const match of res) {
213
+ const s = match[0];
214
+
215
+ if (!s || s === '.')
216
+ continue;
217
+
218
+ if (s[0] === "'" || s[0] === '"') {
219
+ out.push(s.slice(1, -1));
220
+ continue;
221
+ }
222
+
223
+ out.push(s);
224
+ }
225
+
226
+ if (!out.length)
227
+ throw new Error(`Can't parse name (empty): ${input}`);
228
+
229
+ return {
230
+ scheme_parts: out.splice(0, out.length - 1),
231
+ name: out[out.length - 1],
232
+ };
233
+ }
234
+
235
+ /** @param {string | null | undefined} input */
236
+ static sqliteName(input) {
237
+ if (!input)
238
+ throw new Error(`Can't parse name (empty): ${input}`);
239
+
240
+ if (input[0] === '"')
241
+ return input.slice(1, -1);
242
+
243
+ return input;
244
+ }
245
+
246
+ /**
247
+ * @param {string} input
248
+ * @returns {sqlite_column[]}
249
+ */
250
+ static sqliteColumns(input) {
251
+ const out = /** @type {ReturnType<typeof SQLParser['sqliteColumns']>} */ ([]);
252
+ const columns = SQLParser.splitQStrByChar(input, ',')
253
+ .map(str => str.trim().match(SQLParser.regex.column));
254
+
255
+ for (const column of columns) {
256
+ if (!column || !column.groups)
257
+ throw new Error(`Can't parse column: ${input}`);
258
+
259
+ const sort = /** @type {string | null} */ (column.groups.sort?.toUpperCase() ?? null);
260
+
261
+ if (sort && sort !== 'ASC' && sort !== 'DESC')
262
+ throw new Error(`Can't parse sort: ${input}`);
263
+
264
+ out.push({
265
+ name: SQLParser.sqliteName(column.groups.name),
266
+ as: column.groups.as ? SQLParser.sqliteName(column.groups.as) : null,
267
+ sort: /** @type {null | 'ASC' | 'DESC'} */ (sort),
268
+ });
269
+ }
270
+
271
+ return out;
272
+ }
273
+
274
+ /**
275
+ * @param {RegExp} rgx
276
+ * @param {string} input
277
+ */
278
+ static matchAllGroups(rgx, input) {
279
+ /** @type {Record<string, string | undefined>} */
280
+ const out = {};
281
+ const matches = input.matchAll(rgx);
282
+
283
+ for (const match of matches)
284
+ Object.assign(out, ...Object_entries(match.groups ?? {}).map(([ k, v ]) => (v === undefined ? {} : { [k]: v })));
285
+
286
+ return { groups: out };
287
+ }
288
+
289
+ /**
290
+ * @param {string} input
291
+ * @returns {sqlite_column_def[]}
292
+ */
293
+ static sqliteColumnsDef(input) {
294
+ const out = /** @type {ReturnType<typeof SQLParser['sqliteColumnsDef']>} */ ([]);
295
+ const columns = SQLParser.splitQStrByChar(input, ',')
296
+ .map(str => str.trim().match(SQLParser.regex.column_def));
297
+
298
+ for (const column of columns) {
299
+ if (!column || !column.groups)
300
+ throw new Error(`Can't parse column: ${input}`);
301
+
302
+ if (!column.groups.name) { // if not name then it's table constraint
303
+ if (!column.groups.table_constraint)
304
+ throw new Error(`Can't parse table constraint or name: ${input}`);
305
+
306
+ const table_constraint = column.groups.table_constraint.trim()
307
+ .match(SQLParser.regex.table_constraint);
308
+
309
+ if (!table_constraint || !table_constraint.groups)
310
+ throw new Error(`Can't parse table constraint: ${input}`);
311
+
312
+ const inline_columns = /** @type {sqlite_column[]} */ ([]);
313
+ const props = /** @type {{ primary?: boolean, unique?: boolean, auto_increment?: boolean }} */ ({});
314
+
315
+ switch (true) {
316
+ case !!table_constraint.groups.primary_key_expr:
317
+ inline_columns.push(
318
+ ...SQLParser.sqliteColumns(table_constraint.groups.primary_key_expr),
319
+ );
320
+
321
+ if (table_constraint.groups.auto_increment)
322
+ props.auto_increment = true;
323
+
324
+ props.primary = true;
325
+ break;
326
+
327
+ case !!table_constraint.groups.unique_expr:
328
+ inline_columns.push(
329
+ ...SQLParser.sqliteColumns(table_constraint.groups.unique_expr),
330
+ );
331
+
332
+ props.unique = true;
333
+ break;
334
+
335
+ default:
336
+ throw new Error(`Can't parse table constraint type: ${input}`);
337
+ }
338
+
339
+ const inline_names = new Set(inline_columns.map(c => c.name));
340
+
341
+ for (const inline_column of out.filter(c => inline_names.has(c.name))) {
342
+ for (const prop of Object_keys(props)) {
343
+ if (props[prop] === undefined)
344
+ continue;
345
+
346
+ inline_column[prop] = props[prop];
347
+ }
348
+ }
349
+
350
+ continue;
351
+ }
352
+
353
+ const constraints = /** @type {sqlite_constraint} */ ({
354
+ auto_increment: false,
355
+ not_null: false,
356
+ primary: false,
357
+ unique: false,
358
+ default: null,
359
+ references: null,
360
+ });
361
+
362
+ if (column.groups.constraint) {
363
+ const column_constraint = SQLParser.matchAllGroups(SQLParser.regex.constraint, column.groups.constraint.trim());
364
+ // .match(SQLParser.regex.constraint);
365
+
366
+ if (!column_constraint || !column_constraint.groups)
367
+ throw new Error(`Can't parse column constraint: ${column.groups.constraint}`);
368
+
369
+ if (column_constraint.groups.auto_increment)
370
+ constraints.auto_increment = true;
371
+
372
+ if (column_constraint.groups.not_null)
373
+ constraints.not_null = true;
374
+
375
+ if (column_constraint.groups.primary_key)
376
+ constraints.primary = true;
377
+
378
+ if (column_constraint.groups.unique)
379
+ constraints.unique = true;
380
+
381
+ if (column_constraint.groups.default) {
382
+ const g = column_constraint.groups;
383
+
384
+ if (g.default_expr) {
385
+ constraints.default = g.default_expr;
386
+ } else {
387
+ constraints.default = SQLParser.sqliteValue(g.default_number ?? g.default_literal ?? g.default_const);
388
+ }
389
+ }
390
+
391
+ if (column_constraint.groups.references)
392
+ constraints.references = {
393
+ table: SQLParser.sqliteSchema (column_constraint.groups.references_table ?? ''),
394
+ columns: SQLParser.splitQStrByChar(column_constraint.groups.columns_def_expr ?? '', ',').map(v => SQLParser.sqliteName(v.trim())),
395
+ constraints: column_constraint.groups.references_constraint ?? null,
396
+ };
397
+ }
398
+
399
+ /** @type {string | null} */
400
+ const type = column.groups.type?.toUpperCase() ?? null;
401
+
402
+ if (!type)
403
+ throw new Error(`Can't parse column type: ${input}`);
404
+
405
+ out.push({
406
+ name: SQLParser.sqliteName(column.groups.name),
407
+ type: /** @type {sqlite_types} */ (type),
408
+ ...constraints,
409
+ });
410
+ }
411
+
412
+ return out;
413
+ }
414
+
415
+ /**
416
+ * @param {string | null | undefined} input
417
+ * @returns {sqlite_value}
418
+ */
419
+ static sqliteValue(input) {
420
+ input = SQLParser.strOrNull(input);
421
+
422
+ if (input === null)
423
+ return null;
424
+
425
+ const num = parseFloat(input);
426
+
427
+ if (!isNaN(num))
428
+ return num;
429
+
430
+ return input;
431
+ }
432
+
433
+ /** @param {string | null | undefined} str */
434
+ static strOrNull(str) {
435
+ if (str === null || str === undefined)
436
+ return null;
437
+
438
+ if (str[0] === "'")
439
+ return str.slice(1, -1);
440
+
441
+ return str;
442
+ }
443
+
444
+ /** @param {string | null | undefined} str */
445
+ static intOrNull(str) {
446
+ if (str === null || str === undefined)
447
+ return null;
448
+
449
+ return parseInt(str);
450
+ }
451
+
452
+ static regex = {
453
+ PRAGMA: regex `
454
+ ^(?:
455
+ PRAGMA
456
+ (?: \s+ (?<name> \S + ))?
457
+ (?: \s* = \s* (?<value> [\s\S]+ ))?
458
+ ;?)$
459
+ ` `i`,
460
+
461
+ SAVEPOINT: regex `
462
+ ^(?:
463
+ SAVEPOINT
464
+ (?: \s+ (?<name> \S+ ))?
465
+ ;?)$
466
+ ` `i`,
467
+
468
+ RELEASE: regex `
469
+ ^(?:
470
+ RELEASE
471
+ (?: \s+ (?<name> \S+ ))?
472
+ ;?)$
473
+ ` `i`,
474
+
475
+ SELECT: regex`
476
+ ^(?:
477
+ SELECT
478
+ (?: \s+ (?<distinct> DISTINCT ))?
479
+ (?: \s+ (?<columns> [\s\S]+ ))
480
+ (?: \s+ (?<from> FROM \s+ (?<table _expr> [\s\S]+?) ))
481
+ (?: \s+ (?<where> WHERE \s+ (?<where _expr> [\s\S]+?) ))?
482
+ (?: \s+ (?<group_by> GROUP \s+ BY \s+ (?<group _expr> [\s\S]+?) ))?
483
+ (?: \s+ (?<order_by> ORDER \s+ BY \s+ (?<order _expr> [\s\S]+?) ))?
484
+ (?: \s+ (?<limit> LIMIT \s+ (?<limit _expr> [\s\S]+?) ))?
485
+ (?: \s+ (?<offset> OFFSET \s+ (?<offset_expr> [\s\S]+?) ))?
486
+ ;?)$
487
+ ` `i`,
488
+
489
+ CREATE_TABLE: regex`
490
+ ^(?:
491
+ CREATE \s+ TABLE
492
+ (?: \s+ (?<if_not_exists> IF \s+ NOT \s+ EXISTS ))?
493
+ (?: \s+ (?<name> (?<name_expr> [\s\S]+?) ))
494
+ (?: \s+ (?<columns_def> \( (?<columns_def_expr> [\s\S]+?) \) ))
495
+ ;?)$
496
+ ` `i`,
497
+
498
+ CREATE_INDEX: regex `
499
+ ^(?:
500
+ CREATE
501
+ (?: \s+ (?<unique> UNIQUE ))?
502
+ (?: \s+ (?: INDEX ))
503
+ (?: \s+ (?<if_not_exists> IF \s+ NOT \s+ EXISTS ))?
504
+ (?: \s+ (?<name> (?<name_expr> [\s\S]+?) ))
505
+ (?: \s+ (?: ON ))
506
+ (?: \s+ (?<table> (?<table_expr> [\s\S]+?) ))
507
+ (?: \s+ (?<columns> \( (?<columns_expr> [\s\S]+?) \) ))
508
+ ;?)$
509
+ ` `i`,
510
+
511
+ VALUES: regex `
512
+ ^(?:
513
+ VALUES
514
+ (?: \s+ \( (?<values> [\s\S]+?) \) )
515
+ ;?)$
516
+ ` `i`,
517
+
518
+ INSERT: regex `
519
+ ^(?:
520
+ INSERT \s+ INTO
521
+ (?: \s+ (?<table> (?<table_expr> [\s\S]+?) ))
522
+ (?: \s+ (?<columns> \( (?<columns_expr> [\s\S]+?) \) ))
523
+ (?:
524
+ (?: \s+ (?<values_expr> VALUES \s+ \( (?: [\s\S]+?) \) )) |
525
+ (?: \s+ (?<select_expr> SELECT \s+ (?: [\s\S]+?) ))
526
+ )
527
+ ;?)$
528
+ ` `i`,
529
+
530
+ DROP: regex `
531
+ ^(?:
532
+ DROP
533
+ (?:
534
+ (?: \s+ TABLE) |
535
+ (?: \s+ INDEX)
536
+ )
537
+ (?: \s+ (?<if_not_exists> IF \s+ NOT \s+ EXISTS ))?
538
+ (?: \s+ (?<name> [\s\S]+? ))
539
+ ;?)$
540
+ ` `i`,
541
+
542
+ ALTER_TABLE: regex `
543
+ ^(?:
544
+ ALTER \s+ TABLE
545
+ (?: \s+ (?<table> (?<table_expr> [\s\S]+?) ))
546
+ (?:
547
+ (?:
548
+ (?: \s+ RENAME \s+ TO \s+ (?<rename_to > [\s\S]+? )) |
549
+ (?: \s+ RENAME \s+ (?: COLUMN \s+ )?
550
+ (?<rename_col_from> [\s\S]+? )
551
+ (?: \s+ TO \s+ )
552
+ (?<rename_col_to> [\s\S]+? )
553
+ ) |
554
+ (?: \s+ DROP \s+ (?: COLUMN \s+ )? (?<drop_col> [\s\S]+? ))
555
+ ) |
556
+ (?: \s+ ADD \s+ (?: COLUMN \s+ )? (?<add_col> [\s\S]+? ))
557
+ )
558
+ ;?)$
559
+ ` `i`,
560
+
561
+ column: regex`
562
+ ^(?:
563
+ (?: (?<name> [\s\S]+? ))
564
+ (?:
565
+ (?: \s+ (?<as> AS \s+ (?<as_expr> [\s\S]+? ) ))? |
566
+ (?: \s+ (?<sort> (?<sort_expr> ASC | DESC) ))?
567
+ )
568
+ ,?)$
569
+ ` `i`,
570
+
571
+ column_def: regex`
572
+ ^(?:
573
+ (?:
574
+ (?<table_constraint> (?: PRIMARY | UNIQUE | CHECK | FOREIGN ) [\s\S]+ )
575
+ ) |
576
+ (?: (?<name> [\s\S]+? ))
577
+ (?: \s+ (?<type> (?: TEXT | INTEGER | REAL | BLOB | DATETIME ) ))?
578
+ (?: \s+ (?<constraint> [\s\S]+? ))?
579
+ ,?)$
580
+ ` `i`,
581
+
582
+ constraint: regex`
583
+ (?:
584
+ // (?: \s* ) |
585
+ (?:
586
+ (?<primary_key> PRIMARY \s+ KEY
587
+ (?: (?<primary_key_sort> ASC | DESC ))?
588
+ (?: \s+ (?<auto_increment> AUTOINCREMENT ))?
589
+ )
590
+ ) |
591
+ (?: (?<not_null> NOT \s+ NULL )) |
592
+ (?: (?<unique> UNIQUE )) |
593
+ (?: (?<default> DEFAULT \s+
594
+ (?:
595
+ (?<default_expr> \( [\s\S]+ \) ) |
596
+ (?<default_number> [+-]? [\d]+ ) |
597
+ (?<default_literal> ['"] [\s\S]+ ['"] ) |
598
+ (?<default_const> TRUE | FALSE | NULL )
599
+ )
600
+ )) |
601
+ (?:
602
+ (?<references> REFERENCES \s+
603
+ (?<references_table> [\S]+)
604
+ (?: \s* (?<columns_def> \( (?<columns_def_expr> [\s\S]+?) \) ))?
605
+ (?: \s* (?<references_constraint> (?:
606
+ (?: \s*
607
+ ON \s+
608
+ (?: DELETE | UPDATE ) \s+
609
+ (?:
610
+ SET \s+ NULL |
611
+ SET \s+ DEFAULT |
612
+ CASCADE |
613
+ RESTRICT |
614
+ NO \s+ ACTION
615
+ )
616
+ ) |
617
+ (?: \s* MATCH \s+ [\S]+) |
618
+ (?: \s*
619
+ (?: NOT \s+ )?
620
+ DEFERRABLE
621
+ (?:
622
+ \s+ INITIALLY \s+ DEFERRED |
623
+ \s+ INITIALLY \s+ IMMEDIATE
624
+ )?
625
+ ))+
626
+ ))?
627
+ )
628
+ )
629
+ )
630
+ ` `ig`,
631
+
632
+ table_constraint: regex`
633
+ ^(?:
634
+ (?:
635
+ (?<primary_key> PRIMARY \s+ KEY
636
+ \(
637
+ (?: (?<primary_key_expr> [\s\S]+? ))
638
+ (?: \s+ (?<auto_increment> AUTOINCREMENT ))?
639
+ \)
640
+ )
641
+ ) |
642
+ (?: (?<unique> UNIQUE \s+ \((?<unique_expr> [\s\S]+?)\) ))
643
+ )$
644
+ ` `i`,
645
+ };
646
+
647
+ /** @param {string} sql */
648
+ static parse(sql) {
649
+ const out = /** @type {sqlite_EXPRS[]} */ ([]);
650
+ const semi_split = SQLParser.splitQStrByChar(sql, ';');
651
+
652
+ for (const raw_str of semi_split) {
653
+ const str_expr = raw_str.trim();
654
+
655
+ switch (true) {
656
+ case /^PRAGMA/i.test(str_expr): {
657
+ const parse = str_expr.match(SQLParser.regex.PRAGMA);
658
+
659
+ if (!parse || !parse.groups)
660
+ throw new Error(`Can't parse: ${str_expr}`);
661
+
662
+ out.push({
663
+ type: 'PRAGMA',
664
+ name: SQLParser.sqliteSchema(parse.groups.name),
665
+ value: SQLParser.sqliteValue(parse.groups.value),
666
+ });
667
+
668
+ break;
669
+ }
670
+
671
+ case /^SAVEPOINT/i.test(str_expr): {
672
+ const parse = str_expr.match(SQLParser.regex.SAVEPOINT);
673
+
674
+ if (!parse || !parse.groups)
675
+ throw new Error(`Can't parse: ${str_expr}`);
676
+
677
+ out.push({
678
+ type: 'SAVEPOINT',
679
+ savepoint_name: SQLParser.strOrNull(parse.groups.name),
680
+ });
681
+
682
+ break;
683
+ }
684
+
685
+ case /^RELEASE/i.test(str_expr): {
686
+ const parse = str_expr.match(SQLParser.regex.RELEASE);
687
+
688
+ if (!parse || !parse.groups)
689
+ throw new Error(`Can't parse: ${str_expr}`);
690
+
691
+ out.push({
692
+ type: 'RELEASE',
693
+ savepoint_name: SQLParser.strOrNull(parse.groups.name),
694
+ });
695
+
696
+ break;
697
+ }
698
+
699
+ case /^SELECT/i.test(str_expr): {
700
+ const parse = str_expr.match(SQLParser.regex.SELECT);
701
+
702
+ if (!parse || !parse.groups)
703
+ throw new Error(`Can't parse: ${str_expr}`);
704
+
705
+ out.push({
706
+ type: 'SELECT',
707
+ distinct: !!parse.groups.distinct,
708
+ columns: SQLParser.sqliteColumns(parse.groups.columns),
709
+ from: (
710
+ SQLParser.splitQStrByChar(parse.groups.table_expr, ',')
711
+ .map(t => SQLParser.sqliteSchema(t))
712
+ ),
713
+ where: SQLParser.strOrNull(parse.groups.where_expr ),
714
+ group_by: SQLParser.strOrNull(parse.groups.group_expr ),
715
+ order_by: SQLParser.strOrNull(parse.groups.order_expr ),
716
+ offset: SQLParser.intOrNull(parse.groups.offset_expr),
717
+ limit: SQLParser.intOrNull(parse.groups.limit_expr ),
718
+ });
719
+
720
+ break;
721
+ }
722
+
723
+ case /^CREATE TABLE/i.test(str_expr): {
724
+ const parse = str_expr.match(SQLParser.regex.CREATE_TABLE);
725
+
726
+ if (!parse || !parse.groups)
727
+ throw new Error(`Can't parse: ${str_expr}`);
728
+
729
+ out.push({
730
+ type: 'CREATE_TABLE',
731
+ if_not_exists: !!parse.groups.if_not_exists,
732
+ table: SQLParser.sqliteSchema(parse.groups.name_expr),
733
+ columns: SQLParser.sqliteColumnsDef(parse.groups.columns_def_expr),
734
+ });
735
+
736
+ break;
737
+ }
738
+
739
+ case /^CREATE/i.test(str_expr): { // if not CREATE TABLE then CREATE INDEX
740
+ const parse = str_expr.match(SQLParser.regex.CREATE_INDEX);
741
+
742
+ if (!parse || !parse.groups)
743
+ throw new Error(`Can't parse: ${str_expr}`);
744
+
745
+ out.push({
746
+ type: 'CREATE_INDEX',
747
+ name: SQLParser.sqliteSchema(parse.groups.name_expr),
748
+ table_name: SQLParser.sqliteName(parse.groups.table_expr),
749
+ if_not_exists: !!parse.groups.if_not_exists,
750
+ unique: !!parse.groups.unique,
751
+ columns: SQLParser.sqliteColumns(parse.groups.columns_expr),
752
+ });
753
+
754
+ break;
755
+ }
756
+
757
+ case /^INSERT/i.test(str_expr): {
758
+ const parse = str_expr.match(SQLParser.regex.INSERT);
759
+
760
+ if (!parse || !parse.groups)
761
+ throw new Error(`Can't parse: ${str_expr}`);
762
+
763
+ const expr = SQLParser.parse(`${parse.groups.values_expr || parse.groups.select_expr};`)[0];
764
+
765
+ if (!expr || (expr.type !== 'SELECT' && expr.type !== 'VALUES'))
766
+ throw new Error(`Can't parse: ${str_expr}`);
767
+
768
+ out.push({
769
+ type: 'INSERT',
770
+ table: SQLParser.sqliteSchema(parse.groups.table_expr),
771
+ columns: SQLParser.sqliteColumns(parse.groups.columns_expr),
772
+ expr: expr,
773
+ });
774
+
775
+ break;
776
+ }
777
+
778
+ case /^VALUES/i.test(str_expr): {
779
+ const parse = str_expr.match(SQLParser.regex.VALUES);
780
+
781
+ if (!parse || !parse.groups)
782
+ throw new Error(`Can't parse: ${str_expr}`);
783
+
784
+ const values = SQLParser.splitQStrByChar(parse.groups.values, ',')
785
+ .map(v => v[0] !== '"' ? parseFloat(v) : v.slice(1, -1));
786
+
787
+ out.push({
788
+ type: 'VALUES',
789
+ values,
790
+ });
791
+
792
+ break;
793
+ }
794
+
795
+ case /^DROP TABLE/i.test(str_expr): {
796
+ const parse = str_expr.match(SQLParser.regex.DROP);
797
+
798
+ if (!parse || !parse.groups)
799
+ throw new Error(`Can't parse: ${str_expr}`);
800
+
801
+ out.push({
802
+ type: 'DROP_TABLE',
803
+ if_not_exists: !!parse.groups.if_not_exists,
804
+ table: SQLParser.sqliteSchema(parse.groups.name),
805
+ });
806
+
807
+ break;
808
+ }
809
+
810
+ case /^DROP INDEX/i.test(str_expr): {
811
+ const parse = str_expr.match(SQLParser.regex.DROP);
812
+
813
+ if (!parse || !parse.groups)
814
+ throw new Error(`Can't parse: ${str_expr}`);
815
+
816
+ out.push({
817
+ type: 'DROP_INDEX',
818
+ if_not_exists: !!parse.groups.if_not_exists,
819
+ name: SQLParser.sqliteSchema(parse.groups.name),
820
+ });
821
+
822
+ break;
823
+ }
824
+
825
+ case /^ALTER TABLE/i.test(str_expr): {
826
+ const parse = str_expr.match(SQLParser.regex.ALTER_TABLE);
827
+
828
+ if (!parse || !parse.groups)
829
+ throw new Error(`Can't parse: ${str_expr}`);
830
+
831
+ const table = SQLParser.sqliteSchema(parse.groups.table_expr);
832
+
833
+ switch (true) {
834
+ case !!parse.groups.rename_to:
835
+ out.push({
836
+ type: 'ALTER_RENAME_TO',
837
+ table,
838
+ to: SQLParser.sqliteName(parse.groups.rename_to),
839
+ });
840
+ break;
841
+
842
+ case !!parse.groups.rename_col:
843
+ out.push({
844
+ type: 'ALTER_RENAME_COLUMN',
845
+ table,
846
+ from: SQLParser.sqliteName(parse.groups.rename_col_from),
847
+ to: SQLParser.sqliteName(parse.groups.rename_col_to),
848
+ });
849
+ break;
850
+
851
+ case !!parse.groups.add_col: {
852
+ const column = SQLParser.sqliteColumnsDef(parse.groups.add_col)[0];
853
+
854
+ if (!column)
855
+ throw new Error(`Can't parse: ${str_expr}`);
856
+
857
+ out.push({
858
+ type: 'ALTER_ADD',
859
+ table,
860
+ column: column,
861
+ });
862
+ break;
863
+ }
864
+
865
+ case !!parse.groups.drop_col:
866
+ out.push({
867
+ type: 'ALTER_DROP',
868
+ table,
869
+ column_name: SQLParser.sqliteName(parse.groups.drop_col),
870
+ });
871
+ break;
872
+
873
+ default:
874
+ throw new Error(`Can't parse: ${str_expr}`);
875
+ }
876
+
877
+ break;
878
+ }
879
+
880
+ case str_expr.trim() === '':
881
+ break;
882
+
883
+ default:
884
+ throw new Error(`Can't parse (default): ${str_expr}`);
885
+ }
886
+ }
887
+
888
+ return out;
889
+ }
890
+ }
891
+
892
+ export class SQLiteDebugger {
893
+ /*
894
+ TODO:
895
+ Класс который будет разбивать операции с sqlv на отдельные части,
896
+ угадывать что было сделано RENAME/ADD COLUMN/REMOVE COLUMN/EDIT COLUMN и тд,
897
+ и красивенько выводить в stdout, показывать успешные операции, выполнять транзакции
898
+ */
899
+ // /** @param {import('better-sqlite3').Database} db */
900
+ /** @param {any} db */
901
+ constructor(db) {
902
+ this.db = db;
903
+ }
904
+
905
+ /**
906
+ * @param {string} table
907
+ * @returns {{
908
+ * cid: number,
909
+ * name: string,
910
+ * type: 'INTEGER' | 'REAL' | 'TEXT' | 'BLOB' | 'NUMERIC',
911
+ * notnull: boolean,
912
+ * dflt_value: string | null,
913
+ * pk: boolean,
914
+ * }[]}
915
+ */
916
+ tableInfo(table) {
917
+ return this.db.prepare(`SELECT * FROM pragma_table_info('${table}')`)
918
+ .all()
919
+ .map((/** @type {*} */ r) => { return { ...r, notnull: !!r.notnull, pk: !!r.pk }; });
920
+ }
921
+
922
+ /**
923
+ * @returns {{
924
+ * type: 'index' | 'table',
925
+ * name: string,
926
+ * tbl_name: string,
927
+ * rootpage: number,
928
+ * sql: string,
929
+ * }[]}
930
+ */
931
+ masterTable() {
932
+ return /** @type {*} */ (this.db.prepare(`SELECT * FROM sqlite_master`)
933
+ .all().filter((/** @type {*} */ r) => r.name != 'sqlite_sequence'));
934
+ }
935
+
936
+ /**
937
+ * @typedef {number | {
938
+ * from: number,
939
+ * to: number,
940
+ * }} sqlited_index
941
+ *
942
+ * @typedef {{
943
+ * type: 'LINK_SQLITE',
944
+ * index: number,
945
+ * link: sqlite_EXPRS,
946
+ * linked: Map<sqlited_link_logic_sub_type, Set<sqlited_link_logic | sqlited_link_sqlite>>,
947
+ * }} sqlited_link_sqlite
948
+ *
949
+ * @typedef {'SAVE/RELEASE' | 'LINK_TABLE'} sqlited_link_logic_sub_type
950
+ *
951
+ * @typedef {{
952
+ * type: 'LINK',
953
+ * sub_type: sqlited_link_logic_sub_type,
954
+ * index: sqlited_index,
955
+ * child: (sqlited_link_logic | sqlited_link_sqlite)[],
956
+ * }} sqlited_link_logic
957
+ */
958
+
959
+ /*
960
+ TODO:
961
+ t1 - новая
962
+ t2 - старая
963
+ [ ->] - необязательная операция
964
+ {number [/ link]} - номер/ссылка
965
+ {...S} - Миграция схемы
966
+ {...D} - Миграция данных
967
+
968
+ * {1 S} [Добавление столбца (t2) ->] Создание (t1) -> перенос данных (t2 -> t1) -> удаление (t2) -> переименование (t1)
969
+ - Переименование (t2) [При условии, что столбцы те же самые]
970
+ - Добавление/Удаление столбца (t2) [При условии, что столбцы создаваемых таблиц отличаются от исходных]
971
+ - Частичный перенос (t2) [При условии, что столбцы такие же, но выбраны для переноса не все]
972
+ - Изменения типа данных (t2) [При условии, что у любого столба отличается тип данных от исходного]
973
+ - Изменения дефолтного значений (t2) [При условии, что у любого столба отличается дефолтное значение от исходного]
974
+ - Изменение аттрибутов uniq, ...(t2) [При условии, что у любого столба отличается аттрибут uniq, ... от исходного]
975
+
976
+ * {2 S / before 1} Создание индексов (t2)
977
+ - Пересоздание индексов (t1) [При условии, что индексы такие же в двух таблицах до и после]
978
+ - Добавление/Удаление индексов (t2) [При условии, что индексы создаваемых таблиц отличаются от исходных, а так же может значит ошибку в случае если не будет хватать индекса]
979
+
980
+ * {3 D} Добавление данных (t2)
981
+ - Просто вставка данных в таблицу (t2)
982
+
983
+ * {4 D} Удаление данных (t2)
984
+ - Просто удаление данных из таблицы (t2)
985
+ */
986
+
987
+ /**
988
+ * @param {sqlited_index?} i1
989
+ * @param {sqlited_index?} i2
990
+ */
991
+ static sql_idx_min(i1, i2) {
992
+ if (i1 === null && i2 === null)
993
+ return null;
994
+
995
+ if (i1 !== null && typeof i1 == 'object')
996
+ i1 = Math.min(i1.from, i1.to);
997
+
998
+ if (i2 !== null && typeof i2 == 'object')
999
+ i2 = Math.min(i2.from, i2.to);
1000
+
1001
+ if (i1 === null)
1002
+ return i2;
1003
+
1004
+ if (i2 === null)
1005
+ return i1;
1006
+
1007
+ return Math.min(i1, i2);
1008
+ }
1009
+
1010
+ /**
1011
+ * @param {sqlited_index?} i1
1012
+ * @param {sqlited_index?} i2
1013
+ */
1014
+ static sql_idx_max(i1, i2) {
1015
+ if (i1 === null && i2 === null)
1016
+ return null;
1017
+
1018
+ if (i1 !== null && typeof i1 == 'object')
1019
+ i1 = Math.max(i1.from, i1.to);
1020
+
1021
+ if (i2 !== null && typeof i2 == 'object')
1022
+ i2 = Math.max(i2.from, i2.to);
1023
+
1024
+ if (i1 === null)
1025
+ return i2;
1026
+
1027
+ if (i2 === null)
1028
+ return i1;
1029
+
1030
+ return Math.max(i1, i2);
1031
+ }
1032
+
1033
+ /**
1034
+ * @param {sqlited_index?} i1
1035
+ * @param {sqlited_index?} i2
1036
+ */
1037
+ static sql_idx_outRange(i1, i2) {
1038
+ const i1min = SQLiteDebugger.sql_idx_min(i1, null);
1039
+ const i1max = SQLiteDebugger.sql_idx_max(i1, null);
1040
+ const i2min = SQLiteDebugger.sql_idx_min(i2, null);
1041
+ const i2max = SQLiteDebugger.sql_idx_max(i2, null);
1042
+
1043
+ if (i1min === null || i1max === null || i2min === null || i2max === null)
1044
+ return null;
1045
+
1046
+ return i1min < i2min && i1max > i2max;
1047
+ }
1048
+
1049
+ /**
1050
+ * @param {sqlited_index?} i1
1051
+ * @param {sqlited_index?} i2
1052
+ */
1053
+ static sql_idx_inRange(i1, i2) {
1054
+ const i1min = SQLiteDebugger.sql_idx_min(i1, null);
1055
+ const i1max = SQLiteDebugger.sql_idx_max(i1, null);
1056
+ const i2min = SQLiteDebugger.sql_idx_min(i2, null);
1057
+ const i2max = SQLiteDebugger.sql_idx_max(i2, null);
1058
+
1059
+ if (i1min === null || i1max === null || i2min === null || i2max === null)
1060
+ return null;
1061
+
1062
+ return (
1063
+ i1min >= i2min && i1min <= i2max &&
1064
+ i1max <= i2max && i1max >= i2min
1065
+ );
1066
+ }
1067
+
1068
+ /**
1069
+ * @param {sqlited_index?} i1
1070
+ * @param {sqlited_index?} i2
1071
+ */
1072
+ static sql_idx_invalidRange(i1, i2) {
1073
+ const outRange = SQLiteDebugger.sql_idx_outRange(i1, i2);
1074
+ const inRange = SQLiteDebugger.sql_idx_inRange(i1, i2);
1075
+
1076
+ return !outRange && !inRange;
1077
+ }
1078
+
1079
+ /**
1080
+ * @param {sqlited_link_sqlite[]} links
1081
+ * @param {sqlited_link_sqlite} link
1082
+ * @param {sqlited_link_logic_sub_type} sub_type
1083
+ * @param {(link: sqlited_link_sqlite, input: sqlited_link_sqlite) => boolean} predicate
1084
+ * @returns {sqlited_link_logic[]}
1085
+ */
1086
+ link_for_predicate(links, link, sub_type, predicate) {
1087
+ if (!link.linked.has(sub_type))
1088
+ link.linked.set(sub_type, new Set([ link ]));
1089
+
1090
+ /** @type {sqlited_link_sqlite[]} */
1091
+ const out = [ link ];
1092
+ const link_linked = link.linked.get(sub_type);
1093
+
1094
+ if (!link_linked)
1095
+ throw new Error('link_linked is null');
1096
+
1097
+ for (const l of links) {
1098
+ if (!l.linked.has(sub_type))
1099
+ l.linked.set(sub_type, new Set([ l ]));
1100
+
1101
+ const l_linked = l.linked.get(sub_type);
1102
+
1103
+ if (!l_linked)
1104
+ throw new Error('l_linked is undefined');
1105
+
1106
+ if (link_linked.has(l))
1107
+ continue;
1108
+
1109
+ if (predicate(link, l)) {
1110
+ link_linked.add(l);
1111
+ l_linked.add(link);
1112
+
1113
+ out.push(l);
1114
+ }
1115
+ }
1116
+
1117
+ if (!out.length)
1118
+ return [];
1119
+
1120
+ const i_min = out.reduce((prev, l) => SQLiteDebugger.sql_idx_min(prev, l.index), /** @type {number?} */ (null));
1121
+ const i_max = out.reduce((prev, l) => SQLiteDebugger.sql_idx_max(prev, l.index), /** @type {number?} */ (null));
1122
+
1123
+ if (i_min === null || i_max === null)
1124
+ throw new Error('Index is null');
1125
+
1126
+ return [{
1127
+ type: 'LINK',
1128
+ sub_type,
1129
+ child: out,
1130
+ index: { from: i_min, to: i_max },
1131
+ }];
1132
+ }
1133
+
1134
+ /** @param {string} sql */
1135
+ Heuristics_DBBrowserForSQLite(sql) {
1136
+ /** @type {sqlited_link_sqlite[]} */
1137
+ const parsed = SQLParser.parse(sql).map((e, i) => {
1138
+ return {
1139
+ type: 'LINK_SQLITE',
1140
+ index: i,
1141
+ link: e,
1142
+ linked: new Map(),
1143
+ };
1144
+ });
1145
+
1146
+ /** @type {sqlited_link_logic[]} */
1147
+ const logics = [];
1148
+
1149
+ for (const link of parsed) {
1150
+ switch (link.link.type) {
1151
+ case 'SAVEPOINT':
1152
+ case 'RELEASE':
1153
+ logics.push(...this.link_for_predicate(parsed, link, 'SAVE/RELEASE', (stmt_link, l) => {
1154
+ if ('savepoint_name' in stmt_link.link && 'savepoint_name' in l.link)
1155
+ return stmt_link.link.savepoint_name === l.link.savepoint_name;
1156
+
1157
+ return false;
1158
+ }));
1159
+ break;
1160
+
1161
+ case 'SELECT':
1162
+ case 'CREATE_TABLE':
1163
+ case 'INSERT':
1164
+ case 'DROP_TABLE':
1165
+ case 'ALTER_RENAME_TO':
1166
+ case 'ALTER_RENAME_COLUMN':
1167
+ case 'ALTER_ADD':
1168
+ case 'ALTER_DROP': {
1169
+ let table = null;
1170
+
1171
+ if ('table' in link.link) {
1172
+ table = new Set(link.link.table.name);
1173
+ } else if ('from' in link.link) {
1174
+ table = new Set(link.link.from.map(t => t.name));
1175
+ } else {
1176
+ throw new Error('table is null');
1177
+ }
1178
+
1179
+ logics.push(...this.link_for_predicate(parsed, link, 'LINK_TABLE', (stmt_link, l) => {
1180
+ if ('table' in l.link) {
1181
+ return table.has(l.link.table.name);
1182
+ } else if ('from' in l.link) {
1183
+ return l.link.from.some(t => table.has(t.name));
1184
+ }
1185
+
1186
+ return false;
1187
+ }));
1188
+
1189
+ break;
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ console.log(logics);
1195
+ }
1196
+
1197
+ /** @param {string | TemplateStringsArray} sql */
1198
+ execFrom_DBBrowser(sql) {
1199
+ if (Array.isArray(sql))
1200
+ sql = sql[0];
1201
+
1202
+ // this.db.exec(sql.toString());
1203
+ }
1204
+ }