neoorm 0.9.2 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/neoorm.d.ts.map +1 -1
- package/dist/bin/neoorm.js +79 -3
- package/dist/bin/neoorm.js.map +1 -1
- package/dist/codegen/generate.d.ts +8 -0
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/generate.js +57 -12
- package/dist/codegen/generate.js.map +1 -1
- package/dist/codegen/schema-to-manifest.d.ts.map +1 -1
- package/dist/codegen/schema-to-manifest.js +302 -18
- package/dist/codegen/schema-to-manifest.js.map +1 -1
- package/dist/introspect/pull.d.ts +1 -0
- package/dist/introspect/pull.d.ts.map +1 -1
- package/dist/introspect/pull.js +553 -31
- package/dist/introspect/pull.js.map +1 -1
- package/dist/introspect/sqlite/to-manifest.d.ts +10 -0
- package/dist/introspect/sqlite/to-manifest.d.ts.map +1 -1
- package/dist/introspect/sqlite/to-manifest.js +74 -0
- package/dist/introspect/sqlite/to-manifest.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/table.d.ts +36 -6
- package/dist/schema/table.d.ts.map +1 -1
- package/dist/schema/table.js.map +1 -1
- package/docs/cli.md +4 -1
- package/docs/migrations.md +6 -4
- package/docs/schema.md +12 -4
- package/package.json +2 -1
package/dist/introspect/pull.js
CHANGED
|
@@ -246,48 +246,570 @@ function formatTsValue(kind, value) {
|
|
|
246
246
|
}
|
|
247
247
|
return JSON.stringify(value);
|
|
248
248
|
}
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
249
|
+
function stripOuterParens(sql) {
|
|
250
|
+
let text = sql.trim();
|
|
251
|
+
for (;;) {
|
|
252
|
+
if (!text.startsWith("(") || !text.endsWith(")"))
|
|
253
|
+
return text;
|
|
254
|
+
let depth = 0;
|
|
255
|
+
let balanced = true;
|
|
256
|
+
let inStr = false;
|
|
257
|
+
for (let i = 0; i < text.length; i++) {
|
|
258
|
+
const ch = text[i];
|
|
259
|
+
if (inStr) {
|
|
260
|
+
if (ch === "'") {
|
|
261
|
+
if (text[i + 1] === "'") {
|
|
262
|
+
i++;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
inStr = false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (ch === "'") {
|
|
271
|
+
inStr = true;
|
|
272
|
+
}
|
|
273
|
+
else if (ch === "(") {
|
|
274
|
+
depth++;
|
|
275
|
+
}
|
|
276
|
+
else if (ch === ")") {
|
|
277
|
+
depth--;
|
|
278
|
+
if (depth === 0 && i !== text.length - 1) {
|
|
279
|
+
balanced = false;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!balanced || depth !== 0)
|
|
285
|
+
return text;
|
|
286
|
+
text = text.slice(1, -1).trim();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function splitTopLevel(sql, keyword) {
|
|
290
|
+
const parts = [];
|
|
291
|
+
let depth = 0;
|
|
292
|
+
let inStr = false;
|
|
293
|
+
let current = "";
|
|
294
|
+
const upper = sql.toUpperCase();
|
|
295
|
+
let i = 0;
|
|
296
|
+
while (i < sql.length) {
|
|
297
|
+
const ch = sql[i];
|
|
298
|
+
if (inStr) {
|
|
299
|
+
current += ch;
|
|
300
|
+
if (ch === "'") {
|
|
301
|
+
if (sql[i + 1] === "'") {
|
|
302
|
+
current += "'";
|
|
303
|
+
i += 2;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
inStr = false;
|
|
307
|
+
}
|
|
308
|
+
i++;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (ch === "'") {
|
|
312
|
+
inStr = true;
|
|
313
|
+
current += ch;
|
|
314
|
+
i++;
|
|
261
315
|
continue;
|
|
262
316
|
}
|
|
263
|
-
|
|
264
|
-
|
|
317
|
+
if (ch === "(") {
|
|
318
|
+
depth++;
|
|
319
|
+
current += ch;
|
|
320
|
+
i++;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (ch === ")") {
|
|
324
|
+
depth--;
|
|
325
|
+
current += ch;
|
|
326
|
+
i++;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (depth === 0) {
|
|
330
|
+
const rest = upper.slice(i);
|
|
331
|
+
const match = rest.match(/^(AND|OR)\b/);
|
|
332
|
+
if (match?.[1] === keyword) {
|
|
333
|
+
const before = sql[i - 1];
|
|
334
|
+
if (before === undefined ||
|
|
335
|
+
/\s/.test(before) ||
|
|
336
|
+
before === "(" ||
|
|
337
|
+
before === ")") {
|
|
338
|
+
parts.push(current);
|
|
339
|
+
current = "";
|
|
340
|
+
i += keyword.length;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
current += ch;
|
|
346
|
+
i++;
|
|
347
|
+
}
|
|
348
|
+
if (inStr || depth !== 0)
|
|
349
|
+
return undefined;
|
|
350
|
+
parts.push(current);
|
|
351
|
+
if (parts.length <= 1)
|
|
352
|
+
return undefined;
|
|
353
|
+
return parts;
|
|
354
|
+
}
|
|
355
|
+
function parseIndexLiteralValue(valueSql) {
|
|
356
|
+
const text = valueSql.trim();
|
|
357
|
+
if (text === "true" || text === "false") {
|
|
358
|
+
return { ok: true, ts: text };
|
|
359
|
+
}
|
|
360
|
+
if (/^-?\d+(\.\d+)?$/.test(text)) {
|
|
361
|
+
return { ok: true, ts: text };
|
|
362
|
+
}
|
|
363
|
+
if (text.startsWith("'") && text.endsWith("'") && text.length >= 2) {
|
|
364
|
+
return {
|
|
365
|
+
ok: true,
|
|
366
|
+
ts: `"${escapeTsString(text.slice(1, -1).replace(/''/g, "'"))}"`,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (text.startsWith('"') && text.endsWith('"') && text.length >= 2) {
|
|
370
|
+
return { ok: true, ts: `"${escapeTsString(text.slice(1, -1))}"` };
|
|
371
|
+
}
|
|
372
|
+
return { ok: false };
|
|
373
|
+
}
|
|
374
|
+
function unescapeLikePattern(pattern) {
|
|
375
|
+
let out = "";
|
|
376
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
377
|
+
const ch = pattern[i];
|
|
378
|
+
if (ch === "\\") {
|
|
379
|
+
const next = pattern[i + 1];
|
|
380
|
+
if (next === "%" || next === "_" || next === "\\") {
|
|
381
|
+
out += next;
|
|
382
|
+
i++;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
265
385
|
return undefined;
|
|
266
386
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
387
|
+
if (ch === "%" || ch === "_")
|
|
388
|
+
return undefined;
|
|
389
|
+
out += ch;
|
|
390
|
+
}
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
function likePatternToOp(pattern) {
|
|
394
|
+
const starts = pattern.startsWith("%");
|
|
395
|
+
const ends = pattern.endsWith("%");
|
|
396
|
+
const inner = pattern.slice(starts ? 1 : 0, ends ? pattern.length - 1 : pattern.length);
|
|
397
|
+
if (inner.includes("%"))
|
|
398
|
+
return undefined;
|
|
399
|
+
const value = unescapeLikePattern(inner);
|
|
400
|
+
if (value === undefined)
|
|
401
|
+
return undefined;
|
|
402
|
+
if (starts && ends)
|
|
403
|
+
return { op: "contains", value };
|
|
404
|
+
if (ends)
|
|
405
|
+
return { op: "startsWith", value };
|
|
406
|
+
if (starts)
|
|
407
|
+
return { op: "endsWith", value };
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
const INDEX_ATOM_COL = String.raw `(?:"(?<q1>[^"]+)"|(?<q2>[A-Za-z_][\w]*))`;
|
|
411
|
+
function parseIndexAtom(atom, tsNameBySql) {
|
|
412
|
+
const text = stripOuterParens(atom);
|
|
413
|
+
const notMatch = text.match(/^NOT\b\s*([\s\S]*)$/i);
|
|
414
|
+
if (notMatch?.[1]) {
|
|
415
|
+
const inner = parseIndexConjunction(notMatch[1], tsNameBySql);
|
|
416
|
+
if (!inner)
|
|
417
|
+
return undefined;
|
|
418
|
+
return { kind: "not", item: inner };
|
|
419
|
+
}
|
|
420
|
+
const tsOf = (sql) => {
|
|
421
|
+
if (sql === undefined)
|
|
422
|
+
return undefined;
|
|
423
|
+
return tsNameBySql.get(sql) ?? sql;
|
|
424
|
+
};
|
|
425
|
+
let match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+IS\\s+NOT\\s+NULL$`, "i"));
|
|
426
|
+
if (match) {
|
|
427
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
428
|
+
if (!ts)
|
|
429
|
+
return undefined;
|
|
430
|
+
return {
|
|
431
|
+
kind: "col",
|
|
432
|
+
ts,
|
|
433
|
+
expr: "{ isNotNull: true }",
|
|
434
|
+
ops: { isNotNull: "true" },
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+IS\\s+NULL$`, "i"));
|
|
438
|
+
if (match) {
|
|
439
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
440
|
+
if (!ts)
|
|
441
|
+
return undefined;
|
|
442
|
+
return { kind: "col", ts, expr: "null" };
|
|
443
|
+
}
|
|
444
|
+
match = text.match(new RegExp(`^LOWER\\(${INDEX_ATOM_COL}\\)\\s*=\\s*LOWER\\((?<lit>[\\s\\S]+)\\)$`, "i"));
|
|
445
|
+
if (match) {
|
|
446
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
447
|
+
const lit = match.groups?.lit
|
|
448
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
449
|
+
: { ok: false };
|
|
450
|
+
if (!ts || !lit.ok)
|
|
451
|
+
return undefined;
|
|
452
|
+
return {
|
|
453
|
+
kind: "col",
|
|
454
|
+
ts,
|
|
455
|
+
expr: `{ equals: ${lit.ts}, mode: "insensitive" }`,
|
|
456
|
+
ops: { equals: lit.ts, mode: '"insensitive"' },
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
match = text.match(new RegExp(`^LOWER\\(${INDEX_ATOM_COL}\\)\\s+LIKE\\s+LOWER\\((?<lit>[\\s\\S]+?)\\)(?:\\s+ESCAPE\\s+'(?:\\\\|\\\\\\\\)')?$`, "i"));
|
|
460
|
+
if (match) {
|
|
461
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
462
|
+
const lit = match.groups?.lit
|
|
463
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
464
|
+
: { ok: false };
|
|
465
|
+
if (!ts || !lit.ok)
|
|
466
|
+
return undefined;
|
|
467
|
+
const raw = match.groups?.lit?.trim() ?? "";
|
|
468
|
+
const inner = raw.startsWith("'")
|
|
469
|
+
? raw.slice(1, -1).replace(/''/g, "'")
|
|
470
|
+
: undefined;
|
|
471
|
+
if (inner === undefined)
|
|
472
|
+
return undefined;
|
|
473
|
+
const mapped = likePatternToOp(inner);
|
|
474
|
+
if (!mapped)
|
|
475
|
+
return undefined;
|
|
476
|
+
return {
|
|
477
|
+
kind: "col",
|
|
478
|
+
ts,
|
|
479
|
+
expr: `{ ${mapped.op}: "${escapeTsString(mapped.value)}", mode: "insensitive" }`,
|
|
480
|
+
ops: {
|
|
481
|
+
[mapped.op]: `"${escapeTsString(mapped.value)}"`,
|
|
482
|
+
mode: '"insensitive"',
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+NOT\\s+IN\\s*\\((?<list>[\\s\\S]+)\\)$`, "i"));
|
|
487
|
+
if (match) {
|
|
488
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
489
|
+
const list = parseIndexLiteralList(match.groups?.list ?? "");
|
|
490
|
+
if (!ts || !list)
|
|
491
|
+
return undefined;
|
|
492
|
+
return {
|
|
493
|
+
kind: "col",
|
|
494
|
+
ts,
|
|
495
|
+
expr: `{ notIn: [${list.join(", ")}] }`,
|
|
496
|
+
ops: { notIn: `[${list.join(", ")}]` },
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+IN\\s*\\((?<list>[\\s\\S]+)\\)$`, "i"));
|
|
500
|
+
if (match) {
|
|
501
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
502
|
+
const list = parseIndexLiteralList(match.groups?.list ?? "");
|
|
503
|
+
if (!ts || !list)
|
|
504
|
+
return undefined;
|
|
505
|
+
return {
|
|
506
|
+
kind: "col",
|
|
507
|
+
ts,
|
|
508
|
+
expr: `{ in: [${list.join(", ")}] }`,
|
|
509
|
+
ops: { in: `[${list.join(", ")}]` },
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+LIKE\\s+(?<lit>'(?:[^']|'')*')\\s*(?:ESCAPE\\s+'(?:\\\\|\\\\\\\\)')?$`, "i"));
|
|
513
|
+
if (match) {
|
|
514
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
515
|
+
const lit = match.groups?.lit
|
|
516
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
517
|
+
: { ok: false };
|
|
518
|
+
if (!ts || !lit.ok)
|
|
519
|
+
return undefined;
|
|
520
|
+
const raw = (match.groups?.lit ?? "").slice(1, -1).replace(/''/g, "'");
|
|
521
|
+
const mapped = likePatternToOp(raw);
|
|
522
|
+
if (!mapped)
|
|
523
|
+
return undefined;
|
|
524
|
+
return {
|
|
525
|
+
kind: "col",
|
|
526
|
+
ts,
|
|
527
|
+
expr: `{ ${mapped.op}: "${escapeTsString(mapped.value)}" }`,
|
|
528
|
+
ops: { [mapped.op]: `"${escapeTsString(mapped.value)}"` },
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s*(?<op>~\\*|~)\\s*(?<lit>'(?:[^']|'')*')$`, "i"));
|
|
532
|
+
if (match) {
|
|
533
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
534
|
+
const lit = match.groups?.lit
|
|
535
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
536
|
+
: { ok: false };
|
|
537
|
+
if (!ts || !lit.ok)
|
|
538
|
+
return undefined;
|
|
539
|
+
const insensitive = match.groups?.op === "~*";
|
|
540
|
+
return {
|
|
541
|
+
kind: "col",
|
|
542
|
+
ts,
|
|
543
|
+
expr: insensitive
|
|
544
|
+
? `{ search: ${lit.ts}, mode: "insensitive" }`
|
|
545
|
+
: `{ search: ${lit.ts} }`,
|
|
546
|
+
ops: insensitive
|
|
547
|
+
? { search: lit.ts, mode: '"insensitive"' }
|
|
548
|
+
: { search: lit.ts },
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s+REGEXP\\s+(?<lit>'(?:[^']|'')*')$`, "i"));
|
|
552
|
+
if (match) {
|
|
553
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
554
|
+
const lit = match.groups?.lit
|
|
555
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
556
|
+
: { ok: false };
|
|
557
|
+
if (!ts || !lit.ok)
|
|
558
|
+
return undefined;
|
|
559
|
+
return {
|
|
560
|
+
kind: "col",
|
|
561
|
+
ts,
|
|
562
|
+
expr: `{ search: ${lit.ts} }`,
|
|
563
|
+
ops: { search: lit.ts },
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
match = text.match(new RegExp(`^regexp_i\\((?<lit>'(?:[^']|'')*')\\s*,\\s*${INDEX_ATOM_COL}\\)$`, "i"));
|
|
567
|
+
if (match) {
|
|
568
|
+
const allGroups = match.groups ?? {};
|
|
569
|
+
const colSql = allGroups.q1 ?? allGroups.q2;
|
|
570
|
+
const ts = tsOf(colSql);
|
|
571
|
+
const lit = match.groups?.lit
|
|
572
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
573
|
+
: { ok: false };
|
|
574
|
+
if (!ts || !lit.ok)
|
|
575
|
+
return undefined;
|
|
576
|
+
return {
|
|
577
|
+
kind: "col",
|
|
578
|
+
ts,
|
|
579
|
+
expr: `{ search: ${lit.ts}, mode: "insensitive" }`,
|
|
580
|
+
ops: { search: lit.ts, mode: '"insensitive"' },
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
match = text.match(new RegExp(`^${INDEX_ATOM_COL}\\s*(?<op>=|<>|!=|>=|<=|>|<)\\s*(?<lit>[\\s\\S]+)$`, "i"));
|
|
584
|
+
if (match) {
|
|
585
|
+
const ts = tsOf(match.groups?.q1 ?? match.groups?.q2);
|
|
586
|
+
const lit = match.groups?.lit
|
|
587
|
+
? parseIndexLiteralValue(match.groups.lit)
|
|
588
|
+
: { ok: false };
|
|
589
|
+
if (!ts || !lit.ok)
|
|
590
|
+
return undefined;
|
|
591
|
+
const op = match.groups?.op ?? "=";
|
|
592
|
+
if (op === "=") {
|
|
593
|
+
if (lit.ts === "1" || lit.ts === "0") {
|
|
594
|
+
return {
|
|
595
|
+
kind: "col",
|
|
596
|
+
ts,
|
|
597
|
+
expr: lit.ts === "1" ? "true" : "false",
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
if (lit.ts === "true" || lit.ts === "false") {
|
|
601
|
+
return { kind: "col", ts, expr: lit.ts };
|
|
602
|
+
}
|
|
603
|
+
return { kind: "col", ts, expr: lit.ts };
|
|
604
|
+
}
|
|
605
|
+
if (op === "<>" || op === "!=") {
|
|
606
|
+
return {
|
|
607
|
+
kind: "not",
|
|
608
|
+
item: { kind: "col", ts, expr: lit.ts },
|
|
609
|
+
};
|
|
272
610
|
}
|
|
273
|
-
|
|
274
|
-
|
|
611
|
+
const opName = op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : "lte";
|
|
612
|
+
return {
|
|
613
|
+
kind: "col",
|
|
614
|
+
ts,
|
|
615
|
+
expr: `{ ${opName}: ${lit.ts} }`,
|
|
616
|
+
ops: { [opName]: lit.ts },
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
function parseIndexLiteralList(listSql) {
|
|
622
|
+
const items = [];
|
|
623
|
+
let depth = 0;
|
|
624
|
+
let inStr = false;
|
|
625
|
+
let current = "";
|
|
626
|
+
for (let i = 0; i < listSql.length; i++) {
|
|
627
|
+
const ch = listSql[i];
|
|
628
|
+
if (inStr) {
|
|
629
|
+
current += ch;
|
|
630
|
+
if (ch === "'") {
|
|
631
|
+
if (listSql[i + 1] === "'") {
|
|
632
|
+
current += "'";
|
|
633
|
+
i++;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
inStr = false;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
continue;
|
|
275
640
|
}
|
|
276
|
-
|
|
277
|
-
|
|
641
|
+
if (ch === "'") {
|
|
642
|
+
inStr = true;
|
|
643
|
+
current += ch;
|
|
644
|
+
continue;
|
|
278
645
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
646
|
+
if (ch === "(")
|
|
647
|
+
depth++;
|
|
648
|
+
if (ch === ")")
|
|
649
|
+
depth--;
|
|
650
|
+
if (ch === "," && depth === 0) {
|
|
651
|
+
const parsed = parseIndexLiteralValue(current);
|
|
652
|
+
if (!parsed.ok)
|
|
653
|
+
return undefined;
|
|
654
|
+
if (parsed.ts === "1" || parsed.ts === "0")
|
|
655
|
+
return undefined;
|
|
656
|
+
items.push(parsed.ts);
|
|
657
|
+
current = "";
|
|
658
|
+
continue;
|
|
282
659
|
}
|
|
283
|
-
|
|
660
|
+
current += ch;
|
|
661
|
+
}
|
|
662
|
+
if (inStr || depth !== 0)
|
|
663
|
+
return undefined;
|
|
664
|
+
if (current.trim().length > 0) {
|
|
665
|
+
const parsed = parseIndexLiteralValue(current);
|
|
666
|
+
if (!parsed.ok)
|
|
667
|
+
return undefined;
|
|
668
|
+
if (parsed.ts === "1" || parsed.ts === "0")
|
|
669
|
+
return undefined;
|
|
670
|
+
items.push(parsed.ts);
|
|
671
|
+
}
|
|
672
|
+
if (items.length === 0)
|
|
673
|
+
return undefined;
|
|
674
|
+
return items;
|
|
675
|
+
}
|
|
676
|
+
function parseIndexConjunction(sql, tsNameBySql) {
|
|
677
|
+
const text = stripOuterParens(sql);
|
|
678
|
+
const orBranches = splitTopLevel(text, "OR");
|
|
679
|
+
if (orBranches) {
|
|
680
|
+
const items = [];
|
|
681
|
+
for (const branch of orBranches) {
|
|
682
|
+
const parsed = parseIndexConjunction(branch, tsNameBySql);
|
|
683
|
+
if (!parsed)
|
|
684
|
+
return undefined;
|
|
685
|
+
items.push(parsed);
|
|
686
|
+
}
|
|
687
|
+
return { kind: "or", items };
|
|
688
|
+
}
|
|
689
|
+
const andBranches = splitTopLevel(text, "AND");
|
|
690
|
+
if (andBranches) {
|
|
691
|
+
const items = [];
|
|
692
|
+
for (const branch of andBranches) {
|
|
693
|
+
const parsed = parseIndexConjunction(branch, tsNameBySql);
|
|
694
|
+
if (!parsed)
|
|
695
|
+
return undefined;
|
|
696
|
+
items.push(parsed);
|
|
697
|
+
}
|
|
698
|
+
return { kind: "and", items };
|
|
699
|
+
}
|
|
700
|
+
return parseIndexAtom(text, tsNameBySql);
|
|
701
|
+
}
|
|
702
|
+
function renderIndexPredicate(node) {
|
|
703
|
+
switch (node.kind) {
|
|
704
|
+
case "and": {
|
|
705
|
+
const bodies = conjunctBodies(node.items);
|
|
706
|
+
if (!bodies)
|
|
707
|
+
return undefined;
|
|
708
|
+
if (bodies.length === 1)
|
|
709
|
+
return bodies[0];
|
|
710
|
+
return `AND: [${bodies.map((body) => `{ ${body} }`).join(", ")}]`;
|
|
711
|
+
}
|
|
712
|
+
case "or": {
|
|
713
|
+
const elements = [];
|
|
714
|
+
for (const item of node.items) {
|
|
715
|
+
if (item.kind === "and") {
|
|
716
|
+
const bodies = conjunctBodies(item.items);
|
|
717
|
+
if (!bodies)
|
|
718
|
+
return undefined;
|
|
719
|
+
for (const body of bodies)
|
|
720
|
+
elements.push(`{ ${body} }`);
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
const inner = renderIndexPredicate(item);
|
|
724
|
+
if (!inner)
|
|
725
|
+
return undefined;
|
|
726
|
+
elements.push(`{ ${inner} }`);
|
|
727
|
+
}
|
|
728
|
+
return `OR: [${elements.join(", ")}]`;
|
|
729
|
+
}
|
|
730
|
+
case "not": {
|
|
731
|
+
const inner = renderIndexPredicate(node.item);
|
|
732
|
+
if (!inner)
|
|
733
|
+
return undefined;
|
|
734
|
+
return `NOT: { ${inner} }`;
|
|
735
|
+
}
|
|
736
|
+
case "col":
|
|
737
|
+
return `${node.ts}: ${node.expr}`;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
/** Group an AND level into where-object bodies, merging same-column operators. */
|
|
741
|
+
function conjunctBodies(items) {
|
|
742
|
+
const bodies = [];
|
|
743
|
+
const pending = new Map();
|
|
744
|
+
const order = [];
|
|
745
|
+
const flush = () => {
|
|
746
|
+
if (order.length === 0)
|
|
747
|
+
return;
|
|
748
|
+
bodies.push(order
|
|
749
|
+
.map((ts) => `${ts}: ${pending.get(ts)?.expr ?? ""}`)
|
|
750
|
+
.join(", "));
|
|
751
|
+
pending.clear();
|
|
752
|
+
order.length = 0;
|
|
753
|
+
};
|
|
754
|
+
for (const item of items) {
|
|
755
|
+
if (item.kind === "col") {
|
|
756
|
+
const incomingOps = item.ops ??
|
|
757
|
+
(item.expr === "null"
|
|
758
|
+
? { equals: "null" }
|
|
759
|
+
: { equals: item.expr });
|
|
760
|
+
const existing = pending.get(item.ts);
|
|
761
|
+
if (!existing) {
|
|
762
|
+
pending.set(item.ts, item.ops
|
|
763
|
+
? { expr: item.expr, ops: incomingOps }
|
|
764
|
+
: { expr: item.expr, ops: incomingOps });
|
|
765
|
+
order.push(item.ts);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (!existing.ops)
|
|
769
|
+
return undefined;
|
|
770
|
+
const merged = { ...existing.ops };
|
|
771
|
+
for (const [op, val] of Object.entries(incomingOps)) {
|
|
772
|
+
if (op in merged)
|
|
773
|
+
return undefined;
|
|
774
|
+
merged[op] = val;
|
|
775
|
+
}
|
|
776
|
+
pending.set(item.ts, {
|
|
777
|
+
expr: `{ ${Object.entries(merged)
|
|
778
|
+
.map(([op, val]) => `${op}: ${val}`)
|
|
779
|
+
.join(", ")} }`,
|
|
780
|
+
ops: merged,
|
|
781
|
+
});
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
flush();
|
|
785
|
+
const rendered = renderIndexPredicate(item);
|
|
786
|
+
if (!rendered)
|
|
787
|
+
return undefined;
|
|
788
|
+
bodies.push(rendered);
|
|
789
|
+
}
|
|
790
|
+
flush();
|
|
791
|
+
return bodies;
|
|
792
|
+
}
|
|
793
|
+
export function parseIndexWhere(whereSql, tsNameBySql) {
|
|
794
|
+
const parsed = parseIndexConjunction(whereSql, tsNameBySql);
|
|
795
|
+
if (!parsed)
|
|
796
|
+
return undefined;
|
|
797
|
+
if (parsed.kind === "col") {
|
|
798
|
+
return `.where({ ${parsed.ts}: ${parsed.expr} })`;
|
|
799
|
+
}
|
|
800
|
+
if (parsed.kind === "and") {
|
|
801
|
+
const bodies = conjunctBodies(parsed.items);
|
|
802
|
+
if (!bodies || bodies.length === 0)
|
|
284
803
|
return undefined;
|
|
804
|
+
if (bodies.length === 1) {
|
|
805
|
+
return `.where({ ${bodies[0]} })`;
|
|
285
806
|
}
|
|
286
|
-
|
|
807
|
+
return `.where({ AND: [${bodies.map((body) => `{ ${body} }`).join(", ")}] })`;
|
|
287
808
|
}
|
|
288
|
-
|
|
809
|
+
const rendered = renderIndexPredicate(parsed);
|
|
810
|
+
if (!rendered)
|
|
289
811
|
return undefined;
|
|
290
|
-
return `.where({ ${
|
|
812
|
+
return `.where({ ${rendered} })`;
|
|
291
813
|
}
|
|
292
814
|
function emitTableExtras(table, tsNameBySql, usedBuilders, manifest) {
|
|
293
815
|
const extras = [];
|
|
@@ -325,7 +847,7 @@ function emitTableExtras(table, tsNameBySql, usedBuilders, manifest) {
|
|
|
325
847
|
}
|
|
326
848
|
}
|
|
327
849
|
if (index.whereSql) {
|
|
328
|
-
const where =
|
|
850
|
+
const where = parseIndexWhere(index.whereSql, tsNameBySql);
|
|
329
851
|
if (where)
|
|
330
852
|
extra += where;
|
|
331
853
|
}
|