@deepagents/text2sql 0.13.1 → 0.15.1
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.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2446 -2879
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +2 -0
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.d.ts +41 -0
- package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts +11 -0
- package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/index.d.ts +35 -0
- package/dist/lib/adapters/bigquery/index.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/index.js +1321 -0
- package/dist/lib/adapters/bigquery/index.js.map +7 -0
- package/dist/lib/adapters/bigquery/indexes.bigquery.grounding.d.ts +14 -0
- package/dist/lib/adapters/bigquery/indexes.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/info.bigquery.grounding.d.ts +9 -0
- package/dist/lib/adapters/bigquery/info.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/row-count.bigquery.grounding.d.ts +14 -0
- package/dist/lib/adapters/bigquery/row-count.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts +15 -0
- package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/bigquery/view.bigquery.grounding.d.ts +12 -0
- package/dist/lib/adapters/bigquery/view.bigquery.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/index.js +9 -2044
- package/dist/lib/adapters/groundings/index.js.map +4 -4
- package/dist/lib/adapters/mysql/index.js +9 -2044
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts +2 -4
- package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts +0 -8
- package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/index.js +140 -2056
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts +0 -3
- package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +10 -35
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +9 -2044
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/index.js +9 -2044
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/agents/result-tools.d.ts +18 -23
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/fragments/schema.d.ts +2 -0
- package/dist/lib/fragments/schema.d.ts.map +1 -1
- package/dist/lib/fs/index.d.ts +5 -0
- package/dist/lib/fs/index.d.ts.map +1 -0
- package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts +2 -0
- package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts.map +1 -0
- package/dist/lib/fs/mssql/mssql-fs.d.ts +56 -0
- package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -0
- package/dist/lib/fs/scoped-fs.d.ts +55 -0
- package/dist/lib/fs/scoped-fs.d.ts.map +1 -0
- package/dist/lib/fs/sqlite/sqlite-fs.d.ts +68 -0
- package/dist/lib/fs/sqlite/sqlite-fs.d.ts.map +1 -0
- package/dist/lib/fs/tracked-fs.d.ts +42 -0
- package/dist/lib/fs/tracked-fs.d.ts.map +1 -0
- package/dist/lib/instructions.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +5 -6
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +223 -2088
- package/dist/lib/synthesis/index.js.map +4 -4
- package/package.json +18 -11
- package/dist/lib/agents/text2sql.agent.d.ts +0 -3
- package/dist/lib/agents/text2sql.agent.d.ts.map +0 -1
|
@@ -0,0 +1,1321 @@
|
|
|
1
|
+
// packages/text2sql/src/lib/fragments/schema.ts
|
|
2
|
+
function dialectInfo(input) {
|
|
3
|
+
return {
|
|
4
|
+
name: "dialectInfo",
|
|
5
|
+
data: {
|
|
6
|
+
dialect: input.dialect,
|
|
7
|
+
...input.version && { version: input.version },
|
|
8
|
+
...input.database && { database: input.database }
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function table(input) {
|
|
13
|
+
return {
|
|
14
|
+
name: "table",
|
|
15
|
+
data: {
|
|
16
|
+
name: input.name,
|
|
17
|
+
...input.schema && { schema: input.schema },
|
|
18
|
+
...input.rowCount != null && { rowCount: input.rowCount },
|
|
19
|
+
...input.sizeHint && { sizeHint: input.sizeHint },
|
|
20
|
+
columns: input.columns,
|
|
21
|
+
...input.indexes?.length && { indexes: input.indexes },
|
|
22
|
+
...input.constraints?.length && { constraints: input.constraints }
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function column(input) {
|
|
27
|
+
return {
|
|
28
|
+
name: "column",
|
|
29
|
+
data: {
|
|
30
|
+
name: input.name,
|
|
31
|
+
type: input.type,
|
|
32
|
+
...input.pk && { pk: true },
|
|
33
|
+
...input.fk && { fk: input.fk },
|
|
34
|
+
...input.unique && { unique: true },
|
|
35
|
+
...input.notNull && { notNull: true },
|
|
36
|
+
...input.default && { default: input.default },
|
|
37
|
+
...input.indexed && { indexed: true },
|
|
38
|
+
...input.values?.length && { values: input.values },
|
|
39
|
+
...input.stats && { stats: input.stats }
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function index(input) {
|
|
44
|
+
return {
|
|
45
|
+
name: "index",
|
|
46
|
+
data: {
|
|
47
|
+
name: input.name,
|
|
48
|
+
columns: input.columns,
|
|
49
|
+
...input.unique && { unique: true },
|
|
50
|
+
...input.type && { type: input.type }
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function constraint(input) {
|
|
55
|
+
return {
|
|
56
|
+
name: "constraint",
|
|
57
|
+
data: {
|
|
58
|
+
name: input.name,
|
|
59
|
+
type: input.type,
|
|
60
|
+
...input.columns?.length && { columns: input.columns },
|
|
61
|
+
...input.definition && { definition: input.definition },
|
|
62
|
+
...input.defaultValue && { defaultValue: input.defaultValue },
|
|
63
|
+
...input.referencedTable && { referencedTable: input.referencedTable },
|
|
64
|
+
...input.referencedColumns?.length && {
|
|
65
|
+
referencedColumns: input.referencedColumns
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function view(input) {
|
|
71
|
+
return {
|
|
72
|
+
name: "view",
|
|
73
|
+
data: {
|
|
74
|
+
name: input.name,
|
|
75
|
+
...input.schema && { schema: input.schema },
|
|
76
|
+
columns: input.columns,
|
|
77
|
+
...input.definition && { definition: input.definition }
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function relationship(input) {
|
|
82
|
+
return {
|
|
83
|
+
name: "relationship",
|
|
84
|
+
data: {
|
|
85
|
+
from: input.from,
|
|
86
|
+
to: input.to,
|
|
87
|
+
...input.cardinality && { cardinality: input.cardinality }
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// packages/text2sql/src/lib/adapters/groundings/context.ts
|
|
93
|
+
function createGroundingContext() {
|
|
94
|
+
return {
|
|
95
|
+
tables: [],
|
|
96
|
+
views: [],
|
|
97
|
+
relationships: [],
|
|
98
|
+
info: void 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
103
|
+
var Adapter = class {
|
|
104
|
+
/**
|
|
105
|
+
* Introspect the database schema and return context fragments.
|
|
106
|
+
*
|
|
107
|
+
* Executes all configured groundings to populate the context, then
|
|
108
|
+
* generates fragments from the complete context data.
|
|
109
|
+
*
|
|
110
|
+
* @param ctx - Optional grounding context for sharing state between groundings
|
|
111
|
+
* @returns Array of context fragments representing the database schema
|
|
112
|
+
*/
|
|
113
|
+
async introspect(ctx = createGroundingContext()) {
|
|
114
|
+
for (const fn of this.grounding) {
|
|
115
|
+
const grounding = fn(this);
|
|
116
|
+
await grounding.execute(ctx);
|
|
117
|
+
}
|
|
118
|
+
return this.#toSchemaFragments(ctx);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Convert complete grounding context to schema fragments.
|
|
122
|
+
* Called after all groundings have populated ctx with data.
|
|
123
|
+
*/
|
|
124
|
+
#toSchemaFragments(ctx) {
|
|
125
|
+
const fragments = [];
|
|
126
|
+
if (ctx.info) {
|
|
127
|
+
fragments.push(
|
|
128
|
+
dialectInfo({
|
|
129
|
+
dialect: ctx.info.dialect,
|
|
130
|
+
version: ctx.info.version,
|
|
131
|
+
database: ctx.info.database
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
for (const t of ctx.tables) {
|
|
136
|
+
fragments.push(this.#tableToFragment(t));
|
|
137
|
+
}
|
|
138
|
+
for (const v of ctx.views) {
|
|
139
|
+
fragments.push(this.#viewToFragment(v));
|
|
140
|
+
}
|
|
141
|
+
const tableMap = new Map(ctx.tables.map((t) => [t.name, t]));
|
|
142
|
+
for (const rel of ctx.relationships) {
|
|
143
|
+
const sourceTable = tableMap.get(rel.table);
|
|
144
|
+
const targetTable = tableMap.get(rel.referenced_table);
|
|
145
|
+
fragments.push(
|
|
146
|
+
this.#relationshipToFragment(rel, sourceTable, targetTable)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (ctx.report) {
|
|
150
|
+
fragments.push({ name: "businessContext", data: ctx.report });
|
|
151
|
+
}
|
|
152
|
+
return fragments;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Convert a Table to a table fragment with nested column, index, and constraint fragments.
|
|
156
|
+
*/
|
|
157
|
+
#tableToFragment(t) {
|
|
158
|
+
const pkConstraint = t.constraints?.find((c) => c.type === "PRIMARY_KEY");
|
|
159
|
+
const pkColumns = new Set(pkConstraint?.columns ?? []);
|
|
160
|
+
const notNullColumns = new Set(
|
|
161
|
+
t.constraints?.filter((c) => c.type === "NOT_NULL").flatMap((c) => c.columns ?? []) ?? []
|
|
162
|
+
);
|
|
163
|
+
const defaultByColumn = /* @__PURE__ */ new Map();
|
|
164
|
+
for (const c of t.constraints?.filter((c2) => c2.type === "DEFAULT") ?? []) {
|
|
165
|
+
for (const col of c.columns ?? []) {
|
|
166
|
+
if (c.defaultValue != null) {
|
|
167
|
+
defaultByColumn.set(col, c.defaultValue);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const uniqueColumns = new Set(
|
|
172
|
+
t.constraints?.filter((c) => c.type === "UNIQUE" && c.columns?.length === 1).flatMap((c) => c.columns ?? []) ?? []
|
|
173
|
+
);
|
|
174
|
+
const fkByColumn = /* @__PURE__ */ new Map();
|
|
175
|
+
for (const c of t.constraints?.filter((c2) => c2.type === "FOREIGN_KEY") ?? []) {
|
|
176
|
+
const cols = c.columns ?? [];
|
|
177
|
+
const refCols = c.referencedColumns ?? [];
|
|
178
|
+
for (let i = 0; i < cols.length; i++) {
|
|
179
|
+
const refCol = refCols[i] ?? refCols[0] ?? cols[i];
|
|
180
|
+
fkByColumn.set(cols[i], `${c.referencedTable}.${refCol}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const columnFragments = t.columns.map(
|
|
184
|
+
(col) => column({
|
|
185
|
+
name: col.name,
|
|
186
|
+
type: col.type,
|
|
187
|
+
pk: pkColumns.has(col.name) || void 0,
|
|
188
|
+
fk: fkByColumn.get(col.name),
|
|
189
|
+
unique: uniqueColumns.has(col.name) || void 0,
|
|
190
|
+
notNull: notNullColumns.has(col.name) || void 0,
|
|
191
|
+
default: defaultByColumn.get(col.name),
|
|
192
|
+
indexed: col.isIndexed || void 0,
|
|
193
|
+
values: col.values,
|
|
194
|
+
stats: col.stats
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
const indexFragments = (t.indexes ?? []).map(
|
|
198
|
+
(idx) => index({
|
|
199
|
+
name: idx.name,
|
|
200
|
+
columns: idx.columns,
|
|
201
|
+
unique: idx.unique,
|
|
202
|
+
type: idx.type
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
const constraintFragments = (t.constraints ?? []).filter(
|
|
206
|
+
(c) => c.type === "CHECK" || c.type === "UNIQUE" && (c.columns?.length ?? 0) > 1
|
|
207
|
+
).map(
|
|
208
|
+
(c) => constraint({
|
|
209
|
+
name: c.name,
|
|
210
|
+
type: c.type,
|
|
211
|
+
columns: c.columns,
|
|
212
|
+
definition: c.definition
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
return table({
|
|
216
|
+
name: t.name,
|
|
217
|
+
schema: t.schema,
|
|
218
|
+
rowCount: t.rowCount,
|
|
219
|
+
sizeHint: t.sizeHint,
|
|
220
|
+
columns: columnFragments,
|
|
221
|
+
indexes: indexFragments.length > 0 ? indexFragments : void 0,
|
|
222
|
+
constraints: constraintFragments.length > 0 ? constraintFragments : void 0
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Convert a View to a view fragment with nested column fragments.
|
|
227
|
+
*/
|
|
228
|
+
#viewToFragment(v) {
|
|
229
|
+
const columnFragments = v.columns.map(
|
|
230
|
+
(col) => column({
|
|
231
|
+
name: col.name,
|
|
232
|
+
type: col.type,
|
|
233
|
+
values: col.values,
|
|
234
|
+
stats: col.stats
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
return view({
|
|
238
|
+
name: v.name,
|
|
239
|
+
schema: v.schema,
|
|
240
|
+
columns: columnFragments,
|
|
241
|
+
definition: v.definition
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Convert a Relationship to a relationship fragment.
|
|
246
|
+
* Infers cardinality from row counts if available.
|
|
247
|
+
*/
|
|
248
|
+
#relationshipToFragment(rel, sourceTable, targetTable) {
|
|
249
|
+
const sourceCount = sourceTable?.rowCount;
|
|
250
|
+
const targetCount = targetTable?.rowCount;
|
|
251
|
+
let cardinality;
|
|
252
|
+
if (sourceCount != null && targetCount != null && targetCount > 0) {
|
|
253
|
+
const ratio = sourceCount / targetCount;
|
|
254
|
+
if (ratio > 5) {
|
|
255
|
+
cardinality = "many-to-one";
|
|
256
|
+
} else if (ratio < 1.2 && ratio > 0.8) {
|
|
257
|
+
cardinality = "one-to-one";
|
|
258
|
+
} else if (ratio < 0.2) {
|
|
259
|
+
cardinality = "one-to-many";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return relationship({
|
|
263
|
+
from: { table: rel.table, columns: rel.from },
|
|
264
|
+
to: { table: rel.referenced_table, columns: rel.to },
|
|
265
|
+
cardinality
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Convert unknown database value to number.
|
|
270
|
+
* Handles number, bigint, and string types.
|
|
271
|
+
*/
|
|
272
|
+
toNumber(value) {
|
|
273
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
if (typeof value === "bigint") {
|
|
277
|
+
return Number(value);
|
|
278
|
+
}
|
|
279
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
280
|
+
const parsed = Number(value);
|
|
281
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Parse a potentially qualified table name into schema and table parts.
|
|
287
|
+
*/
|
|
288
|
+
parseTableName(name) {
|
|
289
|
+
if (name.includes(".")) {
|
|
290
|
+
const [schema, ...rest] = name.split(".");
|
|
291
|
+
return { schema, table: rest.join(".") };
|
|
292
|
+
}
|
|
293
|
+
return { schema: this.defaultSchema ?? "", table: name };
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Escape a string value for use in SQL string literals (single quotes).
|
|
297
|
+
* Used in WHERE clauses like: WHERE name = '${escapeString(value)}'
|
|
298
|
+
*/
|
|
299
|
+
escapeString(value) {
|
|
300
|
+
return value.replace(/'/g, "''");
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build a SQL filter clause to include/exclude schemas.
|
|
304
|
+
* @param columnName - The schema column name (e.g., 'TABLE_SCHEMA')
|
|
305
|
+
* @param allowedSchemas - If provided, filter to these schemas only
|
|
306
|
+
*/
|
|
307
|
+
buildSchemaFilter(columnName, allowedSchemas) {
|
|
308
|
+
if (allowedSchemas && allowedSchemas.length > 0) {
|
|
309
|
+
const values = allowedSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
|
|
310
|
+
return `AND ${columnName} IN (${values})`;
|
|
311
|
+
}
|
|
312
|
+
if (this.systemSchemas.length > 0) {
|
|
313
|
+
const values = this.systemSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
|
|
314
|
+
return `AND ${columnName} NOT IN (${values})`;
|
|
315
|
+
}
|
|
316
|
+
return "";
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// packages/text2sql/src/lib/adapters/groundings/abstract.grounding.ts
|
|
321
|
+
var AbstractGrounding = class {
|
|
322
|
+
/**
|
|
323
|
+
* Grounding identifier for debugging/logging.
|
|
324
|
+
*/
|
|
325
|
+
name;
|
|
326
|
+
constructor(name) {
|
|
327
|
+
this.name = name;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// packages/text2sql/src/lib/adapters/groundings/constraint.grounding.ts
|
|
332
|
+
var ConstraintGrounding = class extends AbstractGrounding {
|
|
333
|
+
constructor(config = {}) {
|
|
334
|
+
super("constraint");
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Execute the grounding process.
|
|
338
|
+
* Annotates tables in ctx.tables with their constraints.
|
|
339
|
+
*/
|
|
340
|
+
async execute(ctx) {
|
|
341
|
+
for (const table2 of ctx.tables) {
|
|
342
|
+
try {
|
|
343
|
+
table2.constraints = await this.getConstraints(table2.name);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.warn("Error collecting constraints for", table2.name, error);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// packages/text2sql/src/lib/adapters/groundings/indexes.grounding.ts
|
|
352
|
+
var IndexesGrounding = class extends AbstractGrounding {
|
|
353
|
+
constructor(config = {}) {
|
|
354
|
+
super("index");
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Execute the grounding process.
|
|
358
|
+
* Annotates tables in ctx.tables with their indexes and marks indexed columns.
|
|
359
|
+
*/
|
|
360
|
+
async execute(ctx) {
|
|
361
|
+
for (const table2 of ctx.tables) {
|
|
362
|
+
table2.indexes = await this.getIndexes(table2.name);
|
|
363
|
+
for (const index2 of table2.indexes ?? []) {
|
|
364
|
+
for (const colName of index2.columns) {
|
|
365
|
+
const column2 = table2.columns.find((c) => c.name === colName);
|
|
366
|
+
if (column2) {
|
|
367
|
+
column2.isIndexed = true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// packages/text2sql/src/lib/adapters/groundings/info.grounding.ts
|
|
376
|
+
var InfoGrounding = class extends AbstractGrounding {
|
|
377
|
+
constructor(config = {}) {
|
|
378
|
+
super("dialectInfo");
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Execute the grounding process.
|
|
382
|
+
* Writes database info to ctx.info.
|
|
383
|
+
*/
|
|
384
|
+
async execute(ctx) {
|
|
385
|
+
ctx.info = await this.collectInfo();
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
|
|
390
|
+
import { groq } from "@ai-sdk/groq";
|
|
391
|
+
import { tool } from "ai";
|
|
392
|
+
import dedent from "dedent";
|
|
393
|
+
import z from "zod";
|
|
394
|
+
import "@deepagents/agent";
|
|
395
|
+
import {
|
|
396
|
+
ContextEngine,
|
|
397
|
+
InMemoryContextStore,
|
|
398
|
+
agent,
|
|
399
|
+
fragment,
|
|
400
|
+
persona,
|
|
401
|
+
user
|
|
402
|
+
} from "@deepagents/context";
|
|
403
|
+
var ReportGrounding = class extends AbstractGrounding {
|
|
404
|
+
#adapter;
|
|
405
|
+
#model;
|
|
406
|
+
#cache;
|
|
407
|
+
#forceRefresh;
|
|
408
|
+
constructor(adapter, config = {}) {
|
|
409
|
+
super("business_context");
|
|
410
|
+
this.#adapter = adapter;
|
|
411
|
+
this.#model = config.model ?? groq("openai/gpt-oss-20b");
|
|
412
|
+
this.#cache = config.cache;
|
|
413
|
+
this.#forceRefresh = config.forceRefresh ?? false;
|
|
414
|
+
}
|
|
415
|
+
async execute(ctx) {
|
|
416
|
+
if (!this.#forceRefresh && this.#cache) {
|
|
417
|
+
const cached = await this.#cache.get();
|
|
418
|
+
if (cached) {
|
|
419
|
+
ctx.report = cached;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const report2 = await this.#generateReport();
|
|
424
|
+
ctx.report = report2;
|
|
425
|
+
if (this.#cache) {
|
|
426
|
+
await this.#cache.set(report2);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async #generateReport() {
|
|
430
|
+
const context = new ContextEngine({
|
|
431
|
+
store: new InMemoryContextStore(),
|
|
432
|
+
chatId: `report-gen-${crypto.randomUUID()}`,
|
|
433
|
+
userId: "system"
|
|
434
|
+
});
|
|
435
|
+
context.set(
|
|
436
|
+
persona({
|
|
437
|
+
name: "db-report-agent",
|
|
438
|
+
role: "Database analyst",
|
|
439
|
+
objective: "Analyze the database and write a contextual report about what it represents"
|
|
440
|
+
}),
|
|
441
|
+
fragment(
|
|
442
|
+
"instructions",
|
|
443
|
+
dedent`
|
|
444
|
+
Write a business context that helps another agent answer questions accurately.
|
|
445
|
+
|
|
446
|
+
For EACH table, do queries ONE AT A TIME:
|
|
447
|
+
1. SELECT COUNT(*) to get row count
|
|
448
|
+
2. SELECT * LIMIT 3 to see sample data
|
|
449
|
+
|
|
450
|
+
Then write a report with:
|
|
451
|
+
- What business this database is for
|
|
452
|
+
- For each table: purpose, row count, and example of what the data looks like
|
|
453
|
+
|
|
454
|
+
Include concrete examples like "Track prices are $0.99",
|
|
455
|
+
"Customer names like 'Luís Gonçalves'", etc.
|
|
456
|
+
|
|
457
|
+
Keep it 400-600 words, conversational style.
|
|
458
|
+
`
|
|
459
|
+
),
|
|
460
|
+
user(
|
|
461
|
+
"Please analyze the database and write a contextual report about what this database represents."
|
|
462
|
+
)
|
|
463
|
+
);
|
|
464
|
+
const adapter = this.#adapter;
|
|
465
|
+
const reportAgent = agent({
|
|
466
|
+
name: "db-report-agent",
|
|
467
|
+
model: this.#model,
|
|
468
|
+
context,
|
|
469
|
+
tools: {
|
|
470
|
+
query_database: tool({
|
|
471
|
+
description: "Execute a SELECT query to explore the database and gather insights.",
|
|
472
|
+
inputSchema: z.object({
|
|
473
|
+
sql: z.string().describe("The SELECT query to execute"),
|
|
474
|
+
purpose: z.string().describe(
|
|
475
|
+
"What insight you are trying to gather with this query"
|
|
476
|
+
)
|
|
477
|
+
}),
|
|
478
|
+
execute: ({ sql }) => {
|
|
479
|
+
return adapter.execute(sql);
|
|
480
|
+
}
|
|
481
|
+
})
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
const result = await reportAgent.generate({});
|
|
485
|
+
return result.text;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// packages/text2sql/src/lib/adapters/groundings/row-count.grounding.ts
|
|
490
|
+
var RowCountGrounding = class extends AbstractGrounding {
|
|
491
|
+
constructor(config = {}) {
|
|
492
|
+
super("rowCount");
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Execute the grounding process.
|
|
496
|
+
* Annotates tables in ctx.tables with row counts and size hints.
|
|
497
|
+
*/
|
|
498
|
+
async execute(ctx) {
|
|
499
|
+
for (const table2 of ctx.tables) {
|
|
500
|
+
const count = await this.getRowCount(table2.name);
|
|
501
|
+
if (count != null) {
|
|
502
|
+
table2.rowCount = count;
|
|
503
|
+
table2.sizeHint = this.#classifyRowCount(count);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Classify row count into a size hint category.
|
|
509
|
+
*/
|
|
510
|
+
#classifyRowCount(count) {
|
|
511
|
+
if (count < 100) return "tiny";
|
|
512
|
+
if (count < 1e3) return "small";
|
|
513
|
+
if (count < 1e4) return "medium";
|
|
514
|
+
if (count < 1e5) return "large";
|
|
515
|
+
return "huge";
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
|
|
520
|
+
var TableGrounding = class extends AbstractGrounding {
|
|
521
|
+
#filter;
|
|
522
|
+
#forward;
|
|
523
|
+
#backward;
|
|
524
|
+
constructor(config = {}) {
|
|
525
|
+
super("table");
|
|
526
|
+
this.#filter = config.filter;
|
|
527
|
+
this.#forward = config.forward;
|
|
528
|
+
this.#backward = config.backward;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Execute the grounding process.
|
|
532
|
+
* Writes discovered tables and relationships to the context.
|
|
533
|
+
*/
|
|
534
|
+
async execute(ctx) {
|
|
535
|
+
const seedTables = await this.applyFilter();
|
|
536
|
+
const forward = this.#forward;
|
|
537
|
+
const backward = this.#backward;
|
|
538
|
+
if (!forward && !backward) {
|
|
539
|
+
const tables3 = await Promise.all(
|
|
540
|
+
seedTables.map((name) => this.getTable(name))
|
|
541
|
+
);
|
|
542
|
+
ctx.tables.push(...tables3);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const tables2 = {};
|
|
546
|
+
const allRelationships = [];
|
|
547
|
+
const seenRelationships = /* @__PURE__ */ new Set();
|
|
548
|
+
const forwardQueue = [];
|
|
549
|
+
const backwardQueue = [];
|
|
550
|
+
const forwardVisited = /* @__PURE__ */ new Set();
|
|
551
|
+
const backwardVisited = /* @__PURE__ */ new Set();
|
|
552
|
+
for (const name of seedTables) {
|
|
553
|
+
if (forward) forwardQueue.push({ name, depth: 0 });
|
|
554
|
+
if (backward) backwardQueue.push({ name, depth: 0 });
|
|
555
|
+
}
|
|
556
|
+
const forwardLimit = forward === true ? Infinity : forward || 0;
|
|
557
|
+
while (forwardQueue.length > 0) {
|
|
558
|
+
const item = forwardQueue.shift();
|
|
559
|
+
if (!item) break;
|
|
560
|
+
const { name, depth } = item;
|
|
561
|
+
if (forwardVisited.has(name)) continue;
|
|
562
|
+
forwardVisited.add(name);
|
|
563
|
+
if (!tables2[name]) {
|
|
564
|
+
tables2[name] = await this.getTable(name);
|
|
565
|
+
}
|
|
566
|
+
if (depth < forwardLimit) {
|
|
567
|
+
const rels = await this.findOutgoingRelations(name);
|
|
568
|
+
for (const rel of rels) {
|
|
569
|
+
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
570
|
+
if (!forwardVisited.has(rel.referenced_table)) {
|
|
571
|
+
forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const backwardLimit = backward === true ? Infinity : backward || 0;
|
|
577
|
+
while (backwardQueue.length > 0) {
|
|
578
|
+
const item = backwardQueue.shift();
|
|
579
|
+
if (!item) break;
|
|
580
|
+
const { name, depth } = item;
|
|
581
|
+
if (backwardVisited.has(name)) continue;
|
|
582
|
+
backwardVisited.add(name);
|
|
583
|
+
if (!tables2[name]) {
|
|
584
|
+
tables2[name] = await this.getTable(name);
|
|
585
|
+
}
|
|
586
|
+
if (depth < backwardLimit) {
|
|
587
|
+
const rels = await this.findIncomingRelations(name);
|
|
588
|
+
for (const rel of rels) {
|
|
589
|
+
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
590
|
+
if (!backwardVisited.has(rel.table)) {
|
|
591
|
+
backwardQueue.push({ name: rel.table, depth: depth + 1 });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const tablesList = Object.values(tables2);
|
|
597
|
+
ctx.tables.push(...tablesList);
|
|
598
|
+
ctx.relationships.push(...allRelationships);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Apply the filter to get seed table names.
|
|
602
|
+
* If filter is an explicit array, skip querying all table names.
|
|
603
|
+
*/
|
|
604
|
+
async applyFilter() {
|
|
605
|
+
const filter = this.#filter;
|
|
606
|
+
if (Array.isArray(filter)) {
|
|
607
|
+
return filter;
|
|
608
|
+
}
|
|
609
|
+
const names = await this.getAllTableNames();
|
|
610
|
+
if (!filter) {
|
|
611
|
+
return names;
|
|
612
|
+
}
|
|
613
|
+
if (filter instanceof RegExp) {
|
|
614
|
+
return names.filter((name) => filter.test(name));
|
|
615
|
+
}
|
|
616
|
+
return names.filter(filter);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Add a relationship to the collection, deduplicating by key.
|
|
620
|
+
*/
|
|
621
|
+
addRelationship(rel, all, seen) {
|
|
622
|
+
const key = `${rel.table}:${rel.from.join(",")}:${rel.referenced_table}:${rel.to.join(",")}`;
|
|
623
|
+
if (!seen.has(key)) {
|
|
624
|
+
seen.add(key);
|
|
625
|
+
all.push(rel);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
// packages/text2sql/src/lib/adapters/bigquery/bigquery.ts
|
|
631
|
+
function formatBigQueryError(sql, error) {
|
|
632
|
+
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : typeof error === "object" && error !== null ? error.message ?? JSON.stringify(error) : "Unknown error occurred";
|
|
633
|
+
return {
|
|
634
|
+
error: errorMessage,
|
|
635
|
+
error_type: "BIGQUERY_ERROR",
|
|
636
|
+
suggestion: "Validate the query (dry-run) and review table/dataset names, nested field paths, and parameter bindings.",
|
|
637
|
+
sql_attempted: sql
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
var BigQuery = class extends Adapter {
|
|
641
|
+
#options;
|
|
642
|
+
#datasetSet;
|
|
643
|
+
grounding;
|
|
644
|
+
defaultSchema;
|
|
645
|
+
systemSchemas = [];
|
|
646
|
+
constructor(options) {
|
|
647
|
+
super();
|
|
648
|
+
if (!options || typeof options.execute !== "function") {
|
|
649
|
+
throw new Error("BigQuery adapter requires an execute(sql) function.");
|
|
650
|
+
}
|
|
651
|
+
if (typeof options.validate !== "function") {
|
|
652
|
+
throw new Error(
|
|
653
|
+
"BigQuery adapter requires a validate(sql) function. Provide a BigQuery dry-run validator (recommended) so generated SQL can be validated before execution."
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
const datasets = (options.datasets ?? []).map((d) => d.trim()).filter(Boolean);
|
|
657
|
+
if (datasets.length === 0) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
"BigQuery adapter requires a non-empty datasets list (e.g. datasets: ['analytics']). This scopes all introspection."
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
this.#options = { ...options, datasets };
|
|
663
|
+
this.#datasetSet = new Set(datasets);
|
|
664
|
+
this.grounding = options.grounding;
|
|
665
|
+
this.defaultSchema = datasets.length === 1 ? datasets[0] : void 0;
|
|
666
|
+
}
|
|
667
|
+
get datasets() {
|
|
668
|
+
return this.#options.datasets;
|
|
669
|
+
}
|
|
670
|
+
get projectId() {
|
|
671
|
+
return this.#options.projectId;
|
|
672
|
+
}
|
|
673
|
+
isDatasetAllowed(dataset) {
|
|
674
|
+
return this.#datasetSet.has(dataset);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Build a fully-qualified BigQuery INFORMATION_SCHEMA view reference.
|
|
678
|
+
* Uses standard BigQuery backtick quoting on the full path.
|
|
679
|
+
*/
|
|
680
|
+
infoSchemaView(dataset, viewName) {
|
|
681
|
+
const projectPrefix = this.projectId ? `${this.projectId}.` : "";
|
|
682
|
+
return `\`${projectPrefix}${dataset}.INFORMATION_SCHEMA.${viewName}\``;
|
|
683
|
+
}
|
|
684
|
+
async execute(sql) {
|
|
685
|
+
return this.#options.execute(sql);
|
|
686
|
+
}
|
|
687
|
+
async validate(sql) {
|
|
688
|
+
try {
|
|
689
|
+
return await this.#options.validate(sql);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
return JSON.stringify(formatBigQueryError(sql, error));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
async runQuery(sql) {
|
|
695
|
+
const result = await this.#options.execute(sql);
|
|
696
|
+
if (Array.isArray(result)) {
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) {
|
|
700
|
+
return result.rows;
|
|
701
|
+
}
|
|
702
|
+
throw new Error(
|
|
703
|
+
"BigQuery adapter execute() must return an array of rows or an object with a rows array when introspecting."
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
quoteIdentifier(name) {
|
|
707
|
+
return name.split(".").map((part) => `\`${part.replace(/`/g, "``")}\``).join(".");
|
|
708
|
+
}
|
|
709
|
+
escape(value) {
|
|
710
|
+
return value.replace(/`/g, "``");
|
|
711
|
+
}
|
|
712
|
+
buildSampleRowsQuery(tableName, columns, limit) {
|
|
713
|
+
const qualifiedTableName = this.#qualifyTableName(tableName);
|
|
714
|
+
const tableIdentifier = this.quoteIdentifier(qualifiedTableName);
|
|
715
|
+
const columnList = columns?.length ? columns.map((c) => c === "*" ? "*" : this.quoteIdentifier(c)).join(", ") : "*";
|
|
716
|
+
return `SELECT ${columnList} FROM ${tableIdentifier} LIMIT ${limit}`;
|
|
717
|
+
}
|
|
718
|
+
#qualifyTableName(tableName) {
|
|
719
|
+
if (!this.projectId) {
|
|
720
|
+
return tableName;
|
|
721
|
+
}
|
|
722
|
+
if (tableName.split(".").length >= 3) {
|
|
723
|
+
return tableName;
|
|
724
|
+
}
|
|
725
|
+
return `${this.projectId}.${tableName}`;
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// packages/text2sql/src/lib/adapters/bigquery/constraint.bigquery.grounding.ts
|
|
730
|
+
var BigQueryConstraintGrounding = class extends ConstraintGrounding {
|
|
731
|
+
#adapter;
|
|
732
|
+
constructor(adapter, config = {}) {
|
|
733
|
+
super(config);
|
|
734
|
+
this.#adapter = adapter;
|
|
735
|
+
}
|
|
736
|
+
async getConstraints(tableName) {
|
|
737
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(tableName);
|
|
738
|
+
const constraints2 = [];
|
|
739
|
+
const columnRows = await this.#adapter.runQuery(`
|
|
740
|
+
SELECT column_name, is_nullable, column_default
|
|
741
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "COLUMNS")}
|
|
742
|
+
WHERE table_name = '${this.#adapter.escapeString(table2)}'
|
|
743
|
+
ORDER BY ordinal_position
|
|
744
|
+
`);
|
|
745
|
+
for (const row of columnRows) {
|
|
746
|
+
const col = row.column_name;
|
|
747
|
+
if (!col) continue;
|
|
748
|
+
if ((row.is_nullable ?? "").toUpperCase() === "NO") {
|
|
749
|
+
constraints2.push({
|
|
750
|
+
name: `${tableName}.${col}.NOT_NULL`,
|
|
751
|
+
type: "NOT_NULL",
|
|
752
|
+
columns: [col]
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
if (row.column_default != null && row.column_default !== "") {
|
|
756
|
+
constraints2.push({
|
|
757
|
+
name: `${tableName}.${col}.DEFAULT`,
|
|
758
|
+
type: "DEFAULT",
|
|
759
|
+
columns: [col],
|
|
760
|
+
defaultValue: row.column_default
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const keyRows = await this.#adapter.runQuery(`
|
|
765
|
+
SELECT
|
|
766
|
+
tc.constraint_name,
|
|
767
|
+
tc.constraint_type,
|
|
768
|
+
kcu.column_name,
|
|
769
|
+
kcu.ordinal_position,
|
|
770
|
+
kcu.position_in_unique_constraint
|
|
771
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLE_CONSTRAINTS")} AS tc
|
|
772
|
+
JOIN ${this.#adapter.infoSchemaView(dataset, "KEY_COLUMN_USAGE")} AS kcu
|
|
773
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
774
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
|
775
|
+
WHERE tc.table_name = '${this.#adapter.escapeString(table2)}'
|
|
776
|
+
AND tc.constraint_type IN ('PRIMARY KEY', 'FOREIGN KEY')
|
|
777
|
+
ORDER BY tc.constraint_name, kcu.ordinal_position
|
|
778
|
+
`);
|
|
779
|
+
const pkByName = /* @__PURE__ */ new Map();
|
|
780
|
+
const fkByName = /* @__PURE__ */ new Map();
|
|
781
|
+
for (const row of keyRows) {
|
|
782
|
+
if (!row.constraint_name || !row.column_name) continue;
|
|
783
|
+
const type = (row.constraint_type ?? "").toUpperCase();
|
|
784
|
+
if (type === "PRIMARY KEY") {
|
|
785
|
+
const cols = pkByName.get(row.constraint_name) ?? [];
|
|
786
|
+
cols.push(row.column_name);
|
|
787
|
+
pkByName.set(row.constraint_name, cols);
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (type === "FOREIGN KEY") {
|
|
791
|
+
const cols = fkByName.get(row.constraint_name) ?? [];
|
|
792
|
+
cols.push({
|
|
793
|
+
column: row.column_name,
|
|
794
|
+
ordinal: row.ordinal_position ?? 0,
|
|
795
|
+
pkOrdinal: row.position_in_unique_constraint
|
|
796
|
+
});
|
|
797
|
+
fkByName.set(row.constraint_name, cols);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
for (const [name, cols] of pkByName.entries()) {
|
|
801
|
+
constraints2.push({
|
|
802
|
+
name,
|
|
803
|
+
type: "PRIMARY_KEY",
|
|
804
|
+
columns: cols
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
for (const [constraintName, cols] of fkByName.entries()) {
|
|
808
|
+
const fk = await this.#buildForeignKeyConstraint({
|
|
809
|
+
constraintDataset: dataset,
|
|
810
|
+
constraintName,
|
|
811
|
+
childTableName: `${dataset}.${table2}`,
|
|
812
|
+
childColumns: cols
|
|
813
|
+
});
|
|
814
|
+
if (fk) constraints2.push(fk);
|
|
815
|
+
}
|
|
816
|
+
return constraints2;
|
|
817
|
+
}
|
|
818
|
+
async #buildForeignKeyConstraint(args) {
|
|
819
|
+
const refRows = await this.#adapter.runQuery(`
|
|
820
|
+
SELECT DISTINCT table_schema, table_name
|
|
821
|
+
FROM ${this.#adapter.infoSchemaView(args.constraintDataset, "CONSTRAINT_COLUMN_USAGE")}
|
|
822
|
+
WHERE constraint_name = '${this.#adapter.escapeString(args.constraintName)}'
|
|
823
|
+
`);
|
|
824
|
+
const referenced = refRows.find((r) => r.table_schema && r.table_name);
|
|
825
|
+
if (!referenced?.table_schema || !referenced.table_name) {
|
|
826
|
+
return void 0;
|
|
827
|
+
}
|
|
828
|
+
const referencedDataset = referenced.table_schema;
|
|
829
|
+
const referencedTable = referenced.table_name;
|
|
830
|
+
if (!this.#adapter.isDatasetAllowed(referencedDataset)) {
|
|
831
|
+
return void 0;
|
|
832
|
+
}
|
|
833
|
+
const pkConstraintRows = await this.#adapter.runQuery(`
|
|
834
|
+
SELECT constraint_name
|
|
835
|
+
FROM ${this.#adapter.infoSchemaView(referencedDataset, "TABLE_CONSTRAINTS")}
|
|
836
|
+
WHERE constraint_type = 'PRIMARY KEY'
|
|
837
|
+
AND table_name = '${this.#adapter.escapeString(referencedTable)}'
|
|
838
|
+
LIMIT 1
|
|
839
|
+
`);
|
|
840
|
+
const pkConstraintName = pkConstraintRows[0]?.constraint_name;
|
|
841
|
+
if (!pkConstraintName) return void 0;
|
|
842
|
+
const pkColumns = await this.#adapter.runQuery(`
|
|
843
|
+
SELECT column_name, ordinal_position
|
|
844
|
+
FROM ${this.#adapter.infoSchemaView(referencedDataset, "KEY_COLUMN_USAGE")}
|
|
845
|
+
WHERE constraint_name = '${this.#adapter.escapeString(pkConstraintName)}'
|
|
846
|
+
AND table_name = '${this.#adapter.escapeString(referencedTable)}'
|
|
847
|
+
ORDER BY ordinal_position
|
|
848
|
+
`);
|
|
849
|
+
const pkByOrdinal = /* @__PURE__ */ new Map();
|
|
850
|
+
for (const row of pkColumns) {
|
|
851
|
+
if (!row.column_name || row.ordinal_position == null) continue;
|
|
852
|
+
pkByOrdinal.set(row.ordinal_position, row.column_name);
|
|
853
|
+
}
|
|
854
|
+
const orderedChild = [...args.childColumns].sort(
|
|
855
|
+
(a, b) => a.ordinal - b.ordinal
|
|
856
|
+
);
|
|
857
|
+
const columns = orderedChild.map((c) => c.column);
|
|
858
|
+
const referencedColumns = orderedChild.map((c) => {
|
|
859
|
+
const pkOrdinal = c.pkOrdinal ?? c.ordinal;
|
|
860
|
+
return pkByOrdinal.get(pkOrdinal) ?? "unknown";
|
|
861
|
+
});
|
|
862
|
+
return {
|
|
863
|
+
name: args.constraintName,
|
|
864
|
+
type: "FOREIGN_KEY",
|
|
865
|
+
columns,
|
|
866
|
+
referencedTable: `${referencedDataset}.${referencedTable}`,
|
|
867
|
+
referencedColumns
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
// packages/text2sql/src/lib/adapters/bigquery/indexes.bigquery.grounding.ts
|
|
873
|
+
var BigQueryIndexesGrounding = class extends IndexesGrounding {
|
|
874
|
+
#adapter;
|
|
875
|
+
constructor(adapter, config = {}) {
|
|
876
|
+
super(config);
|
|
877
|
+
this.#adapter = adapter;
|
|
878
|
+
}
|
|
879
|
+
async getIndexes(tableName) {
|
|
880
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(tableName);
|
|
881
|
+
const rows = await this.#adapter.runQuery(`
|
|
882
|
+
SELECT column_name, is_partitioning_column, clustering_ordinal_position
|
|
883
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "COLUMNS")}
|
|
884
|
+
WHERE table_name = '${this.#adapter.escapeString(table2)}'
|
|
885
|
+
AND (is_partitioning_column = 'YES' OR clustering_ordinal_position IS NOT NULL)
|
|
886
|
+
ORDER BY clustering_ordinal_position
|
|
887
|
+
`);
|
|
888
|
+
const partitionColumns = [];
|
|
889
|
+
const clusteringColumns = [];
|
|
890
|
+
for (const row of rows) {
|
|
891
|
+
if (!row.column_name) continue;
|
|
892
|
+
if ((row.is_partitioning_column ?? "").toUpperCase() === "YES") {
|
|
893
|
+
partitionColumns.push(row.column_name);
|
|
894
|
+
}
|
|
895
|
+
if (row.clustering_ordinal_position != null) {
|
|
896
|
+
clusteringColumns.push({
|
|
897
|
+
name: row.column_name,
|
|
898
|
+
pos: row.clustering_ordinal_position
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const indexes2 = [];
|
|
903
|
+
if (partitionColumns.length > 0) {
|
|
904
|
+
indexes2.push({
|
|
905
|
+
name: `${table2}_partition`,
|
|
906
|
+
columns: partitionColumns,
|
|
907
|
+
type: "PARTITION"
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (clusteringColumns.length > 0) {
|
|
911
|
+
clusteringColumns.sort((a, b) => a.pos - b.pos);
|
|
912
|
+
indexes2.push({
|
|
913
|
+
name: `${table2}_clustering`,
|
|
914
|
+
columns: clusteringColumns.map((c) => c.name),
|
|
915
|
+
type: "CLUSTERING"
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
return indexes2;
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// packages/text2sql/src/lib/adapters/bigquery/info.bigquery.grounding.ts
|
|
923
|
+
var BigQueryInfoGrounding = class extends InfoGrounding {
|
|
924
|
+
#adapter;
|
|
925
|
+
constructor(adapter) {
|
|
926
|
+
super();
|
|
927
|
+
this.#adapter = adapter;
|
|
928
|
+
}
|
|
929
|
+
async collectInfo() {
|
|
930
|
+
return {
|
|
931
|
+
dialect: "bigquery",
|
|
932
|
+
database: this.#adapter.projectId,
|
|
933
|
+
details: {
|
|
934
|
+
identifierQuote: "`",
|
|
935
|
+
identifiers: {
|
|
936
|
+
qualifiedTable: "dataset.table",
|
|
937
|
+
nestedFieldPath: "col.path.to.field"
|
|
938
|
+
},
|
|
939
|
+
parameters: {
|
|
940
|
+
positional: "?",
|
|
941
|
+
named: "@name"
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
// packages/text2sql/src/lib/adapters/bigquery/row-count.bigquery.grounding.ts
|
|
949
|
+
var BigQueryRowCountGrounding = class extends RowCountGrounding {
|
|
950
|
+
#adapter;
|
|
951
|
+
constructor(adapter, config = {}) {
|
|
952
|
+
super(config);
|
|
953
|
+
this.#adapter = adapter;
|
|
954
|
+
}
|
|
955
|
+
async getRowCount(tableName) {
|
|
956
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(tableName);
|
|
957
|
+
const rows = await this.#adapter.runQuery(`
|
|
958
|
+
SELECT total_rows
|
|
959
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLE_STORAGE")}
|
|
960
|
+
WHERE table_name = '${this.#adapter.escapeString(table2)}'
|
|
961
|
+
LIMIT 1
|
|
962
|
+
`);
|
|
963
|
+
const value = rows[0]?.total_rows;
|
|
964
|
+
return this.#adapter.toNumber(value);
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
// packages/text2sql/src/lib/adapters/bigquery/table.bigquery.grounding.ts
|
|
969
|
+
var BigQueryTableGrounding = class extends TableGrounding {
|
|
970
|
+
#adapter;
|
|
971
|
+
constructor(adapter, config = {}) {
|
|
972
|
+
super(config);
|
|
973
|
+
this.#adapter = adapter;
|
|
974
|
+
}
|
|
975
|
+
async applyFilter() {
|
|
976
|
+
const names = await super.applyFilter();
|
|
977
|
+
return names.filter((name) => this.#isTableInScope(name));
|
|
978
|
+
}
|
|
979
|
+
async getAllTableNames() {
|
|
980
|
+
const names = [];
|
|
981
|
+
for (const dataset of this.#adapter.datasets) {
|
|
982
|
+
const rows = await this.#adapter.runQuery(`
|
|
983
|
+
SELECT table_name
|
|
984
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLES")}
|
|
985
|
+
WHERE table_type = 'BASE TABLE'
|
|
986
|
+
ORDER BY table_name
|
|
987
|
+
`);
|
|
988
|
+
for (const row of rows) {
|
|
989
|
+
if (!row.table_name) continue;
|
|
990
|
+
names.push(`${dataset}.${row.table_name}`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return names;
|
|
994
|
+
}
|
|
995
|
+
async getTable(tableName) {
|
|
996
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(tableName);
|
|
997
|
+
const rows = await this.#adapter.runQuery(`
|
|
998
|
+
SELECT
|
|
999
|
+
f.field_path,
|
|
1000
|
+
f.data_type,
|
|
1001
|
+
c.ordinal_position
|
|
1002
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "COLUMN_FIELD_PATHS")} AS f
|
|
1003
|
+
JOIN ${this.#adapter.infoSchemaView(dataset, "COLUMNS")} AS c
|
|
1004
|
+
ON f.table_name = c.table_name
|
|
1005
|
+
AND f.column_name = c.column_name
|
|
1006
|
+
WHERE f.table_name = '${this.#adapter.escapeString(table2)}'
|
|
1007
|
+
ORDER BY c.ordinal_position, f.field_path
|
|
1008
|
+
`);
|
|
1009
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1010
|
+
const columns = rows.map((r) => ({
|
|
1011
|
+
name: r.field_path ?? "unknown",
|
|
1012
|
+
type: r.data_type ?? "unknown",
|
|
1013
|
+
ordinal: r.ordinal_position ?? 0
|
|
1014
|
+
})).filter((c) => {
|
|
1015
|
+
if (!c.name) return false;
|
|
1016
|
+
if (seen.has(c.name)) return false;
|
|
1017
|
+
seen.add(c.name);
|
|
1018
|
+
return true;
|
|
1019
|
+
}).map((c) => ({ name: c.name, type: c.type }));
|
|
1020
|
+
return {
|
|
1021
|
+
name: `${dataset}.${table2}`,
|
|
1022
|
+
schema: dataset,
|
|
1023
|
+
rawName: table2,
|
|
1024
|
+
columns
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
async findOutgoingRelations(tableName) {
|
|
1028
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(tableName);
|
|
1029
|
+
const rows = await this.#adapter.runQuery(`
|
|
1030
|
+
SELECT
|
|
1031
|
+
kcu.constraint_name,
|
|
1032
|
+
kcu.column_name,
|
|
1033
|
+
kcu.ordinal_position,
|
|
1034
|
+
kcu.position_in_unique_constraint
|
|
1035
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLE_CONSTRAINTS")} AS tc
|
|
1036
|
+
JOIN ${this.#adapter.infoSchemaView(dataset, "KEY_COLUMN_USAGE")} AS kcu
|
|
1037
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
1038
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
|
1039
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
1040
|
+
AND tc.table_name = '${this.#adapter.escapeString(table2)}'
|
|
1041
|
+
ORDER BY kcu.constraint_name, kcu.ordinal_position
|
|
1042
|
+
`);
|
|
1043
|
+
const byConstraint = /* @__PURE__ */ new Map();
|
|
1044
|
+
for (const row of rows) {
|
|
1045
|
+
if (!row.constraint_name || !row.column_name) continue;
|
|
1046
|
+
const list = byConstraint.get(row.constraint_name) ?? [];
|
|
1047
|
+
list.push({
|
|
1048
|
+
column: row.column_name,
|
|
1049
|
+
ordinal: row.ordinal_position ?? 0,
|
|
1050
|
+
pkOrdinal: row.position_in_unique_constraint
|
|
1051
|
+
});
|
|
1052
|
+
byConstraint.set(row.constraint_name, list);
|
|
1053
|
+
}
|
|
1054
|
+
const rels = [];
|
|
1055
|
+
for (const [constraintName, columns] of byConstraint.entries()) {
|
|
1056
|
+
const rel = await this.#buildForeignKeyRelationship({
|
|
1057
|
+
constraintDataset: dataset,
|
|
1058
|
+
childDataset: dataset,
|
|
1059
|
+
childTable: table2,
|
|
1060
|
+
constraintName,
|
|
1061
|
+
childColumns: columns
|
|
1062
|
+
});
|
|
1063
|
+
if (rel) rels.push(rel);
|
|
1064
|
+
}
|
|
1065
|
+
return rels;
|
|
1066
|
+
}
|
|
1067
|
+
async findIncomingRelations(tableName) {
|
|
1068
|
+
const { schema: referencedDataset, table: referencedTable } = this.#adapter.parseTableName(tableName);
|
|
1069
|
+
const rels = [];
|
|
1070
|
+
for (const constraintDataset of this.#adapter.datasets) {
|
|
1071
|
+
const rows = await this.#adapter.runQuery(`
|
|
1072
|
+
SELECT DISTINCT constraint_name
|
|
1073
|
+
FROM ${this.#adapter.infoSchemaView(constraintDataset, "CONSTRAINT_COLUMN_USAGE")}
|
|
1074
|
+
WHERE table_schema = '${this.#adapter.escapeString(referencedDataset)}'
|
|
1075
|
+
AND table_name = '${this.#adapter.escapeString(referencedTable)}'
|
|
1076
|
+
`);
|
|
1077
|
+
for (const row of rows) {
|
|
1078
|
+
if (!row.constraint_name) continue;
|
|
1079
|
+
const rel = await this.#buildForeignKeyRelationshipFromConstraintName(
|
|
1080
|
+
constraintDataset,
|
|
1081
|
+
row.constraint_name
|
|
1082
|
+
);
|
|
1083
|
+
if (rel && rel.referenced_table === `${referencedDataset}.${referencedTable}`) {
|
|
1084
|
+
rels.push(rel);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return rels;
|
|
1089
|
+
}
|
|
1090
|
+
#isTableInScope(tableName) {
|
|
1091
|
+
const { schema } = this.#adapter.parseTableName(tableName);
|
|
1092
|
+
return this.#adapter.isDatasetAllowed(schema);
|
|
1093
|
+
}
|
|
1094
|
+
async #buildForeignKeyRelationshipFromConstraintName(constraintDataset, constraintName) {
|
|
1095
|
+
const keyRows = await this.#adapter.runQuery(`
|
|
1096
|
+
SELECT
|
|
1097
|
+
kcu.constraint_name,
|
|
1098
|
+
tc.table_name AS child_table_name,
|
|
1099
|
+
kcu.column_name,
|
|
1100
|
+
kcu.ordinal_position,
|
|
1101
|
+
kcu.position_in_unique_constraint
|
|
1102
|
+
FROM ${this.#adapter.infoSchemaView(constraintDataset, "TABLE_CONSTRAINTS")} AS tc
|
|
1103
|
+
JOIN ${this.#adapter.infoSchemaView(constraintDataset, "KEY_COLUMN_USAGE")} AS kcu
|
|
1104
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
1105
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
|
1106
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
1107
|
+
AND tc.constraint_name = '${this.#adapter.escapeString(constraintName)}'
|
|
1108
|
+
ORDER BY kcu.ordinal_position
|
|
1109
|
+
`);
|
|
1110
|
+
if (keyRows.length === 0) return void 0;
|
|
1111
|
+
const childTable = keyRows[0]?.child_table_name;
|
|
1112
|
+
if (!childTable) return void 0;
|
|
1113
|
+
const childColumns = keyRows.filter((r) => r.column_name).map((r) => ({
|
|
1114
|
+
column: r.column_name ?? "unknown",
|
|
1115
|
+
ordinal: r.ordinal_position ?? 0,
|
|
1116
|
+
pkOrdinal: r.position_in_unique_constraint
|
|
1117
|
+
}));
|
|
1118
|
+
return this.#buildForeignKeyRelationship({
|
|
1119
|
+
constraintDataset,
|
|
1120
|
+
childDataset: constraintDataset,
|
|
1121
|
+
childTable,
|
|
1122
|
+
constraintName,
|
|
1123
|
+
childColumns
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
async #buildForeignKeyRelationship(args) {
|
|
1127
|
+
const refTableRows = await this.#adapter.runQuery(`
|
|
1128
|
+
SELECT DISTINCT table_schema, table_name
|
|
1129
|
+
FROM ${this.#adapter.infoSchemaView(args.constraintDataset, "CONSTRAINT_COLUMN_USAGE")}
|
|
1130
|
+
WHERE constraint_name = '${this.#adapter.escapeString(args.constraintName)}'
|
|
1131
|
+
`);
|
|
1132
|
+
const referenced = refTableRows.find((r) => r.table_schema && r.table_name);
|
|
1133
|
+
if (!referenced?.table_schema || !referenced.table_name) {
|
|
1134
|
+
return void 0;
|
|
1135
|
+
}
|
|
1136
|
+
const referencedDataset = referenced.table_schema;
|
|
1137
|
+
const referencedTable = referenced.table_name;
|
|
1138
|
+
if (!this.#adapter.isDatasetAllowed(referencedDataset)) {
|
|
1139
|
+
return void 0;
|
|
1140
|
+
}
|
|
1141
|
+
const pkConstraintRows = await this.#adapter.runQuery(`
|
|
1142
|
+
SELECT constraint_name
|
|
1143
|
+
FROM ${this.#adapter.infoSchemaView(referencedDataset, "TABLE_CONSTRAINTS")}
|
|
1144
|
+
WHERE constraint_type = 'PRIMARY KEY'
|
|
1145
|
+
AND table_name = '${this.#adapter.escapeString(referencedTable)}'
|
|
1146
|
+
LIMIT 1
|
|
1147
|
+
`);
|
|
1148
|
+
const pkConstraintName = pkConstraintRows[0]?.constraint_name;
|
|
1149
|
+
if (!pkConstraintName) {
|
|
1150
|
+
return void 0;
|
|
1151
|
+
}
|
|
1152
|
+
const pkColumnRows = await this.#adapter.runQuery(`
|
|
1153
|
+
SELECT column_name, ordinal_position
|
|
1154
|
+
FROM ${this.#adapter.infoSchemaView(referencedDataset, "KEY_COLUMN_USAGE")}
|
|
1155
|
+
WHERE constraint_name = '${this.#adapter.escapeString(pkConstraintName)}'
|
|
1156
|
+
AND table_name = '${this.#adapter.escapeString(referencedTable)}'
|
|
1157
|
+
ORDER BY ordinal_position
|
|
1158
|
+
`);
|
|
1159
|
+
const pkByOrdinal = /* @__PURE__ */ new Map();
|
|
1160
|
+
for (const row of pkColumnRows) {
|
|
1161
|
+
if (!row.column_name || row.ordinal_position == null) continue;
|
|
1162
|
+
pkByOrdinal.set(row.ordinal_position, row.column_name);
|
|
1163
|
+
}
|
|
1164
|
+
const orderedChild = [...args.childColumns].sort(
|
|
1165
|
+
(a, b) => a.ordinal - b.ordinal
|
|
1166
|
+
);
|
|
1167
|
+
const from = orderedChild.map((c) => c.column);
|
|
1168
|
+
const to = orderedChild.map((c) => {
|
|
1169
|
+
const pkOrdinal = c.pkOrdinal ?? c.ordinal;
|
|
1170
|
+
return pkByOrdinal.get(pkOrdinal) ?? "unknown";
|
|
1171
|
+
});
|
|
1172
|
+
return {
|
|
1173
|
+
table: `${args.childDataset}.${args.childTable}`,
|
|
1174
|
+
from,
|
|
1175
|
+
referenced_table: `${referencedDataset}.${referencedTable}`,
|
|
1176
|
+
to
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
// packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
|
|
1182
|
+
var ViewGrounding = class extends AbstractGrounding {
|
|
1183
|
+
#filter;
|
|
1184
|
+
constructor(config = {}) {
|
|
1185
|
+
super("view");
|
|
1186
|
+
this.#filter = config.filter;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Execute the grounding process.
|
|
1190
|
+
* Writes discovered views to the context.
|
|
1191
|
+
*/
|
|
1192
|
+
async execute(ctx) {
|
|
1193
|
+
const viewNames = await this.applyFilter();
|
|
1194
|
+
const views2 = await Promise.all(
|
|
1195
|
+
viewNames.map((name) => this.getView(name))
|
|
1196
|
+
);
|
|
1197
|
+
ctx.views.push(...views2);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Apply the filter to get view names.
|
|
1201
|
+
* If filter is an explicit array, skip querying all view names.
|
|
1202
|
+
*/
|
|
1203
|
+
async applyFilter() {
|
|
1204
|
+
const filter = this.#filter;
|
|
1205
|
+
if (Array.isArray(filter)) {
|
|
1206
|
+
return filter;
|
|
1207
|
+
}
|
|
1208
|
+
const names = await this.getAllViewNames();
|
|
1209
|
+
if (!filter) {
|
|
1210
|
+
return names;
|
|
1211
|
+
}
|
|
1212
|
+
if (filter instanceof RegExp) {
|
|
1213
|
+
return names.filter((name) => filter.test(name));
|
|
1214
|
+
}
|
|
1215
|
+
return names.filter(filter);
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// packages/text2sql/src/lib/adapters/bigquery/view.bigquery.grounding.ts
|
|
1220
|
+
var BigQueryViewGrounding = class extends ViewGrounding {
|
|
1221
|
+
#adapter;
|
|
1222
|
+
constructor(adapter, config = {}) {
|
|
1223
|
+
super(config);
|
|
1224
|
+
this.#adapter = adapter;
|
|
1225
|
+
}
|
|
1226
|
+
async applyFilter() {
|
|
1227
|
+
const names = await super.applyFilter();
|
|
1228
|
+
return names.filter((name) => this.#isViewInScope(name));
|
|
1229
|
+
}
|
|
1230
|
+
async getAllViewNames() {
|
|
1231
|
+
const names = [];
|
|
1232
|
+
for (const dataset of this.#adapter.datasets) {
|
|
1233
|
+
const rows = await this.#adapter.runQuery(`
|
|
1234
|
+
SELECT table_name
|
|
1235
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLES")}
|
|
1236
|
+
WHERE table_type IN ('VIEW', 'MATERIALIZED VIEW')
|
|
1237
|
+
ORDER BY table_name
|
|
1238
|
+
`);
|
|
1239
|
+
for (const row of rows) {
|
|
1240
|
+
if (!row.table_name) continue;
|
|
1241
|
+
names.push(`${dataset}.${row.table_name}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
return names;
|
|
1245
|
+
}
|
|
1246
|
+
async getView(viewName) {
|
|
1247
|
+
const { schema: dataset, table: table2 } = this.#adapter.parseTableName(viewName);
|
|
1248
|
+
const defRows = await this.#adapter.runQuery(`
|
|
1249
|
+
SELECT ddl
|
|
1250
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "TABLES")}
|
|
1251
|
+
WHERE table_name = '${this.#adapter.escapeString(table2)}'
|
|
1252
|
+
AND table_type IN ('VIEW', 'MATERIALIZED VIEW')
|
|
1253
|
+
LIMIT 1
|
|
1254
|
+
`);
|
|
1255
|
+
const columns = await this.#adapter.runQuery(`
|
|
1256
|
+
SELECT column_name, data_type
|
|
1257
|
+
FROM ${this.#adapter.infoSchemaView(dataset, "COLUMNS")}
|
|
1258
|
+
WHERE table_name = '${this.#adapter.escapeString(table2)}'
|
|
1259
|
+
ORDER BY ordinal_position
|
|
1260
|
+
`);
|
|
1261
|
+
return {
|
|
1262
|
+
name: `${dataset}.${table2}`,
|
|
1263
|
+
schema: dataset,
|
|
1264
|
+
rawName: table2,
|
|
1265
|
+
definition: defRows[0]?.ddl ?? void 0,
|
|
1266
|
+
columns: columns.map((c) => ({
|
|
1267
|
+
name: c.column_name ?? "unknown",
|
|
1268
|
+
type: c.data_type ?? "unknown"
|
|
1269
|
+
}))
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
#isViewInScope(viewName) {
|
|
1273
|
+
const { schema } = this.#adapter.parseTableName(viewName);
|
|
1274
|
+
return this.#adapter.isDatasetAllowed(schema);
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// packages/text2sql/src/lib/adapters/bigquery/index.ts
|
|
1279
|
+
function tables(config = {}) {
|
|
1280
|
+
return (adapter) => new BigQueryTableGrounding(adapter, config);
|
|
1281
|
+
}
|
|
1282
|
+
function info(config = {}) {
|
|
1283
|
+
return (adapter) => new BigQueryInfoGrounding(adapter);
|
|
1284
|
+
}
|
|
1285
|
+
function views(config = {}) {
|
|
1286
|
+
return (adapter) => new BigQueryViewGrounding(adapter, config);
|
|
1287
|
+
}
|
|
1288
|
+
function indexes(config = {}) {
|
|
1289
|
+
return (adapter) => new BigQueryIndexesGrounding(adapter, config);
|
|
1290
|
+
}
|
|
1291
|
+
function rowCount(config = {}) {
|
|
1292
|
+
return (adapter) => new BigQueryRowCountGrounding(adapter, config);
|
|
1293
|
+
}
|
|
1294
|
+
function constraints(config = {}) {
|
|
1295
|
+
return (adapter) => new BigQueryConstraintGrounding(adapter, config);
|
|
1296
|
+
}
|
|
1297
|
+
function report(config = {}) {
|
|
1298
|
+
return (adapter) => new ReportGrounding(adapter, config);
|
|
1299
|
+
}
|
|
1300
|
+
var bigquery_default = {
|
|
1301
|
+
tables,
|
|
1302
|
+
info,
|
|
1303
|
+
views,
|
|
1304
|
+
indexes,
|
|
1305
|
+
rowCount,
|
|
1306
|
+
constraints,
|
|
1307
|
+
report,
|
|
1308
|
+
BigQuery
|
|
1309
|
+
};
|
|
1310
|
+
export {
|
|
1311
|
+
BigQuery,
|
|
1312
|
+
constraints,
|
|
1313
|
+
bigquery_default as default,
|
|
1314
|
+
indexes,
|
|
1315
|
+
info,
|
|
1316
|
+
report,
|
|
1317
|
+
rowCount,
|
|
1318
|
+
tables,
|
|
1319
|
+
views
|
|
1320
|
+
};
|
|
1321
|
+
//# sourceMappingURL=index.js.map
|