@deepagents/text2sql 0.29.1 → 0.31.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/dist/index.js +727 -379
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +6 -4
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.d.ts +2 -2
- package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +310 -13
- package/dist/lib/adapters/bigquery/index.js.map +4 -4
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts +23 -0
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +48 -5
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/groundings/table.grounding.d.ts +9 -1
- package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts +7 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/index.js +309 -12
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/mysql/mysql.d.ts +2 -2
- package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/index.js +309 -12
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/postgres.d.ts +2 -2
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +304 -11
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +309 -12
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/sqlite.d.ts +2 -2
- package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
- package/dist/lib/adapters/sqlserver/index.js +309 -12
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts +2 -2
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/agents/result-tools.d.ts +19 -1
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql-transform-plugins.d.ts +19 -0
- package/dist/lib/agents/sql-transform-plugins.d.ts.map +1 -0
- package/package.json +6 -6
|
@@ -19,8 +19,8 @@ export declare class Mysql extends Adapter {
|
|
|
19
19
|
readonly formatterLanguage = "mysql";
|
|
20
20
|
readonly systemSchemas: string[];
|
|
21
21
|
constructor(options: MysqlAdapterOptions);
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
executeImpl(sql: string): Promise<any>;
|
|
23
|
+
validateImpl(sql: string): Promise<string | void>;
|
|
24
24
|
runQuery<Row>(sql: string): Promise<Row[]>;
|
|
25
25
|
quoteIdentifier(name: string): string;
|
|
26
26
|
escape(value: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/mysql/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AA2CF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;;;;;EA2B3D;AAED,qBAAa,KAAM,SAAQ,OAAO;;IAEhC,SAAkB,SAAS,EAAE,WAAW,EAAE,CAAC;IAC3C,SAAkB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAa;IAChE,SAAkB,iBAAiB,WAAW;IAC9C,SAAkB,aAAa,WAK7B;gBAEU,OAAO,EAAE,mBAAmB;IAYzB,
|
|
1
|
+
{"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/mysql/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AA2CF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;;;;;EA2B3D;AAED,qBAAa,KAAM,SAAQ,OAAO;;IAEhC,SAAkB,SAAS,EAAE,WAAW,EAAE,CAAC;IAC3C,SAAkB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAa;IAChE,SAAkB,iBAAiB,WAAW;IAC9C,SAAkB,aAAa,WAK7B;gBAEU,OAAO,EAAE,mBAAmB;IAYzB,WAAW,CAAC,GAAG,EAAE,MAAM;IAIvB,YAAY,CAAC,GAAG,EAAE,MAAM;IAcxB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIhD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIrC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI7B,oBAAoB,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,EAC7B,KAAK,EAAE,MAAM,GACZ,MAAM;IAWT;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,EAAE,GAAG,SAAS,CAEpC;CAiCF;AAGD,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC"}
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
2
2
|
import { format as formatSql } from "sql-formatter";
|
|
3
3
|
|
|
4
|
+
// packages/text2sql/src/lib/agents/exceptions.ts
|
|
5
|
+
var sqlValidationMarker = Symbol("SQLValidationError");
|
|
6
|
+
var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
|
|
7
|
+
var sqlScopeMarker = Symbol("SQLScopeError");
|
|
8
|
+
var SQLValidationError = class _SQLValidationError extends Error {
|
|
9
|
+
[sqlValidationMarker];
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SQLValidationError";
|
|
13
|
+
this[sqlValidationMarker] = true;
|
|
14
|
+
}
|
|
15
|
+
static isInstance(error) {
|
|
16
|
+
return error instanceof _SQLValidationError && error[sqlValidationMarker] === true;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
20
|
+
[unanswerableSqlMarker];
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "UnanswerableSQLError";
|
|
24
|
+
this[unanswerableSqlMarker] = true;
|
|
25
|
+
}
|
|
26
|
+
static isInstance(error) {
|
|
27
|
+
return error instanceof _UnanswerableSQLError && error[unanswerableSqlMarker] === true;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var SQLScopeError = class _SQLScopeError extends Error {
|
|
31
|
+
[sqlScopeMarker];
|
|
32
|
+
payload;
|
|
33
|
+
errorType;
|
|
34
|
+
constructor(payload) {
|
|
35
|
+
super(JSON.stringify(payload));
|
|
36
|
+
this.name = "SQLScopeError";
|
|
37
|
+
this.payload = payload;
|
|
38
|
+
this.errorType = payload.error_type;
|
|
39
|
+
this[sqlScopeMarker] = true;
|
|
40
|
+
}
|
|
41
|
+
static isInstance(error) {
|
|
42
|
+
return error instanceof _SQLScopeError && error[sqlScopeMarker] === true;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
4
46
|
// packages/text2sql/src/lib/fragments/schema.ts
|
|
5
47
|
function dialectInfo(input) {
|
|
6
48
|
return {
|
|
@@ -104,6 +146,134 @@ function createGroundingContext() {
|
|
|
104
146
|
};
|
|
105
147
|
}
|
|
106
148
|
|
|
149
|
+
// packages/text2sql/src/lib/adapters/runtime-scope.ts
|
|
150
|
+
import nodeSqlParser from "node-sql-parser";
|
|
151
|
+
var { Parser } = nodeSqlParser;
|
|
152
|
+
var parser = new Parser();
|
|
153
|
+
function extractBaseEntityReferences(sql, dialect) {
|
|
154
|
+
const ast = parser.astify(sql, { database: dialect });
|
|
155
|
+
const state = {
|
|
156
|
+
cteNames: /* @__PURE__ */ new Set(),
|
|
157
|
+
references: /* @__PURE__ */ new Map()
|
|
158
|
+
};
|
|
159
|
+
visitNode(ast, state);
|
|
160
|
+
return Array.from(state.references.values());
|
|
161
|
+
}
|
|
162
|
+
function buildOutOfScopePayload(sql, referencedEntities, allowedEntities) {
|
|
163
|
+
return {
|
|
164
|
+
error: `Query references entities outside grounded scope: ${referencedEntities.join(", ")}`,
|
|
165
|
+
error_type: "OUT_OF_SCOPE",
|
|
166
|
+
suggestion: "Restrict the query to grounded tables/views or expand grounding to include the referenced entities.",
|
|
167
|
+
sql_attempted: sql,
|
|
168
|
+
referenced_entities: referencedEntities,
|
|
169
|
+
allowed_entities: allowedEntities
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function buildScopeParseErrorPayload(sql, dialect, error) {
|
|
173
|
+
const parserError = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
174
|
+
return {
|
|
175
|
+
error: `SQL scope analysis failed before validation/execution: ${parserError}`,
|
|
176
|
+
error_type: "SQL_SCOPE_PARSE_ERROR",
|
|
177
|
+
suggestion: "Rewrite the query into simpler SQL that can be analyzed safely, or extend parser coverage for this dialect feature.",
|
|
178
|
+
sql_attempted: sql,
|
|
179
|
+
parser_dialect: dialect,
|
|
180
|
+
parser_error: parserError
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function visitNode(node, state) {
|
|
184
|
+
if (Array.isArray(node)) {
|
|
185
|
+
for (const item of node) {
|
|
186
|
+
visitNode(item, state);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!isAstLike(node)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (isStatementNode(node)) {
|
|
194
|
+
visitStatement(node, state);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (isTableReferenceNode(node)) {
|
|
198
|
+
addReference(node, state);
|
|
199
|
+
}
|
|
200
|
+
for (const value of Object.values(node)) {
|
|
201
|
+
visitNode(value, state);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function visitStatement(node, parentState) {
|
|
205
|
+
const localState = {
|
|
206
|
+
cteNames: new Set(parentState.cteNames),
|
|
207
|
+
references: parentState.references
|
|
208
|
+
};
|
|
209
|
+
const withItems = Array.isArray(node.with) ? node.with : [];
|
|
210
|
+
for (const item of withItems) {
|
|
211
|
+
if (!isAstLike(item)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const cteName = readCteName(item);
|
|
215
|
+
if (cteName) {
|
|
216
|
+
localState.cteNames.add(caseFold(cteName));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const item of withItems) {
|
|
220
|
+
if (!isAstLike(item)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
visitNode(item.stmt, localState);
|
|
224
|
+
}
|
|
225
|
+
for (const [key, value] of Object.entries(node)) {
|
|
226
|
+
if (key === "with") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
visitNode(value, localState);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function addReference(node, state) {
|
|
233
|
+
const table2 = typeof node.table === "string" ? node.table : null;
|
|
234
|
+
if (!table2) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (state.cteNames.has(caseFold(table2))) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const db = typeof node.db === "string" ? node.db : null;
|
|
241
|
+
const key = db ? `${db}.${table2}` : table2;
|
|
242
|
+
if (!state.references.has(key)) {
|
|
243
|
+
state.references.set(key, { db, table: table2 });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function readCteName(node) {
|
|
247
|
+
const name = node.name;
|
|
248
|
+
if (typeof name === "string") {
|
|
249
|
+
return name;
|
|
250
|
+
}
|
|
251
|
+
if (!isAstLike(name)) {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
const value = name.value;
|
|
255
|
+
return typeof value === "string" ? value : void 0;
|
|
256
|
+
}
|
|
257
|
+
function isStatementNode(node) {
|
|
258
|
+
const type = node.type;
|
|
259
|
+
return typeof type === "string" && ["delete", "insert", "replace", "select", "update"].includes(type);
|
|
260
|
+
}
|
|
261
|
+
function isTableReferenceNode(node) {
|
|
262
|
+
if (node.type === "column_ref") {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
if (typeof node.table !== "string") {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return "addition" in node || "as" in node || "db" in node || "join" in node || "operator" in node || "surround" in node || "table_hint" in node || "temporal_table" in node;
|
|
269
|
+
}
|
|
270
|
+
function isAstLike(value) {
|
|
271
|
+
return typeof value === "object" && value !== null;
|
|
272
|
+
}
|
|
273
|
+
function caseFold(value) {
|
|
274
|
+
return value.toLowerCase();
|
|
275
|
+
}
|
|
276
|
+
|
|
107
277
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
108
278
|
var Adapter = class {
|
|
109
279
|
/**
|
|
@@ -133,10 +303,7 @@ var Adapter = class {
|
|
|
133
303
|
const grounding = fn(this);
|
|
134
304
|
await grounding.execute(ctx);
|
|
135
305
|
}
|
|
136
|
-
return [
|
|
137
|
-
...ctx.tables.map((t) => t.name),
|
|
138
|
-
...ctx.views.map((v) => v.name)
|
|
139
|
-
];
|
|
306
|
+
return [...ctx.tables.map((t) => t.name), ...ctx.views.map((v) => v.name)];
|
|
140
307
|
}
|
|
141
308
|
/**
|
|
142
309
|
* Convert complete grounding context to schema fragments.
|
|
@@ -149,7 +316,8 @@ var Adapter = class {
|
|
|
149
316
|
dialectInfo({
|
|
150
317
|
dialect: ctx.info.dialect,
|
|
151
318
|
version: ctx.info.version,
|
|
152
|
-
database: ctx.info.database
|
|
319
|
+
database: ctx.info.database,
|
|
320
|
+
details: ctx.info.details
|
|
153
321
|
})
|
|
154
322
|
);
|
|
155
323
|
}
|
|
@@ -160,7 +328,18 @@ var Adapter = class {
|
|
|
160
328
|
fragments.push(this.#viewToFragment(v));
|
|
161
329
|
}
|
|
162
330
|
const tableMap = new Map(ctx.tables.map((t) => [t.name, t]));
|
|
331
|
+
const tableColumnSets = new Map(
|
|
332
|
+
ctx.tables.map((t) => [t.name, new Set(t.columns.map((c) => c.name))])
|
|
333
|
+
);
|
|
163
334
|
for (const rel of ctx.relationships) {
|
|
335
|
+
const sourceColumns = tableColumnSets.get(rel.table);
|
|
336
|
+
const targetColumns = tableColumnSets.get(rel.referenced_table);
|
|
337
|
+
if (sourceColumns && rel.from.some((column2) => !sourceColumns.has(column2))) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (targetColumns && rel.to.some((column2) => !targetColumns.has(column2))) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
164
343
|
const sourceTable = tableMap.get(rel.table);
|
|
165
344
|
const targetTable = tableMap.get(rel.referenced_table);
|
|
166
345
|
fragments.push(
|
|
@@ -215,7 +394,10 @@ var Adapter = class {
|
|
|
215
394
|
stats: col.stats
|
|
216
395
|
})
|
|
217
396
|
);
|
|
218
|
-
const
|
|
397
|
+
const presentColumns = new Set(t.columns.map((c) => c.name));
|
|
398
|
+
const indexFragments = (t.indexes ?? []).filter(
|
|
399
|
+
(idx) => idx.columns.every((column2) => presentColumns.has(column2))
|
|
400
|
+
).map(
|
|
219
401
|
(idx) => index({
|
|
220
402
|
name: idx.name,
|
|
221
403
|
columns: idx.columns,
|
|
@@ -225,6 +407,8 @@ var Adapter = class {
|
|
|
225
407
|
);
|
|
226
408
|
const constraintFragments = (t.constraints ?? []).filter(
|
|
227
409
|
(c) => c.type === "CHECK" || c.type === "UNIQUE" && (c.columns?.length ?? 0) > 1
|
|
410
|
+
).filter(
|
|
411
|
+
(c) => !c.columns?.length || c.columns.every((column2) => presentColumns.has(column2))
|
|
228
412
|
).map(
|
|
229
413
|
(c) => constraint({
|
|
230
414
|
name: c.name,
|
|
@@ -293,6 +477,78 @@ var Adapter = class {
|
|
|
293
477
|
return sql;
|
|
294
478
|
}
|
|
295
479
|
}
|
|
480
|
+
#cachedAllowedEntities = null;
|
|
481
|
+
async #resolveScope() {
|
|
482
|
+
if (this.#cachedAllowedEntities) return this.#cachedAllowedEntities;
|
|
483
|
+
this.#cachedAllowedEntities = await this.resolveAllowedEntities();
|
|
484
|
+
return this.#cachedAllowedEntities;
|
|
485
|
+
}
|
|
486
|
+
async #checkScope(sql, allowedEntities) {
|
|
487
|
+
const dialect = this.formatterLanguage;
|
|
488
|
+
const scopeDialects = {
|
|
489
|
+
sqlite: "mysql",
|
|
490
|
+
postgresql: "postgresql",
|
|
491
|
+
bigquery: "bigquery",
|
|
492
|
+
transactsql: "transactsql",
|
|
493
|
+
mysql: "mysql"
|
|
494
|
+
};
|
|
495
|
+
const scopeDialect = scopeDialects[dialect];
|
|
496
|
+
if (!scopeDialect) {
|
|
497
|
+
throw new TypeError(
|
|
498
|
+
`No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
let references;
|
|
502
|
+
try {
|
|
503
|
+
references = extractBaseEntityReferences(sql, scopeDialect);
|
|
504
|
+
} catch (error) {
|
|
505
|
+
return buildScopeParseErrorPayload(
|
|
506
|
+
sql,
|
|
507
|
+
dialect,
|
|
508
|
+
error
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
if (references.length === 0) return null;
|
|
512
|
+
const allowedQualified = new Set(
|
|
513
|
+
allowedEntities.map((e) => e.toLowerCase())
|
|
514
|
+
);
|
|
515
|
+
const allowedUnqualified = /* @__PURE__ */ new Set();
|
|
516
|
+
for (const entity of allowedEntities) {
|
|
517
|
+
const dot = entity.lastIndexOf(".");
|
|
518
|
+
if (dot !== -1) {
|
|
519
|
+
allowedUnqualified.add(entity.slice(dot + 1).toLowerCase());
|
|
520
|
+
} else {
|
|
521
|
+
allowedUnqualified.add(entity.toLowerCase());
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const outOfScope = references.map((ref) => ref.db ? `${ref.db}.${ref.table}` : ref.table).filter((name) => {
|
|
525
|
+
const lower = name.toLowerCase();
|
|
526
|
+
if (name.includes(".")) {
|
|
527
|
+
if (allowedQualified.has(lower)) return false;
|
|
528
|
+
const parts = lower.split(".");
|
|
529
|
+
if (parts.length >= 3) {
|
|
530
|
+
const datasetTable = parts.slice(-2).join(".");
|
|
531
|
+
if (allowedQualified.has(datasetTable)) return false;
|
|
532
|
+
}
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
return !allowedQualified.has(lower) && !allowedUnqualified.has(lower);
|
|
536
|
+
});
|
|
537
|
+
if (outOfScope.length === 0) return null;
|
|
538
|
+
return buildOutOfScopePayload(sql, outOfScope, allowedEntities);
|
|
539
|
+
}
|
|
540
|
+
async validate(sql) {
|
|
541
|
+
const allowed = await this.#resolveScope();
|
|
542
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
543
|
+
if (scopeError) return JSON.stringify(scopeError);
|
|
544
|
+
return this.validateImpl(sql);
|
|
545
|
+
}
|
|
546
|
+
async execute(sql) {
|
|
547
|
+
const allowed = await this.#resolveScope();
|
|
548
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
549
|
+
if (scopeError) throw new SQLScopeError(scopeError);
|
|
550
|
+
return this.executeImpl(sql);
|
|
551
|
+
}
|
|
296
552
|
/**
|
|
297
553
|
* Convert unknown database value to number.
|
|
298
554
|
* Handles number, bigint, and string types.
|
|
@@ -346,6 +602,21 @@ var Adapter = class {
|
|
|
346
602
|
};
|
|
347
603
|
|
|
348
604
|
// packages/text2sql/src/lib/adapters/groundings/abstract.grounding.ts
|
|
605
|
+
function filterColumns(columns, filter) {
|
|
606
|
+
if (Array.isArray(filter)) {
|
|
607
|
+
return columns.filter((col) => filter.includes(col.name));
|
|
608
|
+
}
|
|
609
|
+
if (filter instanceof RegExp) {
|
|
610
|
+
return columns.filter((col) => filter.test(col.name));
|
|
611
|
+
}
|
|
612
|
+
return columns.filter((col) => filter(col.name));
|
|
613
|
+
}
|
|
614
|
+
function applyColumnFilter(entity, columnsConfig) {
|
|
615
|
+
if (!columnsConfig) return entity;
|
|
616
|
+
const filter = columnsConfig[entity.name];
|
|
617
|
+
if (!filter) return entity;
|
|
618
|
+
return { ...entity, columns: filterColumns(entity.columns, filter) };
|
|
619
|
+
}
|
|
349
620
|
var AbstractGrounding = class {
|
|
350
621
|
/**
|
|
351
622
|
* Grounding identifier for debugging/logging.
|
|
@@ -717,11 +988,13 @@ var RowCountGrounding = class extends AbstractGrounding {
|
|
|
717
988
|
// packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
|
|
718
989
|
var TableGrounding = class extends AbstractGrounding {
|
|
719
990
|
#filter;
|
|
991
|
+
#columns;
|
|
720
992
|
#forward;
|
|
721
993
|
#backward;
|
|
722
994
|
constructor(config = {}) {
|
|
723
995
|
super("table");
|
|
724
996
|
this.#filter = config.filter;
|
|
997
|
+
this.#columns = config.columns;
|
|
725
998
|
this.#forward = config.forward;
|
|
726
999
|
this.#backward = config.backward;
|
|
727
1000
|
}
|
|
@@ -735,7 +1008,9 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
735
1008
|
const backward = this.#backward;
|
|
736
1009
|
if (!forward && !backward) {
|
|
737
1010
|
const tables3 = await Promise.all(
|
|
738
|
-
seedTables.map(
|
|
1011
|
+
seedTables.map(
|
|
1012
|
+
async (name) => applyColumnFilter(await this.getTable(name), this.#columns)
|
|
1013
|
+
)
|
|
739
1014
|
);
|
|
740
1015
|
ctx.tables.push(...tables3);
|
|
741
1016
|
return;
|
|
@@ -759,11 +1034,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
759
1034
|
if (forwardVisited.has(name)) continue;
|
|
760
1035
|
forwardVisited.add(name);
|
|
761
1036
|
if (!tables2[name]) {
|
|
762
|
-
tables2[name] =
|
|
1037
|
+
tables2[name] = applyColumnFilter(
|
|
1038
|
+
await this.getTable(name),
|
|
1039
|
+
this.#columns
|
|
1040
|
+
);
|
|
763
1041
|
}
|
|
764
1042
|
if (depth < forwardLimit) {
|
|
765
1043
|
const rels = await this.findOutgoingRelations(name);
|
|
766
1044
|
for (const rel of rels) {
|
|
1045
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
767
1046
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
768
1047
|
if (!forwardVisited.has(rel.referenced_table)) {
|
|
769
1048
|
forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
|
|
@@ -779,11 +1058,15 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
779
1058
|
if (backwardVisited.has(name)) continue;
|
|
780
1059
|
backwardVisited.add(name);
|
|
781
1060
|
if (!tables2[name]) {
|
|
782
|
-
tables2[name] =
|
|
1061
|
+
tables2[name] = applyColumnFilter(
|
|
1062
|
+
await this.getTable(name),
|
|
1063
|
+
this.#columns
|
|
1064
|
+
);
|
|
783
1065
|
}
|
|
784
1066
|
if (depth < backwardLimit) {
|
|
785
1067
|
const rels = await this.findIncomingRelations(name);
|
|
786
1068
|
for (const rel of rels) {
|
|
1069
|
+
if (!this.isRelationshipVisible(rel)) continue;
|
|
787
1070
|
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
788
1071
|
if (!backwardVisited.has(rel.table)) {
|
|
789
1072
|
backwardQueue.push({ name: rel.table, depth: depth + 1 });
|
|
@@ -823,6 +1106,16 @@ var TableGrounding = class extends AbstractGrounding {
|
|
|
823
1106
|
all.push(rel);
|
|
824
1107
|
}
|
|
825
1108
|
}
|
|
1109
|
+
isRelationshipVisible(rel) {
|
|
1110
|
+
return this.areColumnsVisible(rel.from, this.#columns?.[rel.table]) && this.areColumnsVisible(rel.to, this.#columns?.[rel.referenced_table]);
|
|
1111
|
+
}
|
|
1112
|
+
areColumnsVisible(names, filter) {
|
|
1113
|
+
if (!filter) return true;
|
|
1114
|
+
return filterColumns(
|
|
1115
|
+
names.map((name) => ({ name })),
|
|
1116
|
+
filter
|
|
1117
|
+
).length === names.length;
|
|
1118
|
+
}
|
|
826
1119
|
};
|
|
827
1120
|
|
|
828
1121
|
// packages/text2sql/src/lib/adapters/postgres/column-stats.postgres.grounding.ts
|
|
@@ -1318,10 +1611,10 @@ var Postgres = class extends Adapter {
|
|
|
1318
1611
|
};
|
|
1319
1612
|
this.grounding = options.grounding;
|
|
1320
1613
|
}
|
|
1321
|
-
async
|
|
1614
|
+
async executeImpl(sql) {
|
|
1322
1615
|
return this.#options.execute(sql);
|
|
1323
1616
|
}
|
|
1324
|
-
async
|
|
1617
|
+
async validateImpl(sql) {
|
|
1325
1618
|
const validator = this.#options.validate ?? (async (text) => {
|
|
1326
1619
|
await this.#options.execute(`EXPLAIN ${text}`);
|
|
1327
1620
|
});
|
|
@@ -1885,10 +2178,12 @@ var PostgresTableGrounding = class extends TableGrounding {
|
|
|
1885
2178
|
// packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
|
|
1886
2179
|
var ViewGrounding = class extends AbstractGrounding {
|
|
1887
2180
|
#filter;
|
|
2181
|
+
#columns;
|
|
1888
2182
|
includeDefinition;
|
|
1889
2183
|
constructor(config = {}) {
|
|
1890
2184
|
super("view");
|
|
1891
2185
|
this.#filter = config.filter;
|
|
2186
|
+
this.#columns = config.columns;
|
|
1892
2187
|
this.includeDefinition = config.includeDefinition ?? true;
|
|
1893
2188
|
}
|
|
1894
2189
|
/**
|
|
@@ -1898,7 +2193,9 @@ var ViewGrounding = class extends AbstractGrounding {
|
|
|
1898
2193
|
async execute(ctx) {
|
|
1899
2194
|
const viewNames = await this.applyFilter();
|
|
1900
2195
|
const views2 = await Promise.all(
|
|
1901
|
-
viewNames.map(
|
|
2196
|
+
viewNames.map(
|
|
2197
|
+
async (name) => applyColumnFilter(await this.getView(name), this.#columns)
|
|
2198
|
+
)
|
|
1902
2199
|
);
|
|
1903
2200
|
ctx.views.push(...views2);
|
|
1904
2201
|
}
|