@tablecraft-app/plugin-postgres 1.10.4 → 1.12.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.
package/index.js CHANGED
@@ -10937,7 +10937,12 @@ var connectionUrlSchema = strictObject({
10937
10937
  string$1(),
10938
10938
  number$1(),
10939
10939
  boolean()
10940
- ]))).optional()
10940
+ ]))).optional(),
10941
+ /**
10942
+ * The JDBC SQL Server form, `scheme://host[\instance][:port][;name=value…]` (plan 055): parameters
10943
+ * follow `;`, a `{…}` value may hold one, and `\instance` is the parameter `instanceName`.
10944
+ */
10945
+ semicolonParams: boolean().optional()
10941
10946
  });
10942
10947
  /**
10943
10948
  * Which fields hold the server's address (decision 070), so the host can open an SSH tunnel and
@@ -11503,6 +11508,7 @@ var Q_QUOTE_PAIRS = /* @__PURE__ */ new Map([
11503
11508
  var STRING_QUOTE$1 = "'";
11504
11509
  var BRACKET_OPEN$1 = "[";
11505
11510
  var BRACKET_CLOSE$1 = "]";
11511
+ var VARIABLE_CHARS = /* @__PURE__ */ new Set(["@", "#"]);
11506
11512
  function isSpace(char) {
11507
11513
  return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f" || char === "\v";
11508
11514
  }
@@ -11544,7 +11550,7 @@ var Lexer = class {
11544
11550
  else if (char === "`") token = this.#quoted(at, "`", "quoted", false);
11545
11551
  else if (char === BRACKET_OPEN$1 && this.options.bracketIdentifiers === true) token = this.#bracketed(at);
11546
11552
  else if (char === "$" && this.options.dollarQuotes === true && this.#dollarTag(at) !== null) token = this.#dollarQuoted(at, this.#dollarTag(at) ?? "");
11547
- else if (isWordChar(char)) token = this.#word(at, delimiter);
11553
+ else if (this.#isWordChar(char)) token = this.#word(at, delimiter);
11548
11554
  else token = {
11549
11555
  kind: "symbol",
11550
11556
  from: at,
@@ -11553,6 +11559,9 @@ var Lexer = class {
11553
11559
  this.position = token.to;
11554
11560
  return token;
11555
11561
  }
11562
+ #isWordChar(char) {
11563
+ return isWordChar(char) || this.options.variables === true && VARIABLE_CHARS.has(char);
11564
+ }
11556
11565
  #lineComment(from) {
11557
11566
  const end = this.text.indexOf(NEWLINE$2, from);
11558
11567
  return {
@@ -11648,7 +11657,7 @@ var Lexer = class {
11648
11657
  const text = this.text;
11649
11658
  const customDelimiter = delimiter !== ";";
11650
11659
  let at = from;
11651
- while (at < text.length && isWordChar(text.charAt(at))) {
11660
+ while (at < text.length && this.#isWordChar(text.charAt(at))) {
11652
11661
  if (customDelimiter && at > from && text.startsWith(delimiter, at)) break;
11653
11662
  at += 1;
11654
11663
  }
@@ -12781,6 +12790,25 @@ function searchPathTarget(text, tokens) {
12781
12790
  candidates: names
12782
12791
  };
12783
12792
  }
12793
+ var ALTER_SESSION_PREFIX = [
12794
+ "ALTER",
12795
+ "SESSION",
12796
+ "SET",
12797
+ "CURRENT_SCHEMA",
12798
+ "="
12799
+ ];
12800
+ function alterSessionPrefix(text, tokens) {
12801
+ return ALTER_SESSION_PREFIX.every((word, at) => wordAt$1(text, tokens, at) === word);
12802
+ }
12803
+ function alterSessionTarget(text, tokens) {
12804
+ if (tokens.length !== ALTER_SESSION_PREFIX.length + 1 || !alterSessionPrefix(text, tokens)) return null;
12805
+ const token = tokens[ALTER_SESSION_PREFIX.length];
12806
+ const name = identifier$1(text, token);
12807
+ return name === null || token?.kind === "string" ? null : {
12808
+ kind: "schema",
12809
+ candidates: [name]
12810
+ };
12811
+ }
12784
12812
  var NAME_TOKENS = /* @__PURE__ */ new Set([
12785
12813
  "word",
12786
12814
  "quoted",
@@ -12836,7 +12864,8 @@ function searchPathListStart(text, tokens) {
12836
12864
  }
12837
12865
  /**
12838
12866
  * Where the caret at `offset` names a namespace a session-namespace statement of the dialect moves
12839
- * to: `USE |` or the elements of `SET search_path TO a, |` and `SET SCHEMA |`. Null anywhere else,
12867
+ * to: `USE |`, `ALTER SESSION SET CURRENT_SCHEMA = |` or the elements of `SET search_path TO a, |`
12868
+ * and `SET SCHEMA |`. Null anywhere else,
12840
12869
  * and after a name whose closing quote is typed.
12841
12870
  */
12842
12871
  function namespaceSlotAt(text, offset, dialect) {
@@ -12872,6 +12901,17 @@ function namespaceSlotAt(text, offset, dialect) {
12872
12901
  listed: []
12873
12902
  };
12874
12903
  }
12904
+ if (form === "alterSessionSchema") {
12905
+ if (prefix?.kind === "string" || before.length !== ALTER_SESSION_PREFIX.length) continue;
12906
+ if (!alterSessionPrefix(text, before)) continue;
12907
+ return {
12908
+ kind: "schema",
12909
+ from,
12910
+ to: offset,
12911
+ quote,
12912
+ listed: []
12913
+ };
12914
+ }
12875
12915
  const start = searchPathListStart(text, before);
12876
12916
  if (start === null) continue;
12877
12917
  if (start.single && before.length !== start.at) continue;
@@ -12896,14 +12936,25 @@ function namespaceTarget(sql, dialect) {
12896
12936
  if (forms.length === 0) return null;
12897
12937
  const tokens = significantTokens(sql, dialect).filter((token) => token.kind !== "delimiter");
12898
12938
  for (const form of forms) {
12899
- const target = form === "use" ? useTarget(sql, tokens) : searchPathTarget(sql, tokens);
12939
+ const target = TARGET_READERS[form](sql, tokens);
12900
12940
  if (target !== null) return target;
12901
12941
  }
12902
12942
  return null;
12903
12943
  }
12944
+ var TARGET_READERS = {
12945
+ use: useTarget,
12946
+ searchPath: searchPathTarget,
12947
+ alterSessionSchema: alterSessionTarget
12948
+ };
12904
12949
  var SWITCH_TARGETS = {
12905
12950
  use: "topLevel",
12906
- searchPath: "schema"
12951
+ searchPath: "schema",
12952
+ alterSessionSchema: "schema"
12953
+ };
12954
+ var SWITCH_STATEMENTS = {
12955
+ use: (quoted) => `USE ${quoted}`,
12956
+ searchPath: (quoted) => `SET search_path TO ${quoted}`,
12957
+ alterSessionSchema: (quoted) => `ALTER SESSION SET CURRENT_SCHEMA = ${quoted}`
12907
12958
  };
12908
12959
  /**
12909
12960
  * The console's namespace switch for a dialect moving its namespace by `form` (decision 060):
@@ -12917,7 +12968,7 @@ function namespaceSwitcher(form, quoteIdentifier) {
12917
12968
  statement(path) {
12918
12969
  const name = path.at(-1);
12919
12970
  if (name === void 0) throw new PluginError("statementFailed", "A namespace switch names no namespace; pick one from the list.");
12920
- return form === "use" ? `USE ${quoteIdentifier(name)}` : `SET search_path TO ${quoteIdentifier(name)}`;
12971
+ return SWITCH_STATEMENTS[form](quoteIdentifier(name));
12921
12972
  }
12922
12973
  };
12923
12974
  }
@@ -13420,6 +13471,8 @@ var BOOST = {
13420
13471
  keyword: -10
13421
13472
  };
13422
13473
  var UNQUOTED_WORD = "^[A-Za-z0-9_$]*$";
13474
+ /** Where `@` and `#` start a name (`variables`), the typed `@@` stays part of what is filtered. */
13475
+ var VARIABLE_WORD = "^[@#]*[A-Za-z0-9_$]*$";
13423
13476
  var ALIAS_WORD_BREAK = /[^A-Za-z0-9]+|(?<=[a-z0-9])(?=[A-Z])/;
13424
13477
  var FALLBACK_ALIAS = "t";
13425
13478
  var QUOTES = /* @__PURE__ */ new Set([
@@ -13477,6 +13530,7 @@ function relationKind(relation) {
13477
13530
  /** The shared completion and navigation every built-in plugin spreads into its `SqlLanguage` (decision 028). */
13478
13531
  function sqlCompleter(options) {
13479
13532
  const reserved = new Set(options.keywords.map((keyword) => keyword.toUpperCase()));
13533
+ const unquotedWord = options.dialect.variables === true ? VARIABLE_WORD : UNQUOTED_WORD;
13480
13534
  const { denotes, sameIdentifier, cteNamed, sourceNamed, serverOf, Lookup } = nameRules(options);
13481
13535
  const specs = /* @__PURE__ */ new Map();
13482
13536
  for (const spec of options.functions) {
@@ -13560,7 +13614,8 @@ function sqlCompleter(options) {
13560
13614
  }
13561
13615
  function aliasFor(name, taken) {
13562
13616
  const initials = name.split(ALIAS_WORD_BREAK).filter((word) => word !== "").map((word) => word.charAt(0).toLowerCase()).join("");
13563
- const base = initials === "" || !options.plainIdentifier.test(initials) ? FALLBACK_ALIAS : initials;
13617
+ const folded = options.identifierFolding === "upper" ? initials.toUpperCase() : initials;
13618
+ const base = initials === "" || !options.plainIdentifier.test(folded) ? FALLBACK_ALIAS : initials;
13564
13619
  let alias = base;
13565
13620
  for (let suffix = 1; taken.has(alias) || reserved.has(alias.toUpperCase()); suffix += 1) alias = `${base}${String(suffix)}`;
13566
13621
  taken.add(alias);
@@ -13585,7 +13640,7 @@ function sqlCompleter(options) {
13585
13640
  if (source.qualifier.length === 0 && cteNamed(scope.query, source.name) !== void 0) continue;
13586
13641
  const found = await lookup.relation(source.qualifier, source.name);
13587
13642
  if (found === null) continue;
13588
- const near = source.alias === null ? relationReference(found.path, found.relation.name, lookup.current) : quoted(source.alias.name);
13643
+ const near = source.alias === null ? relationReference(found.path, found.relation.name, lookup.current) : source.alias.quoted ? options.quoteIdentifier(source.alias.name) : source.alias.name;
13589
13644
  for (const key of found.relation.foreignKeys) {
13590
13645
  const alias = aliasFor(key.target.relation, new Set(taken));
13591
13646
  const on = key.columns.map((column, at) => `${alias}.${quoted(key.target.columns[at] ?? column)} = ${near}.${quoted(column)}`).join(" AND ");
@@ -13695,7 +13750,7 @@ function sqlCompleter(options) {
13695
13750
  from: slot.from,
13696
13751
  to: slot.to,
13697
13752
  items,
13698
- validFor: slot.quote === null ? UNQUOTED_WORD : null
13753
+ validFor: slot.quote === null ? unquotedWord : null
13699
13754
  };
13700
13755
  }
13701
13756
  }
@@ -13709,7 +13764,7 @@ function sqlCompleter(options) {
13709
13764
  from: scope.prefixFrom - shift,
13710
13765
  to: offset - shift,
13711
13766
  items: typedQuote ? quotedLabels(items) : items,
13712
- validFor: typedQuote ? null : UNQUOTED_WORD
13767
+ validFor: typedQuote ? null : unquotedWord
13713
13768
  };
13714
13769
  }
13715
13770
  function sourceDeclaring(query, word) {
@@ -14281,6 +14336,7 @@ var PARAMETER_MARKS = /* @__PURE__ */ new Set([
14281
14336
  "?"
14282
14337
  ]);
14283
14338
  var PARAMETER_START = /^[$@:]/;
14339
+ var PARAMETER_OR_TEMPORARY = /^[@#]/;
14284
14340
  /** Rows a statement may name without declaring them: PostgreSQL's `EXCLUDED`, a trigger's `NEW` and `OLD`. */
14285
14341
  var PSEUDO_RELATIONS = /* @__PURE__ */ new Set([
14286
14342
  "EXCLUDED",
@@ -14399,6 +14455,7 @@ async function unresolvedRelation(view, query, source) {
14399
14455
  if (view.verb === "SELECT" && view.roleOf(source) === "target") return [];
14400
14456
  if (source.qualifier.length === 0 && view.rules.cteNamed(query, source.name) !== void 0) return [];
14401
14457
  if (!source.name.quoted && NOT_RELATION_NAMES.has(source.name.name.toUpperCase())) return [];
14458
+ if (!source.name.quoted && PARAMETER_OR_TEMPORARY.test(source.name.name)) return [];
14402
14459
  const { lookup } = view;
14403
14460
  let path = lookup.current;
14404
14461
  if (source.qualifier.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablecraft-app/plugin-postgres",
3
- "version": "1.10.4",
3
+ "version": "1.12.0",
4
4
  "description": "The PostgreSQL datasource plugin for TableCraft, downloaded by the app when a data source needs it.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,6 +24,6 @@
24
24
  },
25
25
  "tableCraft": {
26
26
  "id": "postgres",
27
- "apiVersion": "1.4.0"
27
+ "apiVersion": "1.5.0"
28
28
  }
29
29
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "postgres",
3
3
  "name": "PostgreSQL",
4
- "version": "1.10.4",
5
- "apiVersion": "1.4.0",
4
+ "version": "1.12.0",
5
+ "apiVersion": "1.5.0",
6
6
  "entry": "index.js",
7
7
  "capabilities": [
8
8
  "sql",