@deepagents/text2sql 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -4
- package/dist/bin/sql.js +674 -116
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +344 -24
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +68 -21
- package/dist/lib/adapters/bigquery/index.js.map +2 -2
- package/dist/lib/adapters/mysql/index.js +68 -21
- package/dist/lib/adapters/mysql/index.js.map +2 -2
- package/dist/lib/adapters/postgres/index.js +68 -21
- package/dist/lib/adapters/postgres/index.js.map +2 -2
- package/dist/lib/adapters/runtime-scope.d.ts +2 -0
- package/dist/lib/adapters/runtime-scope.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +68 -21
- package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
- package/dist/lib/adapters/sqlite/index.js +68 -21
- package/dist/lib/adapters/sqlite/index.js.map +2 -2
- package/dist/lib/adapters/sqlserver/index.js +68 -21
- package/dist/lib/adapters/sqlserver/index.js.map +2 -2
- package/dist/lib/sql-command.d.ts +32 -0
- package/dist/lib/sql-command.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +32 -8
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js.map +2 -2
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,6 @@ export * from './lib/fragments/schema.ts';
|
|
|
10
10
|
export * from './lib/fs/index.ts';
|
|
11
11
|
export * from './lib/instructions.ts';
|
|
12
12
|
export * from './lib/sql-hooks.ts';
|
|
13
|
+
export { type CreateSqlCommandOptions, type CreateSqlCommandResult, createSqlCommand, } from './lib/sql-command.ts';
|
|
13
14
|
export * from './lib/sql.ts';
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -189,6 +189,13 @@ function createGroundingContext(options = {}) {
|
|
|
189
189
|
import nodeSqlParser from "node-sql-parser";
|
|
190
190
|
var { Parser } = nodeSqlParser;
|
|
191
191
|
var parser = new Parser();
|
|
192
|
+
function parseStatementTypes(sql, dialect) {
|
|
193
|
+
const ast = parser.astify(sql, { database: dialect });
|
|
194
|
+
const statements = Array.isArray(ast) ? ast : [ast];
|
|
195
|
+
return statements.flatMap(
|
|
196
|
+
(statement) => isAstLike(statement) && typeof statement.type === "string" ? [statement.type] : []
|
|
197
|
+
);
|
|
198
|
+
}
|
|
192
199
|
function extractBaseEntityReferences(sql, dialect) {
|
|
193
200
|
const ast = parser.astify(sql, { database: dialect });
|
|
194
201
|
const state = {
|
|
@@ -314,6 +321,22 @@ function caseFold(value) {
|
|
|
314
321
|
}
|
|
315
322
|
|
|
316
323
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
324
|
+
var PARSER_DIALECTS_BY_FORMATTER_LANGUAGE = {
|
|
325
|
+
sqlite: ["sqlite", "mysql"],
|
|
326
|
+
postgresql: ["postgresql"],
|
|
327
|
+
bigquery: ["bigquery"],
|
|
328
|
+
transactsql: ["transactsql"],
|
|
329
|
+
mysql: ["mysql"]
|
|
330
|
+
};
|
|
331
|
+
function parserDialectsFor(formatterLanguage) {
|
|
332
|
+
const candidates = PARSER_DIALECTS_BY_FORMATTER_LANGUAGE[formatterLanguage];
|
|
333
|
+
if (!candidates) {
|
|
334
|
+
throw new TypeError(
|
|
335
|
+
`No scope dialect mapping for formatter language "${formatterLanguage}". Add it to PARSER_DIALECTS_BY_FORMATTER_LANGUAGE in adapter.ts.`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
return candidates;
|
|
339
|
+
}
|
|
317
340
|
var Adapter = class {
|
|
318
341
|
/**
|
|
319
342
|
* Introspect the database schema and return context fragments.
|
|
@@ -535,10 +558,49 @@ var Adapter = class {
|
|
|
535
558
|
return sql.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\([().*])/g, "$1").replace(/\\(?=$)/gm, "");
|
|
536
559
|
}
|
|
537
560
|
#checkReadOnly(sql) {
|
|
538
|
-
const
|
|
539
|
-
if (
|
|
561
|
+
const statementTypes = this.#readParsedStatementTypes(sql);
|
|
562
|
+
if (statementTypes) {
|
|
563
|
+
if (statementTypes.length === 1 && statementTypes[0] === "select") {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
540
566
|
return "only SELECT or WITH queries allowed";
|
|
541
567
|
}
|
|
568
|
+
const keyword = this.#firstStatementKeyword(sql);
|
|
569
|
+
if (keyword === "SELECT" || keyword === "WITH") return null;
|
|
570
|
+
return "only SELECT or WITH queries allowed";
|
|
571
|
+
}
|
|
572
|
+
#readParsedStatementTypes(sql) {
|
|
573
|
+
for (const dialect of parserDialectsFor(this.formatterLanguage)) {
|
|
574
|
+
try {
|
|
575
|
+
return parseStatementTypes(sql, dialect);
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
#firstStatementKeyword(sql) {
|
|
582
|
+
let offset = 0;
|
|
583
|
+
while (offset < sql.length) {
|
|
584
|
+
if (/\s/.test(sql[offset])) {
|
|
585
|
+
offset++;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (sql.startsWith("--", offset)) {
|
|
589
|
+
offset += 2;
|
|
590
|
+
while (offset < sql.length && sql[offset] !== "\n" && sql[offset] !== "\r") {
|
|
591
|
+
offset++;
|
|
592
|
+
}
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (sql.startsWith("/*", offset)) {
|
|
596
|
+
const commentEnd = sql.indexOf("*/", offset + 2);
|
|
597
|
+
if (commentEnd === -1) return null;
|
|
598
|
+
offset = commentEnd + 2;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const keyword = /^[A-Za-z]+/.exec(sql.slice(offset));
|
|
602
|
+
return keyword ? keyword[0].toUpperCase() : null;
|
|
603
|
+
}
|
|
542
604
|
return null;
|
|
543
605
|
}
|
|
544
606
|
#cachedAllowedEntities = null;
|
|
@@ -548,36 +610,21 @@ var Adapter = class {
|
|
|
548
610
|
return this.#cachedAllowedEntities;
|
|
549
611
|
}
|
|
550
612
|
async #checkScope(sql, allowedEntities) {
|
|
551
|
-
const
|
|
552
|
-
const scopeDialects = {
|
|
553
|
-
sqlite: ["sqlite", "mysql"],
|
|
554
|
-
postgresql: ["postgresql"],
|
|
555
|
-
bigquery: ["bigquery"],
|
|
556
|
-
transactsql: ["transactsql"],
|
|
557
|
-
mysql: ["mysql"]
|
|
558
|
-
};
|
|
559
|
-
const candidates = scopeDialects[dialect];
|
|
560
|
-
if (!candidates) {
|
|
561
|
-
throw new TypeError(
|
|
562
|
-
`No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
|
|
563
|
-
);
|
|
564
|
-
}
|
|
613
|
+
const candidates = parserDialectsFor(this.formatterLanguage);
|
|
565
614
|
let references = null;
|
|
566
615
|
let lastError;
|
|
616
|
+
let lastDialect = candidates[0];
|
|
567
617
|
for (const candidate of candidates) {
|
|
568
618
|
try {
|
|
569
619
|
references = extractBaseEntityReferences(sql, candidate);
|
|
570
620
|
break;
|
|
571
621
|
} catch (error) {
|
|
622
|
+
lastDialect = candidate;
|
|
572
623
|
lastError = error;
|
|
573
624
|
}
|
|
574
625
|
}
|
|
575
626
|
if (references === null) {
|
|
576
|
-
return buildScopeParseErrorPayload(
|
|
577
|
-
sql,
|
|
578
|
-
dialect,
|
|
579
|
-
lastError
|
|
580
|
-
);
|
|
627
|
+
return buildScopeParseErrorPayload(sql, lastDialect, lastError);
|
|
581
628
|
}
|
|
582
629
|
if (references.length === 0) return null;
|
|
583
630
|
const allowedQualified = new Set(
|
|
@@ -5478,8 +5525,48 @@ function createSqlCommandHooks({
|
|
|
5478
5525
|
};
|
|
5479
5526
|
}
|
|
5480
5527
|
|
|
5528
|
+
// packages/text2sql/src/lib/sql-command.ts
|
|
5529
|
+
import { posix as posix5 } from "node:path";
|
|
5530
|
+
import { parseArgs } from "node:util";
|
|
5531
|
+
import { v7 } from "uuid";
|
|
5532
|
+
import {
|
|
5533
|
+
buildSubcommandRepair as buildSubcommandRepair2,
|
|
5534
|
+
defineSubcommandGroup,
|
|
5535
|
+
repairQuotedArg as repairQuotedArg2,
|
|
5536
|
+
useBashMeta as useBashMeta2
|
|
5537
|
+
} from "@deepagents/context";
|
|
5538
|
+
|
|
5481
5539
|
// packages/text2sql/src/lib/sql.ts
|
|
5482
5540
|
import "@deepagents/agent";
|
|
5541
|
+
var text2SqlValidationMarker = Symbol("Text2SqlValidationError");
|
|
5542
|
+
var text2SqlUnknownAdapterMarker = Symbol("Text2SqlUnknownAdapterError");
|
|
5543
|
+
var Text2SqlValidationError = class _Text2SqlValidationError extends Error {
|
|
5544
|
+
[text2SqlValidationMarker];
|
|
5545
|
+
constructor(message) {
|
|
5546
|
+
super(message);
|
|
5547
|
+
this.name = "Text2SqlValidationError";
|
|
5548
|
+
this[text2SqlValidationMarker] = true;
|
|
5549
|
+
}
|
|
5550
|
+
static isInstance(error) {
|
|
5551
|
+
return error instanceof _Text2SqlValidationError && error[text2SqlValidationMarker] === true;
|
|
5552
|
+
}
|
|
5553
|
+
};
|
|
5554
|
+
var Text2SqlUnknownAdapterError = class _Text2SqlUnknownAdapterError extends Error {
|
|
5555
|
+
[text2SqlUnknownAdapterMarker];
|
|
5556
|
+
adapter;
|
|
5557
|
+
available;
|
|
5558
|
+
constructor(adapter, available) {
|
|
5559
|
+
const list = available.join(", ") || "(none configured)";
|
|
5560
|
+
super(`Unknown adapter "${adapter}". Available: ${list}`);
|
|
5561
|
+
this.name = "Text2SqlUnknownAdapterError";
|
|
5562
|
+
this.adapter = adapter;
|
|
5563
|
+
this.available = available;
|
|
5564
|
+
this[text2SqlUnknownAdapterMarker] = true;
|
|
5565
|
+
}
|
|
5566
|
+
static isInstance(error) {
|
|
5567
|
+
return error instanceof _Text2SqlUnknownAdapterError && error[text2SqlUnknownAdapterMarker] === true;
|
|
5568
|
+
}
|
|
5569
|
+
};
|
|
5483
5570
|
var Text2Sql = class {
|
|
5484
5571
|
#config;
|
|
5485
5572
|
#indexer;
|
|
@@ -5498,19 +5585,59 @@ var Text2Sql = class {
|
|
|
5498
5585
|
#requireAdapter(name) {
|
|
5499
5586
|
const adapter = this.#config.adapters[name];
|
|
5500
5587
|
if (!adapter) {
|
|
5501
|
-
|
|
5502
|
-
throw new Error(`Unknown adapter "${name}". Available: ${available}`);
|
|
5588
|
+
throw new Text2SqlUnknownAdapterError(name, this.adapterNames());
|
|
5503
5589
|
}
|
|
5504
5590
|
return adapter;
|
|
5505
5591
|
}
|
|
5592
|
+
#requireModel(op) {
|
|
5593
|
+
const model = this.#config.model;
|
|
5594
|
+
if (!model) {
|
|
5595
|
+
throw new Error(`Text2Sql.${op}() requires a model in Text2SqlConfig`);
|
|
5596
|
+
}
|
|
5597
|
+
return model;
|
|
5598
|
+
}
|
|
5599
|
+
adapterNames() {
|
|
5600
|
+
return Object.keys(this.#config.adapters);
|
|
5601
|
+
}
|
|
5602
|
+
hasAdapter(name) {
|
|
5603
|
+
return name in this.#config.adapters;
|
|
5604
|
+
}
|
|
5605
|
+
async validate(name, sql) {
|
|
5606
|
+
const adapter = this.#requireAdapter(name);
|
|
5607
|
+
const result = this.#validateWith(adapter, sql);
|
|
5608
|
+
return result;
|
|
5609
|
+
}
|
|
5610
|
+
async run(name, sql) {
|
|
5611
|
+
const adapter = this.#requireAdapter(name);
|
|
5612
|
+
const formatted = await this.#validateWith(adapter, sql);
|
|
5613
|
+
const result = await adapter.execute(formatted);
|
|
5614
|
+
if (!Array.isArray(result)) {
|
|
5615
|
+
throw new Error("adapter.execute must return an array of rows");
|
|
5616
|
+
}
|
|
5617
|
+
const columns = result.length > 0 ? Object.keys(result[0]) : [];
|
|
5618
|
+
return { rows: result, columns };
|
|
5619
|
+
}
|
|
5620
|
+
async #validateWith(adapter, sql) {
|
|
5621
|
+
const formatted = adapter.format(sql);
|
|
5622
|
+
const error = await adapter.validate(formatted);
|
|
5623
|
+
if (error) throw new Text2SqlValidationError(error);
|
|
5624
|
+
return formatted;
|
|
5625
|
+
}
|
|
5626
|
+
async index(options = {}) {
|
|
5627
|
+
return this.#indexer.index({
|
|
5628
|
+
adapterNames: options.names,
|
|
5629
|
+
onProgress: options.onProgress
|
|
5630
|
+
});
|
|
5631
|
+
}
|
|
5506
5632
|
async toSql(input, adapterName) {
|
|
5507
5633
|
const adapter = this.#requireAdapter(adapterName);
|
|
5634
|
+
const model = this.#requireModel("toSql");
|
|
5508
5635
|
const fragments = await this.#indexer.indexAdapter(adapterName);
|
|
5509
5636
|
const result = await toSql({
|
|
5510
5637
|
input,
|
|
5511
5638
|
adapter,
|
|
5512
5639
|
fragments,
|
|
5513
|
-
model
|
|
5640
|
+
model
|
|
5514
5641
|
});
|
|
5515
5642
|
return result.sql;
|
|
5516
5643
|
}
|
|
@@ -5520,6 +5647,196 @@ var Text2Sql = class {
|
|
|
5520
5647
|
return producer.toPairs();
|
|
5521
5648
|
}
|
|
5522
5649
|
};
|
|
5650
|
+
|
|
5651
|
+
// packages/text2sql/src/lib/sql-command.ts
|
|
5652
|
+
var SQL_VALIDATE_REMINDER2 = 'Always run `sql validate <db> "..."` before `sql run <db> "..."` to catch syntax errors early.';
|
|
5653
|
+
var SqlCommandError = class extends Error {
|
|
5654
|
+
exitCode;
|
|
5655
|
+
constructor(message, exitCode = 1) {
|
|
5656
|
+
super(message);
|
|
5657
|
+
this.name = "SqlCommandError";
|
|
5658
|
+
this.exitCode = exitCode;
|
|
5659
|
+
}
|
|
5660
|
+
};
|
|
5661
|
+
function createSqlCommand(text2Sql, options = {}) {
|
|
5662
|
+
const defaultOutputDir = options.outputDir ?? "/sql";
|
|
5663
|
+
const subcommands = {
|
|
5664
|
+
run: {
|
|
5665
|
+
usage: 'run <db> "SELECT ..."',
|
|
5666
|
+
description: "Execute query against <db> and store results",
|
|
5667
|
+
repair: repairDbNameAndQuotedArg,
|
|
5668
|
+
handler: (args, ctx) => handleRun(text2Sql, defaultOutputDir, args, ctx)
|
|
5669
|
+
},
|
|
5670
|
+
validate: {
|
|
5671
|
+
usage: 'validate <db> "SELECT ..."',
|
|
5672
|
+
description: "Validate query syntax against <db>",
|
|
5673
|
+
repair: repairDbNameAndQuotedArg,
|
|
5674
|
+
handler: (args) => handleValidate(text2Sql, args)
|
|
5675
|
+
},
|
|
5676
|
+
index: {
|
|
5677
|
+
usage: "index [adapter ...]",
|
|
5678
|
+
description: "Index adapter schemas and write context artifacts",
|
|
5679
|
+
handler: (args, ctx) => handleIndex(text2Sql, defaultOutputDir, args, ctx)
|
|
5680
|
+
}
|
|
5681
|
+
};
|
|
5682
|
+
return {
|
|
5683
|
+
command: defineSubcommandGroup("sql", subcommands),
|
|
5684
|
+
repair: buildSubcommandRepair2("sql", subcommands)
|
|
5685
|
+
};
|
|
5686
|
+
}
|
|
5687
|
+
function parseFlags(args) {
|
|
5688
|
+
let parsed;
|
|
5689
|
+
try {
|
|
5690
|
+
parsed = parseArgs({
|
|
5691
|
+
args,
|
|
5692
|
+
options: {
|
|
5693
|
+
"out-dir": { type: "string" },
|
|
5694
|
+
all: { type: "boolean" }
|
|
5695
|
+
},
|
|
5696
|
+
allowPositionals: true,
|
|
5697
|
+
strict: true
|
|
5698
|
+
});
|
|
5699
|
+
} catch (error) {
|
|
5700
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5701
|
+
throw new SqlCommandError(message, 1);
|
|
5702
|
+
}
|
|
5703
|
+
return {
|
|
5704
|
+
positional: parsed.positionals,
|
|
5705
|
+
outDir: parsed.values["out-dir"],
|
|
5706
|
+
all: parsed.values.all === true
|
|
5707
|
+
};
|
|
5708
|
+
}
|
|
5709
|
+
function resolveOutputDir(ctx, flags, fallback) {
|
|
5710
|
+
const envOutDir = ctx.env.get("TEXT2SQL_OUT_DIR");
|
|
5711
|
+
const candidate = flags.outDir ?? envOutDir ?? fallback;
|
|
5712
|
+
return posix5.isAbsolute(candidate) ? candidate : posix5.resolve(ctx.cwd, candidate);
|
|
5713
|
+
}
|
|
5714
|
+
async function handleRun(text2Sql, defaultOutputDir, args, ctx) {
|
|
5715
|
+
return runHandler("run", async () => {
|
|
5716
|
+
const flags = parseFlags(args);
|
|
5717
|
+
const meta = useBashMeta2();
|
|
5718
|
+
meta?.setReminder(SQL_VALIDATE_REMINDER2);
|
|
5719
|
+
const { name, sql } = takeDbAndSql(flags.positional, "run", text2Sql);
|
|
5720
|
+
const outputDir = resolveOutputDir(ctx, flags, defaultOutputDir);
|
|
5721
|
+
const { rows, columns } = await text2Sql.run(name, sql);
|
|
5722
|
+
const outPath = posix5.join(outputDir, `${v7()}.json`);
|
|
5723
|
+
await ctx.fs.mkdir(outputDir, { recursive: true });
|
|
5724
|
+
await ctx.fs.writeFile(outPath, JSON.stringify(rows, null, 2));
|
|
5725
|
+
return {
|
|
5726
|
+
stdout: `results stored in ${outPath}
|
|
5727
|
+
columns: ${columns.join(", ") || "(none)"}
|
|
5728
|
+
rows: ${rows.length}
|
|
5729
|
+
`,
|
|
5730
|
+
stderr: "",
|
|
5731
|
+
exitCode: 0
|
|
5732
|
+
};
|
|
5733
|
+
});
|
|
5734
|
+
}
|
|
5735
|
+
async function handleValidate(text2Sql, args) {
|
|
5736
|
+
return runHandler("validate", async () => {
|
|
5737
|
+
const flags = parseFlags(args);
|
|
5738
|
+
const { name, sql } = takeDbAndSql(flags.positional, "validate", text2Sql);
|
|
5739
|
+
await text2Sql.validate(name, sql);
|
|
5740
|
+
return { stdout: "valid\n", stderr: "", exitCode: 0 };
|
|
5741
|
+
});
|
|
5742
|
+
}
|
|
5743
|
+
async function handleIndex(text2Sql, defaultOutputDir, args, ctx) {
|
|
5744
|
+
return runHandler("index", async () => {
|
|
5745
|
+
const flags = parseFlags(args);
|
|
5746
|
+
const available = text2Sql.adapterNames();
|
|
5747
|
+
const requested = flags.all ? [] : dedupe(flags.positional);
|
|
5748
|
+
const names = requested.length === 0 ? available : requested;
|
|
5749
|
+
for (const name of names) {
|
|
5750
|
+
if (!text2Sql.hasAdapter(name)) {
|
|
5751
|
+
throw new SqlCommandError(
|
|
5752
|
+
`unknown adapter "${name}". Available: ${available.join(", ")}`,
|
|
5753
|
+
1
|
|
5754
|
+
);
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
const outputDir = resolveOutputDir(ctx, flags, defaultOutputDir);
|
|
5758
|
+
const fragments = await text2Sql.index({ names });
|
|
5759
|
+
const fragmentsPath = posix5.join(outputDir, `index-${v7()}.json`);
|
|
5760
|
+
await ctx.fs.mkdir(outputDir, { recursive: true });
|
|
5761
|
+
await ctx.fs.writeFile(fragmentsPath, JSON.stringify(fragments, null, 2));
|
|
5762
|
+
const manifest = {
|
|
5763
|
+
fragmentsPath,
|
|
5764
|
+
eventsPath: null,
|
|
5765
|
+
adapters: names,
|
|
5766
|
+
fragments: countSchemaFragments(fragments)
|
|
5767
|
+
};
|
|
5768
|
+
return {
|
|
5769
|
+
stdout: JSON.stringify(manifest, null, 2) + "\n",
|
|
5770
|
+
stderr: "",
|
|
5771
|
+
exitCode: 0
|
|
5772
|
+
};
|
|
5773
|
+
});
|
|
5774
|
+
}
|
|
5775
|
+
async function runHandler(name, fn) {
|
|
5776
|
+
try {
|
|
5777
|
+
return await fn();
|
|
5778
|
+
} catch (error) {
|
|
5779
|
+
if (error instanceof SqlCommandError) {
|
|
5780
|
+
return {
|
|
5781
|
+
stdout: "",
|
|
5782
|
+
stderr: `sql ${name}: ${error.message}
|
|
5783
|
+
`,
|
|
5784
|
+
exitCode: error.exitCode
|
|
5785
|
+
};
|
|
5786
|
+
}
|
|
5787
|
+
if (Text2SqlValidationError.isInstance(error) || Text2SqlUnknownAdapterError.isInstance(error)) {
|
|
5788
|
+
return {
|
|
5789
|
+
stdout: "",
|
|
5790
|
+
stderr: `sql ${name}: ${error.message}
|
|
5791
|
+
`,
|
|
5792
|
+
exitCode: 1
|
|
5793
|
+
};
|
|
5794
|
+
}
|
|
5795
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5796
|
+
return { stdout: "", stderr: `sql ${name}: ${message}
|
|
5797
|
+
`, exitCode: 1 };
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
function takeDbAndSql(positional, cmd, text2Sql) {
|
|
5801
|
+
const [db, ...rest] = positional;
|
|
5802
|
+
const available = text2Sql.adapterNames().join(", ");
|
|
5803
|
+
if (!db) {
|
|
5804
|
+
throw new SqlCommandError(
|
|
5805
|
+
`missing database name. Usage: sql ${cmd} <db> "SELECT ...". Available: ${available}`,
|
|
5806
|
+
1
|
|
5807
|
+
);
|
|
5808
|
+
}
|
|
5809
|
+
if (!text2Sql.hasAdapter(db)) {
|
|
5810
|
+
throw new SqlCommandError(
|
|
5811
|
+
`unknown database "${db}". Available: ${available}`,
|
|
5812
|
+
1
|
|
5813
|
+
);
|
|
5814
|
+
}
|
|
5815
|
+
const sql = rest.join(" ").trim();
|
|
5816
|
+
if (!sql) {
|
|
5817
|
+
throw new SqlCommandError("no query provided", 1);
|
|
5818
|
+
}
|
|
5819
|
+
return { name: db, sql };
|
|
5820
|
+
}
|
|
5821
|
+
function repairDbNameAndQuotedArg(rawArgs) {
|
|
5822
|
+
const trimmed = rawArgs.trim();
|
|
5823
|
+
if (!trimmed) return null;
|
|
5824
|
+
const firstSpace = trimmed.search(/\s/);
|
|
5825
|
+
if (firstSpace === -1) return null;
|
|
5826
|
+
const dbName = trimmed.slice(0, firstSpace);
|
|
5827
|
+
const rest = trimmed.slice(firstSpace + 1);
|
|
5828
|
+
const repaired = repairQuotedArg2(rest);
|
|
5829
|
+
if (repaired == null) return null;
|
|
5830
|
+
return `${dbName} ${repaired}`;
|
|
5831
|
+
}
|
|
5832
|
+
function dedupe(values) {
|
|
5833
|
+
return [...new Set(values)];
|
|
5834
|
+
}
|
|
5835
|
+
function countSchemaFragments(fragments) {
|
|
5836
|
+
return fragments.reduce((count, adapterFragment) => {
|
|
5837
|
+
return Array.isArray(adapterFragment.data) ? count + adapterFragment.data.length : count + 1;
|
|
5838
|
+
}, 0);
|
|
5839
|
+
}
|
|
5523
5840
|
export {
|
|
5524
5841
|
Adapter,
|
|
5525
5842
|
AdapterIndexer,
|
|
@@ -5536,10 +5853,13 @@ export {
|
|
|
5536
5853
|
SqliteFs,
|
|
5537
5854
|
TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
5538
5855
|
Text2Sql,
|
|
5856
|
+
Text2SqlUnknownAdapterError,
|
|
5857
|
+
Text2SqlValidationError,
|
|
5539
5858
|
UnanswerableSQLError,
|
|
5540
5859
|
applyTablesFilter,
|
|
5541
5860
|
column,
|
|
5542
5861
|
constraint,
|
|
5862
|
+
createSqlCommand,
|
|
5543
5863
|
createSqlCommandHooks,
|
|
5544
5864
|
createSqlMetaHook,
|
|
5545
5865
|
dialectInfo,
|