@deepagents/text2sql 0.29.1 → 0.30.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 +710 -378
- 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 +248 -8
- package/dist/lib/adapters/bigquery/index.js.map +4 -4
- package/dist/lib/adapters/mysql/index.js +247 -7
- 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 +247 -7
- 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 +247 -7
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +247 -7
- 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 +247 -7
- 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
|
@@ -18,8 +18,8 @@ export declare class Postgres extends Adapter {
|
|
|
18
18
|
readonly systemSchemas: string[];
|
|
19
19
|
readonly formatterLanguage = "postgresql";
|
|
20
20
|
constructor(options: PostgresAdapterOptions);
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
executeImpl(sql: string): Promise<any>;
|
|
22
|
+
validateImpl(sql: string): Promise<string | void>;
|
|
23
23
|
runQuery<Row>(sql: string): Promise<Row[]>;
|
|
24
24
|
quoteIdentifier(name: string): string;
|
|
25
25
|
escape(value: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/postgres/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,WAAW,EAKhB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAwEF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;;;;;EA0B9D;AAED,qBAAa,QAAS,SAAQ,OAAO;;IAEnC,SAAkB,SAAS,EAAE,WAAW,EAAE,CAAC;IAC3C,SAAkB,aAAa,YAAY;IAC3C,SAAkB,aAAa,WAAwC;IACvE,SAAkB,iBAAiB,gBAAgB;gBAEvC,OAAO,EAAE,sBAAsB;IAY5B,
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/postgres/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,eAAe,EACpB,KAAK,WAAW,EAKhB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAwEF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;;;;;EA0B9D;AAED,qBAAa,QAAS,SAAQ,OAAO;;IAEnC,SAAkB,SAAS,EAAE,WAAW,EAAE,CAAC;IAC3C,SAAkB,aAAa,YAAY;IAC3C,SAAkB,aAAa,WAAwC;IACvE,SAAkB,iBAAiB,gBAAgB;gBAEvC,OAAO,EAAE,sBAAsB;IAY5B,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;CAmdV"}
|
|
@@ -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
|
}
|
|
@@ -293,6 +461,78 @@ var Adapter = class {
|
|
|
293
461
|
return sql;
|
|
294
462
|
}
|
|
295
463
|
}
|
|
464
|
+
#cachedAllowedEntities = null;
|
|
465
|
+
async #resolveScope() {
|
|
466
|
+
if (this.#cachedAllowedEntities) return this.#cachedAllowedEntities;
|
|
467
|
+
this.#cachedAllowedEntities = await this.resolveAllowedEntities();
|
|
468
|
+
return this.#cachedAllowedEntities;
|
|
469
|
+
}
|
|
470
|
+
async #checkScope(sql, allowedEntities) {
|
|
471
|
+
const dialect = this.formatterLanguage;
|
|
472
|
+
const scopeDialects = {
|
|
473
|
+
sqlite: "mysql",
|
|
474
|
+
postgresql: "postgresql",
|
|
475
|
+
bigquery: "bigquery",
|
|
476
|
+
transactsql: "transactsql",
|
|
477
|
+
mysql: "mysql"
|
|
478
|
+
};
|
|
479
|
+
const scopeDialect = scopeDialects[dialect];
|
|
480
|
+
if (!scopeDialect) {
|
|
481
|
+
throw new TypeError(
|
|
482
|
+
`No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
let references;
|
|
486
|
+
try {
|
|
487
|
+
references = extractBaseEntityReferences(sql, scopeDialect);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return buildScopeParseErrorPayload(
|
|
490
|
+
sql,
|
|
491
|
+
dialect,
|
|
492
|
+
error
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
if (references.length === 0) return null;
|
|
496
|
+
const allowedQualified = new Set(
|
|
497
|
+
allowedEntities.map((e) => e.toLowerCase())
|
|
498
|
+
);
|
|
499
|
+
const allowedUnqualified = /* @__PURE__ */ new Set();
|
|
500
|
+
for (const entity of allowedEntities) {
|
|
501
|
+
const dot = entity.lastIndexOf(".");
|
|
502
|
+
if (dot !== -1) {
|
|
503
|
+
allowedUnqualified.add(entity.slice(dot + 1).toLowerCase());
|
|
504
|
+
} else {
|
|
505
|
+
allowedUnqualified.add(entity.toLowerCase());
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const outOfScope = references.map((ref) => ref.db ? `${ref.db}.${ref.table}` : ref.table).filter((name) => {
|
|
509
|
+
const lower = name.toLowerCase();
|
|
510
|
+
if (name.includes(".")) {
|
|
511
|
+
if (allowedQualified.has(lower)) return false;
|
|
512
|
+
const parts = lower.split(".");
|
|
513
|
+
if (parts.length >= 3) {
|
|
514
|
+
const datasetTable = parts.slice(-2).join(".");
|
|
515
|
+
if (allowedQualified.has(datasetTable)) return false;
|
|
516
|
+
}
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
return !allowedQualified.has(lower) && !allowedUnqualified.has(lower);
|
|
520
|
+
});
|
|
521
|
+
if (outOfScope.length === 0) return null;
|
|
522
|
+
return buildOutOfScopePayload(sql, outOfScope, allowedEntities);
|
|
523
|
+
}
|
|
524
|
+
async validate(sql) {
|
|
525
|
+
const allowed = await this.#resolveScope();
|
|
526
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
527
|
+
if (scopeError) return JSON.stringify(scopeError);
|
|
528
|
+
return this.validateImpl(sql);
|
|
529
|
+
}
|
|
530
|
+
async execute(sql) {
|
|
531
|
+
const allowed = await this.#resolveScope();
|
|
532
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
533
|
+
if (scopeError) throw new SQLScopeError(scopeError);
|
|
534
|
+
return this.executeImpl(sql);
|
|
535
|
+
}
|
|
296
536
|
/**
|
|
297
537
|
* Convert unknown database value to number.
|
|
298
538
|
* Handles number, bigint, and string types.
|
|
@@ -919,10 +1159,10 @@ var Sqlite = class extends Adapter {
|
|
|
919
1159
|
this.#options = options;
|
|
920
1160
|
this.grounding = options.grounding;
|
|
921
1161
|
}
|
|
922
|
-
async
|
|
1162
|
+
async executeImpl(sql) {
|
|
923
1163
|
return this.#options.execute(sql);
|
|
924
1164
|
}
|
|
925
|
-
async
|
|
1165
|
+
async validateImpl(sql) {
|
|
926
1166
|
const validator = this.#options.validate ?? (async (text) => {
|
|
927
1167
|
await this.#options.execute(`EXPLAIN ${text}`);
|
|
928
1168
|
});
|