@tablecraft-app/plugin-postgres 1.10.3 → 1.11.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
@@ -11215,7 +11220,13 @@ var SHOWN_ARROW = "→";
11215
11220
  var OPTIONAL_MARK = "?";
11216
11221
  var VARIADIC_MARK = "...";
11217
11222
  var COMPARATOR = /^(>=|<=|>|<|=)?(\d+(?:\.\d+)*)$/;
11218
- function parameter$1(written) {
11223
+ var BARE_MARK = "=";
11224
+ var LEAD_WORD = /^[A-Z][A-Z_]*$/;
11225
+ var LEAD_CHOICE = "|";
11226
+ var PARAMETER_SEPARATOR = ",";
11227
+ var COMMA_SHOWN = ", ";
11228
+ var MEMBER_SEPARATOR$2 = ".";
11229
+ function parameter$1(written, lead) {
11219
11230
  let name = written.trim();
11220
11231
  const variadic = name.endsWith(VARIADIC_MARK);
11221
11232
  if (variadic) name = name.slice(0, -3).trim();
@@ -11224,28 +11235,78 @@ function parameter$1(written) {
11224
11235
  return {
11225
11236
  name,
11226
11237
  ...optional ? { optional } : {},
11227
- ...variadic ? { variadic } : {}
11238
+ ...variadic ? { variadic } : {},
11239
+ ...lead === null ? {} : { lead }
11228
11240
  };
11229
11241
  }
11230
- /** Reads `"str, len? -> string"`: `?` marks an optional parameter, `...` one that repeats. */
11242
+ function isLeadWord(word) {
11243
+ return word.split(LEAD_CHOICE).every((choice) => LEAD_WORD.test(choice));
11244
+ }
11245
+ /** Whether `lead` is `word` or offers it among its `|` alternatives. */
11246
+ function leadMatches(lead, word) {
11247
+ return lead.split(LEAD_CHOICE).includes(word);
11248
+ }
11249
+ /**
11250
+ * One comma-separated part: an upper-case word between names leads the next, as one before the
11251
+ * first parameter does; one standing alone is a keyword choice. After a comma the keyword is the
11252
+ * argument's own (`date, INTERVAL expr unit`), since the comma already separates it.
11253
+ */
11254
+ function leadParameters(part, index) {
11255
+ const words = part.trim().split(/\s+/);
11256
+ const parameters = [];
11257
+ let lead = null;
11258
+ let name = [];
11259
+ const flush = () => {
11260
+ if (name.length > 0) parameters.push(parameter$1(name.join(" "), lead));
11261
+ name = [];
11262
+ lead = null;
11263
+ };
11264
+ words.forEach((word, at) => {
11265
+ const next = words[at + 1];
11266
+ if (!(at === 0 && index > 0) && isLeadWord(word) && next !== void 0 && !isLeadWord(next)) {
11267
+ flush();
11268
+ lead = word;
11269
+ } else name.push(word);
11270
+ });
11271
+ flush();
11272
+ return parameters;
11273
+ }
11274
+ function isBareSignature(written) {
11275
+ return written.trim().startsWith(BARE_MARK);
11276
+ }
11277
+ /**
11278
+ * Reads `"str, len? -> string"`: `?` marks an optional parameter, `...` one that repeats, an
11279
+ * upper-case word between names the keyword before the next (`"unit FROM date -> integer"`).
11280
+ * `"= date"` is a bare function's and `"str ->"` a procedure's, which returns nothing.
11281
+ */
11231
11282
  function parseSignature(written) {
11283
+ if (isBareSignature(written)) return {
11284
+ parameters: [],
11285
+ returns: written.trim().slice(1).trim()
11286
+ };
11232
11287
  const arrow = written.lastIndexOf(RETURNS_ARROW);
11233
11288
  if (arrow < 0) throw new Error(`Signature "${written}" has no "${RETURNS_ARROW} type"; add what it returns.`);
11234
11289
  const list = written.slice(0, arrow).trim();
11235
11290
  return {
11236
- parameters: list === "" ? [] : list.split(",").map(parameter$1),
11291
+ parameters: list === "" ? [] : list.split(PARAMETER_SEPARATOR).flatMap(leadParameters),
11237
11292
  returns: written.slice(arrow + 2).trim()
11238
11293
  };
11239
11294
  }
11240
11295
  /** Specs for one category, from compact entries a plugin's data file lists. */
11241
11296
  function functionSpecs(category, entries) {
11242
- return entries.map(([name, signatures, summary, available]) => ({
11243
- name,
11244
- signatures: (typeof signatures === "string" ? [signatures] : signatures).map(parseSignature),
11245
- summary,
11246
- category,
11247
- ...available === void 0 ? {} : { available }
11248
- }));
11297
+ return entries.map(([name, signatures, summary, available]) => {
11298
+ const written = typeof signatures === "string" ? [signatures] : signatures;
11299
+ const bare = written.filter(isBareSignature).length;
11300
+ if (bare > 0 && bare < written.length) throw new Error(`${name} mixes "${BARE_MARK} type" with called signatures; write the bare form as "-> type".`);
11301
+ return {
11302
+ name,
11303
+ signatures: written.map(parseSignature),
11304
+ summary,
11305
+ category,
11306
+ ...available === void 0 ? {} : { available },
11307
+ ...bare > 0 ? { bare: true } : {}
11308
+ };
11309
+ });
11249
11310
  }
11250
11311
  function compareVersions(left, right) {
11251
11312
  for (let at = 0; at < Math.max(left.length, right.length); at += 1) {
@@ -11274,20 +11335,48 @@ function isAvailable(spec, server) {
11274
11335
  if (range === false) return false;
11275
11336
  return range.split(/\s+/).filter((comparator) => comparator !== "").every((comparator) => satisfies(server.version, comparator));
11276
11337
  }
11277
- function shownParameter(parameter) {
11338
+ function shownParameter(parameter, index) {
11278
11339
  const name = parameter.variadic === true ? `${parameter.name}${VARIADIC_MARK}` : parameter.name;
11279
- return parameter.optional === true ? `[${name}]` : name;
11340
+ const led = parameter.lead === void 0 ? name : `${parameter.lead} ${name}`;
11341
+ return (index === 0 ? "" : parameter.lead === void 0 ? COMMA_SHOWN : " ") + (parameter.optional === true ? `[${led}]` : led);
11342
+ }
11343
+ function returnsLabel(returns) {
11344
+ return returns === "" ? "" : `${SHOWN_ARROW} ${returns}`;
11280
11345
  }
11281
- /** `(date, format) → string`, the popup's detail for one signature. */
11346
+ /** `(date, format) → string`, the popup's detail for one signature; a lead replaces the comma, a procedure has no `→`. */
11282
11347
  function signatureLabel(signature) {
11283
- return `(${signature.parameters.map(shownParameter).join(", ")}) ${SHOWN_ARROW} ${signature.returns}`;
11348
+ const list = `(${signature.parameters.map(shownParameter).join("")})`;
11349
+ const returns = returnsLabel(signature.returns);
11350
+ return returns === "" ? list : `${list} ${returns}`;
11351
+ }
11352
+ function shownSignature(spec, signature) {
11353
+ return spec.bare === true ? returnsLabel(signature.returns) : signatureLabel(signature);
11284
11354
  }
11285
11355
  /** Every signature on its own line, then the summary. */
11286
11356
  function functionInfo(spec) {
11287
11357
  const label = spec.name.toLowerCase();
11288
- return `${spec.signatures.map((signature) => `${label}${signatureLabel(signature)}`).join("\n")}\n\n${spec.summary}`;
11358
+ const gap = spec.bare === true ? " " : "";
11359
+ return `${spec.signatures.map((signature) => `${label}${gap}${shownSignature(spec, signature)}`).join("\n")}\n\n${spec.summary}`;
11289
11360
  }
11290
- /** A spec as an item: `name()` with the caret between the parentheses when it takes arguments. */
11361
+ /**
11362
+ * A spec as an item: `name()` with the caret between the parentheses when it takes arguments, or
11363
+ * the name alone for a bare one. `label` is what the popup offers, the member alone after `pkg.`.
11364
+ */
11365
+ function specItem(spec, label, boost) {
11366
+ const first = spec.signatures[0];
11367
+ const takesArguments = spec.signatures.some((signature) => signature.parameters.length > 0);
11368
+ const bare = spec.bare === true;
11369
+ return {
11370
+ label,
11371
+ kind: "function",
11372
+ detail: first === void 0 ? null : shownSignature(spec, first),
11373
+ apply: bare ? label : `${label}()`,
11374
+ boost,
11375
+ info: functionInfo(spec),
11376
+ caret: takesArguments && !bare ? label.length + 1 : null
11377
+ };
11378
+ }
11379
+ /** A spec as an item: `name()` with the caret between the parentheses when it takes arguments, the name alone when bare. */
11291
11380
  function functionItem(spec, boost) {
11292
11381
  if (typeof spec === "string") return {
11293
11382
  label: spec,
@@ -11298,18 +11387,17 @@ function functionItem(spec, boost) {
11298
11387
  info: null,
11299
11388
  caret: null
11300
11389
  };
11301
- const label = spec.name.toLowerCase();
11302
- const first = spec.signatures[0];
11303
- const takesArguments = spec.signatures.some((signature) => signature.parameters.length > 0);
11304
- return {
11305
- label,
11306
- kind: "function",
11307
- detail: first === void 0 ? null : signatureLabel(first),
11308
- apply: `${label}()`,
11309
- boost,
11310
- info: functionInfo(spec),
11311
- caret: takesArguments ? label.length + 1 : null
11312
- };
11390
+ return specItem(spec, spec.name.toLowerCase(), boost);
11391
+ }
11392
+ /** The package a spec named `PKG.MEMBER` belongs to, upper case; null for a plain function. */
11393
+ function functionPackage(spec) {
11394
+ const name = functionName(spec);
11395
+ const dot = name.lastIndexOf(MEMBER_SEPARATOR$2);
11396
+ return dot < 0 ? null : name.slice(0, dot).toUpperCase();
11397
+ }
11398
+ /** A package member as offered after `pkg.`: the member's name alone, the info naming it in full. */
11399
+ function memberItem(spec, boost) {
11400
+ return specItem(spec, spec.name.slice(spec.name.lastIndexOf(MEMBER_SEPARATOR$2) + 1).toLowerCase(), boost);
11313
11401
  }
11314
11402
  /** The name a function entry is offered under, either form. */
11315
11403
  function functionName(spec) {
@@ -11420,6 +11508,7 @@ var Q_QUOTE_PAIRS = /* @__PURE__ */ new Map([
11420
11508
  var STRING_QUOTE$1 = "'";
11421
11509
  var BRACKET_OPEN$1 = "[";
11422
11510
  var BRACKET_CLOSE$1 = "]";
11511
+ var VARIABLE_CHARS = /* @__PURE__ */ new Set(["@", "#"]);
11423
11512
  function isSpace(char) {
11424
11513
  return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f" || char === "\v";
11425
11514
  }
@@ -11461,7 +11550,7 @@ var Lexer = class {
11461
11550
  else if (char === "`") token = this.#quoted(at, "`", "quoted", false);
11462
11551
  else if (char === BRACKET_OPEN$1 && this.options.bracketIdentifiers === true) token = this.#bracketed(at);
11463
11552
  else if (char === "$" && this.options.dollarQuotes === true && this.#dollarTag(at) !== null) token = this.#dollarQuoted(at, this.#dollarTag(at) ?? "");
11464
- else if (isWordChar(char)) token = this.#word(at, delimiter);
11553
+ else if (this.#isWordChar(char)) token = this.#word(at, delimiter);
11465
11554
  else token = {
11466
11555
  kind: "symbol",
11467
11556
  from: at,
@@ -11470,6 +11559,9 @@ var Lexer = class {
11470
11559
  this.position = token.to;
11471
11560
  return token;
11472
11561
  }
11562
+ #isWordChar(char) {
11563
+ return isWordChar(char) || this.options.variables === true && VARIABLE_CHARS.has(char);
11564
+ }
11473
11565
  #lineComment(from) {
11474
11566
  const end = this.text.indexOf(NEWLINE$2, from);
11475
11567
  return {
@@ -11565,7 +11657,7 @@ var Lexer = class {
11565
11657
  const text = this.text;
11566
11658
  const customDelimiter = delimiter !== ";";
11567
11659
  let at = from;
11568
- while (at < text.length && isWordChar(text.charAt(at))) {
11660
+ while (at < text.length && this.#isWordChar(text.charAt(at))) {
11569
11661
  if (customDelimiter && at > from && text.startsWith(delimiter, at)) break;
11570
11662
  at += 1;
11571
11663
  }
@@ -11637,7 +11729,7 @@ function wordOf(text, token) {
11637
11729
  var OPEN$6 = "(";
11638
11730
  var CLOSE$5 = ")";
11639
11731
  var DOT$3 = ".";
11640
- var COMMA$7 = ",";
11732
+ var COMMA$9 = ",";
11641
11733
  var QUERY_STARTS$2 = /* @__PURE__ */ new Set(["SELECT", "WITH"]);
11642
11734
  var RELATION_INTRODUCERS$1 = /* @__PURE__ */ new Set([
11643
11735
  "FROM",
@@ -11894,7 +11986,7 @@ var QueryReader = class {
11894
11986
  flush();
11895
11987
  selecting = false;
11896
11988
  selected = true;
11897
- } else if (statement.isSymbol(index, COMMA$7)) {
11989
+ } else if (statement.isSymbol(index, COMMA$9)) {
11898
11990
  flush();
11899
11991
  continue;
11900
11992
  } else {
@@ -11926,7 +12018,7 @@ var QueryReader = class {
11926
12018
  into = false;
11927
12019
  continue;
11928
12020
  }
11929
- if (fromList && statement.isSymbol(index, COMMA$7)) expectRelation = true;
12021
+ if (fromList && statement.isSymbol(index, COMMA$9)) expectRelation = true;
11930
12022
  }
11931
12023
  if (selecting) flush();
11932
12024
  }
@@ -11954,7 +12046,7 @@ var QueryReader = class {
11954
12046
  query: statement.query(body)
11955
12047
  });
11956
12048
  this.#at += 1;
11957
- if (!statement.isSymbol(this.#index(), COMMA$7)) return;
12049
+ if (!statement.isSymbol(this.#index(), COMMA$9)) return;
11958
12050
  this.#at += 1;
11959
12051
  }
11960
12052
  }
@@ -12116,7 +12208,7 @@ function clauseOf(statement, queryOpen, group, anchor, query) {
12116
12208
  }
12117
12209
  const lastWord = statement.word(last ?? void 0);
12118
12210
  if (lastWord !== null && (RELATION_INTRODUCERS$1.has(lastWord) || RELATION_MODIFIERS.has(lastWord))) return lastWord === "JOIN" || lastWord === "STRAIGHT_JOIN" ? "join" : "relation";
12119
- if (fromList && last !== null && statement.isSymbol(last, COMMA$7)) return "relation";
12211
+ if (fromList && last !== null && statement.isSymbol(last, COMMA$9)) return "relation";
12120
12212
  if (clause !== null && EXPRESSION_CLAUSES.has(clause)) return "columns";
12121
12213
  return "keywords";
12122
12214
  }
@@ -12163,6 +12255,10 @@ function scopeAt(text, offset, dialect, mode = "complete") {
12163
12255
  };
12164
12256
  }
12165
12257
  // ../plugin-api/src/sql/signature.ts
12258
+ var COMMA$8 = ",";
12259
+ var MEMBER_SEPARATOR$1 = ".";
12260
+ /** Calls a procedure by name, so it may come before a package member as it never may before a function. */
12261
+ var CALL_WORD = "CALL";
12166
12262
  /** Words a relation or routine name follows, so `TABLE upper (` defines rather than calls. */
12167
12263
  var NAME_INTRODUCERS = /* @__PURE__ */ new Set([
12168
12264
  "CALL",
@@ -12189,7 +12285,8 @@ function upperWord(text, token) {
12189
12285
  }
12190
12286
  /**
12191
12287
  * Finds the innermost call around `offset` whose name `known` accepts, counting its top-level
12192
- * commas; strings, comments and quoted names are tokens of their own, so their commas never count.
12288
+ * commas and listing its separators; strings, comments and quoted names are tokens of their own,
12289
+ * so their commas never count. `pkg.member (` asks `known` for `PKG.MEMBER`.
12193
12290
  * Grouping parentheses are looked through; a subquery, a non-function name before `(` or a caret
12194
12291
  * in a string or comment ends the search with null. `reserved` holds the dialect's keywords: a
12195
12292
  * function spelled like one (`VALUES`, `LEFT`) is a call only where an operand fits, not after a
@@ -12198,16 +12295,35 @@ function upperWord(text, token) {
12198
12295
  function callAt(text, offset, dialect, reserved, known) {
12199
12296
  const { tokens, literal } = statementTokens(text, offset, dialect);
12200
12297
  if (literal) return null;
12298
+ /** `pkg.member` ending at `at`, when the package's spec knows the member. */
12299
+ function member(at) {
12300
+ const name = tokens[at];
12301
+ const owner = tokens[at - 2];
12302
+ const word = upperWord(text, name);
12303
+ const ownerWord = upperWord(text, owner);
12304
+ if (name === void 0 || owner === void 0 || word === null || ownerWord === null) return null;
12305
+ if (!known(`${ownerWord}${MEMBER_SEPARATOR$1}${word}`)) return null;
12306
+ const previous = upperWord(text, tokens[at - 3]);
12307
+ if (previous !== null && previous !== CALL_WORD && NAME_INTRODUCERS.has(previous)) return null;
12308
+ return {
12309
+ written: `${text.slice(owner.from, owner.to)}${MEMBER_SEPARATOR$1}${text.slice(name.from, name.to)}`,
12310
+ from: owner.from
12311
+ };
12312
+ }
12201
12313
  function callable(at) {
12202
12314
  const name = tokens[at];
12315
+ const before = tokens[at - 1];
12316
+ if (symbol(text, before, MEMBER_SEPARATOR$1)) return member(at);
12203
12317
  const word = upperWord(text, name);
12204
12318
  if (name === void 0 || word === null || !known(word)) return null;
12205
- const before = tokens[at - 1];
12206
- if (symbol(text, before, ".")) return null;
12319
+ const found = {
12320
+ written: text.slice(name.from, name.to),
12321
+ from: name.from
12322
+ };
12207
12323
  const previous = upperWord(text, before);
12208
12324
  if (previous !== null && NAME_INTRODUCERS.has(previous)) return null;
12209
- if (!reserved.has(word)) return name;
12210
- return before === void 0 || before.kind === "symbol" && !symbol(text, before, ")") || previous !== null && reserved.has(previous) ? name : null;
12325
+ if (!reserved.has(word)) return found;
12326
+ return before === void 0 || before.kind === "symbol" && !symbol(text, before, ")") || previous !== null && reserved.has(previous) ? found : null;
12211
12327
  }
12212
12328
  const frames = [];
12213
12329
  let index = 0;
@@ -12220,12 +12336,19 @@ function callAt(text, offset, dialect, reserved, known) {
12220
12336
  name,
12221
12337
  named: name === null && !grouping(text, tokens[index - 1]),
12222
12338
  open: index,
12223
- commas: 0
12339
+ commas: 0,
12340
+ separators: []
12224
12341
  });
12225
12342
  } else if (symbol(text, token, ")")) frames.pop();
12226
- else if (symbol(text, token, ",")) {
12343
+ else if (symbol(text, token, COMMA$8)) {
12227
12344
  const top = frames.at(-1);
12228
- if (top !== void 0) top.commas += 1;
12345
+ if (top !== void 0) {
12346
+ top.commas += 1;
12347
+ top.separators.push(COMMA$8);
12348
+ }
12349
+ } else {
12350
+ const word = upperWord(text, token);
12351
+ if (word !== null) frames.at(-1)?.separators.push(word);
12229
12352
  }
12230
12353
  }
12231
12354
  for (let depth = frames.length - 1; depth >= 0; depth -= 1) {
@@ -12236,8 +12359,9 @@ function callAt(text, offset, dialect, reserved, known) {
12236
12359
  continue;
12237
12360
  }
12238
12361
  return {
12239
- name: text.slice(frame.name.from, frame.name.to),
12362
+ name: frame.name.written,
12240
12363
  argument: frame.commas,
12364
+ separators: frame.separators,
12241
12365
  from: frame.name.from,
12242
12366
  to: closeOf(text, tokens, index, frames.length - depth) ?? offset
12243
12367
  };
@@ -12304,7 +12428,7 @@ var FROM = "FROM";
12304
12428
  var AS$4 = "AS";
12305
12429
  var STAR$1 = "*";
12306
12430
  var DOT$2 = ".";
12307
- var COMMA$6 = ",";
12431
+ var COMMA$7 = ",";
12308
12432
  var OPEN$5 = "(";
12309
12433
  var CLOSE$4 = ")";
12310
12434
  var OVER = "OVER";
@@ -12502,7 +12626,7 @@ function tailRefusal(reader, from, depth) {
12502
12626
  const tokens = reader.tokens;
12503
12627
  const first = reader.word(tokens[from]);
12504
12628
  if (from < tokens.length && (first === null || !TAIL_CLAUSES.has(first))) {
12505
- if (reader.symbol(tokens[from]) === COMMA$6 || first !== null && JOINS.has(first)) return SELECT_READ_ONLY_REASONS.joins;
12629
+ if (reader.symbol(tokens[from]) === COMMA$7 || first !== null && JOINS.has(first)) return SELECT_READ_ONLY_REASONS.joins;
12506
12630
  if (first !== null && GROUPING.has(first)) return SELECT_READ_ONLY_REASONS.aggregates;
12507
12631
  if (first !== null && SET_OPERATIONS.has(first)) return SELECT_READ_ONLY_REASONS.combines;
12508
12632
  return SELECT_READ_ONLY_REASONS.notSelect;
@@ -12540,7 +12664,7 @@ function parseSimpleSelect(sql, dialect) {
12540
12664
  if (refusal !== null) return { readOnly: refusal };
12541
12665
  const items = [];
12542
12666
  let itemFrom = at;
12543
- for (let index = at; index <= fromAt; index += 1) if (index === fromAt || depth[index] === 0 && reader.symbol(tokens[index]) === COMMA$6) {
12667
+ for (let index = at; index <= fromAt; index += 1) if (index === fromAt || depth[index] === 0 && reader.symbol(tokens[index]) === COMMA$7) {
12544
12668
  if (index === itemFrom) return { readOnly: SELECT_READ_ONLY_REASONS.notSelect };
12545
12669
  items.push(selectItem(reader, itemFrom, index));
12546
12670
  itemFrom = index + 1;
@@ -12666,6 +12790,25 @@ function searchPathTarget(text, tokens) {
12666
12790
  candidates: names
12667
12791
  };
12668
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
+ }
12669
12812
  var NAME_TOKENS = /* @__PURE__ */ new Set([
12670
12813
  "word",
12671
12814
  "quoted",
@@ -12721,7 +12864,8 @@ function searchPathListStart(text, tokens) {
12721
12864
  }
12722
12865
  /**
12723
12866
  * Where the caret at `offset` names a namespace a session-namespace statement of the dialect moves
12724
- * 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,
12725
12869
  * and after a name whose closing quote is typed.
12726
12870
  */
12727
12871
  function namespaceSlotAt(text, offset, dialect) {
@@ -12757,6 +12901,17 @@ function namespaceSlotAt(text, offset, dialect) {
12757
12901
  listed: []
12758
12902
  };
12759
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
+ }
12760
12915
  const start = searchPathListStart(text, before);
12761
12916
  if (start === null) continue;
12762
12917
  if (start.single && before.length !== start.at) continue;
@@ -12781,14 +12936,25 @@ function namespaceTarget(sql, dialect) {
12781
12936
  if (forms.length === 0) return null;
12782
12937
  const tokens = significantTokens(sql, dialect).filter((token) => token.kind !== "delimiter");
12783
12938
  for (const form of forms) {
12784
- const target = form === "use" ? useTarget(sql, tokens) : searchPathTarget(sql, tokens);
12939
+ const target = TARGET_READERS[form](sql, tokens);
12785
12940
  if (target !== null) return target;
12786
12941
  }
12787
12942
  return null;
12788
12943
  }
12944
+ var TARGET_READERS = {
12945
+ use: useTarget,
12946
+ searchPath: searchPathTarget,
12947
+ alterSessionSchema: alterSessionTarget
12948
+ };
12789
12949
  var SWITCH_TARGETS = {
12790
12950
  use: "topLevel",
12791
- 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}`
12792
12958
  };
12793
12959
  /**
12794
12960
  * The console's namespace switch for a dialect moving its namespace by `form` (decision 060):
@@ -12802,7 +12968,7 @@ function namespaceSwitcher(form, quoteIdentifier) {
12802
12968
  statement(path) {
12803
12969
  const name = path.at(-1);
12804
12970
  if (name === void 0) throw new PluginError("statementFailed", "A namespace switch names no namespace; pick one from the list.");
12805
- return form === "use" ? `USE ${quoteIdentifier(name)}` : `SET search_path TO ${quoteIdentifier(name)}`;
12971
+ return SWITCH_STATEMENTS[form](quoteIdentifier(name));
12806
12972
  }
12807
12973
  };
12808
12974
  }
@@ -13305,6 +13471,8 @@ var BOOST = {
13305
13471
  keyword: -10
13306
13472
  };
13307
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_$]*$";
13308
13476
  var ALIAS_WORD_BREAK = /[^A-Za-z0-9]+|(?<=[a-z0-9])(?=[A-Z])/;
13309
13477
  var FALLBACK_ALIAS = "t";
13310
13478
  var QUOTES = /* @__PURE__ */ new Set([
@@ -13315,12 +13483,54 @@ var QUOTES = /* @__PURE__ */ new Set([
13315
13483
  /** Below every other namespace: `USE mysql` is legitimate but rarely meant. */
13316
13484
  var SYSTEM_NAMESPACE_BOOST = -20;
13317
13485
  var CURRENT_DETAIL = "current";
13486
+ var COMMA$6 = ",";
13487
+ var MEMBER_SEPARATOR = ".";
13488
+ /**
13489
+ * Walks `separators` through `signature`: a comma moves to the next parameter without a lead, a
13490
+ * word to the next one it leads. A word nothing leads is an argument's own (`YEAR`, `DECIMAL`).
13491
+ * Past the last parameter a comma keeps counting, as the hint's variadic rule expects; a variadic
13492
+ * parameter keyword-led ones follow (`CHAR(code... USING charset)`) takes every comma.
13493
+ */
13494
+ function argumentAt(signature, separators) {
13495
+ const { parameters } = signature;
13496
+ let at = 0;
13497
+ let entered = false;
13498
+ let led = 0;
13499
+ for (const separator of separators) {
13500
+ if (separator === COMMA$6) {
13501
+ const next = parameters.findIndex((parameter, index) => index > at && parameter.lead === void 0);
13502
+ if (next >= 0) at = next;
13503
+ else if (!(parameters[at]?.variadic === true && at < parameters.length - 1)) at += 1;
13504
+ entered = true;
13505
+ continue;
13506
+ }
13507
+ const next = parameters.findIndex((parameter, index) => (index > at || index === at && !entered) && parameter.lead !== void 0 && leadMatches(parameter.lead, separator));
13508
+ if (next < 0) continue;
13509
+ at = next;
13510
+ entered = true;
13511
+ led += 1;
13512
+ }
13513
+ return {
13514
+ argument: at,
13515
+ led
13516
+ };
13517
+ }
13518
+ /** The argument the caret is in, by the overload whose leads the call follows most; the first on a tie. */
13519
+ function argumentOf(spec, call) {
13520
+ let best = null;
13521
+ for (const signature of spec.signatures) {
13522
+ const found = argumentAt(signature, call.separators ?? []);
13523
+ if (best === null || found.led > best.led) best = found;
13524
+ }
13525
+ return best?.argument ?? call.argument;
13526
+ }
13318
13527
  function relationKind(relation) {
13319
13528
  return relation.kind === "table" ? "table" : "view";
13320
13529
  }
13321
13530
  /** The shared completion and navigation every built-in plugin spreads into its `SqlLanguage` (decision 028). */
13322
13531
  function sqlCompleter(options) {
13323
13532
  const reserved = new Set(options.keywords.map((keyword) => keyword.toUpperCase()));
13533
+ const unquotedWord = options.dialect.variables === true ? VARIABLE_WORD : UNQUOTED_WORD;
13324
13534
  const { denotes, sameIdentifier, cteNamed, sourceNamed, serverOf, Lookup } = nameRules(options);
13325
13535
  const specs = /* @__PURE__ */ new Map();
13326
13536
  for (const spec of options.functions) {
@@ -13331,6 +13541,11 @@ function sqlCompleter(options) {
13331
13541
  function specNamed(name, server) {
13332
13542
  return specs.get(name.toUpperCase())?.find((spec) => isAvailable(spec, server));
13333
13543
  }
13544
+ /** The members of the package `qualifier` names, as offered after its dot. */
13545
+ function memberItems(qualifier, server) {
13546
+ const name = qualifier.map((part) => part.name.toUpperCase()).join(MEMBER_SEPARATOR);
13547
+ return availableFunctions(server, true).filter((spec) => typeof spec !== "string" && functionPackage(spec) === name).map((spec) => memberItem(spec, BOOST.function));
13548
+ }
13334
13549
  function quoted(name) {
13335
13550
  return options.plainIdentifier.test(name) && !reserved.has(name.toUpperCase()) ? name : options.quoteIdentifier(name);
13336
13551
  }
@@ -13355,10 +13570,12 @@ function sqlCompleter(options) {
13355
13570
  function keywordItems(except = /* @__PURE__ */ new Set()) {
13356
13571
  return options.keywords.filter((keyword) => !except.has(keyword.toUpperCase())).map((keyword) => item("keyword", keyword, keyword, null));
13357
13572
  }
13358
- function availableFunctions(server) {
13573
+ /** Plain functions, or with `members` the package members only: a bare name never offers `pkg.member`. */
13574
+ function availableFunctions(server, members = false) {
13359
13575
  const offered = /* @__PURE__ */ new Set();
13360
13576
  return options.functions.filter((spec) => {
13361
13577
  const name = functionName(spec).toUpperCase();
13578
+ if (functionPackage(spec) !== null !== members) return false;
13362
13579
  if (offered.has(name) || typeof spec !== "string" && !isAvailable(spec, server)) return false;
13363
13580
  offered.add(name);
13364
13581
  return true;
@@ -13369,17 +13586,18 @@ function sqlCompleter(options) {
13369
13586
  const last = qualifier.at(-1);
13370
13587
  if (last === void 0) return [];
13371
13588
  const relational = clause === "relation" || clause === "join";
13589
+ const members = relational ? [] : memberItems(qualifier, lookup.server);
13372
13590
  if (qualifier.length === 1 && !relational) {
13373
13591
  const source = sourceNamed(query, last);
13374
- if (source !== void 0) return columnItems(await lookup.columnsOf(source, query));
13592
+ if (source !== void 0) return [...columnItems(await lookup.columnsOf(source, query)), ...members];
13375
13593
  const cte = cteNamed(query, last);
13376
- if (cte !== void 0) return columnItems(await lookup.cteColumns(cte, 1));
13594
+ if (cte !== void 0) return [...columnItems(await lookup.cteColumns(cte, 1)), ...members];
13377
13595
  }
13378
13596
  const namespace = await lookup.namespace(qualifier);
13379
- if (namespace !== void 0) return relationItems(await lookup.relations(namespace.path), null);
13597
+ if (namespace !== void 0) return [...members, ...relationItems(await lookup.relations(namespace.path), null)];
13380
13598
  if (relational) return [];
13381
13599
  const found = await lookup.relation(qualifier.slice(0, -1), last);
13382
- return found === null ? [] : columnItems(found.relation.columns);
13600
+ return found === null ? members : [...columnItems(found.relation.columns), ...members];
13383
13601
  }
13384
13602
  async function relationContextItems(scope, lookup) {
13385
13603
  const current = lookup.current;
@@ -13396,7 +13614,8 @@ function sqlCompleter(options) {
13396
13614
  }
13397
13615
  function aliasFor(name, taken) {
13398
13616
  const initials = name.split(ALIAS_WORD_BREAK).filter((word) => word !== "").map((word) => word.charAt(0).toLowerCase()).join("");
13399
- 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;
13400
13619
  let alias = base;
13401
13620
  for (let suffix = 1; taken.has(alias) || reserved.has(alias.toUpperCase()); suffix += 1) alias = `${base}${String(suffix)}`;
13402
13621
  taken.add(alias);
@@ -13421,7 +13640,7 @@ function sqlCompleter(options) {
13421
13640
  if (source.qualifier.length === 0 && cteNamed(scope.query, source.name) !== void 0) continue;
13422
13641
  const found = await lookup.relation(source.qualifier, source.name);
13423
13642
  if (found === null) continue;
13424
- 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;
13425
13644
  for (const key of found.relation.foreignKeys) {
13426
13645
  const alias = aliasFor(key.target.relation, new Set(taken));
13427
13646
  const on = key.columns.map((column, at) => `${alias}.${quoted(key.target.columns[at] ?? column)} = ${near}.${quoted(column)}`).join(" AND ");
@@ -13531,7 +13750,7 @@ function sqlCompleter(options) {
13531
13750
  from: slot.from,
13532
13751
  to: slot.to,
13533
13752
  items,
13534
- validFor: slot.quote === null ? UNQUOTED_WORD : null
13753
+ validFor: slot.quote === null ? unquotedWord : null
13535
13754
  };
13536
13755
  }
13537
13756
  }
@@ -13545,7 +13764,7 @@ function sqlCompleter(options) {
13545
13764
  from: scope.prefixFrom - shift,
13546
13765
  to: offset - shift,
13547
13766
  items: typedQuote ? quotedLabels(items) : items,
13548
- validFor: typedQuote ? null : UNQUOTED_WORD
13767
+ validFor: typedQuote ? null : unquotedWord
13549
13768
  };
13550
13769
  }
13551
13770
  function sourceDeclaring(query, word) {
@@ -13688,14 +13907,18 @@ function sqlCompleter(options) {
13688
13907
  function signatureAt(request, catalog) {
13689
13908
  const { text, offset, shift } = wrap(request);
13690
13909
  const server = serverOf(catalog);
13691
- const call = callAt(text, offset, options.dialect, reserved, (name) => specNamed(name, server) !== void 0);
13910
+ const callable = (name) => {
13911
+ const spec = specNamed(name, server);
13912
+ return spec !== void 0 && spec.bare !== true;
13913
+ };
13914
+ const call = callAt(text, offset, options.dialect, reserved, callable);
13692
13915
  const spec = call === null ? void 0 : specNamed(call.name, server);
13693
13916
  if (call === null || spec === void 0) return Promise.resolve(null);
13694
13917
  return Promise.resolve({
13695
13918
  name: spec.name.toLowerCase(),
13696
13919
  summary: spec.summary,
13697
13920
  signatures: spec.signatures,
13698
- argument: call.argument,
13921
+ argument: argumentOf(spec, call),
13699
13922
  from: call.from - shift,
13700
13923
  to: call.to - shift
13701
13924
  });
@@ -14113,6 +14336,7 @@ var PARAMETER_MARKS = /* @__PURE__ */ new Set([
14113
14336
  "?"
14114
14337
  ]);
14115
14338
  var PARAMETER_START = /^[$@:]/;
14339
+ var PARAMETER_OR_TEMPORARY = /^[@#]/;
14116
14340
  /** Rows a statement may name without declaring them: PostgreSQL's `EXCLUDED`, a trigger's `NEW` and `OLD`. */
14117
14341
  var PSEUDO_RELATIONS = /* @__PURE__ */ new Set([
14118
14342
  "EXCLUDED",
@@ -14231,6 +14455,7 @@ async function unresolvedRelation(view, query, source) {
14231
14455
  if (view.verb === "SELECT" && view.roleOf(source) === "target") return [];
14232
14456
  if (source.qualifier.length === 0 && view.rules.cteNamed(query, source.name) !== void 0) return [];
14233
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 [];
14234
14459
  const { lookup } = view;
14235
14460
  let path = lookup.current;
14236
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.3",
3
+ "version": "1.11.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.3.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.3",
5
- "apiVersion": "1.3.0",
4
+ "version": "1.11.0",
5
+ "apiVersion": "1.5.0",
6
6
  "entry": "index.js",
7
7
  "capabilities": [
8
8
  "sql",