biz-a-cli 2.3.80-15357 → 2.3.80-15359

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.
@@ -521,8 +521,7 @@ const coerceOne = (table, column, value, dbIndex) => {
521
521
  };
522
522
 
523
523
  /* `INSERT INTO t (a, b) VALUES (x, y)` — positional, so a column/value count mismatch means the
524
- alignment cannot be trusted and nothing is touched. An `INSERT … SELECT` has no list to align to
525
- and is left alone by construction. */
524
+ alignment cannot be trusted and nothing is touched. */
526
525
  const INSERT_VALUES = /^(INSERT\s+INTO\s+([\w$."]+)\s*\(([^)]*)\)\s*VALUES\s*\()([\s\S]*)(\)\s*)$/i;
527
526
  const coerceInsert = (statement, dbIndex) => {
528
527
  const match = INSERT_VALUES.exec(statement.trim());
@@ -534,6 +533,52 @@ const coerceInsert = (statement, dbIndex) => {
534
533
  return head + values.map((v, i) => coerceOne(table, columns[i], v, dbIndex)).join(",") + tail;
535
534
  };
536
535
 
536
+ /*
537
+ * `INSERT INTO t (a, b) SELECT <literals> [WHERE …]` — the guarded-insert idiom.
538
+ *
539
+ * ⚠️⚠️ THIS FORM WAS LEFT ALONE ON PURPOSE AND THAT WAS WRONG. The note here used to read "an
540
+ * `INSERT … SELECT` has no list to align to", which is true of a SELECT reading a real table and
541
+ * false of the shape applications actually write:
542
+ *
543
+ * INSERT INTO ST_ITEM_CATEGORY (ID, CODE, NAME, ACTIVE, …)
544
+ * SELECT '{…}', 'makanan', 'makanan', 1, NULL, NULL
545
+ * WHERE NOT EXISTS (SELECT 1 FROM ST_ITEM_CATEGORY WHERE ID = '{…}')
546
+ *
547
+ * That select list IS a fixed tuple aligned to the column list — which `stampAuthorshipLiterals`
548
+ * below had already concluded, and has been relying on for months, **on these very statements**.
549
+ * The two halves of this module simply disagreed about whether the form was safe to align, and QA
550
+ * found the disagreement: `column "active" is of type boolean but expression is of type integer`.
551
+ *
552
+ * ⚠️ THE SAFETY CONDITION IS BORROWED VERBATIM from that function, because it is the part that
553
+ * matters: nothing may read from a real table (no top-level FROM, once the translator has stripped
554
+ * Firebird's `FROM RDB$DATABASE`), and the tuple must split to exactly the column count. Outside
555
+ * those two conditions the alignment is a guess, and a wrong guess quotes somebody else's value.
556
+ */
557
+ const INSERT_SELECT = /^INSERT\s+INTO\s+([\w$."]+)\s*\(([^)]*)\)\s*(SELECT\s[\s\S]*)$/i;
558
+ const coerceInsertSelect = (statement, dbIndex) => {
559
+ const match = INSERT_SELECT.exec(statement.trim());
560
+ if (!match) return statement;
561
+ const [, table, columnText, rest] = match;
562
+
563
+ /* ⚠️ Reading a real table means the select list is expressions, not a tuple. */
564
+ if (topLevelIndexOf(rest, "FROM") !== -1) return statement;
565
+
566
+ const whereAt = topLevelIndexOf(rest, "WHERE");
567
+ const selectList = whereAt === -1 ? rest.slice("SELECT".length) : rest.slice("SELECT".length, whereAt);
568
+ /* whereAt points AT the keyword, so the separating space belongs to the tail. */
569
+ const tail = whereAt === -1 ? "" : " " + rest.slice(whereAt);
570
+
571
+ const columns = columnText.split(",").map((c) => c.trim().replace(/^"|"$/g, ""));
572
+ const values = splitValueList(selectList);
573
+ if (columns.length !== values.length) return statement;
574
+
575
+ return (
576
+ "INSERT INTO " + table + " (" + columnText + ") SELECT" +
577
+ values.map((v, i) => coerceOne(table, columns[i], v, dbIndex)).join(",") +
578
+ tail
579
+ );
580
+ };
581
+
537
582
  /* `UPDATE t SET a = x, b = y [WHERE …]` — ASSIGNMENTS ONLY. The WHERE clause is a filter, handled
538
583
  where filters are built; rewriting it here too risks corrupting a comparison meant literally. */
539
584
  const UPDATE_SET = /^(UPDATE\s+([\w$."]+)\s+SET\s+)([\s\S]*?)(\s+WHERE[\s\S]*)?$/i;
@@ -687,7 +732,12 @@ const coerceBooleanLiterals = (statement, dbIndex) => {
687
732
  if (/^\s*INSERT\s/i.test(statement)) {
688
733
  /* ⚠️ Booleans FIRST, then the stamps. Coercion aligns values to the ORIGINAL column list, so
689
734
  appending first would shift every value one place and quote the wrong one. */
690
- return stampAuthorshipLiterals(coerceInsert(statement, dbIndex), dbIndex);
735
+ /* ⚠️ Both INSERT shapes, then the stamps. Coercion aligns values to the ORIGINAL column
736
+ list, so appending the stamp columns first would shift every value one place. */
737
+ return stampAuthorshipLiterals(
738
+ coerceInsertSelect(coerceInsert(statement, dbIndex), dbIndex),
739
+ dbIndex,
740
+ );
691
741
  }
692
742
  if (/^\s*UPDATE\s/i.test(statement)) return coerceUpdate(statement, dbIndex);
693
743
  return statement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biz-a-cli",
3
- "version": "2.3.80-15357",
3
+ "version": "2.3.80-15359",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",