@tablecraft-app/plugin-postgres 1.10.3 → 1.10.4
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 +221 -53
- package/package.json +2 -2
- package/table-craft-plugin.json +2 -2
package/index.js
CHANGED
|
@@ -11215,7 +11215,13 @@ var SHOWN_ARROW = "→";
|
|
|
11215
11215
|
var OPTIONAL_MARK = "?";
|
|
11216
11216
|
var VARIADIC_MARK = "...";
|
|
11217
11217
|
var COMPARATOR = /^(>=|<=|>|<|=)?(\d+(?:\.\d+)*)$/;
|
|
11218
|
-
|
|
11218
|
+
var BARE_MARK = "=";
|
|
11219
|
+
var LEAD_WORD = /^[A-Z][A-Z_]*$/;
|
|
11220
|
+
var LEAD_CHOICE = "|";
|
|
11221
|
+
var PARAMETER_SEPARATOR = ",";
|
|
11222
|
+
var COMMA_SHOWN = ", ";
|
|
11223
|
+
var MEMBER_SEPARATOR$2 = ".";
|
|
11224
|
+
function parameter$1(written, lead) {
|
|
11219
11225
|
let name = written.trim();
|
|
11220
11226
|
const variadic = name.endsWith(VARIADIC_MARK);
|
|
11221
11227
|
if (variadic) name = name.slice(0, -3).trim();
|
|
@@ -11224,28 +11230,78 @@ function parameter$1(written) {
|
|
|
11224
11230
|
return {
|
|
11225
11231
|
name,
|
|
11226
11232
|
...optional ? { optional } : {},
|
|
11227
|
-
...variadic ? { variadic } : {}
|
|
11233
|
+
...variadic ? { variadic } : {},
|
|
11234
|
+
...lead === null ? {} : { lead }
|
|
11228
11235
|
};
|
|
11229
11236
|
}
|
|
11230
|
-
|
|
11237
|
+
function isLeadWord(word) {
|
|
11238
|
+
return word.split(LEAD_CHOICE).every((choice) => LEAD_WORD.test(choice));
|
|
11239
|
+
}
|
|
11240
|
+
/** Whether `lead` is `word` or offers it among its `|` alternatives. */
|
|
11241
|
+
function leadMatches(lead, word) {
|
|
11242
|
+
return lead.split(LEAD_CHOICE).includes(word);
|
|
11243
|
+
}
|
|
11244
|
+
/**
|
|
11245
|
+
* One comma-separated part: an upper-case word between names leads the next, as one before the
|
|
11246
|
+
* first parameter does; one standing alone is a keyword choice. After a comma the keyword is the
|
|
11247
|
+
* argument's own (`date, INTERVAL expr unit`), since the comma already separates it.
|
|
11248
|
+
*/
|
|
11249
|
+
function leadParameters(part, index) {
|
|
11250
|
+
const words = part.trim().split(/\s+/);
|
|
11251
|
+
const parameters = [];
|
|
11252
|
+
let lead = null;
|
|
11253
|
+
let name = [];
|
|
11254
|
+
const flush = () => {
|
|
11255
|
+
if (name.length > 0) parameters.push(parameter$1(name.join(" "), lead));
|
|
11256
|
+
name = [];
|
|
11257
|
+
lead = null;
|
|
11258
|
+
};
|
|
11259
|
+
words.forEach((word, at) => {
|
|
11260
|
+
const next = words[at + 1];
|
|
11261
|
+
if (!(at === 0 && index > 0) && isLeadWord(word) && next !== void 0 && !isLeadWord(next)) {
|
|
11262
|
+
flush();
|
|
11263
|
+
lead = word;
|
|
11264
|
+
} else name.push(word);
|
|
11265
|
+
});
|
|
11266
|
+
flush();
|
|
11267
|
+
return parameters;
|
|
11268
|
+
}
|
|
11269
|
+
function isBareSignature(written) {
|
|
11270
|
+
return written.trim().startsWith(BARE_MARK);
|
|
11271
|
+
}
|
|
11272
|
+
/**
|
|
11273
|
+
* Reads `"str, len? -> string"`: `?` marks an optional parameter, `...` one that repeats, an
|
|
11274
|
+
* upper-case word between names the keyword before the next (`"unit FROM date -> integer"`).
|
|
11275
|
+
* `"= date"` is a bare function's and `"str ->"` a procedure's, which returns nothing.
|
|
11276
|
+
*/
|
|
11231
11277
|
function parseSignature(written) {
|
|
11278
|
+
if (isBareSignature(written)) return {
|
|
11279
|
+
parameters: [],
|
|
11280
|
+
returns: written.trim().slice(1).trim()
|
|
11281
|
+
};
|
|
11232
11282
|
const arrow = written.lastIndexOf(RETURNS_ARROW);
|
|
11233
11283
|
if (arrow < 0) throw new Error(`Signature "${written}" has no "${RETURNS_ARROW} type"; add what it returns.`);
|
|
11234
11284
|
const list = written.slice(0, arrow).trim();
|
|
11235
11285
|
return {
|
|
11236
|
-
parameters: list === "" ? [] : list.split(
|
|
11286
|
+
parameters: list === "" ? [] : list.split(PARAMETER_SEPARATOR).flatMap(leadParameters),
|
|
11237
11287
|
returns: written.slice(arrow + 2).trim()
|
|
11238
11288
|
};
|
|
11239
11289
|
}
|
|
11240
11290
|
/** Specs for one category, from compact entries a plugin's data file lists. */
|
|
11241
11291
|
function functionSpecs(category, entries) {
|
|
11242
|
-
return entries.map(([name, signatures, summary, available]) =>
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11292
|
+
return entries.map(([name, signatures, summary, available]) => {
|
|
11293
|
+
const written = typeof signatures === "string" ? [signatures] : signatures;
|
|
11294
|
+
const bare = written.filter(isBareSignature).length;
|
|
11295
|
+
if (bare > 0 && bare < written.length) throw new Error(`${name} mixes "${BARE_MARK} type" with called signatures; write the bare form as "-> type".`);
|
|
11296
|
+
return {
|
|
11297
|
+
name,
|
|
11298
|
+
signatures: written.map(parseSignature),
|
|
11299
|
+
summary,
|
|
11300
|
+
category,
|
|
11301
|
+
...available === void 0 ? {} : { available },
|
|
11302
|
+
...bare > 0 ? { bare: true } : {}
|
|
11303
|
+
};
|
|
11304
|
+
});
|
|
11249
11305
|
}
|
|
11250
11306
|
function compareVersions(left, right) {
|
|
11251
11307
|
for (let at = 0; at < Math.max(left.length, right.length); at += 1) {
|
|
@@ -11274,20 +11330,48 @@ function isAvailable(spec, server) {
|
|
|
11274
11330
|
if (range === false) return false;
|
|
11275
11331
|
return range.split(/\s+/).filter((comparator) => comparator !== "").every((comparator) => satisfies(server.version, comparator));
|
|
11276
11332
|
}
|
|
11277
|
-
function shownParameter(parameter) {
|
|
11333
|
+
function shownParameter(parameter, index) {
|
|
11278
11334
|
const name = parameter.variadic === true ? `${parameter.name}${VARIADIC_MARK}` : parameter.name;
|
|
11279
|
-
|
|
11335
|
+
const led = parameter.lead === void 0 ? name : `${parameter.lead} ${name}`;
|
|
11336
|
+
return (index === 0 ? "" : parameter.lead === void 0 ? COMMA_SHOWN : " ") + (parameter.optional === true ? `[${led}]` : led);
|
|
11280
11337
|
}
|
|
11281
|
-
|
|
11338
|
+
function returnsLabel(returns) {
|
|
11339
|
+
return returns === "" ? "" : `${SHOWN_ARROW} ${returns}`;
|
|
11340
|
+
}
|
|
11341
|
+
/** `(date, format) → string`, the popup's detail for one signature; a lead replaces the comma, a procedure has no `→`. */
|
|
11282
11342
|
function signatureLabel(signature) {
|
|
11283
|
-
|
|
11343
|
+
const list = `(${signature.parameters.map(shownParameter).join("")})`;
|
|
11344
|
+
const returns = returnsLabel(signature.returns);
|
|
11345
|
+
return returns === "" ? list : `${list} ${returns}`;
|
|
11346
|
+
}
|
|
11347
|
+
function shownSignature(spec, signature) {
|
|
11348
|
+
return spec.bare === true ? returnsLabel(signature.returns) : signatureLabel(signature);
|
|
11284
11349
|
}
|
|
11285
11350
|
/** Every signature on its own line, then the summary. */
|
|
11286
11351
|
function functionInfo(spec) {
|
|
11287
11352
|
const label = spec.name.toLowerCase();
|
|
11288
|
-
|
|
11353
|
+
const gap = spec.bare === true ? " " : "";
|
|
11354
|
+
return `${spec.signatures.map((signature) => `${label}${gap}${shownSignature(spec, signature)}`).join("\n")}\n\n${spec.summary}`;
|
|
11289
11355
|
}
|
|
11290
|
-
/**
|
|
11356
|
+
/**
|
|
11357
|
+
* A spec as an item: `name()` with the caret between the parentheses when it takes arguments, or
|
|
11358
|
+
* the name alone for a bare one. `label` is what the popup offers, the member alone after `pkg.`.
|
|
11359
|
+
*/
|
|
11360
|
+
function specItem(spec, label, boost) {
|
|
11361
|
+
const first = spec.signatures[0];
|
|
11362
|
+
const takesArguments = spec.signatures.some((signature) => signature.parameters.length > 0);
|
|
11363
|
+
const bare = spec.bare === true;
|
|
11364
|
+
return {
|
|
11365
|
+
label,
|
|
11366
|
+
kind: "function",
|
|
11367
|
+
detail: first === void 0 ? null : shownSignature(spec, first),
|
|
11368
|
+
apply: bare ? label : `${label}()`,
|
|
11369
|
+
boost,
|
|
11370
|
+
info: functionInfo(spec),
|
|
11371
|
+
caret: takesArguments && !bare ? label.length + 1 : null
|
|
11372
|
+
};
|
|
11373
|
+
}
|
|
11374
|
+
/** A spec as an item: `name()` with the caret between the parentheses when it takes arguments, the name alone when bare. */
|
|
11291
11375
|
function functionItem(spec, boost) {
|
|
11292
11376
|
if (typeof spec === "string") return {
|
|
11293
11377
|
label: spec,
|
|
@@ -11298,18 +11382,17 @@ function functionItem(spec, boost) {
|
|
|
11298
11382
|
info: null,
|
|
11299
11383
|
caret: null
|
|
11300
11384
|
};
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
};
|
|
11385
|
+
return specItem(spec, spec.name.toLowerCase(), boost);
|
|
11386
|
+
}
|
|
11387
|
+
/** The package a spec named `PKG.MEMBER` belongs to, upper case; null for a plain function. */
|
|
11388
|
+
function functionPackage(spec) {
|
|
11389
|
+
const name = functionName(spec);
|
|
11390
|
+
const dot = name.lastIndexOf(MEMBER_SEPARATOR$2);
|
|
11391
|
+
return dot < 0 ? null : name.slice(0, dot).toUpperCase();
|
|
11392
|
+
}
|
|
11393
|
+
/** A package member as offered after `pkg.`: the member's name alone, the info naming it in full. */
|
|
11394
|
+
function memberItem(spec, boost) {
|
|
11395
|
+
return specItem(spec, spec.name.slice(spec.name.lastIndexOf(MEMBER_SEPARATOR$2) + 1).toLowerCase(), boost);
|
|
11313
11396
|
}
|
|
11314
11397
|
/** The name a function entry is offered under, either form. */
|
|
11315
11398
|
function functionName(spec) {
|
|
@@ -11637,7 +11720,7 @@ function wordOf(text, token) {
|
|
|
11637
11720
|
var OPEN$6 = "(";
|
|
11638
11721
|
var CLOSE$5 = ")";
|
|
11639
11722
|
var DOT$3 = ".";
|
|
11640
|
-
var COMMA$
|
|
11723
|
+
var COMMA$9 = ",";
|
|
11641
11724
|
var QUERY_STARTS$2 = /* @__PURE__ */ new Set(["SELECT", "WITH"]);
|
|
11642
11725
|
var RELATION_INTRODUCERS$1 = /* @__PURE__ */ new Set([
|
|
11643
11726
|
"FROM",
|
|
@@ -11894,7 +11977,7 @@ var QueryReader = class {
|
|
|
11894
11977
|
flush();
|
|
11895
11978
|
selecting = false;
|
|
11896
11979
|
selected = true;
|
|
11897
|
-
} else if (statement.isSymbol(index, COMMA$
|
|
11980
|
+
} else if (statement.isSymbol(index, COMMA$9)) {
|
|
11898
11981
|
flush();
|
|
11899
11982
|
continue;
|
|
11900
11983
|
} else {
|
|
@@ -11926,7 +12009,7 @@ var QueryReader = class {
|
|
|
11926
12009
|
into = false;
|
|
11927
12010
|
continue;
|
|
11928
12011
|
}
|
|
11929
|
-
if (fromList && statement.isSymbol(index, COMMA$
|
|
12012
|
+
if (fromList && statement.isSymbol(index, COMMA$9)) expectRelation = true;
|
|
11930
12013
|
}
|
|
11931
12014
|
if (selecting) flush();
|
|
11932
12015
|
}
|
|
@@ -11954,7 +12037,7 @@ var QueryReader = class {
|
|
|
11954
12037
|
query: statement.query(body)
|
|
11955
12038
|
});
|
|
11956
12039
|
this.#at += 1;
|
|
11957
|
-
if (!statement.isSymbol(this.#index(), COMMA$
|
|
12040
|
+
if (!statement.isSymbol(this.#index(), COMMA$9)) return;
|
|
11958
12041
|
this.#at += 1;
|
|
11959
12042
|
}
|
|
11960
12043
|
}
|
|
@@ -12116,7 +12199,7 @@ function clauseOf(statement, queryOpen, group, anchor, query) {
|
|
|
12116
12199
|
}
|
|
12117
12200
|
const lastWord = statement.word(last ?? void 0);
|
|
12118
12201
|
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$
|
|
12202
|
+
if (fromList && last !== null && statement.isSymbol(last, COMMA$9)) return "relation";
|
|
12120
12203
|
if (clause !== null && EXPRESSION_CLAUSES.has(clause)) return "columns";
|
|
12121
12204
|
return "keywords";
|
|
12122
12205
|
}
|
|
@@ -12163,6 +12246,10 @@ function scopeAt(text, offset, dialect, mode = "complete") {
|
|
|
12163
12246
|
};
|
|
12164
12247
|
}
|
|
12165
12248
|
// ../plugin-api/src/sql/signature.ts
|
|
12249
|
+
var COMMA$8 = ",";
|
|
12250
|
+
var MEMBER_SEPARATOR$1 = ".";
|
|
12251
|
+
/** Calls a procedure by name, so it may come before a package member as it never may before a function. */
|
|
12252
|
+
var CALL_WORD = "CALL";
|
|
12166
12253
|
/** Words a relation or routine name follows, so `TABLE upper (` defines rather than calls. */
|
|
12167
12254
|
var NAME_INTRODUCERS = /* @__PURE__ */ new Set([
|
|
12168
12255
|
"CALL",
|
|
@@ -12189,7 +12276,8 @@ function upperWord(text, token) {
|
|
|
12189
12276
|
}
|
|
12190
12277
|
/**
|
|
12191
12278
|
* 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,
|
|
12279
|
+
* commas and listing its separators; strings, comments and quoted names are tokens of their own,
|
|
12280
|
+
* so their commas never count. `pkg.member (` asks `known` for `PKG.MEMBER`.
|
|
12193
12281
|
* Grouping parentheses are looked through; a subquery, a non-function name before `(` or a caret
|
|
12194
12282
|
* in a string or comment ends the search with null. `reserved` holds the dialect's keywords: a
|
|
12195
12283
|
* function spelled like one (`VALUES`, `LEFT`) is a call only where an operand fits, not after a
|
|
@@ -12198,16 +12286,35 @@ function upperWord(text, token) {
|
|
|
12198
12286
|
function callAt(text, offset, dialect, reserved, known) {
|
|
12199
12287
|
const { tokens, literal } = statementTokens(text, offset, dialect);
|
|
12200
12288
|
if (literal) return null;
|
|
12289
|
+
/** `pkg.member` ending at `at`, when the package's spec knows the member. */
|
|
12290
|
+
function member(at) {
|
|
12291
|
+
const name = tokens[at];
|
|
12292
|
+
const owner = tokens[at - 2];
|
|
12293
|
+
const word = upperWord(text, name);
|
|
12294
|
+
const ownerWord = upperWord(text, owner);
|
|
12295
|
+
if (name === void 0 || owner === void 0 || word === null || ownerWord === null) return null;
|
|
12296
|
+
if (!known(`${ownerWord}${MEMBER_SEPARATOR$1}${word}`)) return null;
|
|
12297
|
+
const previous = upperWord(text, tokens[at - 3]);
|
|
12298
|
+
if (previous !== null && previous !== CALL_WORD && NAME_INTRODUCERS.has(previous)) return null;
|
|
12299
|
+
return {
|
|
12300
|
+
written: `${text.slice(owner.from, owner.to)}${MEMBER_SEPARATOR$1}${text.slice(name.from, name.to)}`,
|
|
12301
|
+
from: owner.from
|
|
12302
|
+
};
|
|
12303
|
+
}
|
|
12201
12304
|
function callable(at) {
|
|
12202
12305
|
const name = tokens[at];
|
|
12306
|
+
const before = tokens[at - 1];
|
|
12307
|
+
if (symbol(text, before, MEMBER_SEPARATOR$1)) return member(at);
|
|
12203
12308
|
const word = upperWord(text, name);
|
|
12204
12309
|
if (name === void 0 || word === null || !known(word)) return null;
|
|
12205
|
-
const
|
|
12206
|
-
|
|
12310
|
+
const found = {
|
|
12311
|
+
written: text.slice(name.from, name.to),
|
|
12312
|
+
from: name.from
|
|
12313
|
+
};
|
|
12207
12314
|
const previous = upperWord(text, before);
|
|
12208
12315
|
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) ?
|
|
12316
|
+
if (!reserved.has(word)) return found;
|
|
12317
|
+
return before === void 0 || before.kind === "symbol" && !symbol(text, before, ")") || previous !== null && reserved.has(previous) ? found : null;
|
|
12211
12318
|
}
|
|
12212
12319
|
const frames = [];
|
|
12213
12320
|
let index = 0;
|
|
@@ -12220,12 +12327,19 @@ function callAt(text, offset, dialect, reserved, known) {
|
|
|
12220
12327
|
name,
|
|
12221
12328
|
named: name === null && !grouping(text, tokens[index - 1]),
|
|
12222
12329
|
open: index,
|
|
12223
|
-
commas: 0
|
|
12330
|
+
commas: 0,
|
|
12331
|
+
separators: []
|
|
12224
12332
|
});
|
|
12225
12333
|
} else if (symbol(text, token, ")")) frames.pop();
|
|
12226
|
-
else if (symbol(text, token,
|
|
12334
|
+
else if (symbol(text, token, COMMA$8)) {
|
|
12227
12335
|
const top = frames.at(-1);
|
|
12228
|
-
if (top !== void 0)
|
|
12336
|
+
if (top !== void 0) {
|
|
12337
|
+
top.commas += 1;
|
|
12338
|
+
top.separators.push(COMMA$8);
|
|
12339
|
+
}
|
|
12340
|
+
} else {
|
|
12341
|
+
const word = upperWord(text, token);
|
|
12342
|
+
if (word !== null) frames.at(-1)?.separators.push(word);
|
|
12229
12343
|
}
|
|
12230
12344
|
}
|
|
12231
12345
|
for (let depth = frames.length - 1; depth >= 0; depth -= 1) {
|
|
@@ -12236,8 +12350,9 @@ function callAt(text, offset, dialect, reserved, known) {
|
|
|
12236
12350
|
continue;
|
|
12237
12351
|
}
|
|
12238
12352
|
return {
|
|
12239
|
-
name:
|
|
12353
|
+
name: frame.name.written,
|
|
12240
12354
|
argument: frame.commas,
|
|
12355
|
+
separators: frame.separators,
|
|
12241
12356
|
from: frame.name.from,
|
|
12242
12357
|
to: closeOf(text, tokens, index, frames.length - depth) ?? offset
|
|
12243
12358
|
};
|
|
@@ -12304,7 +12419,7 @@ var FROM = "FROM";
|
|
|
12304
12419
|
var AS$4 = "AS";
|
|
12305
12420
|
var STAR$1 = "*";
|
|
12306
12421
|
var DOT$2 = ".";
|
|
12307
|
-
var COMMA$
|
|
12422
|
+
var COMMA$7 = ",";
|
|
12308
12423
|
var OPEN$5 = "(";
|
|
12309
12424
|
var CLOSE$4 = ")";
|
|
12310
12425
|
var OVER = "OVER";
|
|
@@ -12502,7 +12617,7 @@ function tailRefusal(reader, from, depth) {
|
|
|
12502
12617
|
const tokens = reader.tokens;
|
|
12503
12618
|
const first = reader.word(tokens[from]);
|
|
12504
12619
|
if (from < tokens.length && (first === null || !TAIL_CLAUSES.has(first))) {
|
|
12505
|
-
if (reader.symbol(tokens[from]) === COMMA$
|
|
12620
|
+
if (reader.symbol(tokens[from]) === COMMA$7 || first !== null && JOINS.has(first)) return SELECT_READ_ONLY_REASONS.joins;
|
|
12506
12621
|
if (first !== null && GROUPING.has(first)) return SELECT_READ_ONLY_REASONS.aggregates;
|
|
12507
12622
|
if (first !== null && SET_OPERATIONS.has(first)) return SELECT_READ_ONLY_REASONS.combines;
|
|
12508
12623
|
return SELECT_READ_ONLY_REASONS.notSelect;
|
|
@@ -12540,7 +12655,7 @@ function parseSimpleSelect(sql, dialect) {
|
|
|
12540
12655
|
if (refusal !== null) return { readOnly: refusal };
|
|
12541
12656
|
const items = [];
|
|
12542
12657
|
let itemFrom = at;
|
|
12543
|
-
for (let index = at; index <= fromAt; index += 1) if (index === fromAt || depth[index] === 0 && reader.symbol(tokens[index]) === COMMA$
|
|
12658
|
+
for (let index = at; index <= fromAt; index += 1) if (index === fromAt || depth[index] === 0 && reader.symbol(tokens[index]) === COMMA$7) {
|
|
12544
12659
|
if (index === itemFrom) return { readOnly: SELECT_READ_ONLY_REASONS.notSelect };
|
|
12545
12660
|
items.push(selectItem(reader, itemFrom, index));
|
|
12546
12661
|
itemFrom = index + 1;
|
|
@@ -13315,6 +13430,47 @@ var QUOTES = /* @__PURE__ */ new Set([
|
|
|
13315
13430
|
/** Below every other namespace: `USE mysql` is legitimate but rarely meant. */
|
|
13316
13431
|
var SYSTEM_NAMESPACE_BOOST = -20;
|
|
13317
13432
|
var CURRENT_DETAIL = "current";
|
|
13433
|
+
var COMMA$6 = ",";
|
|
13434
|
+
var MEMBER_SEPARATOR = ".";
|
|
13435
|
+
/**
|
|
13436
|
+
* Walks `separators` through `signature`: a comma moves to the next parameter without a lead, a
|
|
13437
|
+
* word to the next one it leads. A word nothing leads is an argument's own (`YEAR`, `DECIMAL`).
|
|
13438
|
+
* Past the last parameter a comma keeps counting, as the hint's variadic rule expects; a variadic
|
|
13439
|
+
* parameter keyword-led ones follow (`CHAR(code... USING charset)`) takes every comma.
|
|
13440
|
+
*/
|
|
13441
|
+
function argumentAt(signature, separators) {
|
|
13442
|
+
const { parameters } = signature;
|
|
13443
|
+
let at = 0;
|
|
13444
|
+
let entered = false;
|
|
13445
|
+
let led = 0;
|
|
13446
|
+
for (const separator of separators) {
|
|
13447
|
+
if (separator === COMMA$6) {
|
|
13448
|
+
const next = parameters.findIndex((parameter, index) => index > at && parameter.lead === void 0);
|
|
13449
|
+
if (next >= 0) at = next;
|
|
13450
|
+
else if (!(parameters[at]?.variadic === true && at < parameters.length - 1)) at += 1;
|
|
13451
|
+
entered = true;
|
|
13452
|
+
continue;
|
|
13453
|
+
}
|
|
13454
|
+
const next = parameters.findIndex((parameter, index) => (index > at || index === at && !entered) && parameter.lead !== void 0 && leadMatches(parameter.lead, separator));
|
|
13455
|
+
if (next < 0) continue;
|
|
13456
|
+
at = next;
|
|
13457
|
+
entered = true;
|
|
13458
|
+
led += 1;
|
|
13459
|
+
}
|
|
13460
|
+
return {
|
|
13461
|
+
argument: at,
|
|
13462
|
+
led
|
|
13463
|
+
};
|
|
13464
|
+
}
|
|
13465
|
+
/** The argument the caret is in, by the overload whose leads the call follows most; the first on a tie. */
|
|
13466
|
+
function argumentOf(spec, call) {
|
|
13467
|
+
let best = null;
|
|
13468
|
+
for (const signature of spec.signatures) {
|
|
13469
|
+
const found = argumentAt(signature, call.separators ?? []);
|
|
13470
|
+
if (best === null || found.led > best.led) best = found;
|
|
13471
|
+
}
|
|
13472
|
+
return best?.argument ?? call.argument;
|
|
13473
|
+
}
|
|
13318
13474
|
function relationKind(relation) {
|
|
13319
13475
|
return relation.kind === "table" ? "table" : "view";
|
|
13320
13476
|
}
|
|
@@ -13331,6 +13487,11 @@ function sqlCompleter(options) {
|
|
|
13331
13487
|
function specNamed(name, server) {
|
|
13332
13488
|
return specs.get(name.toUpperCase())?.find((spec) => isAvailable(spec, server));
|
|
13333
13489
|
}
|
|
13490
|
+
/** The members of the package `qualifier` names, as offered after its dot. */
|
|
13491
|
+
function memberItems(qualifier, server) {
|
|
13492
|
+
const name = qualifier.map((part) => part.name.toUpperCase()).join(MEMBER_SEPARATOR);
|
|
13493
|
+
return availableFunctions(server, true).filter((spec) => typeof spec !== "string" && functionPackage(spec) === name).map((spec) => memberItem(spec, BOOST.function));
|
|
13494
|
+
}
|
|
13334
13495
|
function quoted(name) {
|
|
13335
13496
|
return options.plainIdentifier.test(name) && !reserved.has(name.toUpperCase()) ? name : options.quoteIdentifier(name);
|
|
13336
13497
|
}
|
|
@@ -13355,10 +13516,12 @@ function sqlCompleter(options) {
|
|
|
13355
13516
|
function keywordItems(except = /* @__PURE__ */ new Set()) {
|
|
13356
13517
|
return options.keywords.filter((keyword) => !except.has(keyword.toUpperCase())).map((keyword) => item("keyword", keyword, keyword, null));
|
|
13357
13518
|
}
|
|
13358
|
-
|
|
13519
|
+
/** Plain functions, or with `members` the package members only: a bare name never offers `pkg.member`. */
|
|
13520
|
+
function availableFunctions(server, members = false) {
|
|
13359
13521
|
const offered = /* @__PURE__ */ new Set();
|
|
13360
13522
|
return options.functions.filter((spec) => {
|
|
13361
13523
|
const name = functionName(spec).toUpperCase();
|
|
13524
|
+
if (functionPackage(spec) !== null !== members) return false;
|
|
13362
13525
|
if (offered.has(name) || typeof spec !== "string" && !isAvailable(spec, server)) return false;
|
|
13363
13526
|
offered.add(name);
|
|
13364
13527
|
return true;
|
|
@@ -13369,17 +13532,18 @@ function sqlCompleter(options) {
|
|
|
13369
13532
|
const last = qualifier.at(-1);
|
|
13370
13533
|
if (last === void 0) return [];
|
|
13371
13534
|
const relational = clause === "relation" || clause === "join";
|
|
13535
|
+
const members = relational ? [] : memberItems(qualifier, lookup.server);
|
|
13372
13536
|
if (qualifier.length === 1 && !relational) {
|
|
13373
13537
|
const source = sourceNamed(query, last);
|
|
13374
|
-
if (source !== void 0) return columnItems(await lookup.columnsOf(source, query));
|
|
13538
|
+
if (source !== void 0) return [...columnItems(await lookup.columnsOf(source, query)), ...members];
|
|
13375
13539
|
const cte = cteNamed(query, last);
|
|
13376
|
-
if (cte !== void 0) return columnItems(await lookup.cteColumns(cte, 1));
|
|
13540
|
+
if (cte !== void 0) return [...columnItems(await lookup.cteColumns(cte, 1)), ...members];
|
|
13377
13541
|
}
|
|
13378
13542
|
const namespace = await lookup.namespace(qualifier);
|
|
13379
|
-
if (namespace !== void 0) return relationItems(await lookup.relations(namespace.path), null);
|
|
13543
|
+
if (namespace !== void 0) return [...members, ...relationItems(await lookup.relations(namespace.path), null)];
|
|
13380
13544
|
if (relational) return [];
|
|
13381
13545
|
const found = await lookup.relation(qualifier.slice(0, -1), last);
|
|
13382
|
-
return found === null ?
|
|
13546
|
+
return found === null ? members : [...columnItems(found.relation.columns), ...members];
|
|
13383
13547
|
}
|
|
13384
13548
|
async function relationContextItems(scope, lookup) {
|
|
13385
13549
|
const current = lookup.current;
|
|
@@ -13688,14 +13852,18 @@ function sqlCompleter(options) {
|
|
|
13688
13852
|
function signatureAt(request, catalog) {
|
|
13689
13853
|
const { text, offset, shift } = wrap(request);
|
|
13690
13854
|
const server = serverOf(catalog);
|
|
13691
|
-
const
|
|
13855
|
+
const callable = (name) => {
|
|
13856
|
+
const spec = specNamed(name, server);
|
|
13857
|
+
return spec !== void 0 && spec.bare !== true;
|
|
13858
|
+
};
|
|
13859
|
+
const call = callAt(text, offset, options.dialect, reserved, callable);
|
|
13692
13860
|
const spec = call === null ? void 0 : specNamed(call.name, server);
|
|
13693
13861
|
if (call === null || spec === void 0) return Promise.resolve(null);
|
|
13694
13862
|
return Promise.resolve({
|
|
13695
13863
|
name: spec.name.toLowerCase(),
|
|
13696
13864
|
summary: spec.summary,
|
|
13697
13865
|
signatures: spec.signatures,
|
|
13698
|
-
argument: call
|
|
13866
|
+
argument: argumentOf(spec, call),
|
|
13699
13867
|
from: call.from - shift,
|
|
13700
13868
|
to: call.to - shift
|
|
13701
13869
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tablecraft-app/plugin-postgres",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.4",
|
|
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.4.0"
|
|
28
28
|
}
|
|
29
29
|
}
|