@tablecraft-app/plugin-api 0.0.0-stage → 1.2.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/LICENSE +21 -0
- package/README.md +46 -2
- package/dist/catalog.d.ts +112 -0
- package/dist/catalog.js +5 -0
- package/dist/completion.d.ts +109 -0
- package/dist/completion.js +12 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.js +124 -0
- package/dist/explain.d.ts +58 -0
- package/dist/explain.js +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/inspection.d.ts +38 -0
- package/dist/inspection.js +21 -0
- package/dist/keyspace.d.ts +180 -0
- package/dist/keyspace.js +2 -0
- package/dist/manifest.d.ts +200 -0
- package/dist/manifest.js +238 -0
- package/dist/objects.d.ts +140 -0
- package/dist/objects.js +2 -0
- package/dist/plugin.d.ts +565 -0
- package/dist/plugin.js +58 -0
- package/dist/sessions.d.ts +59 -0
- package/dist/sessions.js +2 -0
- package/dist/sql/classify.d.ts +19 -0
- package/dist/sql/classify.js +95 -0
- package/dist/sql/complete.d.ts +23 -0
- package/dist/sql/complete.js +480 -0
- package/dist/sql/explain.d.ts +42 -0
- package/dist/sql/explain.js +108 -0
- package/dist/sql/format.d.ts +26 -0
- package/dist/sql/format.js +117 -0
- package/dist/sql/functions.d.ts +47 -0
- package/dist/sql/functions.js +159 -0
- package/dist/sql/generate.d.ts +22 -0
- package/dist/sql/generate.js +64 -0
- package/dist/sql/index.d.ts +16 -0
- package/dist/sql/index.js +16 -0
- package/dist/sql/inspect.d.ts +10 -0
- package/dist/sql/inspect.js +134 -0
- package/dist/sql/inspectContext.d.ts +78 -0
- package/dist/sql/inspectContext.js +251 -0
- package/dist/sql/inspectResolve.d.ts +6 -0
- package/dist/sql/inspectResolve.js +391 -0
- package/dist/sql/inspectStyle.d.ts +3 -0
- package/dist/sql/inspectStyle.js +384 -0
- package/dist/sql/inspectSyntax.d.ts +10 -0
- package/dist/sql/inspectSyntax.js +179 -0
- package/dist/sql/layout.d.ts +8 -0
- package/dist/sql/layout.js +114 -0
- package/dist/sql/layoutNodes.d.ts +46 -0
- package/dist/sql/layoutNodes.js +317 -0
- package/dist/sql/layoutPrint.d.ts +60 -0
- package/dist/sql/layoutPrint.js +806 -0
- package/dist/sql/layoutWriter.d.ts +21 -0
- package/dist/sql/layoutWriter.js +77 -0
- package/dist/sql/literals.d.ts +28 -0
- package/dist/sql/literals.js +78 -0
- package/dist/sql/namespace.d.ts +41 -0
- package/dist/sql/namespace.js +220 -0
- package/dist/sql/objects.d.ts +42 -0
- package/dist/sql/objects.js +222 -0
- package/dist/sql/parameters.d.ts +20 -0
- package/dist/sql/parameters.js +88 -0
- package/dist/sql/resolution.d.ts +73 -0
- package/dist/sql/resolution.js +192 -0
- package/dist/sql/routines.d.ts +27 -0
- package/dist/sql/routines.js +42 -0
- package/dist/sql/scope.d.ts +117 -0
- package/dist/sql/scope.js +540 -0
- package/dist/sql/select.d.ts +41 -0
- package/dist/sql/select.js +287 -0
- package/dist/sql/signature.d.ts +18 -0
- package/dist/sql/signature.js +137 -0
- package/dist/sql/splitter.d.ts +15 -0
- package/dist/sql/splitter.js +217 -0
- package/dist/sql/suggest.d.ts +6 -0
- package/dist/sql/suggest.js +36 -0
- package/dist/sql/tokens.d.ts +62 -0
- package/dist/sql/tokens.js +227 -0
- package/dist/sql/words.d.ts +23 -0
- package/dist/sql/words.js +72 -0
- package/dist/testing/index.d.ts +274 -0
- package/dist/testing/index.js +2398 -0
- package/dist/tls.d.ts +33 -0
- package/dist/tls.js +118 -0
- package/dist/url.d.ts +54 -0
- package/dist/url.js +228 -0
- package/dist/values.d.ts +44 -0
- package/dist/values.js +44 -0
- package/dist/version.d.ts +22 -0
- package/dist/version.js +29 -0
- package/package.json +42 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** What a server session is doing, from the dialect's own state (plan 045). */
|
|
2
|
+
export declare const SERVER_SESSION_STATES: readonly ["active", "idle", "idleInTransaction", "blocked", "other"];
|
|
3
|
+
export type ServerSessionState = (typeof SERVER_SESSION_STATES)[number];
|
|
4
|
+
/**
|
|
5
|
+
* One session on the server, as `SessionMonitor.list` reads it (decision 088). Every field the
|
|
6
|
+
* dialect or the account's grants cannot supply is null.
|
|
7
|
+
*/
|
|
8
|
+
export interface ServerSession {
|
|
9
|
+
/** The server's id: PostgreSQL's pid, MySQL's connection id. Text, because TiDB's pass 2^53. */
|
|
10
|
+
readonly id: string;
|
|
11
|
+
/** The cluster member the session lives on, as the server names it; null on a single server. */
|
|
12
|
+
readonly instance: string | null;
|
|
13
|
+
readonly state: ServerSessionState;
|
|
14
|
+
/** The server's own words for the state, shown beside it. */
|
|
15
|
+
readonly rawState: string | null;
|
|
16
|
+
readonly user: string | null;
|
|
17
|
+
readonly database: string | null;
|
|
18
|
+
/** Client address, as the server reports it. */
|
|
19
|
+
readonly client: string | null;
|
|
20
|
+
readonly application: string | null;
|
|
21
|
+
/** How long the current or last query has run, by the server's clock when listed. */
|
|
22
|
+
readonly queryMs: number | null;
|
|
23
|
+
/** How long its open transaction has been open, by the server's clock; null when none is. */
|
|
24
|
+
readonly transactionMs: number | null;
|
|
25
|
+
readonly waitEvent: string | null;
|
|
26
|
+
/** Ids of the sessions this one waits on; null when the dialect or the grants cannot tell. */
|
|
27
|
+
readonly blockedBy: readonly string[] | null;
|
|
28
|
+
readonly query: string | null;
|
|
29
|
+
/** A server worker rather than a client connection. */
|
|
30
|
+
readonly system: boolean;
|
|
31
|
+
/** The exact statement `cancelSession` runs for it; null when cancelling it is unsupported or unsafe. */
|
|
32
|
+
readonly cancel: string | null;
|
|
33
|
+
/** The exact statement `terminateSession` runs for it; null when terminating it is unsupported or unsafe. */
|
|
34
|
+
readonly terminate: string | null;
|
|
35
|
+
}
|
|
36
|
+
export interface SessionListing {
|
|
37
|
+
readonly sessions: readonly ServerSession[];
|
|
38
|
+
/** What the listing leaves out and why, such as a missing grant, with the grant to ask for. */
|
|
39
|
+
readonly notes: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
/** Which session `cancelSession` and `terminateSession` act on: a listed row's `id` and `instance`. */
|
|
42
|
+
export interface SessionTarget {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly instance: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The server's sessions, read and stopped by the plugin's own SQL (decisions 008, 088). Calls
|
|
48
|
+
* queue on the session they hang off, like the introspector's.
|
|
49
|
+
*/
|
|
50
|
+
export interface SessionMonitor {
|
|
51
|
+
list(): Promise<SessionListing>;
|
|
52
|
+
/**
|
|
53
|
+
* Interrupts the target's running statement and leaves it connected. Rejects with
|
|
54
|
+
* `statementFailed` when the server refuses, the target is gone, or it cannot be reached safely.
|
|
55
|
+
*/
|
|
56
|
+
cancelSession(target: SessionTarget): Promise<void>;
|
|
57
|
+
/** Ends the target's connection; rejects as `cancelSession` does. */
|
|
58
|
+
terminateSession(target: SessionTarget): Promise<void>;
|
|
59
|
+
}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StatementClass } from "@tablecraft-app/plugin-api";
|
|
2
|
+
import { type SqlDialectOptions } from "./tokens.js";
|
|
3
|
+
/** Prefixes the explained statement's destructive reason, so the confirmation says why it matters. */
|
|
4
|
+
export declare const EXPLAIN_ANALYZE_REASON_PREFIX = "EXPLAIN ANALYZE runs it: ";
|
|
5
|
+
/**
|
|
6
|
+
* A token-level classifier plugins may build `SqlLanguage.classify` on (decision 023). The first
|
|
7
|
+
* keyword picks the kind; `UPDATE` or `DELETE` without a top-level `WHERE`, `DROP` and `TRUNCATE`
|
|
8
|
+
* are destructive (decision 011). `EXPLAIN` is a read unless it analyses, which runs the statement,
|
|
9
|
+
* so `EXPLAIN ANALYZE` takes the statement's kind and destructive reason (decision 031). It never
|
|
10
|
+
* parses, so it never rejects a statement.
|
|
11
|
+
*/
|
|
12
|
+
export declare function classifyTokens(sql: string, options?: SqlDialectOptions): StatementClass;
|
|
13
|
+
export type TransactionEffect = "begin" | "end";
|
|
14
|
+
/**
|
|
15
|
+
* Whether a statement opens or ends a transaction, for plugins that track `Transactions.open` in
|
|
16
|
+
* auto mode: `BEGIN` and `START TRANSACTION` open one; `COMMIT`, `ROLLBACK` (not `ROLLBACK TO`),
|
|
17
|
+
* `END`, `ABORT` and `PREPARE TRANSACTION` end it. Null for anything else.
|
|
18
|
+
*/
|
|
19
|
+
export declare function transactionEffect(sql: string, options?: SqlDialectOptions): TransactionEffect | null;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { namespaceTarget } from "./namespace.js";
|
|
2
|
+
import { significantTokens } from "./tokens.js";
|
|
3
|
+
import { explainPrefix, mainVerb, words } from "./words.js";
|
|
4
|
+
const KINDS = new Map([
|
|
5
|
+
...["SELECT", "SHOW", "EXPLAIN", "DESCRIBE", "DESC", "VALUES", "TABLE"].map((word) => [word, "read"]),
|
|
6
|
+
...["INSERT", "UPDATE", "DELETE", "MERGE", "REPLACE", "UPSERT", "COPY", "LOAD"].map((word) => [word, "write"]),
|
|
7
|
+
...["CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME", "COMMENT"].map((word) => [word, "ddl"]),
|
|
8
|
+
...["BEGIN", "COMMIT", "ROLLBACK", "SAVEPOINT", "RELEASE", "END", "ABORT"].map((word) => [word, "transaction"]),
|
|
9
|
+
]);
|
|
10
|
+
const START = "START";
|
|
11
|
+
const TRANSACTION = "TRANSACTION";
|
|
12
|
+
const WHERE = "WHERE";
|
|
13
|
+
const LIMIT = "LIMIT";
|
|
14
|
+
const WITH = "WITH";
|
|
15
|
+
/** Prefixes the explained statement's destructive reason, so the confirmation says why it matters. */
|
|
16
|
+
export const EXPLAIN_ANALYZE_REASON_PREFIX = "EXPLAIN ANALYZE runs it: ";
|
|
17
|
+
function hasTopLevel(list, from, word) {
|
|
18
|
+
return list.some((entry, at) => at > from && entry.depth === 0 && entry.word === word);
|
|
19
|
+
}
|
|
20
|
+
function destructiveReason(list, verbAt) {
|
|
21
|
+
const verb = list[verbAt]?.word;
|
|
22
|
+
switch (verb) {
|
|
23
|
+
case "DELETE":
|
|
24
|
+
if (hasTopLevel(list, verbAt, WHERE))
|
|
25
|
+
return null;
|
|
26
|
+
return hasTopLevel(list, verbAt, LIMIT)
|
|
27
|
+
? "DELETE without WHERE (LIMIT does not choose the rows)"
|
|
28
|
+
: "DELETE without WHERE";
|
|
29
|
+
case "UPDATE":
|
|
30
|
+
return hasTopLevel(list, verbAt, WHERE) ? null : "UPDATE without WHERE";
|
|
31
|
+
case "DROP": {
|
|
32
|
+
const object = list[verbAt + 1]?.word;
|
|
33
|
+
return object === undefined ? "DROP" : `DROP ${object}`;
|
|
34
|
+
}
|
|
35
|
+
case "TRUNCATE":
|
|
36
|
+
return "TRUNCATE";
|
|
37
|
+
default:
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A token-level classifier plugins may build `SqlLanguage.classify` on (decision 023). The first
|
|
43
|
+
* keyword picks the kind; `UPDATE` or `DELETE` without a top-level `WHERE`, `DROP` and `TRUNCATE`
|
|
44
|
+
* are destructive (decision 011). `EXPLAIN` is a read unless it analyses, which runs the statement,
|
|
45
|
+
* so `EXPLAIN ANALYZE` takes the statement's kind and destructive reason (decision 031). It never
|
|
46
|
+
* parses, so it never rejects a statement.
|
|
47
|
+
*/
|
|
48
|
+
export function classifyTokens(sql, options = {}) {
|
|
49
|
+
const statementClass = classifyWords(words(sql, significantTokens(sql, options)));
|
|
50
|
+
return namespaceTarget(sql, options) === null ? statementClass : { ...statementClass, setsNamespace: true };
|
|
51
|
+
}
|
|
52
|
+
function classifyWords(list) {
|
|
53
|
+
const first = list[0]?.word;
|
|
54
|
+
if (first === undefined)
|
|
55
|
+
return { kind: "other", destructive: null };
|
|
56
|
+
if (first === START) {
|
|
57
|
+
return { kind: list[1]?.word === TRANSACTION ? "transaction" : "other", destructive: null };
|
|
58
|
+
}
|
|
59
|
+
const explain = explainPrefix(list);
|
|
60
|
+
if (explain !== null) {
|
|
61
|
+
if (!explain.analyze)
|
|
62
|
+
return { kind: "read", destructive: null };
|
|
63
|
+
const inner = classifyWords(list.slice(explain.statementAt));
|
|
64
|
+
return {
|
|
65
|
+
kind: inner.kind,
|
|
66
|
+
destructive: inner.destructive === null ? null : `${EXPLAIN_ANALYZE_REASON_PREFIX}${inner.destructive}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const verbAt = mainVerb(list);
|
|
70
|
+
const verb = list[verbAt]?.word ?? first;
|
|
71
|
+
const kind = first === WITH && verbAt === 0 ? "read" : (KINDS.get(verb) ?? "other");
|
|
72
|
+
return { kind, destructive: destructiveReason(list, verbAt) };
|
|
73
|
+
}
|
|
74
|
+
const ENDS = new Set(["COMMIT", "END", "ABORT"]);
|
|
75
|
+
const ROLLBACK = "ROLLBACK";
|
|
76
|
+
const TO = "TO";
|
|
77
|
+
const PREPARE = "PREPARE";
|
|
78
|
+
/**
|
|
79
|
+
* Whether a statement opens or ends a transaction, for plugins that track `Transactions.open` in
|
|
80
|
+
* auto mode: `BEGIN` and `START TRANSACTION` open one; `COMMIT`, `ROLLBACK` (not `ROLLBACK TO`),
|
|
81
|
+
* `END`, `ABORT` and `PREPARE TRANSACTION` end it. Null for anything else.
|
|
82
|
+
*/
|
|
83
|
+
export function transactionEffect(sql, options = {}) {
|
|
84
|
+
const list = words(sql, significantTokens(sql, options)).map((entry) => entry.word);
|
|
85
|
+
const [first, second] = list;
|
|
86
|
+
if (first === "BEGIN" || (first === START && second === TRANSACTION))
|
|
87
|
+
return "begin";
|
|
88
|
+
if (first !== undefined && ENDS.has(first))
|
|
89
|
+
return "end";
|
|
90
|
+
if (first === PREPARE && second === TRANSACTION)
|
|
91
|
+
return "end";
|
|
92
|
+
if (first === ROLLBACK)
|
|
93
|
+
return list.includes(TO) ? null : "end";
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SqlLanguage } from "@tablecraft-app/plugin-api";
|
|
2
|
+
import { type FunctionSpec, type ServerIdentity } from "./functions.js";
|
|
3
|
+
import { type IdentifierFolding } from "./resolution.js";
|
|
4
|
+
import type { SqlDialectOptions } from "./tokens.js";
|
|
5
|
+
export type { IdentifierFolding } from "./resolution.js";
|
|
6
|
+
/** What the shared completer needs to know about a dialect (decision 028). */
|
|
7
|
+
export interface CompleterOptions {
|
|
8
|
+
readonly dialect: SqlDialectOptions;
|
|
9
|
+
readonly identifierFolding: IdentifierFolding;
|
|
10
|
+
/** A name the dialect reads back unchanged without quotes; any other is quoted when inserted. */
|
|
11
|
+
readonly plainIdentifier: RegExp;
|
|
12
|
+
readonly quoteIdentifier: (name: string) => string;
|
|
13
|
+
/** Offered where a keyword fits; a name that spells one is quoted. Upper case. */
|
|
14
|
+
readonly keywords: readonly string[];
|
|
15
|
+
/** Built-in functions; a spec brings its signatures, summary and server range (decision 097). */
|
|
16
|
+
readonly functions: readonly (string | FunctionSpec)[];
|
|
17
|
+
/** Reads the catalog's server version into the flavour and version the specs' ranges name; null when it cannot. */
|
|
18
|
+
readonly server?: (version: string) => ServerIdentity | null;
|
|
19
|
+
}
|
|
20
|
+
/** Reserved words every built-in dialect shares; a plugin adds its own. */
|
|
21
|
+
export declare const SQL_KEYWORDS: readonly string[];
|
|
22
|
+
/** The shared completion and navigation every built-in plugin spreads into its `SqlLanguage` (decision 028). */
|
|
23
|
+
export declare function sqlCompleter(options: CompleterOptions): Pick<SqlLanguage, "complete" | "resolve"> & Required<Pick<SqlLanguage, "selectSource" | "signatureAt">>;
|