@tablecraft-app/plugin-duckdb-linux-x64 1.28.0 → 1.35.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 +161 -35
- package/package.json +2 -2
- package/table-craft-plugin.json +4 -3
package/index.js
CHANGED
|
@@ -52,10 +52,12 @@ var PluginError = class extends Error {
|
|
|
52
52
|
code;
|
|
53
53
|
name = "PluginError";
|
|
54
54
|
position;
|
|
55
|
+
sent;
|
|
55
56
|
constructor(code, message, options) {
|
|
56
57
|
super(message, options);
|
|
57
58
|
this.code = code;
|
|
58
59
|
this.position = options?.position;
|
|
60
|
+
if (options?.sent !== void 0) this.sent = options.sent;
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
63
|
/**
|
|
@@ -5535,7 +5537,8 @@ var CAPABILITIES$1 = [
|
|
|
5535
5537
|
"maintenance",
|
|
5536
5538
|
"coordination",
|
|
5537
5539
|
"bulkTransfer",
|
|
5538
|
-
"savepoints"
|
|
5540
|
+
"savepoints",
|
|
5541
|
+
"rowLimit"
|
|
5539
5542
|
];
|
|
5540
5543
|
/**
|
|
5541
5544
|
* Whether a switch over `targets` can name `namespace`. Not by `Namespace.kind` alone: MySQL lists
|
|
@@ -5593,6 +5596,8 @@ var connectionFieldSchema = strictObject({
|
|
|
5593
5596
|
]).optional(),
|
|
5594
5597
|
required: boolean().optional(),
|
|
5595
5598
|
placeholder: string$1().optional(),
|
|
5599
|
+
/** Explains the field in an (i) beside it: what it does, its unit, and what blank means. */
|
|
5600
|
+
hint: string$1().min(1).optional(),
|
|
5596
5601
|
/** The choices of a `select`, in display order. */
|
|
5597
5602
|
options: array(strictObject({
|
|
5598
5603
|
value: string$1(),
|
|
@@ -5824,6 +5829,9 @@ strictObject({
|
|
|
5824
5829
|
}).refine((manifest) => !manifest.capabilities.includes("savepoints") || manifest.capabilities.includes("sql") && manifest.capabilities.includes("transactions"), {
|
|
5825
5830
|
message: "the savepoints capability requires sql and transactions",
|
|
5826
5831
|
path: ["capabilities"]
|
|
5832
|
+
}).refine((manifest) => !manifest.capabilities.includes("rowLimit") || manifest.capabilities.includes("sql"), {
|
|
5833
|
+
message: "the rowLimit capability requires sql",
|
|
5834
|
+
path: ["capabilities"]
|
|
5827
5835
|
});
|
|
5828
5836
|
// ../plugin-api/src/services.ts
|
|
5829
5837
|
var SESSION_KEYS = {
|
|
@@ -5854,7 +5862,8 @@ var LANGUAGE_KEYS = {
|
|
|
5854
5862
|
bulkInsert: true,
|
|
5855
5863
|
objects: true,
|
|
5856
5864
|
routineCall: true,
|
|
5857
|
-
savepoints: true
|
|
5865
|
+
savepoints: true,
|
|
5866
|
+
rowLimit: true
|
|
5858
5867
|
};
|
|
5859
5868
|
Object.keys(SESSION_KEYS);
|
|
5860
5869
|
Object.keys(LANGUAGE_KEYS);
|
|
@@ -16436,7 +16445,7 @@ var NOT_A_NAME$1 = /* @__PURE__ */ new Set([
|
|
|
16436
16445
|
"NULL"
|
|
16437
16446
|
]);
|
|
16438
16447
|
var AS$5 = "AS";
|
|
16439
|
-
var WITH$
|
|
16448
|
+
var WITH$6 = "WITH";
|
|
16440
16449
|
var RECURSIVE$1 = "RECURSIVE";
|
|
16441
16450
|
var CTE_MODIFIERS$1 = /* @__PURE__ */ new Set(["NOT", "MATERIALIZED"]);
|
|
16442
16451
|
var SELECT_MODIFIERS$2 = /* @__PURE__ */ new Set(["DISTINCT", "ALL"]);
|
|
@@ -16592,7 +16601,7 @@ var QueryReader = class {
|
|
|
16592
16601
|
}
|
|
16593
16602
|
read() {
|
|
16594
16603
|
const statement = this.#statement;
|
|
16595
|
-
if (statement.word(this.#index()) === WITH$
|
|
16604
|
+
if (statement.word(this.#index()) === WITH$6) this.#readCtes();
|
|
16596
16605
|
let expectRelation = false;
|
|
16597
16606
|
let fromList = false;
|
|
16598
16607
|
let into = false;
|
|
@@ -17048,7 +17057,7 @@ var SELECT_READ_ONLY_REASONS = {
|
|
|
17048
17057
|
derived: "the result reads a subquery or function, not a table",
|
|
17049
17058
|
noTable: "the result reads no table"
|
|
17050
17059
|
};
|
|
17051
|
-
var SELECT$
|
|
17060
|
+
var SELECT$2 = "SELECT";
|
|
17052
17061
|
var FROM = "FROM";
|
|
17053
17062
|
var AS$4 = "AS";
|
|
17054
17063
|
var STAR$1 = "*";
|
|
@@ -17060,7 +17069,7 @@ var OVER = "OVER";
|
|
|
17060
17069
|
var ONLY = "ONLY";
|
|
17061
17070
|
var ON$2 = "ON";
|
|
17062
17071
|
var NOT = "NOT";
|
|
17063
|
-
var WITH$
|
|
17072
|
+
var WITH$5 = "WITH";
|
|
17064
17073
|
var DISTINCT = /* @__PURE__ */ new Set([
|
|
17065
17074
|
"DISTINCT",
|
|
17066
17075
|
"DISTINCTROW",
|
|
@@ -17087,7 +17096,7 @@ var JOINS = /* @__PURE__ */ new Set([
|
|
|
17087
17096
|
"OUTER"
|
|
17088
17097
|
]);
|
|
17089
17098
|
var GROUPING = /* @__PURE__ */ new Set(["GROUP", "HAVING"]);
|
|
17090
|
-
var SET_OPERATIONS = /* @__PURE__ */ new Set([
|
|
17099
|
+
var SET_OPERATIONS$1 = /* @__PURE__ */ new Set([
|
|
17091
17100
|
"UNION",
|
|
17092
17101
|
"INTERSECT",
|
|
17093
17102
|
"EXCEPT",
|
|
@@ -17185,7 +17194,7 @@ var Reader = class {
|
|
|
17185
17194
|
if (symbol === CLOSE$4) subqueries.pop();
|
|
17186
17195
|
depth.push(subqueries.length);
|
|
17187
17196
|
inSubquery.push(subqueries.some(Boolean));
|
|
17188
|
-
if (symbol === OPEN$5) subqueries.push(this.word(this.tokens[index + 1]) === SELECT$
|
|
17197
|
+
if (symbol === OPEN$5) subqueries.push(this.word(this.tokens[index + 1]) === SELECT$2);
|
|
17189
17198
|
});
|
|
17190
17199
|
return {
|
|
17191
17200
|
depth,
|
|
@@ -17253,7 +17262,7 @@ function tailRefusal(reader, from, depth) {
|
|
|
17253
17262
|
if (from < tokens.length && (first === null || !TAIL_CLAUSES.has(first))) {
|
|
17254
17263
|
if (reader.symbol(tokens[from]) === COMMA$7 || first !== null && JOINS.has(first)) return SELECT_READ_ONLY_REASONS.joins;
|
|
17255
17264
|
if (first !== null && GROUPING.has(first)) return SELECT_READ_ONLY_REASONS.aggregates;
|
|
17256
|
-
if (first !== null && SET_OPERATIONS.has(first)) return SELECT_READ_ONLY_REASONS.combines;
|
|
17265
|
+
if (first !== null && SET_OPERATIONS$1.has(first)) return SELECT_READ_ONLY_REASONS.combines;
|
|
17257
17266
|
return SELECT_READ_ONLY_REASONS.notSelect;
|
|
17258
17267
|
}
|
|
17259
17268
|
for (let at = from; at < tokens.length; at += 1) {
|
|
@@ -17261,7 +17270,7 @@ function tailRefusal(reader, from, depth) {
|
|
|
17261
17270
|
const word = reader.word(tokens[at]);
|
|
17262
17271
|
if (word === null) continue;
|
|
17263
17272
|
if (GROUPING.has(word)) return SELECT_READ_ONLY_REASONS.aggregates;
|
|
17264
|
-
if (SET_OPERATIONS.has(word)) return SELECT_READ_ONLY_REASONS.combines;
|
|
17273
|
+
if (SET_OPERATIONS$1.has(word)) return SELECT_READ_ONLY_REASONS.combines;
|
|
17265
17274
|
if (JOINS.has(word) || word === ON$2 && at > from) return SELECT_READ_ONLY_REASONS.joins;
|
|
17266
17275
|
if (OTHER_TAIL.has(word) && word !== NOT) return SELECT_READ_ONLY_REASONS.notSelect;
|
|
17267
17276
|
}
|
|
@@ -17275,9 +17284,9 @@ function parseSimpleSelect(sql, dialect) {
|
|
|
17275
17284
|
const tokens = significantTokens(sql, dialect).filter((token) => token.kind !== "delimiter");
|
|
17276
17285
|
const reader = new Reader(sql, tokens);
|
|
17277
17286
|
const head = reader.word(tokens[0]);
|
|
17278
|
-
if (head === WITH$
|
|
17287
|
+
if (head === WITH$5) return { readOnly: SELECT_READ_ONLY_REASONS.with };
|
|
17279
17288
|
if (reader.symbol(tokens[0]) === OPEN$5) return { readOnly: SELECT_READ_ONLY_REASONS.combines };
|
|
17280
|
-
if (head !== SELECT$
|
|
17289
|
+
if (head !== SELECT$2) return { readOnly: SELECT_READ_ONLY_REASONS.notSelect };
|
|
17281
17290
|
const { depth, inSubquery } = reader.depths();
|
|
17282
17291
|
let at = 1;
|
|
17283
17292
|
const modifier = reader.word(tokens[at]);
|
|
@@ -17322,7 +17331,7 @@ function parseSimpleSelect(sql, dialect) {
|
|
|
17322
17331
|
};
|
|
17323
17332
|
}
|
|
17324
17333
|
function isClauseWord(word) {
|
|
17325
|
-
return TAIL_CLAUSES.has(word) || JOINS.has(word) || GROUPING.has(word) || SET_OPERATIONS.has(word) || OTHER_TAIL.has(word) || word === ON$2 || word === ONLY;
|
|
17334
|
+
return TAIL_CLAUSES.has(word) || JOINS.has(word) || GROUPING.has(word) || SET_OPERATIONS$1.has(word) || OTHER_TAIL.has(word) || word === ON$2 || word === ONLY;
|
|
17326
17335
|
}
|
|
17327
17336
|
// ../plugin-api/src/sql/namespace.ts
|
|
17328
17337
|
var USE = "USE";
|
|
@@ -18530,10 +18539,10 @@ function sqlCompleter(options) {
|
|
|
18530
18539
|
};
|
|
18531
18540
|
}
|
|
18532
18541
|
// ../plugin-api/src/sql/words.ts
|
|
18533
|
-
var OPEN_PAREN = "(";
|
|
18534
|
-
var CLOSE_PAREN = ")";
|
|
18535
|
-
var WITH$
|
|
18536
|
-
var MAIN_VERBS = /* @__PURE__ */ new Set([
|
|
18542
|
+
var OPEN_PAREN$1 = "(";
|
|
18543
|
+
var CLOSE_PAREN$1 = ")";
|
|
18544
|
+
var WITH$4 = "WITH";
|
|
18545
|
+
var MAIN_VERBS$1 = /* @__PURE__ */ new Set([
|
|
18537
18546
|
"SELECT",
|
|
18538
18547
|
"INSERT",
|
|
18539
18548
|
"UPDATE",
|
|
@@ -18546,8 +18555,8 @@ function words(sql, tokens) {
|
|
|
18546
18555
|
let depth = 0;
|
|
18547
18556
|
for (const token of tokens) if (token.kind === "symbol") {
|
|
18548
18557
|
const char = sql.charAt(token.from);
|
|
18549
|
-
if (char === OPEN_PAREN) depth += 1;
|
|
18550
|
-
else if (char === CLOSE_PAREN) depth = Math.max(0, depth - 1);
|
|
18558
|
+
if (char === OPEN_PAREN$1) depth += 1;
|
|
18559
|
+
else if (char === CLOSE_PAREN$1) depth = Math.max(0, depth - 1);
|
|
18551
18560
|
} else if (token.kind === "word") found.push({
|
|
18552
18561
|
word: wordOf(sql, token),
|
|
18553
18562
|
depth
|
|
@@ -18556,8 +18565,8 @@ function words(sql, tokens) {
|
|
|
18556
18565
|
}
|
|
18557
18566
|
/** The verb that decides what a `WITH …` statement does: the first one outside the CTEs' parentheses. */
|
|
18558
18567
|
function mainVerb(list) {
|
|
18559
|
-
if (list[0]?.word !== WITH$
|
|
18560
|
-
const index = list.findIndex((entry, at) => at > 0 && entry.depth === 0 && MAIN_VERBS.has(entry.word));
|
|
18568
|
+
if (list[0]?.word !== WITH$4) return 0;
|
|
18569
|
+
const index = list.findIndex((entry, at) => at > 0 && entry.depth === 0 && MAIN_VERBS$1.has(entry.word));
|
|
18561
18570
|
return index < 0 ? 0 : index;
|
|
18562
18571
|
}
|
|
18563
18572
|
/** MySQL reads `DESCRIBE` and `DESC` before a statement as `EXPLAIN`, `ANALYZE` included. */
|
|
@@ -18620,7 +18629,7 @@ var BY_OWNERS$1 = /* @__PURE__ */ new Set([
|
|
|
18620
18629
|
var ON$1 = "ON";
|
|
18621
18630
|
/** `ON DUPLICATE KEY UPDATE` and `ON CONFLICT`: an `ON` that starts an action, not a join condition. */
|
|
18622
18631
|
var ON_ACTIONS = /* @__PURE__ */ new Set(["DUPLICATE", "CONFLICT"]);
|
|
18623
|
-
var WITH$
|
|
18632
|
+
var WITH$3 = "WITH";
|
|
18624
18633
|
var CREATE$1 = "CREATE";
|
|
18625
18634
|
var AS$3 = "AS";
|
|
18626
18635
|
/** Statements whose clauses the inspections understand; anything else gets only the lexical checks. */
|
|
@@ -18632,7 +18641,7 @@ var INSPECTED_VERBS = /* @__PURE__ */ new Set([
|
|
|
18632
18641
|
"REPLACE"
|
|
18633
18642
|
]);
|
|
18634
18643
|
var CREATED_FROM_QUERY = /* @__PURE__ */ new Set(["TABLE", "VIEW"]);
|
|
18635
|
-
var QUERY_STARTS = /* @__PURE__ */ new Set(["SELECT", WITH$
|
|
18644
|
+
var QUERY_STARTS = /* @__PURE__ */ new Set(["SELECT", WITH$3]);
|
|
18636
18645
|
/** Words an expression may hold that no built-in keyword list names, so they are never taken for columns. */
|
|
18637
18646
|
var EXPRESSION_WORDS = [
|
|
18638
18647
|
"INTERVAL",
|
|
@@ -19272,7 +19281,7 @@ var ASC = "ASC";
|
|
|
19272
19281
|
var ELSE$1 = "ELSE";
|
|
19273
19282
|
var END$3 = "END";
|
|
19274
19283
|
var AS$1 = "AS";
|
|
19275
|
-
var WITH$
|
|
19284
|
+
var WITH$2 = "WITH";
|
|
19276
19285
|
var ROW = "ROW";
|
|
19277
19286
|
var VALUES_WORDS = /* @__PURE__ */ new Set(["VALUES", "VALUE"]);
|
|
19278
19287
|
var RECURSIVE = "RECURSIVE";
|
|
@@ -19567,7 +19576,7 @@ function cteRemoval(view, nameIndex, close) {
|
|
|
19567
19576
|
}
|
|
19568
19577
|
const withIndex = view.word(nameIndex - 1) === RECURSIVE ? nameIndex - 2 : nameIndex - 1;
|
|
19569
19578
|
const withToken = view.token(withIndex);
|
|
19570
|
-
if (view.word(withIndex) !== WITH$
|
|
19579
|
+
if (view.word(withIndex) !== WITH$2 || withToken === void 0 || following === void 0) return null;
|
|
19571
19580
|
return removal("Remove CTE", {
|
|
19572
19581
|
from: withToken.from,
|
|
19573
19582
|
to: following.from
|
|
@@ -19652,7 +19661,7 @@ var OPEN$1 = "(";
|
|
|
19652
19661
|
var CLOSE$1 = ")";
|
|
19653
19662
|
var COMMA$3 = ",";
|
|
19654
19663
|
var BY = "BY";
|
|
19655
|
-
var SELECT = "SELECT";
|
|
19664
|
+
var SELECT$1 = "SELECT";
|
|
19656
19665
|
var EXPRESSION_EXPECTED = "Expression expected";
|
|
19657
19666
|
/** Keywords that start the next clause, so a clause keyword right before one has no body. */
|
|
19658
19667
|
var CLAUSE_STARTS = /* @__PURE__ */ new Set([
|
|
@@ -19689,7 +19698,7 @@ var NEEDS_BODY = /* @__PURE__ */ new Set([
|
|
|
19689
19698
|
"HAVING",
|
|
19690
19699
|
"ON",
|
|
19691
19700
|
"SET",
|
|
19692
|
-
SELECT
|
|
19701
|
+
SELECT$1
|
|
19693
19702
|
]);
|
|
19694
19703
|
var BY_OWNERS = /* @__PURE__ */ new Set([
|
|
19695
19704
|
"ORDER",
|
|
@@ -19733,14 +19742,14 @@ function emptyClause(view, index) {
|
|
|
19733
19742
|
if (owner === null || !BY_OWNERS.has(owner)) return null;
|
|
19734
19743
|
first = index - 1;
|
|
19735
19744
|
} else if (!NEEDS_BODY.has(word)) return null;
|
|
19736
|
-
if (word === SELECT) while (SELECT_MODIFIERS$1.has(view.word(last + 1) ?? "")) last += 1;
|
|
19745
|
+
if (word === SELECT$1) while (SELECT_MODIFIERS$1.has(view.word(last + 1) ?? "")) last += 1;
|
|
19737
19746
|
if (!nothingFollows(view, last)) return null;
|
|
19738
19747
|
const start = view.token(first);
|
|
19739
19748
|
const end = view.token(last);
|
|
19740
19749
|
if (start === void 0 || end === void 0) return null;
|
|
19741
19750
|
const label = view.text.slice(start.from, end.to).toUpperCase().replace(/\s+/g, " ");
|
|
19742
19751
|
const fixes = [];
|
|
19743
|
-
if (word !== SELECT) {
|
|
19752
|
+
if (word !== SELECT$1) {
|
|
19744
19753
|
const following = view.token(last + 1);
|
|
19745
19754
|
const span = following === void 0 || view.isSymbol(last + 1, CLOSE$1) ? view.withLeadingSpace(first, last) : {
|
|
19746
19755
|
from: start.from,
|
|
@@ -19984,7 +19993,7 @@ var START$1 = "START";
|
|
|
19984
19993
|
var TRANSACTION = "TRANSACTION";
|
|
19985
19994
|
var WHERE = "WHERE";
|
|
19986
19995
|
var LIMIT$1 = "LIMIT";
|
|
19987
|
-
var WITH = "WITH";
|
|
19996
|
+
var WITH$1 = "WITH";
|
|
19988
19997
|
/** Prefixes the explained statement's destructive reason, so the confirmation says why it matters. */
|
|
19989
19998
|
var EXPLAIN_ANALYZE_REASON_PREFIX = "EXPLAIN ANALYZE runs it: ";
|
|
19990
19999
|
function hasTopLevel(list, from, word) {
|
|
@@ -20043,7 +20052,7 @@ function classifyWords(list) {
|
|
|
20043
20052
|
const verbAt = mainVerb(list);
|
|
20044
20053
|
const verb = list[verbAt]?.word ?? first;
|
|
20045
20054
|
return {
|
|
20046
|
-
kind: first === WITH && verbAt === 0 ? "read" : KINDS.get(verb) ?? "other",
|
|
20055
|
+
kind: first === WITH$1 && verbAt === 0 ? "read" : KINDS.get(verb) ?? "other",
|
|
20047
20056
|
destructive: destructiveReason(list, verbAt)
|
|
20048
20057
|
};
|
|
20049
20058
|
}
|
|
@@ -28350,7 +28359,7 @@ var SET_OPERATORS = /* @__PURE__ */ new Set([
|
|
|
28350
28359
|
"EXCEPT",
|
|
28351
28360
|
"MINUS"
|
|
28352
28361
|
]);
|
|
28353
|
-
var SET_QUANTIFIERS = /* @__PURE__ */ new Set(["ALL", "DISTINCT"]);
|
|
28362
|
+
var SET_QUANTIFIERS$1 = /* @__PURE__ */ new Set(["ALL", "DISTINCT"]);
|
|
28354
28363
|
var SELECT_MODIFIERS = /* @__PURE__ */ new Set([
|
|
28355
28364
|
"ALL",
|
|
28356
28365
|
"DISTINCT",
|
|
@@ -28637,7 +28646,7 @@ var AlignedPrinter = class {
|
|
|
28637
28646
|
if (node.type === "tok" && node.keyword && SET_OPERATORS.has(node.upper)) {
|
|
28638
28647
|
const operator = [node];
|
|
28639
28648
|
const quantifier = nodes[index + 1];
|
|
28640
|
-
if (quantifier?.type === "tok" && quantifier.keyword && SET_QUANTIFIERS.has(quantifier.upper)) {
|
|
28649
|
+
if (quantifier?.type === "tok" && quantifier.keyword && SET_QUANTIFIERS$1.has(quantifier.upper)) {
|
|
28641
28650
|
operator.push(quantifier);
|
|
28642
28651
|
index += 1;
|
|
28643
28652
|
}
|
|
@@ -29583,6 +29592,121 @@ function sqlExplainable(options) {
|
|
|
29583
29592
|
};
|
|
29584
29593
|
return { explainable };
|
|
29585
29594
|
}
|
|
29595
|
+
// ../plugin-api/src/sql/limit.ts
|
|
29596
|
+
var SELECT = "SELECT";
|
|
29597
|
+
var WITH = "WITH";
|
|
29598
|
+
var TOP = "TOP";
|
|
29599
|
+
var OPEN_PAREN = "(";
|
|
29600
|
+
var CLOSE_PAREN = ")";
|
|
29601
|
+
/** What may stand between `SELECT` and `TOP`. */
|
|
29602
|
+
var SET_QUANTIFIERS = /* @__PURE__ */ new Set(["ALL", "DISTINCT"]);
|
|
29603
|
+
var MAIN_VERBS = /* @__PURE__ */ new Set([
|
|
29604
|
+
"SELECT",
|
|
29605
|
+
"INSERT",
|
|
29606
|
+
"UPDATE",
|
|
29607
|
+
"DELETE",
|
|
29608
|
+
"MERGE"
|
|
29609
|
+
]);
|
|
29610
|
+
/** A top-level row limit the statement already has. */
|
|
29611
|
+
var OWN_LIMITS = /* @__PURE__ */ new Set([
|
|
29612
|
+
"LIMIT",
|
|
29613
|
+
"OFFSET",
|
|
29614
|
+
"FETCH"
|
|
29615
|
+
]);
|
|
29616
|
+
/**
|
|
29617
|
+
* Top-level words that make a SELECT more than a read of rows a limit may trim: `INTO` writes,
|
|
29618
|
+
* `FOR UPDATE` and `LOCK IN SHARE MODE` lock what they read, `PROCEDURE` hands the rows to MySQL.
|
|
29619
|
+
*/
|
|
29620
|
+
var NOT_A_PLAIN_READ = /* @__PURE__ */ new Set([
|
|
29621
|
+
"INTO",
|
|
29622
|
+
"FOR",
|
|
29623
|
+
"LOCK",
|
|
29624
|
+
"PROCEDURE"
|
|
29625
|
+
]);
|
|
29626
|
+
/** A data-modifying CTE or subquery runs whatever the limit trims. */
|
|
29627
|
+
var WRITES = /* @__PURE__ */ new Set([
|
|
29628
|
+
"INSERT",
|
|
29629
|
+
"UPDATE",
|
|
29630
|
+
"DELETE",
|
|
29631
|
+
"MERGE"
|
|
29632
|
+
]);
|
|
29633
|
+
/** `TOP` limits only the SELECT it opens, not the whole combined query. */
|
|
29634
|
+
var SET_OPERATIONS = /* @__PURE__ */ new Set([
|
|
29635
|
+
"UNION",
|
|
29636
|
+
"INTERSECT",
|
|
29637
|
+
"EXCEPT",
|
|
29638
|
+
"MINUS"
|
|
29639
|
+
]);
|
|
29640
|
+
function placedWords(sql, tokens) {
|
|
29641
|
+
const found = [];
|
|
29642
|
+
let depth = 0;
|
|
29643
|
+
for (const token of tokens) if (token.kind === "symbol") {
|
|
29644
|
+
const char = sql.charAt(token.from);
|
|
29645
|
+
if (char === OPEN_PAREN) depth += 1;
|
|
29646
|
+
else if (char === CLOSE_PAREN) depth = Math.max(0, depth - 1);
|
|
29647
|
+
} else if (token.kind === "word") found.push({
|
|
29648
|
+
word: wordOf(sql, token),
|
|
29649
|
+
depth,
|
|
29650
|
+
token
|
|
29651
|
+
});
|
|
29652
|
+
return found;
|
|
29653
|
+
}
|
|
29654
|
+
function mainSelect(list) {
|
|
29655
|
+
const first = list[0];
|
|
29656
|
+
if (first === void 0 || first.depth !== 0) return null;
|
|
29657
|
+
if (first.word === SELECT) return 0;
|
|
29658
|
+
if (first.word !== WITH) return null;
|
|
29659
|
+
const verb = list.findIndex((entry, at) => at > 0 && entry.depth === 0 && MAIN_VERBS.has(entry.word));
|
|
29660
|
+
return verb > 0 && list[verb]?.word === SELECT ? verb : null;
|
|
29661
|
+
}
|
|
29662
|
+
/** The statement's tokens up to its delimiter; null when another statement follows it. */
|
|
29663
|
+
function ownTokens(tokens) {
|
|
29664
|
+
const end = tokens.findIndex((token) => token.kind === "delimiter");
|
|
29665
|
+
if (end < 0) return tokens;
|
|
29666
|
+
return tokens.slice(end).every((token) => token.kind === "delimiter") ? tokens.slice(0, end) : null;
|
|
29667
|
+
}
|
|
29668
|
+
function insert(sql, offset, text) {
|
|
29669
|
+
return {
|
|
29670
|
+
sql: sql.slice(0, offset) + text + sql.slice(offset),
|
|
29671
|
+
inserted: {
|
|
29672
|
+
offset,
|
|
29673
|
+
length: text.length
|
|
29674
|
+
}
|
|
29675
|
+
};
|
|
29676
|
+
}
|
|
29677
|
+
function topOffset(list, verb) {
|
|
29678
|
+
let at = verb + 1;
|
|
29679
|
+
if (SET_QUANTIFIERS.has(list[at]?.word ?? "")) at += 1;
|
|
29680
|
+
if (list[at]?.word === TOP) return null;
|
|
29681
|
+
return list[at - 1]?.token.to ?? null;
|
|
29682
|
+
}
|
|
29683
|
+
/**
|
|
29684
|
+
* The `rowLimit` language service for a dialect (decision 162). It limits a plain SELECT, `WITH`
|
|
29685
|
+
* included, by one insertion, and answers null for anything it cannot limit without changing what
|
|
29686
|
+
* the statement does or reading it wrongly: another verb, a limit of its own, `INTO`, a locking
|
|
29687
|
+
* read, a data-modifying CTE, more than one statement, and under `top` a combined query.
|
|
29688
|
+
*/
|
|
29689
|
+
function sqlRowLimiter(options, style) {
|
|
29690
|
+
return { limit(sql, rows) {
|
|
29691
|
+
if (!Number.isSafeInteger(rows) || rows < 1) throw new RangeError(`A row limit is a positive integer, not ${rows}`);
|
|
29692
|
+
const tokens = ownTokens(significantTokens(sql, options));
|
|
29693
|
+
const last = tokens?.at(-1);
|
|
29694
|
+
if (tokens === null || last === void 0) return null;
|
|
29695
|
+
const list = placedWords(sql, tokens);
|
|
29696
|
+
const verb = mainSelect(list);
|
|
29697
|
+
if (verb === null) return null;
|
|
29698
|
+
if (list.some((entry) => WRITES.has(entry.word))) return null;
|
|
29699
|
+
const top = list.slice(verb + 1).filter((entry) => entry.depth === 0);
|
|
29700
|
+
if (top.some((entry) => OWN_LIMITS.has(entry.word) || NOT_A_PLAIN_READ.has(entry.word))) return null;
|
|
29701
|
+
if (style === "top") {
|
|
29702
|
+
if (top.some((entry) => SET_OPERATIONS.has(entry.word))) return null;
|
|
29703
|
+
const offset = topOffset(list, verb);
|
|
29704
|
+
return offset === null ? null : insert(sql, offset, ` ${TOP} (${rows})`);
|
|
29705
|
+
}
|
|
29706
|
+
const clause = style === "limit" ? ` LIMIT ${rows}` : ` FETCH FIRST ${rows} ROWS ONLY`;
|
|
29707
|
+
return insert(sql, last.to, clause);
|
|
29708
|
+
} };
|
|
29709
|
+
}
|
|
29586
29710
|
// ../../plugins/duckdb/src/explain.ts
|
|
29587
29711
|
/** What DuckDB's `EXPLAIN` plans; anything else answers an empty tree. */
|
|
29588
29712
|
var EXPLAIN_VERBS = [
|
|
@@ -40325,7 +40449,8 @@ var CAPABILITIES = [
|
|
|
40325
40449
|
"ddl",
|
|
40326
40450
|
"explain",
|
|
40327
40451
|
"explainAnalyze",
|
|
40328
|
-
"bulkInsert"
|
|
40452
|
+
"bulkInsert",
|
|
40453
|
+
"rowLimit"
|
|
40329
40454
|
];
|
|
40330
40455
|
/** DuckDB reads PostgreSQL's lexis: dollar quotes and nested block comments. */
|
|
40331
40456
|
var DIALECT = {
|
|
@@ -40430,7 +40555,8 @@ var language = {
|
|
|
40430
40555
|
keywords: KEYWORDS,
|
|
40431
40556
|
types: IMPORT_TYPES,
|
|
40432
40557
|
decimal: duckdbDecimal
|
|
40433
|
-
})
|
|
40558
|
+
}),
|
|
40559
|
+
rowLimit: sqlRowLimiter(DIALECT, "limit")
|
|
40434
40560
|
}
|
|
40435
40561
|
};
|
|
40436
40562
|
// ../../plugins/duckdb/src/errorPosition.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tablecraft-app/plugin-duckdb-linux-x64",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"description": "The DuckDB datasource plugin for TableCraft, downloaded by the app when a data source needs it. This package is for linux-x64.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"tableCraft": {
|
|
29
29
|
"id": "duckdb",
|
|
30
|
-
"apiVersion": "2.
|
|
30
|
+
"apiVersion": "2.2.0",
|
|
31
31
|
"platform": "linux-x64"
|
|
32
32
|
},
|
|
33
33
|
"os": [
|
package/table-craft-plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "duckdb",
|
|
3
3
|
"name": "DuckDB",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"apiVersion": "2.
|
|
4
|
+
"version": "1.35.0",
|
|
5
|
+
"apiVersion": "2.2.0",
|
|
6
6
|
"entry": "index.js",
|
|
7
7
|
"capabilities": [
|
|
8
8
|
"sql",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"ddl",
|
|
13
13
|
"explain",
|
|
14
14
|
"explainAnalyze",
|
|
15
|
-
"bulkInsert"
|
|
15
|
+
"bulkInsert",
|
|
16
|
+
"rowLimit"
|
|
16
17
|
],
|
|
17
18
|
"highlight": "postgresql",
|
|
18
19
|
"brands": [
|