@sqlanvil/cli 1.36.2 → 1.36.3

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/README.md CHANGED
@@ -12,7 +12,7 @@ authority for the version you have installed: `sqlanvil help` lists the commands
12
12
 
13
13
  ```bash
14
14
  npm install -g @sqlanvil/cli
15
- sqlanvil --version # sqlanvil 1.36.2 (Dataform core 3.1.2)
15
+ sqlanvil --version # sqlanvil 1.36.3 (Dataform core 3.1.2)
16
16
  ```
17
17
 
18
18
  Requires Node.js 20.19+ or 22.12+. `--version` reports the sqlanvil release and the Dataform core
package/bundle.js CHANGED
@@ -55124,7 +55124,7 @@ function requireVersion () {
55124
55124
  hasRequiredVersion = 1;
55125
55125
  Object.defineProperty(version, "__esModule", { value: true });
55126
55126
  version.dataformVersion = version.version = void 0;
55127
- version.version = "1.36.2";
55127
+ version.version = "1.36.3";
55128
55128
  version.dataformVersion = "3.1.2";
55129
55129
 
55130
55130
  return version;
@@ -62482,6 +62482,7 @@ function requireSql_lex () {
62482
62482
  hasRequiredSql_lex = 1;
62483
62483
  Object.defineProperty(sql_lex, "__esModule", { value: true });
62484
62484
  sql_lex.tokenize = tokenize;
62485
+ sql_lex.interpolationSpans = interpolationSpans;
62485
62486
  sql_lex.significant = significant;
62486
62487
  sql_lex.isWord = isWord;
62487
62488
  sql_lex.matchBracket = matchBracket;
@@ -62504,16 +62505,7 @@ function requireSql_lex () {
62504
62505
  }
62505
62506
  if (ch === "$" && sql[i + 1] === "{") {
62506
62507
  const start = i;
62507
- let depth = 0;
62508
- for (; i < sql.length; i++) {
62509
- if (sql[i] === "{") {
62510
- depth++;
62511
- }
62512
- else if (sql[i] === "}" && --depth === 0) {
62513
- i++;
62514
- break;
62515
- }
62516
- }
62508
+ i = templateEnd(sql, i);
62517
62509
  push("template", start, i);
62518
62510
  continue;
62519
62511
  }
@@ -62552,6 +62544,10 @@ function requireSql_lex () {
62552
62544
  const quote = sql[i];
62553
62545
  i++;
62554
62546
  for (; i < sql.length; i++) {
62547
+ if (sql[i] === "$" && sql[i + 1] === "{") {
62548
+ i = templateEnd(sql, i) - 1;
62549
+ continue;
62550
+ }
62555
62551
  if (sql[i] !== quote) {
62556
62552
  continue;
62557
62553
  }
@@ -62598,6 +62594,36 @@ function requireSql_lex () {
62598
62594
  }
62599
62595
  return tokens;
62600
62596
  }
62597
+ function templateEnd(sql, start) {
62598
+ let depth = 0;
62599
+ for (let i = start; i < sql.length; i++) {
62600
+ if (sql[i] === "{") {
62601
+ depth++;
62602
+ }
62603
+ else if (sql[i] === "}" && --depth === 0) {
62604
+ return i + 1;
62605
+ }
62606
+ }
62607
+ return sql.length;
62608
+ }
62609
+ function interpolationSpans(sql) {
62610
+ const spans = [];
62611
+ for (const t of tokenize(sql)) {
62612
+ if (t.kind === "template") {
62613
+ spans.push({ start: t.start, end: t.end });
62614
+ }
62615
+ else if (t.kind === "string" || t.kind === "quoted-ident") {
62616
+ for (let i = t.start; i < t.end; i++) {
62617
+ if (sql[i] === "$" && sql[i + 1] === "{") {
62618
+ const end = Math.min(templateEnd(sql, i), t.end);
62619
+ spans.push({ start: i, end });
62620
+ i = end - 1;
62621
+ }
62622
+ }
62623
+ }
62624
+ }
62625
+ return spans;
62626
+ }
62601
62627
  function significant(tokens) {
62602
62628
  return tokens.filter((t) => t.kind !== "whitespace" && t.kind !== "comment");
62603
62629
  }
@@ -63637,11 +63663,22 @@ ${description} columnTypes: {
63637
63663
  rewrite: (args) => (args.length === 1 ? `extract(epoch from ${args[0]})::bigint` : null),
63638
63664
  },
63639
63665
  ];
63666
+ function callRuleView(text, isRewritableOffset) {
63667
+ const chars = Array.from(text, (c, i) => (c === "\n" || isRewritableOffset(i) ? c : " "));
63668
+ for (const t of (0, sql_lex_1.tokenize)(chars.join(""))) {
63669
+ if (t.kind === "quoted-ident" && !t.text.includes("\n")) {
63670
+ for (let i = t.start + 1; i < t.end - 1; i++) {
63671
+ chars[i] = " ";
63672
+ }
63673
+ }
63674
+ }
63675
+ return chars.join("");
63676
+ }
63640
63677
  function applyCallRules(text, isRewritableOffset) {
63641
63678
  const applied = [];
63642
63679
  let result = text;
63643
63680
  for (let pass = 0; pass < 10; pass++) {
63644
- const mask = maskSql(result);
63681
+ const mask = maskSql(callRuleView(result, isRewritableOffset));
63645
63682
  const sites = [];
63646
63683
  for (const rule of CALL_RULES) {
63647
63684
  const finder = new RegExp(`\\b${rule.name}\\s*\\(`, "gi");
@@ -63704,9 +63741,65 @@ ${description} columnTypes: {
63704
63741
  }
63705
63742
  return -1;
63706
63743
  }
63707
- function applyTextRules(text, isSqlOffset, applied) {
63744
+ function blankSpans(text, spans) {
63745
+ if (spans.length === 0) {
63746
+ return text;
63747
+ }
63748
+ return Array.from(text, (c, i) => c !== "\n" && spans.some((s) => i >= s.start && i < s.end) ? " " : c).join("");
63749
+ }
63750
+ const DQ_STRING_NOTE = "BigQuery \"string\" → 'string' (double quotes are identifiers in PostgreSQL)";
63751
+ const TRIPLE_QUOTED_NOTE = 'BigQuery """string""" → $sa$string$sa$ (PostgreSQL has no triple-quoted strings)';
63752
+ function convertDoubleQuotedStrings(text, isSqlOffset, note) {
63753
+ const chars = Array.from(text, (c, i) => (c === "\n" || isSqlOffset(i) ? c : " "));
63754
+ const edits = [];
63755
+ const triple = /"""[\s\S]*?"""/g;
63756
+ let m;
63757
+ while ((m = triple.exec(chars.join(""))) !== null) {
63758
+ const inner = text.slice(m.index + 3, m.index + m[0].length - 3);
63759
+ let tag = "$sa$";
63760
+ for (let n = 1; inner.includes(tag); n++) {
63761
+ tag = `$sa${n}$`;
63762
+ }
63763
+ edits.push({ start: m.index, end: m.index + m[0].length, out: `${tag}${inner}${tag}` });
63764
+ for (let i = m.index; i < m.index + m[0].length; i++) {
63765
+ chars[i] = chars[i] === "\n" ? "\n" : " ";
63766
+ }
63767
+ triple.lastIndex = m.index + m[0].length;
63768
+ }
63769
+ const toks = (0, sql_lex_1.significant)((0, sql_lex_1.tokenize)(chars.join("")));
63770
+ toks.forEach((t, k) => {
63771
+ if (t.kind !== "quoted-ident" || t.text.length < 2 || !t.text.endsWith('"')) {
63772
+ return;
63773
+ }
63774
+ if (t.text.includes("\n")) {
63775
+ return;
63776
+ }
63777
+ if ((0, sql_lex_1.isWord)(toks[k - 1], "as")) {
63778
+ return;
63779
+ }
63780
+ let inner = "";
63781
+ for (let i = t.start + 1; i < t.end - 1; i++) {
63782
+ if (text[i] === "\\" && text[i + 1] === "'" && isSqlOffset(i)) {
63783
+ inner += "''";
63784
+ i++;
63785
+ }
63786
+ else {
63787
+ inner += text[i] === "'" && isSqlOffset(i) ? "''" : text[i];
63788
+ }
63789
+ }
63790
+ edits.push({ start: t.start, end: t.end, out: `'${inner}'` });
63791
+ });
63792
+ let out = text;
63793
+ for (const e of edits.sort((a, b) => b.start - a.start)) {
63794
+ note(e.start, e.out.startsWith("$") ? TRIPLE_QUOTED_NOTE : DQ_STRING_NOTE);
63795
+ out = out.slice(0, e.start) + e.out + out.slice(e.end);
63796
+ }
63797
+ return out;
63798
+ }
63799
+ function applyTextRules(text, sqlOffsetsOf, applied) {
63708
63800
  const note = (offset, n) => applied.push({ line: text.slice(0, offset).split("\n").length, note: n });
63709
63801
  const rewrite = (src, mask, pattern, replace, ruleNote) => {
63802
+ const isSqlOffset = sqlOffsetsOf(src);
63710
63803
  const edits = [];
63711
63804
  let m;
63712
63805
  pattern.lastIndex = 0;
@@ -63729,18 +63822,13 @@ ${description} columnTypes: {
63729
63822
  let result = text;
63730
63823
  result = rewrite(result, maskSql(result, true), /(?<![\w"'])#[^\n]*/g, (m) => `--${m[0].slice(1)}`, "# line comment → -- (PostgreSQL has no # comment)");
63731
63824
  result = rewrite(result, maskSql(result, true), /(?<![A-Za-z0-9_])[rR](?=['"])/g, () => "", "r'…' raw string → '…' (no raw-string prefix in PostgreSQL)");
63732
- result = rewrite(result, maskSql(result, true), /(\bas\s+)?"([^"\n]*)"/gi, (m) => {
63733
- if (m[1]) {
63734
- return null;
63735
- }
63736
- return m[2].includes("${") || m[2].includes("'") ? null : `'${m[2]}'`;
63737
- }, "BigQuery \"string\" → 'string' (double quotes are identifiers in PostgreSQL)");
63825
+ result = convertDoubleQuotedStrings(result, sqlOffsetsOf(result), note);
63738
63826
  result = rewrite(result, maskSql(result), /`([^`\n]+)`/g, (m) => (m[1].includes(".") || m[1].includes("${") ? null : `"${m[1]}"`), '`identifier` → "identifier"');
63739
63827
  result = rewrite(result, maskSql(result, true), /\s+collate\s+(''|"")/gi, () => "", "x COLLATE '' → x (collation stripping is a no-op in PostgreSQL)");
63740
63828
  for (;;) {
63741
63829
  const mask = maskSql(result);
63742
63830
  const m = /\bextract\s*\(\s*dayofweek\s+from\s+/i.exec(mask);
63743
- if (!m || !isSqlOffset(m.index)) {
63831
+ if (!m || !sqlOffsetsOf(result)(m.index)) {
63744
63832
  break;
63745
63833
  }
63746
63834
  const close = matchParen(mask, mask.indexOf("(", m.index));
@@ -63752,7 +63840,7 @@ ${description} columnTypes: {
63752
63840
  result =
63753
63841
  result.slice(0, m.index) + `(extract(dow from ${inner}) + 1)` + result.slice(close + 1);
63754
63842
  }
63755
- return rewriteUnenforcedConstraints(result, isSqlOffset, applied);
63843
+ return rewriteUnenforcedConstraints(result, sqlOffsetsOf(result), applied);
63756
63844
  }
63757
63845
  const UNENFORCED_HEADER = [
63758
63846
  "BigQuery declared the constraint below NOT ENFORCED — advisory metadata for BI tools,",
@@ -63869,6 +63957,13 @@ ${description} columnTypes: {
63869
63957
  const dialectPass = dialect === "postgres";
63870
63958
  let span = jsMode ? null : findConfigBlock(source);
63871
63959
  const findings = [];
63960
+ const configAndJsSpans = (text) => {
63961
+ if (jsMode) {
63962
+ return [{ start: 0, end: text.length }];
63963
+ }
63964
+ const config = findConfigBlock(text);
63965
+ return [...(config ? [{ start: config.start, end: config.end }] : []), ...jsBlockSpans(text)];
63966
+ };
63872
63967
  if (dialectPass) {
63873
63968
  const outsideConfig = (off) => {
63874
63969
  const s = jsMode ? null : findConfigBlock(source);
@@ -63882,11 +63977,12 @@ ${description} columnTypes: {
63882
63977
  findings.push({ line: a.line, kind: "rewrite", note: a.note, class: classIdFor(a.note) });
63883
63978
  }
63884
63979
  const textApplied = [];
63885
- const jsSpans = jsMode ? [{ start: 0, end: source.length }] : jsBlockSpans(source);
63886
- const templates = (0, sql_lex_1.tokenize)(source).filter((t) => t.kind === "template");
63887
- source = applyTextRules(source, (off) => outsideConfig(off) &&
63888
- !jsSpans.some((s) => off >= s.start && off < s.end) &&
63889
- !templates.some((t) => off >= t.start && off < t.end), textApplied);
63980
+ source = applyTextRules(source, (text) => {
63981
+ const notSql = configAndJsSpans(text);
63982
+ const templates = (0, sql_lex_1.interpolationSpans)(blankSpans(text, notSql));
63983
+ return (off) => !notSql.some((s) => off >= s.start && off < s.end) &&
63984
+ !templates.some((t) => off >= t.start && off < t.end);
63985
+ }, textApplied);
63890
63986
  for (const a of textApplied) {
63891
63987
  findings.push({
63892
63988
  line: a.line,
@@ -63899,6 +63995,20 @@ ${description} columnTypes: {
63899
63995
  }
63900
63996
  const lines = source.split("\n");
63901
63997
  const jsLines = jsMode ? new Set() : jsBlockLines(source);
63998
+ if (!jsMode) {
63999
+ const sql = blankSpans(source, configAndJsSpans(source));
64000
+ const spans = [
64001
+ ...(0, sql_lex_1.interpolationSpans)(sql),
64002
+ ...(0, sql_lex_1.tokenize)(sql).filter((t) => t.kind === "string" || t.kind === "quoted-ident"),
64003
+ ];
64004
+ let lineStart = 0;
64005
+ lines.forEach((line, i) => {
64006
+ if (spans.some((s) => lineStart > s.start && lineStart < s.end)) {
64007
+ jsLines.add(i);
64008
+ }
64009
+ lineStart += line.length + 1;
64010
+ });
64011
+ }
63902
64012
  let configStartLine = -1;
63903
64013
  let configEndLine = -1;
63904
64014
  if (span) {
package/package.json CHANGED
@@ -90,7 +90,7 @@
90
90
  "engines": {
91
91
  "node": "^20.19.0 || >=22.12.0"
92
92
  },
93
- "version": "1.36.2",
93
+ "version": "1.36.3",
94
94
  "name": "@sqlanvil/cli",
95
95
  "description": "sqlanvil command line interface.",
96
96
  "main": "bundle.js"
package/._LICENSE DELETED
Binary file
package/._NOTICE DELETED
Binary file
package/._README.md DELETED
Binary file
package/._bundle.js DELETED
Binary file
package/._package.json DELETED
Binary file
Binary file