@nifrajs/mcp-db 3.5.0 → 4.0.2
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/README.md +83 -0
- package/dist/engine.d.ts +71 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +494 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +8 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -610
- package/dist/index.js.map +1 -1
- package/dist/internal/pg-exec.d.ts +113 -0
- package/dist/internal/pg-exec.d.ts.map +1 -0
- package/dist/internal/pg-exec.js +286 -0
- package/dist/internal/pg-exec.js.map +1 -0
- package/dist/internal/pg-lexer.d.ts +52 -0
- package/dist/internal/pg-lexer.d.ts.map +1 -0
- package/dist/internal/pg-lexer.js +273 -0
- package/dist/internal/pg-lexer.js.map +1 -0
- package/dist/internal/shared.d.ts +161 -0
- package/dist/internal/shared.d.ts.map +1 -0
- package/dist/internal/shared.js +538 -0
- package/dist/internal/shared.js.map +1 -0
- package/dist/internal/sqlite-session.d.ts +21 -0
- package/dist/internal/sqlite-session.d.ts.map +1 -0
- package/dist/internal/sqlite-session.js +254 -0
- package/dist/internal/sqlite-session.js.map +1 -0
- package/dist/postgres.d.ts +148 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +580 -0
- package/dist/postgres.js.map +1 -0
- package/package.json +15 -2
- package/src/engine.ts +664 -0
- package/src/index.ts +43 -640
- package/src/internal/pg-exec.ts +386 -0
- package/src/internal/pg-lexer.ts +279 -0
- package/src/internal/shared.ts +705 -0
- package/src/internal/sqlite-session.ts +273 -0
- package/src/postgres.ts +777 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What both database engines share and the MCP layer reuses: the refusal codes, the SQLite statement
|
|
3
|
+
* gates, the caps and the JSON-safe result shape. No runtime imports, so `@nifrajs/mcp-db` itself
|
|
4
|
+
* stays free of `node:*` modules; `engine.ts` re-exports all of it.
|
|
5
|
+
*/
|
|
6
|
+
const REFUSAL_FIXES = {
|
|
7
|
+
NIFRA_DB_NOT_DECLARED: 'Export devDatabase from nifra.config.ts, e.g. export const devDatabase = { kind: "sqlite", file: "./data/app.db" }. nifra never reads DATABASE_URL on its own.',
|
|
8
|
+
NIFRA_DB_CONFIG: "Correct the devDatabase field the message names. Unknown fields are refused, so a misspelled guard cannot silently drop out.",
|
|
9
|
+
NIFRA_DB_OUTSIDE_ROOT: "Keep the database file inside the project, or list its path in devDatabase.allowFiles.",
|
|
10
|
+
NIFRA_DB_REMOTE_HOST: "Point devDatabase.url at a local database (localhost, 127.0.0.1, ::1, *.localhost or a unix socket), or name the host in devDatabase.allowHosts.",
|
|
11
|
+
NIFRA_DB_SUPERUSER: "Connect as a read-only role: run `nifra db role`, apply the SQL it prints, and use that role in devDatabase.url. `nifra db schema` keeps working meanwhile.",
|
|
12
|
+
NIFRA_DB_EXTENSION: "Connect as a role that cannot use the extension or language, or name it in devDatabase.allowExtensions to accept that a query can reach outside this database.",
|
|
13
|
+
NIFRA_DB_QUERY_OFF: "devDatabase.query is false. Remove it to allow read-only queries, or use `nifra db schema`.",
|
|
14
|
+
NIFRA_DB_WRITE_REFUSED: "Send one read-only SELECT (or WITH ... SELECT). Writes, DDL, CALL, DO, COPY and several statements at once are refused.",
|
|
15
|
+
NIFRA_DB_FUNCTION_REFUSED: "Remove the named function: it reaches outside a read-only query (server files, other sessions, settings, locks, sequences, notifications or another database).",
|
|
16
|
+
NIFRA_DB_TABLE_EXCLUDED: "The query reads a relation the declaration does not expose: a table in devDatabase.exclude, a system catalog, a schema outside the allowed ones, or a name that does not exist. Run `nifra db schema` for what is exposed.",
|
|
17
|
+
NIFRA_DB_COLUMN_REFUSED: "The query reads a masked column (a credential name such as password or token, or one in devDatabase.redactColumns) in some form. A masked column cannot be read at all, not even aliased, filtered on or passed to a function: select the other columns by name, or list the column in devDatabase.revealColumns if it holds no secret.",
|
|
18
|
+
NIFRA_DB_TIMEOUT: "Narrow the query (a WHERE clause, an index, a LIMIT) or raise devDatabase.timeoutMs.",
|
|
19
|
+
NIFRA_DB_QUERY_FAILED: "The database rejected the statement. Correct the SQL and retry.",
|
|
20
|
+
NIFRA_DB_DRIVER: "Check that the database is running and that devDatabase points at it. The message carries the driver's error.",
|
|
21
|
+
};
|
|
22
|
+
/** Build a {@link DbRefusal} for `code` with its fix and docs anchor. */
|
|
23
|
+
export function dbRefusal(code, message) {
|
|
24
|
+
return {
|
|
25
|
+
code,
|
|
26
|
+
message,
|
|
27
|
+
fix: REFUSAL_FIXES[code],
|
|
28
|
+
docsAnchor: `agents#${code
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/^nifra_/, "")
|
|
31
|
+
.replaceAll("_", "-")}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** True for a value {@link dbRefusal} built. */
|
|
35
|
+
export function isDbRefusal(value) {
|
|
36
|
+
return (typeof value === "object" &&
|
|
37
|
+
value !== null &&
|
|
38
|
+
"code" in value &&
|
|
39
|
+
typeof value.code === "string" &&
|
|
40
|
+
Object.hasOwn(REFUSAL_FIXES, value.code) &&
|
|
41
|
+
"message" in value &&
|
|
42
|
+
typeof value.message === "string");
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------------------------------
|
|
45
|
+
// Statement gates
|
|
46
|
+
// ---------------------------------------------------------------------------------------------------
|
|
47
|
+
const stripSqlNoise = (sql) => sql
|
|
48
|
+
// Strings first so comment markers inside literals don't count.
|
|
49
|
+
.replace(/'(?:[^']|'')*'/g, "''")
|
|
50
|
+
.replace(/"(?:[^"]|"")*"/g, '""')
|
|
51
|
+
.replace(/--[^\n]*/g, " ")
|
|
52
|
+
.replace(/\/\*[\s\S]*?\*\//g, " ");
|
|
53
|
+
// SQLite's bare-identifier alphabet. Non-ASCII is included because SQLite treats any byte >= 0x80 as
|
|
54
|
+
// an identifier character; leaving it out would end a word early and split one table name into two.
|
|
55
|
+
const WORD_START = /[A-Za-z_\u0080-\uffff]/;
|
|
56
|
+
const WORD_PART = /[A-Za-z0-9_$\u0080-\uffff]/;
|
|
57
|
+
/**
|
|
58
|
+
* Tokenize far enough to name every relation. A regex cannot do this job: SQLite needs no separator
|
|
59
|
+
* between a keyword and a quoted identifier (`FROM"users"` is legal), and it accepts four identifier
|
|
60
|
+
* quotings - `"x"`, `[x]`, `` `x` ``, and bare. A `\s+`-anchored pattern silently reads none of
|
|
61
|
+
* those as a table reference, which is exactly how an aliased `FROM"users"AS habits` slipped past
|
|
62
|
+
* the allowlist and returned a non-exposed table's rows.
|
|
63
|
+
*
|
|
64
|
+
* String literals and comments become nothing, so a `FROM` inside attacker-controlled text is inert.
|
|
65
|
+
*/
|
|
66
|
+
function tokenizeSql(sql) {
|
|
67
|
+
const tokens = [];
|
|
68
|
+
let i = 0;
|
|
69
|
+
/** Read a quoted identifier, collapsing the doubled-delimiter escape SQLite uses for `"` and `` ` ``. */
|
|
70
|
+
const readQuoted = (close, escapable) => {
|
|
71
|
+
let value = "";
|
|
72
|
+
i++; // past the opening delimiter
|
|
73
|
+
while (i < sql.length) {
|
|
74
|
+
if (sql[i] === close) {
|
|
75
|
+
if (escapable && sql[i + 1] === close) {
|
|
76
|
+
value += close;
|
|
77
|
+
i += 2;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
i++;
|
|
81
|
+
tokens.push({ kind: "word", value, bare: false });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
value += sql[i];
|
|
85
|
+
i++;
|
|
86
|
+
}
|
|
87
|
+
// Unterminated - emit what we have. The caller's single-statement and plan checks still run.
|
|
88
|
+
tokens.push({ kind: "word", value, bare: false });
|
|
89
|
+
};
|
|
90
|
+
while (i < sql.length) {
|
|
91
|
+
const char = sql[i];
|
|
92
|
+
if (char === " " || char === "\t" || char === "\n" || char === "\r" || char === "\f") {
|
|
93
|
+
i++;
|
|
94
|
+
}
|
|
95
|
+
else if (char === "-" && sql[i + 1] === "-") {
|
|
96
|
+
const end = sql.indexOf("\n", i);
|
|
97
|
+
i = end === -1 ? sql.length : end + 1;
|
|
98
|
+
}
|
|
99
|
+
else if (char === "/" && sql[i + 1] === "*") {
|
|
100
|
+
const end = sql.indexOf("*/", i + 2);
|
|
101
|
+
i = end === -1 ? sql.length : end + 2;
|
|
102
|
+
}
|
|
103
|
+
else if (char === "'") {
|
|
104
|
+
// Literal: consumed and dropped, so its contents can never be read as SQL.
|
|
105
|
+
i++;
|
|
106
|
+
while (i < sql.length) {
|
|
107
|
+
if (sql[i] === "'") {
|
|
108
|
+
if (sql[i + 1] === "'") {
|
|
109
|
+
i += 2;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
i++;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (char === '"') {
|
|
119
|
+
readQuoted('"', true);
|
|
120
|
+
}
|
|
121
|
+
else if (char === "`") {
|
|
122
|
+
readQuoted("`", true);
|
|
123
|
+
}
|
|
124
|
+
else if (char === "[") {
|
|
125
|
+
readQuoted("]", false); // MSSQL-style bracket quoting: no escape form
|
|
126
|
+
}
|
|
127
|
+
else if (WORD_START.test(char)) {
|
|
128
|
+
const start = i;
|
|
129
|
+
while (i < sql.length && WORD_PART.test(sql[i]))
|
|
130
|
+
i++;
|
|
131
|
+
tokens.push({ kind: "word", value: sql.slice(start, i), bare: true });
|
|
132
|
+
}
|
|
133
|
+
else if (char >= "0" && char <= "9") {
|
|
134
|
+
while (i < sql.length && /[0-9A-Za-z._]/.test(sql[i]))
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
tokens.push({ kind: "punct", value: char, bare: false });
|
|
139
|
+
i++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return tokens;
|
|
143
|
+
}
|
|
144
|
+
/** True when `token` is the unquoted keyword `word` (case-insensitive). */
|
|
145
|
+
const isKeyword = (token, word) => token?.bare === true && token.value.toLowerCase() === word;
|
|
146
|
+
/**
|
|
147
|
+
* Every relation the statement names after `FROM`/`JOIN`, lowercased, with CTE names excluded.
|
|
148
|
+
* A schema qualifier resolves to its table (`main.habits` -> `habits`); a subquery contributes
|
|
149
|
+
* nothing of its own because its inner `FROM` is tokenized alongside everything else.
|
|
150
|
+
*/
|
|
151
|
+
function relationNames(sql) {
|
|
152
|
+
const tokens = tokenizeSql(sql);
|
|
153
|
+
// `name AS (` only ever introduces a CTE - a column alias cannot be followed by a paren - so this
|
|
154
|
+
// needs no `WITH` tracking to be exact.
|
|
155
|
+
const ctes = new Set();
|
|
156
|
+
for (let i = 1; i < tokens.length - 1; i++) {
|
|
157
|
+
const name = tokens[i - 1];
|
|
158
|
+
if (isKeyword(tokens[i], "as") && tokens[i + 1]?.value === "(" && name.kind === "word") {
|
|
159
|
+
ctes.add(name.value.toLowerCase());
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const names = [];
|
|
163
|
+
const relationAt = (index) => {
|
|
164
|
+
const first = tokens[index];
|
|
165
|
+
if (first === undefined || first.kind !== "word")
|
|
166
|
+
return undefined; // `FROM (subquery)` - nothing to name here
|
|
167
|
+
// A qualified reference names the table second; an unqualified one names it first.
|
|
168
|
+
const qualified = tokens[index + 1]?.value === "." && tokens[index + 2]?.kind === "word";
|
|
169
|
+
const table = qualified ? tokens[index + 2].value : first.value;
|
|
170
|
+
return { name: table.toLowerCase(), next: qualified ? index + 3 : index + 1 };
|
|
171
|
+
};
|
|
172
|
+
const addRelation = (index) => {
|
|
173
|
+
const relation = relationAt(index);
|
|
174
|
+
if (relation === undefined)
|
|
175
|
+
return undefined;
|
|
176
|
+
if (relation.name !== "" && !ctes.has(relation.name))
|
|
177
|
+
names.push(relation.name);
|
|
178
|
+
return relation.next;
|
|
179
|
+
};
|
|
180
|
+
// Comma joins are part of the FROM table-source grammar but do not have a JOIN keyword. Walk the
|
|
181
|
+
// source list after every FROM and collect each top-level comma operand. The walk is deliberately
|
|
182
|
+
// conservative: an uncertain token is left for SQLite's plan check, while an apparent relation is
|
|
183
|
+
// always added so an alias cannot hide an unexposed table.
|
|
184
|
+
const clauseBoundary = new Set([
|
|
185
|
+
"where",
|
|
186
|
+
"group",
|
|
187
|
+
"order",
|
|
188
|
+
"having",
|
|
189
|
+
"limit",
|
|
190
|
+
"offset",
|
|
191
|
+
"union",
|
|
192
|
+
"except",
|
|
193
|
+
"intersect",
|
|
194
|
+
"window",
|
|
195
|
+
"returning",
|
|
196
|
+
]);
|
|
197
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
198
|
+
const from = isKeyword(tokens[i], "from");
|
|
199
|
+
const join = isKeyword(tokens[i], "join");
|
|
200
|
+
if (!from && !join)
|
|
201
|
+
continue;
|
|
202
|
+
const next = addRelation(i + 1);
|
|
203
|
+
if (join || next === undefined)
|
|
204
|
+
continue;
|
|
205
|
+
let depth = 0;
|
|
206
|
+
for (let j = next; j < tokens.length; j++) {
|
|
207
|
+
const token = tokens[j];
|
|
208
|
+
if (token === undefined)
|
|
209
|
+
break;
|
|
210
|
+
if (token.value === "(") {
|
|
211
|
+
depth++;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (token.value === ")") {
|
|
215
|
+
if (depth === 0)
|
|
216
|
+
break;
|
|
217
|
+
depth--;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (depth !== 0)
|
|
221
|
+
continue;
|
|
222
|
+
if (token.bare && clauseBoundary.has(token.value.toLowerCase()))
|
|
223
|
+
break;
|
|
224
|
+
if (token.value !== ",")
|
|
225
|
+
continue;
|
|
226
|
+
// A comma at the source-list level is another table reference. If it is a subquery, the nested
|
|
227
|
+
// FROM scan below still finds its base tables; no relation is inferred from the opening paren.
|
|
228
|
+
const afterComma = addRelation(j + 1);
|
|
229
|
+
if (afterComma !== undefined)
|
|
230
|
+
j = afterComma - 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return [...new Set(names)];
|
|
234
|
+
}
|
|
235
|
+
/** Reject multi-statement input: allow one terminator only when it is the final character. */
|
|
236
|
+
/**
|
|
237
|
+
* Names a statement defines as CTEs, column lists included (`WITH RECURSIVE c(x) AS (...)`), which
|
|
238
|
+
* {@link gateSqliteStatement}'s relation scan does not see past. Only for callers whose own check of
|
|
239
|
+
* the compiled statement is the authority on real tables: a CTE that reads a table still opens it.
|
|
240
|
+
*/
|
|
241
|
+
export function sqliteCteNames(sql) {
|
|
242
|
+
const tokens = tokenizeSql(stripSqlNoise(sql));
|
|
243
|
+
const names = new Set();
|
|
244
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
245
|
+
const name = tokens[i];
|
|
246
|
+
if (name?.kind !== "word")
|
|
247
|
+
continue;
|
|
248
|
+
let j = i + 1;
|
|
249
|
+
if (tokens[j]?.value === "(" && tokens[j]?.kind === "punct") {
|
|
250
|
+
let depth = 0;
|
|
251
|
+
for (; j < tokens.length; j++) {
|
|
252
|
+
const value = tokens[j]?.kind === "punct" ? tokens[j]?.value : undefined;
|
|
253
|
+
if (value === "(")
|
|
254
|
+
depth++;
|
|
255
|
+
else if (value === ")" && --depth === 0)
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
j++;
|
|
259
|
+
}
|
|
260
|
+
if (!isKeyword(tokens[j], "as"))
|
|
261
|
+
continue;
|
|
262
|
+
j++;
|
|
263
|
+
if (isKeyword(tokens[j], "not"))
|
|
264
|
+
j++;
|
|
265
|
+
if (isKeyword(tokens[j], "materialized"))
|
|
266
|
+
j++;
|
|
267
|
+
if (tokens[j]?.value === "(" && tokens[j]?.kind === "punct")
|
|
268
|
+
names.add(name.value.toLowerCase());
|
|
269
|
+
}
|
|
270
|
+
return [...names];
|
|
271
|
+
}
|
|
272
|
+
const isSingleStatement = (sql) => {
|
|
273
|
+
let quote = null;
|
|
274
|
+
let lineComment = false;
|
|
275
|
+
let blockComment = false;
|
|
276
|
+
for (let i = 0; i < sql.length; i++) {
|
|
277
|
+
const char = sql[i];
|
|
278
|
+
const next = sql[i + 1];
|
|
279
|
+
if (lineComment) {
|
|
280
|
+
if (char === "\n")
|
|
281
|
+
lineComment = false;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (blockComment) {
|
|
285
|
+
if (char === "*" && next === "/") {
|
|
286
|
+
blockComment = false;
|
|
287
|
+
i++;
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (quote !== null) {
|
|
292
|
+
if (char === quote) {
|
|
293
|
+
if (next === quote)
|
|
294
|
+
i++;
|
|
295
|
+
else
|
|
296
|
+
quote = null;
|
|
297
|
+
}
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (char === "'" || char === '"') {
|
|
301
|
+
quote = char;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (char === "-" && next === "-") {
|
|
305
|
+
lineComment = true;
|
|
306
|
+
i++;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (char === "/" && next === "*") {
|
|
310
|
+
blockComment = true;
|
|
311
|
+
i++;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (char === ";") {
|
|
315
|
+
return sql.slice(i + 1).trim() === "";
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
};
|
|
320
|
+
const isReadStatement = (sql) => {
|
|
321
|
+
const head = stripSqlNoise(sql).trim().toLowerCase();
|
|
322
|
+
return head.startsWith("select") || head.startsWith("with");
|
|
323
|
+
};
|
|
324
|
+
/** Remove one trailing statement terminator (and anything after it) while preserving literals and
|
|
325
|
+
* identifiers. Wrapping the query in a bounded subquery must not let a trailing comment consume the
|
|
326
|
+
* wrapper's closing parenthesis. */
|
|
327
|
+
function executableSql(sql) {
|
|
328
|
+
let quote = null;
|
|
329
|
+
let lineComment = false;
|
|
330
|
+
let lineCommentStart = -1;
|
|
331
|
+
let blockComment = false;
|
|
332
|
+
for (let i = 0; i < sql.length; i++) {
|
|
333
|
+
const char = sql[i];
|
|
334
|
+
const next = sql[i + 1];
|
|
335
|
+
if (lineComment) {
|
|
336
|
+
if (char === "\n") {
|
|
337
|
+
lineComment = false;
|
|
338
|
+
lineCommentStart = -1;
|
|
339
|
+
}
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (blockComment) {
|
|
343
|
+
if (char === "*" && next === "/") {
|
|
344
|
+
blockComment = false;
|
|
345
|
+
i++;
|
|
346
|
+
}
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (quote !== null) {
|
|
350
|
+
if (char === quote) {
|
|
351
|
+
if (next === quote)
|
|
352
|
+
i++;
|
|
353
|
+
else
|
|
354
|
+
quote = null;
|
|
355
|
+
}
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (char === "'" || char === '"') {
|
|
359
|
+
quote = char;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (char === "-" && next === "-") {
|
|
363
|
+
lineComment = true;
|
|
364
|
+
lineCommentStart = i;
|
|
365
|
+
i++;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (char === "/" && next === "*") {
|
|
369
|
+
blockComment = true;
|
|
370
|
+
i++;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (char === ";")
|
|
374
|
+
return sql.slice(0, i).trim();
|
|
375
|
+
}
|
|
376
|
+
if (lineComment && lineCommentStart >= 0)
|
|
377
|
+
return sql.slice(0, lineCommentStart).trim();
|
|
378
|
+
return sql.trim();
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Gate one statement before SQLite sees it: non-empty, a single statement, SELECT/WITH only, and every
|
|
382
|
+
* relation it names after `FROM`/`JOIN` exposed. Returns the statement without its terminator, ready
|
|
383
|
+
* to wrap. Relation names are checked here as well as in the plan because SQLite's plan names an
|
|
384
|
+
* alias as the scan target (`users AS habits` plans as `SCAN habits`).
|
|
385
|
+
*/
|
|
386
|
+
export function gateSqliteStatement(input, exposed) {
|
|
387
|
+
const sql = input.trim();
|
|
388
|
+
if (sql === "")
|
|
389
|
+
return { ok: false, reason: "empty", text: "empty query" };
|
|
390
|
+
if (!isSingleStatement(sql)) {
|
|
391
|
+
return { ok: false, reason: "multiple", text: "only a single statement is allowed" };
|
|
392
|
+
}
|
|
393
|
+
if (!isReadStatement(sql)) {
|
|
394
|
+
return {
|
|
395
|
+
ok: false,
|
|
396
|
+
reason: "not-read",
|
|
397
|
+
text: "only SELECT (or WITH…SELECT) statements are allowed",
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const query = executableSql(sql);
|
|
401
|
+
for (const relation of relationNames(query)) {
|
|
402
|
+
if (!exposed(relation)) {
|
|
403
|
+
return {
|
|
404
|
+
ok: false,
|
|
405
|
+
reason: "unexposed",
|
|
406
|
+
relation,
|
|
407
|
+
text: `query touches ${JSON.stringify(relation)}, which is not exposed`,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return { ok: true, query };
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The first relation an `EXPLAIN QUERY PLAN` result scans or searches that `exposed` rejects, as the
|
|
415
|
+
* plan spells it, or `undefined` when every scanned relation is exposed.
|
|
416
|
+
*/
|
|
417
|
+
export function unexposedPlanRelation(planRows, exposed) {
|
|
418
|
+
for (const row of planRows) {
|
|
419
|
+
const detail = typeof row.detail === "string" ? row.detail : "";
|
|
420
|
+
// SQLite also emits non-table nodes such as `SCAN CONSTANT ROW` for a
|
|
421
|
+
// constant-only SELECT. Only treat a scan/search target as a relation
|
|
422
|
+
// when it is not one of those planner pseudo-nodes.
|
|
423
|
+
if (/^SCAN\s+CONSTANT\s+ROW$/i.test(detail.trim()) ||
|
|
424
|
+
/^SCAN\s+SUBQUERY\s+\d+$/i.test(detail.trim())) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
// A schema-qualified reference plans as `SCAN main.habits`. The table is the last dotted
|
|
428
|
+
// segment; reading the first would reject `main.habits` as a table literally named "main".
|
|
429
|
+
const match = /(?:SCAN|SEARCH)\s+(?:TABLE\s+)?(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)/i.exec(detail);
|
|
430
|
+
if (match !== null) {
|
|
431
|
+
const relation = match[1]?.toLowerCase() ?? "";
|
|
432
|
+
if (!exposed(relation))
|
|
433
|
+
return match[1];
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
/** Wrap a gated query so SQLite materializes at most one row beyond `maxRows`. */
|
|
439
|
+
export function boundedSqliteQuery(query, maxRows) {
|
|
440
|
+
// nifra-expect sql-dynamic: wraps a query already through gateSqliteStatement in a bounding subselect; it runs on a query-only connection
|
|
441
|
+
return `SELECT * FROM (${query}) AS "__nifra_result" LIMIT ${maxRows + 1}`;
|
|
442
|
+
}
|
|
443
|
+
/** Wrap a gated query to count its rows (re-executes it). */
|
|
444
|
+
export function countSqliteQuery(query) {
|
|
445
|
+
// nifra-expect sql-dynamic: wraps a query already through gateSqliteStatement in a counting subselect; it runs on a query-only connection
|
|
446
|
+
return `SELECT count(*) AS "__nifra_total" FROM (${query}) AS "__nifra_count"`;
|
|
447
|
+
}
|
|
448
|
+
// ---------------------------------------------------------------------------------------------------
|
|
449
|
+
// Caps and result shape
|
|
450
|
+
// ---------------------------------------------------------------------------------------------------
|
|
451
|
+
/**
|
|
452
|
+
* Serialize `build(shown)` for the largest `shown <= count` (halving from `count`) whose JSON fits
|
|
453
|
+
* `maxBytes`. `undefined` when even `build(0)` does not fit.
|
|
454
|
+
*/
|
|
455
|
+
export function fitToBytes(count, build, maxBytes) {
|
|
456
|
+
const encoder = new TextEncoder();
|
|
457
|
+
let shown = count;
|
|
458
|
+
let value = build(shown);
|
|
459
|
+
let serialized = JSON.stringify(value);
|
|
460
|
+
while (encoder.encode(serialized).byteLength > maxBytes && shown > 0) {
|
|
461
|
+
shown = Math.max(0, Math.floor(shown / 2));
|
|
462
|
+
value = build(shown);
|
|
463
|
+
serialized = JSON.stringify(value);
|
|
464
|
+
}
|
|
465
|
+
if (encoder.encode(serialized).byteLength > maxBytes)
|
|
466
|
+
return undefined;
|
|
467
|
+
return { value, serialized, shown };
|
|
468
|
+
}
|
|
469
|
+
/** The value a masked cell carries. */
|
|
470
|
+
export const REDACTED_CELL = "[redacted]";
|
|
471
|
+
const MAX_CELL_DEPTH = 32;
|
|
472
|
+
const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
|
|
473
|
+
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
|
474
|
+
/**
|
|
475
|
+
* One database value as JSON: bytes become `<n bytes>`, a bigint outside the safe range a string,
|
|
476
|
+
* a date an ISO string, a non-finite number a string. Strings go through `redaction.text`, and a
|
|
477
|
+
* key inside a JSON value that `redaction.column` matches is masked.
|
|
478
|
+
*/
|
|
479
|
+
export function toJsonCell(value, redaction = {}, depth = 0) {
|
|
480
|
+
if (value === null || value === undefined)
|
|
481
|
+
return null;
|
|
482
|
+
switch (typeof value) {
|
|
483
|
+
case "string":
|
|
484
|
+
return redaction.text === undefined ? value : redaction.text(value);
|
|
485
|
+
case "number":
|
|
486
|
+
return Number.isFinite(value) ? value : String(value);
|
|
487
|
+
case "bigint":
|
|
488
|
+
return value >= MIN_SAFE && value <= MAX_SAFE ? Number(value) : value.toString();
|
|
489
|
+
case "boolean":
|
|
490
|
+
return value;
|
|
491
|
+
case "object":
|
|
492
|
+
break;
|
|
493
|
+
default:
|
|
494
|
+
return String(value);
|
|
495
|
+
}
|
|
496
|
+
if (value instanceof Date)
|
|
497
|
+
return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
498
|
+
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
499
|
+
return `<${value.byteLength} bytes>`;
|
|
500
|
+
}
|
|
501
|
+
if (depth >= MAX_CELL_DEPTH)
|
|
502
|
+
return "[nested]";
|
|
503
|
+
if (Array.isArray(value))
|
|
504
|
+
return value.map((item) => toJsonCell(item, redaction, depth + 1));
|
|
505
|
+
// Null prototype: a `__proto__` key in database JSON stays an ordinary key.
|
|
506
|
+
const out = Object.create(null);
|
|
507
|
+
for (const [key, item] of Object.entries(value)) {
|
|
508
|
+
out[key] =
|
|
509
|
+
item !== null && redaction.column?.(key) === true
|
|
510
|
+
? REDACTED_CELL
|
|
511
|
+
: toJsonCell(item, redaction, depth + 1);
|
|
512
|
+
}
|
|
513
|
+
return out;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Cap, mask and normalize rows fetched as arrays (at most `maxRows + 1`, so truncation is known
|
|
517
|
+
* without counting). The byte cap halves the row count until the JSON fits.
|
|
518
|
+
*/
|
|
519
|
+
export function shapeRows(columns, rows, options) {
|
|
520
|
+
const redaction = options.redaction ?? {};
|
|
521
|
+
const masked = columns.map((column) => redaction.column?.(column) === true);
|
|
522
|
+
const kept = rows.slice(0, options.maxRows);
|
|
523
|
+
const cells = kept.map((row) => row.map((value, index) => masked[index] === true
|
|
524
|
+
? value === null || value === undefined
|
|
525
|
+
? null
|
|
526
|
+
: REDACTED_CELL
|
|
527
|
+
: toJsonCell(value, redaction)));
|
|
528
|
+
const fit = fitToBytes(cells.length, (shown) => ({ columns, rows: cells.slice(0, shown) }), options.maxResultBytes);
|
|
529
|
+
const shown = fit?.shown ?? 0;
|
|
530
|
+
return {
|
|
531
|
+
columns,
|
|
532
|
+
rows: cells.slice(0, shown),
|
|
533
|
+
rowCount: shown,
|
|
534
|
+
truncated: rows.length > options.maxRows || shown < cells.length,
|
|
535
|
+
redactedColumns: columns.filter((_, index) => masked[index] === true),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/internal/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The execution lanes `run_query` uses: a reusable read-only worker for a file-backed database, and an
|
|
3
|
+
* in-process lane for one that cannot be reopened. Not part of the public engine - the CLI's database
|
|
4
|
+
* tool runs each query in its own subprocess instead, which bounds the work by killing the process.
|
|
5
|
+
*/
|
|
6
|
+
import type { SqliteDatabaseLike } from "./shared.js";
|
|
7
|
+
export declare class QueryTimeoutError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
/** One execution lane for `run_query`: `run` resolves rows, or rejects once the deadline passes. */
|
|
11
|
+
export interface QuerySession {
|
|
12
|
+
run(sql: string, deadline: number): Promise<unknown[]>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Pick the lane once, at construction. A file-backed database is isolated in a reusable read-only
|
|
17
|
+
* worker, which is the only arrangement that can actually stop a running statement. Anything else
|
|
18
|
+
* runs in process - every database keeps working, none is refused.
|
|
19
|
+
*/
|
|
20
|
+
export declare function openQuerySession(db: SqliteDatabaseLike): QuerySession;
|
|
21
|
+
//# sourceMappingURL=sqlite-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-session.d.ts","sourceRoot":"","sources":["../../src/internal/sqlite-session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD,qBAAa,iBAAkB,SAAQ,KAAK;;CAK3C;AAED,oGAAoG;AACpG,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAoPD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,kBAAkB,GAAG,YAAY,CAIrE"}
|