@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 +289 -64
- package/package.json +2 -2
- package/table-craft-plugin.json +2 -2
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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,
|
|
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
|
|
12206
|
-
|
|
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
|
|
12210
|
-
return before === void 0 || before.kind === "symbol" && !symbol(text, before, ")") || previous !== null && reserved.has(previous) ?
|
|
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)
|
|
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:
|
|
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$
|
|
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$
|
|
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$
|
|
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, |`
|
|
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
|
|
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
|
|
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
|
-
|
|
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 ?
|
|
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
|
|
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 ?
|
|
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 :
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
27
|
+
"apiVersion": "1.5.0"
|
|
28
28
|
}
|
|
29
29
|
}
|