@askdb/core 1.0.0-beta.41 → 1.0.0-beta.43
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 +44 -4
- package/dist/ask.d.ts +129 -2
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +391 -15
- package/dist/ask.js.map +1 -1
- package/dist/errors.d.ts +87 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/logging/log-events.d.ts +13 -0
- package/dist/logging/log-events.d.ts.map +1 -1
- package/dist/logging/log-events.js +13 -0
- package/dist/logging/log-events.js.map +1 -1
- package/dist/schema/v2/index.d.ts +1 -1
- package/dist/schema/v2/index.d.ts.map +1 -1
- package/dist/schema/v2/loader.d.ts +9 -1
- package/dist/schema/v2/loader.d.ts.map +1 -1
- package/dist/schema/v2/loader.js +171 -49
- package/dist/schema/v2/loader.js.map +1 -1
- package/dist/schema/v2/normalized.d.ts +33 -0
- package/dist/schema/v2/normalized.d.ts.map +1 -1
- package/dist/schema/v2/parser.d.ts +9 -0
- package/dist/schema/v2/parser.d.ts.map +1 -1
- package/dist/schema/v2/parser.js +18 -2
- package/dist/schema/v2/parser.js.map +1 -1
- package/dist/schema/v2/tenant-policy-loader.d.ts.map +1 -1
- package/dist/schema/v2/tenant-policy-loader.js +7 -3
- package/dist/schema/v2/tenant-policy-loader.js.map +1 -1
- package/dist/schema/v2/tenant-policy.d.ts +28 -21
- package/dist/schema/v2/tenant-policy.d.ts.map +1 -1
- package/dist/schema/v2/tenant-policy.js +67 -11
- package/dist/schema/v2/tenant-policy.js.map +1 -1
- package/dist/sql/bind.d.ts +102 -0
- package/dist/sql/bind.d.ts.map +1 -0
- package/dist/sql/bind.js +422 -0
- package/dist/sql/bind.js.map +1 -0
- package/dist/sql/dialect-spec.d.ts +24 -1
- package/dist/sql/dialect-spec.d.ts.map +1 -1
- package/dist/sql/dialect-spec.js +82 -3
- package/dist/sql/dialect-spec.js.map +1 -1
- package/dist/sql/extract-sql.d.ts +8 -1
- package/dist/sql/extract-sql.d.ts.map +1 -1
- package/dist/sql/extract-sql.js +27 -4
- package/dist/sql/extract-sql.js.map +1 -1
- package/dist/sql/generate.d.ts +22 -0
- package/dist/sql/generate.d.ts.map +1 -1
- package/dist/sql/generate.js +92 -16
- package/dist/sql/generate.js.map +1 -1
- package/dist/sql/index.d.ts +4 -2
- package/dist/sql/index.d.ts.map +1 -1
- package/dist/sql/index.js +4 -2
- package/dist/sql/index.js.map +1 -1
- package/dist/sql/lexer.d.ts +114 -0
- package/dist/sql/lexer.d.ts.map +1 -0
- package/dist/sql/lexer.js +397 -0
- package/dist/sql/lexer.js.map +1 -0
- package/dist/sql/parameter-manifest.d.ts +34 -0
- package/dist/sql/parameter-manifest.d.ts.map +1 -0
- package/dist/sql/parameter-manifest.js +150 -0
- package/dist/sql/parameter-manifest.js.map +1 -0
- package/dist/sql/prompt.d.ts +7 -1
- package/dist/sql/prompt.d.ts.map +1 -1
- package/dist/sql/prompt.js +24 -1
- package/dist/sql/prompt.js.map +1 -1
- package/dist/sql/sensitive-guardrail.d.ts +62 -0
- package/dist/sql/sensitive-guardrail.d.ts.map +1 -0
- package/dist/sql/sensitive-guardrail.js +876 -0
- package/dist/sql/sensitive-guardrail.js.map +1 -0
- package/dist/sql/tenant-guardrail.d.ts +70 -8
- package/dist/sql/tenant-guardrail.d.ts.map +1 -1
- package/dist/sql/tenant-guardrail.js +799 -52
- package/dist/sql/tenant-guardrail.js.map +1 -1
- package/dist/sql/tenant-hierarchy.d.ts +19 -0
- package/dist/sql/tenant-hierarchy.d.ts.map +1 -0
- package/dist/sql/tenant-hierarchy.js +53 -0
- package/dist/sql/tenant-hierarchy.js.map +1 -0
- package/dist/sql/tenant-placeholders.d.ts +60 -9
- package/dist/sql/tenant-placeholders.d.ts.map +1 -1
- package/dist/sql/tenant-placeholders.js +237 -63
- package/dist/sql/tenant-placeholders.js.map +1 -1
- package/dist/sql/tenant-prompt.d.ts +4 -0
- package/dist/sql/tenant-prompt.d.ts.map +1 -1
- package/dist/sql/tenant-prompt.js +79 -11
- package/dist/sql/tenant-prompt.js.map +1 -1
- package/dist/sql/tenant-scope-validate.d.ts +3 -1
- package/dist/sql/tenant-scope-validate.d.ts.map +1 -1
- package/dist/sql/tenant-scope-validate.js +5 -2
- package/dist/sql/tenant-scope-validate.js.map +1 -1
- package/dist/sql/validate.d.ts +24 -4
- package/dist/sql/validate.d.ts.map +1 -1
- package/dist/sql/validate.js +123 -83
- package/dist/sql/validate.js.map +1 -1
- package/package.json +9 -7
- package/dist/exec/postgres.d.ts +0 -6
- package/dist/exec/postgres.d.ts.map +0 -1
- package/dist/exec/postgres.js +0 -30
- package/dist/exec/postgres.js.map +0 -1
- package/dist/modes/post-execute-log.d.ts +0 -9
- package/dist/modes/post-execute-log.d.ts.map +0 -1
- package/dist/modes/post-execute-log.js +0 -26
- package/dist/modes/post-execute-log.js.map +0 -1
package/dist/sql/bind.js
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { QueryParameterError, } from "../errors.js";
|
|
2
|
+
import { BUILT_IN_DIALECTS, } from "./dialect-spec.js";
|
|
3
|
+
import { GENERIC_LEXER, lexSql, lexerProfileFor } from "./lexer.js";
|
|
4
|
+
import { validateSelectSql } from "./validate.js";
|
|
5
|
+
/**
|
|
6
|
+
* Split SQL into contiguous code / quoted / comment regions using the shared lexer
|
|
7
|
+
* (`lexer.ts`). With a dialect, strings, quoted identifiers, and comments follow that
|
|
8
|
+
* engine's rules (Postgres `E'…'` and exact-tag `$tag$…$tag$`, MySQL backslash escapes
|
|
9
|
+
* and `#` comments, SQL Server `[…]`, …). Without one, the generic profile recognizes
|
|
10
|
+
* the union of quoting forms. An unterminated quote or comment runs to the end.
|
|
11
|
+
*/
|
|
12
|
+
export function tokenizeSqlSpans(sql, dialect) {
|
|
13
|
+
const profile = (dialect && lexerProfileFor(dialect)) ?? GENERIC_LEXER;
|
|
14
|
+
const spans = [];
|
|
15
|
+
let codeStart = 0;
|
|
16
|
+
const pushCode = (end) => {
|
|
17
|
+
if (end > codeStart)
|
|
18
|
+
spans.push({ kind: "code", start: codeStart, end });
|
|
19
|
+
};
|
|
20
|
+
for (const token of lexSql(sql, profile)) {
|
|
21
|
+
const kind = token.kind === "string" || token.kind === "quoted_identifier"
|
|
22
|
+
? "quoted"
|
|
23
|
+
: token.kind === "comment"
|
|
24
|
+
? "comment"
|
|
25
|
+
: undefined;
|
|
26
|
+
if (!kind)
|
|
27
|
+
continue;
|
|
28
|
+
pushCode(token.start);
|
|
29
|
+
spans.push({ kind, start: token.start, end: token.end });
|
|
30
|
+
codeStart = token.end;
|
|
31
|
+
}
|
|
32
|
+
pushCode(sql.length);
|
|
33
|
+
return spans;
|
|
34
|
+
}
|
|
35
|
+
// `(?<!:)` keeps the type in a `value::type` cast from reading as a `:type` placeholder.
|
|
36
|
+
const PLACEHOLDER_TOKEN_RE = /(?<!:):([a-z][a-z0-9_]*)/g;
|
|
37
|
+
/**
|
|
38
|
+
* Find `:name` placeholders only in code regions, in source order.
|
|
39
|
+
* A quoted `':name'` or a commented-out `-- :name` is invisible by construction.
|
|
40
|
+
*/
|
|
41
|
+
export function scanPlaceholders(sql, dialect) {
|
|
42
|
+
const spans = tokenizeSqlSpans(sql, dialect);
|
|
43
|
+
const out = [];
|
|
44
|
+
for (const span of spans) {
|
|
45
|
+
if (span.kind !== "code")
|
|
46
|
+
continue;
|
|
47
|
+
const segment = sql.slice(span.start, span.end);
|
|
48
|
+
PLACEHOLDER_TOKEN_RE.lastIndex = 0;
|
|
49
|
+
let m;
|
|
50
|
+
while ((m = PLACEHOLDER_TOKEN_RE.exec(segment)) !== null) {
|
|
51
|
+
const name = m[1];
|
|
52
|
+
const start = span.start + m.index;
|
|
53
|
+
const end = start + m[0].length;
|
|
54
|
+
out.push({ name, placeholder: m[0], start, end });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* `:tenant_<root>_ids` placeholders in code regions. Pass the dialect so strings and
|
|
61
|
+
* comments are recognized the way the target engine reads them (MySQL backslash
|
|
62
|
+
* escapes and `#` comments, Postgres nested block comments, …); without one the
|
|
63
|
+
* generic profile is used.
|
|
64
|
+
*/
|
|
65
|
+
export function scanTenantPlaceholders(sql, dialect) {
|
|
66
|
+
return scanPlaceholders(sql, dialect).filter((p) => /^tenant_[a-z0-9_]+_ids$/.test(p.name));
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// List-context validation
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* A `many` placeholder must be the sole expression inside `IN (...)` / `NOT IN (...)`,
|
|
73
|
+
* or the sole argument of `= ANY(...)` on array dialects.
|
|
74
|
+
*/
|
|
75
|
+
export function isValidListContext(sql, occurrence, listBinding) {
|
|
76
|
+
const before = sql.slice(0, occurrence.start);
|
|
77
|
+
const after = sql.slice(occurrence.end);
|
|
78
|
+
const inBefore = /(?:NOT\s+)?IN\s*\(\s*$/i.exec(before);
|
|
79
|
+
if (inBefore) {
|
|
80
|
+
return /^\s*\)/.test(after);
|
|
81
|
+
}
|
|
82
|
+
if (listBinding === "array") {
|
|
83
|
+
const anyBefore = /=\s*ANY\s*\(\s*$/i.exec(before);
|
|
84
|
+
if (anyBefore) {
|
|
85
|
+
return /^\s*\)/.test(after);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
/** Detect whether an occurrence sits inside IN ( … ) and return the span of `IN (` … `)`. */
|
|
91
|
+
function inListSpan(sql, occ) {
|
|
92
|
+
const before = sql.slice(0, occ.start);
|
|
93
|
+
const after = sql.slice(occ.end);
|
|
94
|
+
const inBefore = /(?:NOT\s+)?IN\s*\(\s*$/i.exec(before);
|
|
95
|
+
if (!inBefore || !/^\s*\)/.test(after))
|
|
96
|
+
return undefined;
|
|
97
|
+
const closeMatch = /^\s*\)/.exec(after);
|
|
98
|
+
return {
|
|
99
|
+
start: occ.start - inBefore[0].length,
|
|
100
|
+
end: occ.end + closeMatch[0].length,
|
|
101
|
+
isNot: /^NOT\s+/i.test(inBefore[0]),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function anySpan(sql, occ) {
|
|
105
|
+
const before = sql.slice(0, occ.start);
|
|
106
|
+
const after = sql.slice(occ.end);
|
|
107
|
+
const anyBefore = /=\s*ANY\s*\(\s*$/i.exec(before);
|
|
108
|
+
if (!anyBefore || !/^\s*\)/.test(after))
|
|
109
|
+
return undefined;
|
|
110
|
+
const closeMatch = /^\s*\)/.exec(after);
|
|
111
|
+
return {
|
|
112
|
+
start: occ.start - anyBefore[0].length,
|
|
113
|
+
end: occ.end + closeMatch[0].length,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Dialect-aware literal escaping
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
const CONTROL_CHAR_RE = /[\u0000-\u001f\u007f]/;
|
|
120
|
+
/**
|
|
121
|
+
* Escape a scalar value as a SQL literal for the given dialect.
|
|
122
|
+
* Never includes the raw parameter value in thrown error messages.
|
|
123
|
+
*/
|
|
124
|
+
export function escapeSqlLiteral(value, dialect) {
|
|
125
|
+
if (typeof value === "boolean") {
|
|
126
|
+
return value ? "TRUE" : "FALSE";
|
|
127
|
+
}
|
|
128
|
+
if (typeof value === "number") {
|
|
129
|
+
if (!Number.isFinite(value)) {
|
|
130
|
+
throw paramError("INVALID_VALUE", "Numeric parameter must be a finite number.");
|
|
131
|
+
}
|
|
132
|
+
return String(value);
|
|
133
|
+
}
|
|
134
|
+
if (CONTROL_CHAR_RE.test(value)) {
|
|
135
|
+
throw paramError("INVALID_VALUE", "String parameter contains a disallowed control character.");
|
|
136
|
+
}
|
|
137
|
+
let escaped = value.replace(/'/g, "''");
|
|
138
|
+
if (dialect?.backslashEscapes === true) {
|
|
139
|
+
escaped = escaped.replace(/\\/g, "\\\\");
|
|
140
|
+
}
|
|
141
|
+
return `'${escaped}'`;
|
|
142
|
+
}
|
|
143
|
+
export function escapeSqlLiteralList(values, dialect) {
|
|
144
|
+
return `(${values.map((v) => escapeSqlLiteral(v, dialect)).join(", ")})`;
|
|
145
|
+
}
|
|
146
|
+
/** Quote-doubling only — used when no dialect is supplied (legacy tenant path). */
|
|
147
|
+
export function escapeSqlLiteralLegacy(value) {
|
|
148
|
+
return "'" + value.replace(/'/g, "''") + "'";
|
|
149
|
+
}
|
|
150
|
+
export function markerStyleForDialect(id) {
|
|
151
|
+
switch (id) {
|
|
152
|
+
case "postgres":
|
|
153
|
+
case "cockroachdb":
|
|
154
|
+
return "dollar";
|
|
155
|
+
case "sqlserver":
|
|
156
|
+
return "atp";
|
|
157
|
+
case "mysql":
|
|
158
|
+
case "mariadb":
|
|
159
|
+
case "sqlite":
|
|
160
|
+
return "question";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export function formatMarker(style, ordinal) {
|
|
164
|
+
switch (style) {
|
|
165
|
+
case "dollar":
|
|
166
|
+
return `$${ordinal}`;
|
|
167
|
+
case "question":
|
|
168
|
+
return "?";
|
|
169
|
+
case "atp":
|
|
170
|
+
return `@p${ordinal}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function listBindingOf(spec) {
|
|
174
|
+
return spec.listBinding ?? "expand";
|
|
175
|
+
}
|
|
176
|
+
function nextMarker(style, counters) {
|
|
177
|
+
switch (style) {
|
|
178
|
+
case "dollar":
|
|
179
|
+
return formatMarker("dollar", counters.dollar++);
|
|
180
|
+
case "atp":
|
|
181
|
+
return formatMarker("atp", counters.atp++);
|
|
182
|
+
case "question":
|
|
183
|
+
return formatMarker("question", 0);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// bindPreparedQuery
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
function paramError(reason, message) {
|
|
190
|
+
return new QueryParameterError(message, reason);
|
|
191
|
+
}
|
|
192
|
+
function lookupValue(values, name, placeholder) {
|
|
193
|
+
if (Object.prototype.hasOwnProperty.call(values, placeholder)) {
|
|
194
|
+
return values[placeholder];
|
|
195
|
+
}
|
|
196
|
+
if (Object.prototype.hasOwnProperty.call(values, name)) {
|
|
197
|
+
return values[name];
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
function assertValueMatchesDeclaration(value, type, cardinality) {
|
|
202
|
+
if (cardinality === "many") {
|
|
203
|
+
if (!Array.isArray(value)) {
|
|
204
|
+
throw paramError("INVALID_VALUE", "List parameter requires a non-empty array value.");
|
|
205
|
+
}
|
|
206
|
+
if (value.length === 0) {
|
|
207
|
+
throw paramError("INVALID_VALUE", "List parameter requires a non-empty array value.");
|
|
208
|
+
}
|
|
209
|
+
return value.map((v) => assertScalar(v, type));
|
|
210
|
+
}
|
|
211
|
+
if (Array.isArray(value)) {
|
|
212
|
+
throw paramError("INVALID_VALUE", "Scalar parameter must not be an array.");
|
|
213
|
+
}
|
|
214
|
+
return assertScalar(value, type);
|
|
215
|
+
}
|
|
216
|
+
function assertScalar(value, type) {
|
|
217
|
+
if (value === null || value === undefined) {
|
|
218
|
+
throw paramError("INVALID_VALUE", "Parameter value must not be null or undefined.");
|
|
219
|
+
}
|
|
220
|
+
if (typeof value === "object") {
|
|
221
|
+
throw paramError("INVALID_VALUE", "Parameter value must be a string, number, or boolean.");
|
|
222
|
+
}
|
|
223
|
+
switch (type) {
|
|
224
|
+
case "string":
|
|
225
|
+
case "date":
|
|
226
|
+
case "datetime":
|
|
227
|
+
if (typeof value !== "string") {
|
|
228
|
+
throw paramError("INVALID_VALUE", `Parameter of type ${type} requires a string value.`);
|
|
229
|
+
}
|
|
230
|
+
if (type === "date" && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
231
|
+
throw paramError("INVALID_VALUE", "Date parameter must be an ISO date string (YYYY-MM-DD).");
|
|
232
|
+
}
|
|
233
|
+
if (type === "datetime" &&
|
|
234
|
+
!/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/.test(value)) {
|
|
235
|
+
throw paramError("INVALID_VALUE", "Datetime parameter must be an ISO datetime string.");
|
|
236
|
+
}
|
|
237
|
+
return value;
|
|
238
|
+
case "number":
|
|
239
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
240
|
+
throw paramError("INVALID_VALUE", "Numeric parameter must be a finite number.");
|
|
241
|
+
}
|
|
242
|
+
return value;
|
|
243
|
+
case "boolean":
|
|
244
|
+
if (typeof value !== "boolean") {
|
|
245
|
+
throw paramError("INVALID_VALUE", "Boolean parameter requires a boolean value.");
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Pure, synchronous binder. Substitutes placeholders with escaped literals and
|
|
252
|
+
* allocates dialect-correct driver markers. Does not authorize tenant IDs.
|
|
253
|
+
*/
|
|
254
|
+
export function bindPreparedQuery(prepared, values) {
|
|
255
|
+
const spec = BUILT_IN_DIALECTS[prepared.dialect];
|
|
256
|
+
if (!spec) {
|
|
257
|
+
throw paramError("DIALECT_UNSUPPORTED", `Unknown dialect '${prepared.dialect}'.`);
|
|
258
|
+
}
|
|
259
|
+
const listBinding = listBindingOf(spec);
|
|
260
|
+
const style = markerStyleForDialect(prepared.dialect);
|
|
261
|
+
const namedSql = prepared.namedSql;
|
|
262
|
+
const occurrences = scanPlaceholders(namedSql, spec);
|
|
263
|
+
const declByName = new Map(prepared.parameters.map((p) => [p.name, p]));
|
|
264
|
+
for (const p of prepared.parameters) {
|
|
265
|
+
if (!occurrences.some((o) => o.name === p.name)) {
|
|
266
|
+
throw paramError("UNRESOLVED_PLACEHOLDER", `Declared parameter '${p.name}' does not appear in namedSql.`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const occ of occurrences) {
|
|
270
|
+
if (!declByName.has(occ.name)) {
|
|
271
|
+
throw paramError("UNRESOLVED_PLACEHOLDER", `Placeholder '${occ.placeholder}' has no matching parameter declaration.`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const resolvedValues = new Map();
|
|
275
|
+
for (const p of prepared.parameters) {
|
|
276
|
+
const raw = lookupValue(values, p.name, p.placeholder);
|
|
277
|
+
if (raw === undefined) {
|
|
278
|
+
throw paramError("MISSING_VALUE", `Missing value for parameter '${p.name}'.`);
|
|
279
|
+
}
|
|
280
|
+
resolvedValues.set(p.name, assertValueMatchesDeclaration(raw, p.type, p.cardinality));
|
|
281
|
+
}
|
|
282
|
+
for (const occ of occurrences) {
|
|
283
|
+
const decl = declByName.get(occ.name);
|
|
284
|
+
if (decl.cardinality === "many" && !isValidListContext(namedSql, occ, listBinding)) {
|
|
285
|
+
throw paramError("INVALID_LIST_CONTEXT", `List parameter '${occ.name}' must appear as the sole expression inside IN (...)` +
|
|
286
|
+
(listBinding === "array" ? " or = ANY(...)" : "") +
|
|
287
|
+
".");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const edits = [];
|
|
291
|
+
const params = [];
|
|
292
|
+
const bindings = [];
|
|
293
|
+
const bindingByName = new Map();
|
|
294
|
+
const counters = { dollar: 1, atp: 0 };
|
|
295
|
+
const ensureBinding = (decl, value) => {
|
|
296
|
+
let binding = bindingByName.get(decl.name);
|
|
297
|
+
if (!binding) {
|
|
298
|
+
binding = {
|
|
299
|
+
name: decl.name,
|
|
300
|
+
placeholder: decl.placeholder,
|
|
301
|
+
type: decl.type,
|
|
302
|
+
cardinality: decl.cardinality,
|
|
303
|
+
description: decl.description,
|
|
304
|
+
value,
|
|
305
|
+
markers: [],
|
|
306
|
+
indices: [],
|
|
307
|
+
source: decl.source,
|
|
308
|
+
};
|
|
309
|
+
bindingByName.set(decl.name, binding);
|
|
310
|
+
bindings.push(binding);
|
|
311
|
+
}
|
|
312
|
+
return binding;
|
|
313
|
+
};
|
|
314
|
+
for (const occ of occurrences) {
|
|
315
|
+
const decl = declByName.get(occ.name);
|
|
316
|
+
const value = resolvedValues.get(occ.name);
|
|
317
|
+
if (decl.cardinality === "many") {
|
|
318
|
+
const list = value;
|
|
319
|
+
const listLiteral = escapeSqlLiteralList(list, spec);
|
|
320
|
+
const binding = ensureBinding(decl, value);
|
|
321
|
+
if (listBinding === "array") {
|
|
322
|
+
// One array-typed driver param. unboundSql uses = ANY($n); bound sql uses IN (...).
|
|
323
|
+
const marker = nextMarker(style, counters);
|
|
324
|
+
const indices = [params.length];
|
|
325
|
+
params.push(list);
|
|
326
|
+
binding.markers.push(marker);
|
|
327
|
+
binding.indices.push(...indices);
|
|
328
|
+
const inSp = inListSpan(namedSql, occ);
|
|
329
|
+
const anySp = anySpan(namedSql, occ);
|
|
330
|
+
if (inSp) {
|
|
331
|
+
edits.push({
|
|
332
|
+
start: inSp.start,
|
|
333
|
+
end: inSp.end,
|
|
334
|
+
literal: `${inSp.isNot ? "NOT " : ""}IN ${listLiteral}`,
|
|
335
|
+
marker: inSp.isNot ? `<> ALL(${marker})` : `= ANY(${marker})`,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
else if (anySp) {
|
|
339
|
+
edits.push({
|
|
340
|
+
start: anySp.start,
|
|
341
|
+
end: anySp.end,
|
|
342
|
+
literal: `IN ${listLiteral}`,
|
|
343
|
+
marker: `= ANY(${marker})`,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Validated above; defensive.
|
|
348
|
+
edits.push({
|
|
349
|
+
start: occ.start,
|
|
350
|
+
end: occ.end,
|
|
351
|
+
literal: listLiteral,
|
|
352
|
+
marker,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
// expand: one marker / literal element per value
|
|
358
|
+
const markerTexts = [];
|
|
359
|
+
for (const item of list) {
|
|
360
|
+
const marker = nextMarker(style, counters);
|
|
361
|
+
markerTexts.push(marker);
|
|
362
|
+
binding.indices.push(params.length);
|
|
363
|
+
binding.markers.push(marker);
|
|
364
|
+
params.push(item);
|
|
365
|
+
}
|
|
366
|
+
const inSp = inListSpan(namedSql, occ);
|
|
367
|
+
if (inSp) {
|
|
368
|
+
// Replace only the placeholder — outer IN ( ) stays; emit bare list.
|
|
369
|
+
edits.push({
|
|
370
|
+
start: occ.start,
|
|
371
|
+
end: occ.end,
|
|
372
|
+
literal: list.map((v) => escapeSqlLiteral(v, spec)).join(", "),
|
|
373
|
+
marker: markerTexts.join(", "),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
edits.push({
|
|
378
|
+
start: occ.start,
|
|
379
|
+
end: occ.end,
|
|
380
|
+
literal: listLiteral,
|
|
381
|
+
marker: `(${markerTexts.join(", ")})`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const scalar = value;
|
|
388
|
+
const literal = escapeSqlLiteral(scalar, spec);
|
|
389
|
+
const marker = nextMarker(style, counters);
|
|
390
|
+
const binding = ensureBinding(decl, value);
|
|
391
|
+
binding.markers.push(marker);
|
|
392
|
+
binding.indices.push(params.length);
|
|
393
|
+
params.push(scalar);
|
|
394
|
+
edits.push({ start: occ.start, end: occ.end, literal, marker });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
edits.sort((a, b) => b.start - a.start);
|
|
398
|
+
let sql = namedSql;
|
|
399
|
+
let unboundSql = namedSql;
|
|
400
|
+
for (const edit of edits) {
|
|
401
|
+
sql = sql.slice(0, edit.start) + edit.literal + sql.slice(edit.end);
|
|
402
|
+
unboundSql = unboundSql.slice(0, edit.start) + edit.marker + unboundSql.slice(edit.end);
|
|
403
|
+
}
|
|
404
|
+
if (scanPlaceholders(sql, spec).length > 0 || scanPlaceholders(unboundSql, spec).length > 0) {
|
|
405
|
+
throw paramError("UNRESOLVED_PLACEHOLDER", "One or more placeholders remain after binding.");
|
|
406
|
+
}
|
|
407
|
+
validateSelectSql(spec, sql);
|
|
408
|
+
validateSelectSql(spec, unboundSql);
|
|
409
|
+
return { sql, unboundSql, params, bindings };
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Compare two SQL strings structurally for the parameterize consistency check.
|
|
413
|
+
*/
|
|
414
|
+
export function sqlStructurallyEqual(a, b) {
|
|
415
|
+
const norm = (s) => s
|
|
416
|
+
.trim()
|
|
417
|
+
.replace(/;\s*$/, "")
|
|
418
|
+
.replace(/\s+/g, " ")
|
|
419
|
+
.toLowerCase();
|
|
420
|
+
return norm(a) === norm(b);
|
|
421
|
+
}
|
|
422
|
+
//# sourceMappingURL=bind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind.js","sourceRoot":"","sources":["../../src/sql/bind.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,9 +6,19 @@
|
|
|
6
6
|
* Authoring guidance:
|
|
7
7
|
* - Keep `promptBrief` to one short paragraph; surfaced in the model's user prompt.
|
|
8
8
|
* - `extraForbiddenKeywords` is additive on top of the dialect-agnostic read-only
|
|
9
|
-
* denylist (`insert`, `update`, `delete`, `drop`, …) baked into
|
|
9
|
+
* denylist (`insert`, `update`, `delete`, `drop`, `into`, …) baked into
|
|
10
|
+
* `validateSelectSql`. Matched against unquoted keyword tokens only.
|
|
11
|
+
* - `blockedFunctions` lists functions with side effects (writes, file/network access,
|
|
12
|
+
* sleeping, session changes) that `validateSelectSql` rejects when called.
|
|
10
13
|
* - `extraValidate` runs *after* the base validator passes; throw a
|
|
11
14
|
* `SqlValidationError` to reject dialect-specific shapes.
|
|
15
|
+
* - `id` also selects how SQL is lexed (string escapes, quoting, comment syntax) — see
|
|
16
|
+
* `lexer.ts`. Specs that spread a built-in (`{ ...POSTGRES_DIALECT, … }`) inherit its
|
|
17
|
+
* denylists; a spec that overrides `extraForbiddenKeywords` / `blockedFunctions`
|
|
18
|
+
* replaces them.
|
|
19
|
+
*
|
|
20
|
+
* These lists are defense in depth for model-generated SQL, not a security boundary:
|
|
21
|
+
* execute generated SQL under a read-only database role.
|
|
12
22
|
*/
|
|
13
23
|
/** Stable identifier for a built-in dialect. Connectors may surface this via `IntrospectionResult.provider`. */
|
|
14
24
|
export type DialectId = "postgres" | "cockroachdb" | "mysql" | "mariadb" | "sqlite" | "sqlserver";
|
|
@@ -21,8 +31,21 @@ export type DialectSpec = {
|
|
|
21
31
|
identifierQuote: '"' | '`';
|
|
22
32
|
/** Extra keywords to forbid on top of the dialect-agnostic base denylist. */
|
|
23
33
|
extraForbiddenKeywords?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Function names `validateSelectSql` rejects when called (`name(`, including
|
|
36
|
+
* schema-qualified and quoted spellings). Case-insensitive.
|
|
37
|
+
*/
|
|
38
|
+
blockedFunctions?: readonly string[];
|
|
24
39
|
/** Optional engine-specific post-validator. Receives SQL already passing the base shape checks. */
|
|
25
40
|
extraValidate?: (sql: string) => void;
|
|
41
|
+
/** How list-valued placeholders bind in `unboundSql`. Default "expand" when absent. */
|
|
42
|
+
listBinding?: "array" | "expand";
|
|
43
|
+
/**
|
|
44
|
+
* Whether backslash is an escape character inside string literals. Default false for
|
|
45
|
+
* escaping bound values; the validator's lexer treats an unset value on a MySQL-family
|
|
46
|
+
* spec as the server default (escapes on).
|
|
47
|
+
*/
|
|
48
|
+
backslashEscapes?: boolean;
|
|
26
49
|
};
|
|
27
50
|
/** PostgreSQL — the original AskDB target. */
|
|
28
51
|
export declare const POSTGRES_DIALECT: DialectSpec;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialect-spec.d.ts","sourceRoot":"","sources":["../../src/sql/dialect-spec.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dialect-spec.d.ts","sourceRoot":"","sources":["../../src/sql/dialect-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,gHAAgH;AAChH,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,aAAa,GACb,OAAO,GACP,SAAS,GACT,QAAQ,GACR,WAAW,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,SAAS,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,eAAe,EAAE,GAAG,GAAG,GAAG,CAAC;IAC3B,6EAA6E;IAC7E,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,mGAAmG;IACnG,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,uFAAuF;IACvF,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACjC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AA8CF,8CAA8C;AAC9C,eAAO,MAAM,gBAAgB,EAAE,WAW9B,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,EAAE,WAIjC,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,aAAa,EAAE,WAmB3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,cAAc,EAAE,WAkB5B,CAAC;AAEF,oCAAoC;AACpC,eAAO,MAAM,iBAAiB,EAAE,WAwB/B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;CAOqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,SAAS,gBAAgB,EAE7B,CAAC;AAEjC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,WAAW,CAQhE"}
|
package/dist/sql/dialect-spec.js
CHANGED
|
@@ -6,10 +6,63 @@
|
|
|
6
6
|
* Authoring guidance:
|
|
7
7
|
* - Keep `promptBrief` to one short paragraph; surfaced in the model's user prompt.
|
|
8
8
|
* - `extraForbiddenKeywords` is additive on top of the dialect-agnostic read-only
|
|
9
|
-
* denylist (`insert`, `update`, `delete`, `drop`, …) baked into
|
|
9
|
+
* denylist (`insert`, `update`, `delete`, `drop`, `into`, …) baked into
|
|
10
|
+
* `validateSelectSql`. Matched against unquoted keyword tokens only.
|
|
11
|
+
* - `blockedFunctions` lists functions with side effects (writes, file/network access,
|
|
12
|
+
* sleeping, session changes) that `validateSelectSql` rejects when called.
|
|
10
13
|
* - `extraValidate` runs *after* the base validator passes; throw a
|
|
11
14
|
* `SqlValidationError` to reject dialect-specific shapes.
|
|
15
|
+
* - `id` also selects how SQL is lexed (string escapes, quoting, comment syntax) — see
|
|
16
|
+
* `lexer.ts`. Specs that spread a built-in (`{ ...POSTGRES_DIALECT, … }`) inherit its
|
|
17
|
+
* denylists; a spec that overrides `extraForbiddenKeywords` / `blockedFunctions`
|
|
18
|
+
* replaces them.
|
|
19
|
+
*
|
|
20
|
+
* These lists are defense in depth for model-generated SQL, not a security boundary:
|
|
21
|
+
* execute generated SQL under a read-only database role.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Postgres functions with side effects or host/file/network access. Superuser-only
|
|
25
|
+
* functions are listed too: the validator does not know the executing role.
|
|
12
26
|
*/
|
|
27
|
+
const POSTGRES_BLOCKED_FUNCTIONS = [
|
|
28
|
+
// session / server configuration and process control
|
|
29
|
+
"set_config", "pg_reload_conf", "pg_rotate_logfile", "pg_terminate_backend",
|
|
30
|
+
"pg_cancel_backend", "pg_log_backend_memory_contexts", "pg_promote",
|
|
31
|
+
"pg_switch_wal", "pg_create_restore_point", "pg_start_backup", "pg_stop_backup",
|
|
32
|
+
"pg_backup_start", "pg_backup_stop", "pg_import_system_collations",
|
|
33
|
+
// sleeping
|
|
34
|
+
"pg_sleep", "pg_sleep_for", "pg_sleep_until",
|
|
35
|
+
// server file system
|
|
36
|
+
"pg_read_file", "pg_read_binary_file", "pg_ls_dir", "pg_stat_file", "pg_ls_logdir",
|
|
37
|
+
"pg_ls_waldir", "pg_ls_tmpdir", "pg_ls_archive_statusdir", "pg_ls_logicalsnapdir",
|
|
38
|
+
"pg_ls_logicalmapdir", "pg_ls_replslotdir", "pg_file_write", "pg_file_rename",
|
|
39
|
+
"pg_file_unlink", "pg_file_sync",
|
|
40
|
+
// large objects (lo_import / lo_export touch the server file system)
|
|
41
|
+
"lo_import", "lo_export", "lo_unlink", "lo_create", "lo_creat", "lo_from_bytea",
|
|
42
|
+
"lo_put", "lo_open", "lo_write", "lowrite", "lo_truncate", "lo_truncate64",
|
|
43
|
+
// dblink runs SQL on another (or the same) server
|
|
44
|
+
"dblink", "dblink_exec", "dblink_connect", "dblink_connect_u", "dblink_open",
|
|
45
|
+
"dblink_send_query", "dblink_fetch", "dblink_get_result",
|
|
46
|
+
// replication slots, messages, notifications
|
|
47
|
+
"pg_create_physical_replication_slot", "pg_create_logical_replication_slot",
|
|
48
|
+
"pg_drop_replication_slot", "pg_copy_physical_replication_slot",
|
|
49
|
+
"pg_copy_logical_replication_slot", "pg_replication_slot_advance",
|
|
50
|
+
"pg_logical_emit_message", "pg_notify",
|
|
51
|
+
// advisory locks
|
|
52
|
+
"pg_advisory_lock", "pg_advisory_lock_shared", "pg_advisory_xact_lock",
|
|
53
|
+
"pg_advisory_xact_lock_shared", "pg_try_advisory_lock", "pg_try_advisory_lock_shared",
|
|
54
|
+
"pg_try_advisory_xact_lock", "pg_try_advisory_xact_lock_shared", "pg_advisory_unlock",
|
|
55
|
+
"pg_advisory_unlock_shared", "pg_advisory_unlock_all",
|
|
56
|
+
// statistics resets
|
|
57
|
+
"pg_stat_reset", "pg_stat_reset_shared", "pg_stat_reset_single_table_counters",
|
|
58
|
+
"pg_stat_reset_single_function_counters", "pg_stat_reset_slru",
|
|
59
|
+
"pg_stat_reset_replication_slot",
|
|
60
|
+
// sequences
|
|
61
|
+
"nextval", "setval",
|
|
62
|
+
// functions that execute a query passed as a string
|
|
63
|
+
"query_to_xml", "query_to_xmlschema", "query_to_xml_and_xmlschema", "cursor_to_xml",
|
|
64
|
+
"ts_stat",
|
|
65
|
+
];
|
|
13
66
|
/** PostgreSQL — the original AskDB target. */
|
|
14
67
|
export const POSTGRES_DIALECT = {
|
|
15
68
|
id: "postgres",
|
|
@@ -18,6 +71,9 @@ export const POSTGRES_DIALECT = {
|
|
|
18
71
|
"Quote identifiers with double quotes when they collide with keywords or contain mixed case. " +
|
|
19
72
|
'Cast with `value::type`. Use NOW(), CURRENT_DATE, date_trunc(). Concatenate with `||`.',
|
|
20
73
|
identifierQuote: '"',
|
|
74
|
+
blockedFunctions: POSTGRES_BLOCKED_FUNCTIONS,
|
|
75
|
+
listBinding: "array",
|
|
76
|
+
backslashEscapes: false,
|
|
21
77
|
};
|
|
22
78
|
/** CockroachDB — PostgreSQL-wire-compatible; reuses the Postgres prompt brief. */
|
|
23
79
|
export const COCKROACHDB_DIALECT = {
|
|
@@ -35,6 +91,15 @@ export const MYSQL_DIALECT = {
|
|
|
35
91
|
"Concatenate with `CONCAT(a, b)` — `||` is logical OR in MySQL, not string concat. " +
|
|
36
92
|
"Limit rows with `LIMIT n` (or `LIMIT offset, n`).",
|
|
37
93
|
identifierQuote: "`",
|
|
94
|
+
// INTO OUTFILE / DUMPFILE are also covered by the base `into` keyword.
|
|
95
|
+
extraForbiddenKeywords: ["outfile", "dumpfile"],
|
|
96
|
+
blockedFunctions: [
|
|
97
|
+
"load_file", "sleep", "benchmark", "get_lock", "release_lock", "release_all_locks",
|
|
98
|
+
"master_pos_wait", "source_pos_wait", "wait_for_executed_gtid_set",
|
|
99
|
+
"wait_until_sql_thread_after_gtids", "sys_exec", "sys_eval",
|
|
100
|
+
],
|
|
101
|
+
listBinding: "expand",
|
|
102
|
+
backslashEscapes: true,
|
|
38
103
|
};
|
|
39
104
|
/**
|
|
40
105
|
* MariaDB — MySQL-protocol-compatible; the SELECT surface is functionally
|
|
@@ -60,6 +125,10 @@ export const SQLITE_DIALECT = {
|
|
|
60
125
|
// is maintenance. None belong in a generated read-only SELECT. (`vacuum` is
|
|
61
126
|
// already in the dialect-agnostic base denylist.)
|
|
62
127
|
extraForbiddenKeywords: ["attach", "detach", "pragma", "reindex"],
|
|
128
|
+
// load_extension loads native code; readfile/writefile/edit are CLI-shell extensions.
|
|
129
|
+
blockedFunctions: ["load_extension", "readfile", "writefile", "edit", "fts3_tokenizer"],
|
|
130
|
+
listBinding: "expand",
|
|
131
|
+
backslashEscapes: false,
|
|
63
132
|
};
|
|
64
133
|
/** Microsoft SQL Server (T-SQL). */
|
|
65
134
|
export const SQLSERVER_DIALECT = {
|
|
@@ -73,8 +142,18 @@ export const SQLSERVER_DIALECT = {
|
|
|
73
142
|
"Limit rows with `SELECT TOP (n) …` or `ORDER BY … OFFSET m ROWS FETCH NEXT n ROWS ONLY` — there is no LIMIT keyword.",
|
|
74
143
|
identifierQuote: '"',
|
|
75
144
|
// T-SQL keywords that shouldn't appear in read-only analytics SQL. (`call`
|
|
76
|
-
// is already in the base denylist; T-SQL uses EXEC / EXECUTE for procs
|
|
77
|
-
|
|
145
|
+
// is already in the base denylist; T-SQL uses EXEC / EXECUTE for procs, which also
|
|
146
|
+
// covers `EXEC sp_…` / `EXEC xp_…`.) T-SQL runs a batch without semicolons —
|
|
147
|
+
// `SELECT 1 SHUTDOWN` is two statements — so statement-level verbs are listed here
|
|
148
|
+
// even though they cannot appear inside a SELECT.
|
|
149
|
+
extraForbiddenKeywords: [
|
|
150
|
+
"exec", "execute", "merge", "openrowset", "openquery", "opendatasource",
|
|
151
|
+
"shutdown", "kill", "backup", "restore", "dbcc", "reconfigure", "deny", "waitfor",
|
|
152
|
+
"bulk", "use", "set", "revert", "setuser", "checkpoint", "commit", "rollback",
|
|
153
|
+
"writetext", "updatetext", "disable", "enable", "receive", "send", "xp_cmdshell",
|
|
154
|
+
],
|
|
155
|
+
listBinding: "expand",
|
|
156
|
+
backslashEscapes: false,
|
|
78
157
|
};
|
|
79
158
|
/**
|
|
80
159
|
* Registry of dialect specs shipped with `@askdb/core`. Connectors that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialect-spec.js","sourceRoot":"","sources":["../../src/sql/dialect-spec.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dialect-spec.js","sourceRoot":"","sources":["../../src/sql/dialect-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prefer
|
|
2
|
+
* Prefer an explicitly `sql`-tagged fence; fall back to an untagged fence.
|
|
3
|
+
* Never return a fence tagged with anything else (e.g. `json`, `sql-unbound`).
|
|
4
|
+
* Keep single-block and bare-prose behavior identical to the pre-parameterize path.
|
|
3
5
|
*/
|
|
4
6
|
export declare function extractSqlFromModelText(raw: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Extract the ```sql-unbound fence from model text, if present.
|
|
9
|
+
* Returns undefined when the fence is missing or empty.
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractUnboundSqlFromModelText(raw: string): string | undefined;
|
|
5
12
|
//# sourceMappingURL=extract-sql.d.ts.map
|