@deepagents/text2sql 0.2.3 → 0.3.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.d.ts +7 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +882 -2730
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +67 -10
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/grounding.ticket.d.ts +21 -0
- package/dist/lib/adapters/grounding.ticket.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/column-stats.grounding.d.ts +32 -0
- package/dist/lib/adapters/groundings/column-stats.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/constraint.grounding.d.ts +31 -0
- package/dist/lib/adapters/groundings/constraint.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/context.d.ts +41 -0
- package/dist/lib/adapters/groundings/context.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/grounding.d.ts +8 -0
- package/dist/lib/adapters/groundings/grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/grounding.js +507 -0
- package/dist/lib/adapters/groundings/grounding.js.map +7 -0
- package/dist/lib/adapters/groundings/indexes.grounding.d.ts +30 -0
- package/dist/lib/adapters/groundings/indexes.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/info.grounding.d.ts +29 -0
- package/dist/lib/adapters/groundings/info.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts +35 -0
- package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/report.grounding.d.ts +38 -0
- package/dist/lib/adapters/groundings/report.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/row-count.grounding.d.ts +30 -0
- package/dist/lib/adapters/groundings/row-count.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/table.grounding.d.ts +61 -0
- package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/groundings/view.grounding.d.ts +57 -0
- package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts +12 -0
- package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts +11 -0
- package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/index.d.ts +43 -0
- package/dist/lib/adapters/postgres/index.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/index.js +1640 -0
- package/dist/lib/adapters/postgres/index.js.map +7 -0
- package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts +15 -0
- package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts +11 -0
- package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts +14 -0
- package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/postgres.d.ts +26 -0
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts +11 -0
- package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts +21 -0
- package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts +16 -0
- package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts +12 -0
- package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts +15 -0
- package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/index.d.ts +43 -0
- package/dist/lib/adapters/sqlite/index.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/index.js +1215 -0
- package/dist/lib/adapters/sqlite/index.js.map +7 -0
- package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts +14 -0
- package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/sqlite.d.ts +25 -0
- package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts +17 -0
- package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts +12 -0
- package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts +12 -0
- package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/index.d.ts +43 -0
- package/dist/lib/adapters/sqlserver/index.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/index.js +1693 -0
- package/dist/lib/adapters/sqlserver/index.js.map +7 -0
- package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts +15 -0
- package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts +14 -0
- package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts +11 -0
- package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts +26 -0
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts +21 -0
- package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts +16 -0
- package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts.map +1 -0
- package/dist/lib/agents/suggestions.agents.d.ts +0 -2
- package/dist/lib/agents/suggestions.agents.d.ts.map +1 -1
- package/dist/lib/agents/teachables.agent.d.ts +0 -3
- package/dist/lib/agents/teachables.agent.d.ts.map +1 -1
- package/dist/lib/agents/text2sql.agent.d.ts +69 -29
- package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
- package/dist/lib/file-cache.d.ts +12 -0
- package/dist/lib/file-cache.d.ts.map +1 -0
- package/dist/lib/instructions.d.ts +3 -0
- package/dist/lib/instructions.d.ts.map +1 -0
- package/dist/lib/instructions.js +386 -0
- package/dist/lib/instructions.js.map +7 -0
- package/dist/lib/memory/memory.prompt.d.ts +3 -0
- package/dist/lib/memory/memory.prompt.d.ts.map +1 -0
- package/dist/lib/memory/memory.store.d.ts +5 -0
- package/dist/lib/memory/memory.store.d.ts.map +1 -0
- package/dist/lib/memory/sqlite.store.d.ts +14 -0
- package/dist/lib/memory/sqlite.store.d.ts.map +1 -0
- package/dist/lib/memory/store.d.ts +40 -0
- package/dist/lib/memory/store.d.ts.map +1 -0
- package/dist/lib/prompt.d.ts +1 -6
- package/dist/lib/prompt.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +26 -35
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/teach/teachables.d.ts +184 -13
- package/dist/lib/teach/teachables.d.ts.map +1 -1
- package/dist/lib/teach/teachings.d.ts.map +1 -1
- package/dist/lib/teach/xml.d.ts.map +1 -1
- package/package.json +38 -4
- package/dist/lib/adapters/postgres.d.ts +0 -31
- package/dist/lib/adapters/postgres.d.ts.map +0 -1
- package/dist/lib/adapters/resolveTables.spec.d.ts +0 -2
- package/dist/lib/adapters/resolveTables.spec.d.ts.map +0 -1
- package/dist/lib/adapters/sqlite.d.ts +0 -30
- package/dist/lib/adapters/sqlite.d.ts.map +0 -1
- package/dist/lib/adapters/sqlserver.d.ts +0 -31
- package/dist/lib/adapters/sqlserver.d.ts.map +0 -1
- package/dist/lib/agents/brief.agent.d.ts +0 -21
- package/dist/lib/agents/brief.agent.d.ts.map +0 -1
- package/dist/lib/memory/user-profile.d.ts +0 -39
- package/dist/lib/memory/user-profile.d.ts.map +0 -1
|
@@ -0,0 +1,1640 @@
|
|
|
1
|
+
// packages/text2sql/src/lib/adapters/groundings/context.ts
|
|
2
|
+
function createGroundingContext() {
|
|
3
|
+
return {
|
|
4
|
+
tables: [],
|
|
5
|
+
views: [],
|
|
6
|
+
relationships: [],
|
|
7
|
+
info: void 0
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
12
|
+
var Adapter = class {
|
|
13
|
+
async introspect() {
|
|
14
|
+
const lines = [];
|
|
15
|
+
const ctx = createGroundingContext();
|
|
16
|
+
for (const fn of this.grounding) {
|
|
17
|
+
const grounding = fn(this);
|
|
18
|
+
lines.push({
|
|
19
|
+
tag: grounding.tag,
|
|
20
|
+
fn: await grounding.execute(ctx)
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return lines.map(({ fn, tag }) => {
|
|
24
|
+
const description = fn();
|
|
25
|
+
if (description === null) {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
return `<${tag}>
|
|
29
|
+
${description}
|
|
30
|
+
</${tag}>`;
|
|
31
|
+
}).join("\n");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Convert unknown database value to number.
|
|
35
|
+
* Handles number, bigint, and string types.
|
|
36
|
+
*/
|
|
37
|
+
toNumber(value) {
|
|
38
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
if (typeof value === "bigint") {
|
|
42
|
+
return Number(value);
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
45
|
+
const parsed = Number(value);
|
|
46
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
47
|
+
}
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a potentially qualified table name into schema and table parts.
|
|
52
|
+
*/
|
|
53
|
+
parseTableName(name) {
|
|
54
|
+
if (name.includes(".")) {
|
|
55
|
+
const [schema, ...rest] = name.split(".");
|
|
56
|
+
return { schema, table: rest.join(".") };
|
|
57
|
+
}
|
|
58
|
+
return { schema: this.defaultSchema ?? "", table: name };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Escape a string value for use in SQL string literals (single quotes).
|
|
62
|
+
* Used in WHERE clauses like: WHERE name = '${escapeString(value)}'
|
|
63
|
+
*/
|
|
64
|
+
escapeString(value) {
|
|
65
|
+
return value.replace(/'/g, "''");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build a SQL filter clause to include/exclude schemas.
|
|
69
|
+
* @param columnName - The schema column name (e.g., 'TABLE_SCHEMA')
|
|
70
|
+
* @param allowedSchemas - If provided, filter to these schemas only
|
|
71
|
+
*/
|
|
72
|
+
buildSchemaFilter(columnName, allowedSchemas) {
|
|
73
|
+
if (allowedSchemas && allowedSchemas.length > 0) {
|
|
74
|
+
const values = allowedSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
|
|
75
|
+
return `AND ${columnName} IN (${values})`;
|
|
76
|
+
}
|
|
77
|
+
if (this.systemSchemas.length > 0) {
|
|
78
|
+
const values = this.systemSchemas.map((s) => `'${this.escapeString(s)}'`).join(", ");
|
|
79
|
+
return `AND ${columnName} NOT IN (${values})`;
|
|
80
|
+
}
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// packages/text2sql/src/lib/adapters/grounding.ticket.ts
|
|
86
|
+
var AbstractGrounding = class {
|
|
87
|
+
tag;
|
|
88
|
+
constructor(tag) {
|
|
89
|
+
this.tag = tag;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// packages/text2sql/src/lib/adapters/groundings/column-stats.grounding.ts
|
|
94
|
+
var ColumnStatsGrounding = class extends AbstractGrounding {
|
|
95
|
+
constructor(config = {}) {
|
|
96
|
+
super("column_stats");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Execute the grounding process.
|
|
100
|
+
* Annotates columns in ctx.tables and ctx.views with statistics.
|
|
101
|
+
*/
|
|
102
|
+
async execute(ctx) {
|
|
103
|
+
const allContainers = [...ctx.tables, ...ctx.views];
|
|
104
|
+
for (const container of allContainers) {
|
|
105
|
+
for (const column of container.columns) {
|
|
106
|
+
try {
|
|
107
|
+
const stats = await this.collectStats(container.name, column);
|
|
108
|
+
if (stats) {
|
|
109
|
+
column.stats = stats;
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.warn(
|
|
113
|
+
"Error collecting stats for",
|
|
114
|
+
container.name,
|
|
115
|
+
column.name,
|
|
116
|
+
error
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return () => this.#describe();
|
|
122
|
+
}
|
|
123
|
+
#describe() {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// packages/text2sql/src/lib/adapters/groundings/constraint.grounding.ts
|
|
129
|
+
var ConstraintGrounding = class extends AbstractGrounding {
|
|
130
|
+
constructor(config = {}) {
|
|
131
|
+
super("constraints");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Execute the grounding process.
|
|
135
|
+
* Annotates tables in ctx.tables with their constraints.
|
|
136
|
+
*/
|
|
137
|
+
async execute(ctx) {
|
|
138
|
+
for (const table of ctx.tables) {
|
|
139
|
+
try {
|
|
140
|
+
table.constraints = await this.getConstraints(table.name);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.warn("Error collecting constraints for", table.name, error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return () => null;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// packages/text2sql/src/lib/adapters/groundings/indexes.grounding.ts
|
|
150
|
+
var IndexesGrounding = class extends AbstractGrounding {
|
|
151
|
+
constructor(config = {}) {
|
|
152
|
+
super("indexes");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Execute the grounding process.
|
|
156
|
+
* Annotates tables in ctx.tables with their indexes and marks indexed columns.
|
|
157
|
+
*/
|
|
158
|
+
async execute(ctx) {
|
|
159
|
+
for (const table of ctx.tables) {
|
|
160
|
+
table.indexes = await this.getIndexes(table.name);
|
|
161
|
+
for (const index of table.indexes ?? []) {
|
|
162
|
+
for (const colName of index.columns) {
|
|
163
|
+
const column = table.columns.find((c) => c.name === colName);
|
|
164
|
+
if (column) {
|
|
165
|
+
column.isIndexed = true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return () => null;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// packages/text2sql/src/lib/adapters/groundings/info.grounding.ts
|
|
175
|
+
var InfoGrounding = class extends AbstractGrounding {
|
|
176
|
+
constructor(config = {}) {
|
|
177
|
+
super("dialect_info");
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Execute the grounding process.
|
|
181
|
+
* Writes database info to ctx.info.
|
|
182
|
+
*/
|
|
183
|
+
async execute(ctx) {
|
|
184
|
+
ctx.info = await this.collectInfo();
|
|
185
|
+
const lines = [`Dialect: ${ctx.info.dialect ?? "unknown"}`];
|
|
186
|
+
if (ctx.info.version) {
|
|
187
|
+
lines.push(`Version: ${ctx.info.version}`);
|
|
188
|
+
}
|
|
189
|
+
if (ctx.info.database) {
|
|
190
|
+
lines.push(`Database: ${ctx.info.database}`);
|
|
191
|
+
}
|
|
192
|
+
if (ctx.info.details && Object.keys(ctx.info.details).length) {
|
|
193
|
+
lines.push(`Details: ${JSON.stringify(ctx.info.details)}`);
|
|
194
|
+
}
|
|
195
|
+
return () => lines.join("\n");
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// packages/text2sql/src/lib/adapters/groundings/low-cardinality.grounding.ts
|
|
200
|
+
var LowCardinalityGrounding = class extends AbstractGrounding {
|
|
201
|
+
constructor(config = {}) {
|
|
202
|
+
super("low_cardinality");
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Execute the grounding process.
|
|
206
|
+
* Annotates columns in ctx.tables and ctx.views with low cardinality values.
|
|
207
|
+
*/
|
|
208
|
+
async execute(ctx) {
|
|
209
|
+
const allContainers = [...ctx.tables, ...ctx.views];
|
|
210
|
+
for (const container of allContainers) {
|
|
211
|
+
for (const column of container.columns) {
|
|
212
|
+
try {
|
|
213
|
+
const lowCard = await this.collectLowCardinality(
|
|
214
|
+
container.name,
|
|
215
|
+
column
|
|
216
|
+
);
|
|
217
|
+
if (lowCard) {
|
|
218
|
+
column.kind = lowCard.kind;
|
|
219
|
+
column.values = lowCard.values;
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.warn(
|
|
223
|
+
"Error collecting low cardinality values for",
|
|
224
|
+
container.name,
|
|
225
|
+
column.name,
|
|
226
|
+
error
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return () => this.#describe();
|
|
232
|
+
}
|
|
233
|
+
#describe() {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
|
|
239
|
+
import { groq } from "@ai-sdk/groq";
|
|
240
|
+
import { tool } from "ai";
|
|
241
|
+
import dedent from "dedent";
|
|
242
|
+
import z from "zod";
|
|
243
|
+
import {
|
|
244
|
+
agent,
|
|
245
|
+
generate,
|
|
246
|
+
toState,
|
|
247
|
+
user
|
|
248
|
+
} from "@deepagents/agent";
|
|
249
|
+
var ReportGrounding = class extends AbstractGrounding {
|
|
250
|
+
#adapter;
|
|
251
|
+
#model;
|
|
252
|
+
#cache;
|
|
253
|
+
#forceRefresh;
|
|
254
|
+
constructor(adapter, config = {}) {
|
|
255
|
+
super("business_context");
|
|
256
|
+
this.#adapter = adapter;
|
|
257
|
+
this.#model = config.model ?? groq("openai/gpt-oss-20b");
|
|
258
|
+
this.#cache = config.cache;
|
|
259
|
+
this.#forceRefresh = config.forceRefresh ?? false;
|
|
260
|
+
}
|
|
261
|
+
async execute(ctx) {
|
|
262
|
+
if (!this.#forceRefresh && this.#cache) {
|
|
263
|
+
const cached = await this.#cache.get();
|
|
264
|
+
if (cached) {
|
|
265
|
+
ctx.report = cached;
|
|
266
|
+
return () => cached;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const report2 = await this.#generateReport();
|
|
270
|
+
ctx.report = report2;
|
|
271
|
+
if (this.#cache) {
|
|
272
|
+
await this.#cache.set(report2);
|
|
273
|
+
}
|
|
274
|
+
return () => report2;
|
|
275
|
+
}
|
|
276
|
+
async #generateReport() {
|
|
277
|
+
const reportAgent = agent({
|
|
278
|
+
name: "db-report-agent",
|
|
279
|
+
model: this.#model,
|
|
280
|
+
prompt: () => dedent`
|
|
281
|
+
<identity>
|
|
282
|
+
You are a database analyst expert. Your job is to understand what
|
|
283
|
+
a database represents and provide business context about it.
|
|
284
|
+
You have READ-ONLY access to the database.
|
|
285
|
+
</identity>
|
|
286
|
+
|
|
287
|
+
<instructions>
|
|
288
|
+
Write a business context that helps another agent answer questions accurately.
|
|
289
|
+
|
|
290
|
+
For EACH table, do queries ONE AT A TIME:
|
|
291
|
+
1. SELECT COUNT(*) to get row count
|
|
292
|
+
2. SELECT * LIMIT 3 to see sample data
|
|
293
|
+
|
|
294
|
+
Then write a report with:
|
|
295
|
+
- What business this database is for
|
|
296
|
+
- For each table: purpose, row count, and example of what the data looks like
|
|
297
|
+
|
|
298
|
+
Include concrete examples like "Track prices are $0.99",
|
|
299
|
+
"Customer names like 'Luís Gonçalves'", etc.
|
|
300
|
+
|
|
301
|
+
Keep it 400-600 words, conversational style.
|
|
302
|
+
</instructions>
|
|
303
|
+
`,
|
|
304
|
+
tools: {
|
|
305
|
+
query_database: tool({
|
|
306
|
+
description: "Execute a SELECT query to explore the database and gather insights.",
|
|
307
|
+
inputSchema: z.object({
|
|
308
|
+
sql: z.string().describe("The SELECT query to execute"),
|
|
309
|
+
purpose: z.string().describe(
|
|
310
|
+
"What insight you are trying to gather with this query"
|
|
311
|
+
)
|
|
312
|
+
}),
|
|
313
|
+
execute: ({ sql }, options) => {
|
|
314
|
+
const state = toState(options);
|
|
315
|
+
return state.adapter.execute(sql);
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
const { text } = await generate(
|
|
321
|
+
reportAgent,
|
|
322
|
+
[
|
|
323
|
+
user(
|
|
324
|
+
"Please analyze the database and write a contextual report about what this database represents."
|
|
325
|
+
)
|
|
326
|
+
],
|
|
327
|
+
{ adapter: this.#adapter }
|
|
328
|
+
);
|
|
329
|
+
return text;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// packages/text2sql/src/lib/adapters/groundings/row-count.grounding.ts
|
|
334
|
+
var RowCountGrounding = class extends AbstractGrounding {
|
|
335
|
+
constructor(config = {}) {
|
|
336
|
+
super("row_counts");
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Execute the grounding process.
|
|
340
|
+
* Annotates tables in ctx.tables with row counts and size hints.
|
|
341
|
+
*/
|
|
342
|
+
async execute(ctx) {
|
|
343
|
+
for (const table of ctx.tables) {
|
|
344
|
+
const count = await this.getRowCount(table.name);
|
|
345
|
+
if (count != null) {
|
|
346
|
+
table.rowCount = count;
|
|
347
|
+
table.sizeHint = this.#classifyRowCount(count);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return () => null;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Classify row count into a size hint category.
|
|
354
|
+
*/
|
|
355
|
+
#classifyRowCount(count) {
|
|
356
|
+
if (count < 100) return "tiny";
|
|
357
|
+
if (count < 1e3) return "small";
|
|
358
|
+
if (count < 1e4) return "medium";
|
|
359
|
+
if (count < 1e5) return "large";
|
|
360
|
+
return "huge";
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// packages/text2sql/src/lib/adapters/groundings/table.grounding.ts
|
|
365
|
+
import pluralize from "pluralize";
|
|
366
|
+
var TableGrounding = class extends AbstractGrounding {
|
|
367
|
+
#filter;
|
|
368
|
+
#forward;
|
|
369
|
+
#backward;
|
|
370
|
+
constructor(config = {}) {
|
|
371
|
+
super("tables");
|
|
372
|
+
this.#filter = config.filter;
|
|
373
|
+
this.#forward = config.forward;
|
|
374
|
+
this.#backward = config.backward;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Execute the grounding process.
|
|
378
|
+
* Writes discovered tables and relationships to the context.
|
|
379
|
+
*/
|
|
380
|
+
async execute(ctx) {
|
|
381
|
+
const seedTables = await this.applyFilter();
|
|
382
|
+
const forward = this.#forward;
|
|
383
|
+
const backward = this.#backward;
|
|
384
|
+
if (!forward && !backward) {
|
|
385
|
+
const tables3 = await Promise.all(
|
|
386
|
+
seedTables.map((name) => this.getTable(name))
|
|
387
|
+
);
|
|
388
|
+
ctx.tables.push(...tables3);
|
|
389
|
+
return () => this.#describeTables(tables3);
|
|
390
|
+
}
|
|
391
|
+
const tables2 = {};
|
|
392
|
+
const allRelationships = [];
|
|
393
|
+
const seenRelationships = /* @__PURE__ */ new Set();
|
|
394
|
+
const forwardQueue = [];
|
|
395
|
+
const backwardQueue = [];
|
|
396
|
+
const forwardVisited = /* @__PURE__ */ new Set();
|
|
397
|
+
const backwardVisited = /* @__PURE__ */ new Set();
|
|
398
|
+
for (const name of seedTables) {
|
|
399
|
+
if (forward) forwardQueue.push({ name, depth: 0 });
|
|
400
|
+
if (backward) backwardQueue.push({ name, depth: 0 });
|
|
401
|
+
}
|
|
402
|
+
const forwardLimit = forward === true ? Infinity : forward || 0;
|
|
403
|
+
while (forwardQueue.length > 0) {
|
|
404
|
+
const item = forwardQueue.shift();
|
|
405
|
+
if (!item) break;
|
|
406
|
+
const { name, depth } = item;
|
|
407
|
+
if (forwardVisited.has(name)) continue;
|
|
408
|
+
forwardVisited.add(name);
|
|
409
|
+
if (!tables2[name]) {
|
|
410
|
+
tables2[name] = await this.getTable(name);
|
|
411
|
+
}
|
|
412
|
+
if (depth < forwardLimit) {
|
|
413
|
+
const rels = await this.findOutgoingRelations(name);
|
|
414
|
+
for (const rel of rels) {
|
|
415
|
+
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
416
|
+
if (!forwardVisited.has(rel.referenced_table)) {
|
|
417
|
+
forwardQueue.push({ name: rel.referenced_table, depth: depth + 1 });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const backwardLimit = backward === true ? Infinity : backward || 0;
|
|
423
|
+
while (backwardQueue.length > 0) {
|
|
424
|
+
const item = backwardQueue.shift();
|
|
425
|
+
if (!item) break;
|
|
426
|
+
const { name, depth } = item;
|
|
427
|
+
if (backwardVisited.has(name)) continue;
|
|
428
|
+
backwardVisited.add(name);
|
|
429
|
+
if (!tables2[name]) {
|
|
430
|
+
tables2[name] = await this.getTable(name);
|
|
431
|
+
}
|
|
432
|
+
if (depth < backwardLimit) {
|
|
433
|
+
const rels = await this.findIncomingRelations(name);
|
|
434
|
+
for (const rel of rels) {
|
|
435
|
+
this.addRelationship(rel, allRelationships, seenRelationships);
|
|
436
|
+
if (!backwardVisited.has(rel.table)) {
|
|
437
|
+
backwardQueue.push({ name: rel.table, depth: depth + 1 });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const tablesList = Object.values(tables2);
|
|
443
|
+
ctx.tables.push(...tablesList);
|
|
444
|
+
ctx.relationships.push(...allRelationships);
|
|
445
|
+
return () => this.#describeTables(tablesList);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Apply the filter to get seed table names.
|
|
449
|
+
* If filter is an explicit array, skip querying all table names.
|
|
450
|
+
*/
|
|
451
|
+
async applyFilter() {
|
|
452
|
+
const filter = this.#filter;
|
|
453
|
+
if (Array.isArray(filter)) {
|
|
454
|
+
return filter;
|
|
455
|
+
}
|
|
456
|
+
const names = await this.getAllTableNames();
|
|
457
|
+
if (!filter) {
|
|
458
|
+
return names;
|
|
459
|
+
}
|
|
460
|
+
if (filter instanceof RegExp) {
|
|
461
|
+
return names.filter((name) => filter.test(name));
|
|
462
|
+
}
|
|
463
|
+
return names.filter(filter);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Add a relationship to the collection, deduplicating by key.
|
|
467
|
+
*/
|
|
468
|
+
addRelationship(rel, all, seen) {
|
|
469
|
+
const key = `${rel.table}:${rel.from.join(",")}:${rel.referenced_table}:${rel.to.join(",")}`;
|
|
470
|
+
if (!seen.has(key)) {
|
|
471
|
+
seen.add(key);
|
|
472
|
+
all.push(rel);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
#describeTables(tables2) {
|
|
476
|
+
if (!tables2.length) {
|
|
477
|
+
return "Schema unavailable.";
|
|
478
|
+
}
|
|
479
|
+
return tables2.map((table) => {
|
|
480
|
+
const rowCountInfo = table.rowCount != null ? ` [rows: ${table.rowCount}${table.sizeHint ? `, size: ${table.sizeHint}` : ""}]` : "";
|
|
481
|
+
const pkConstraint = table.constraints?.find((c) => c.type === "PRIMARY_KEY");
|
|
482
|
+
const pkColumns = new Set(pkConstraint?.columns ?? []);
|
|
483
|
+
const columns = table.columns.map((column) => {
|
|
484
|
+
const annotations = [];
|
|
485
|
+
const isPrimaryKey = pkColumns.has(column.name);
|
|
486
|
+
if (isPrimaryKey) {
|
|
487
|
+
annotations.push("PK");
|
|
488
|
+
}
|
|
489
|
+
if (column.isIndexed && !isPrimaryKey) {
|
|
490
|
+
annotations.push("Indexed");
|
|
491
|
+
}
|
|
492
|
+
if (column.kind === "LowCardinality" && column.values?.length) {
|
|
493
|
+
annotations.push(`LowCardinality: ${column.values.join(", ")}`);
|
|
494
|
+
}
|
|
495
|
+
if (column.stats) {
|
|
496
|
+
const statParts = [];
|
|
497
|
+
if (column.stats.min != null || column.stats.max != null) {
|
|
498
|
+
const minText = column.stats.min ?? "n/a";
|
|
499
|
+
const maxText = column.stats.max ?? "n/a";
|
|
500
|
+
statParts.push(`range ${minText} \u2192 ${maxText}`);
|
|
501
|
+
}
|
|
502
|
+
if (column.stats.nullFraction != null && Number.isFinite(column.stats.nullFraction)) {
|
|
503
|
+
const percent = Math.round(column.stats.nullFraction * 1e3) / 10;
|
|
504
|
+
statParts.push(`null\u2248${percent}%`);
|
|
505
|
+
}
|
|
506
|
+
if (statParts.length) {
|
|
507
|
+
annotations.push(statParts.join(", "));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const annotationText = annotations.length ? ` [${annotations.join(", ")}]` : "";
|
|
511
|
+
return ` - ${column.name} (${column.type})${annotationText}`;
|
|
512
|
+
}).join("\n");
|
|
513
|
+
const indexes2 = table.indexes?.length ? `
|
|
514
|
+
Indexes:
|
|
515
|
+
${table.indexes.map((index) => {
|
|
516
|
+
const props = [];
|
|
517
|
+
if (index.unique) {
|
|
518
|
+
props.push("UNIQUE");
|
|
519
|
+
}
|
|
520
|
+
if (index.type) {
|
|
521
|
+
props.push(index.type);
|
|
522
|
+
}
|
|
523
|
+
const propsText = props.length ? ` (${props.join(", ")})` : "";
|
|
524
|
+
const columnsText = index.columns?.length ? index.columns.join(", ") : "expression";
|
|
525
|
+
return ` - ${index.name}${propsText}: ${columnsText}`;
|
|
526
|
+
}).join("\n")}` : "";
|
|
527
|
+
return `- Table: ${table.name}${rowCountInfo}
|
|
528
|
+
Columns:
|
|
529
|
+
${columns}${indexes2}`;
|
|
530
|
+
}).join("\n\n");
|
|
531
|
+
}
|
|
532
|
+
#formatTableLabel = (tableName) => {
|
|
533
|
+
const base = tableName.split(".").pop() ?? tableName;
|
|
534
|
+
return base.replace(/_/g, " ");
|
|
535
|
+
};
|
|
536
|
+
#describeRelationships = (tables2, relationships) => {
|
|
537
|
+
if (!relationships.length) {
|
|
538
|
+
return "None detected";
|
|
539
|
+
}
|
|
540
|
+
const tableMap = new Map(tables2.map((table) => [table.name, table]));
|
|
541
|
+
return relationships.map((relationship) => {
|
|
542
|
+
const sourceLabel = this.#formatTableLabel(relationship.table);
|
|
543
|
+
const targetLabel = this.#formatTableLabel(
|
|
544
|
+
relationship.referenced_table
|
|
545
|
+
);
|
|
546
|
+
const singularSource = pluralize.singular(sourceLabel);
|
|
547
|
+
const pluralSource = pluralize.plural(sourceLabel);
|
|
548
|
+
const singularTarget = pluralize.singular(targetLabel);
|
|
549
|
+
const pluralTarget = pluralize.plural(targetLabel);
|
|
550
|
+
const sourceTable = tableMap.get(relationship.table);
|
|
551
|
+
const targetTable = tableMap.get(relationship.referenced_table);
|
|
552
|
+
const sourceCount = sourceTable?.rowCount;
|
|
553
|
+
const targetCount = targetTable?.rowCount;
|
|
554
|
+
const ratio = sourceCount != null && targetCount != null && targetCount > 0 ? sourceCount / targetCount : null;
|
|
555
|
+
let cardinality = "each";
|
|
556
|
+
if (ratio != null) {
|
|
557
|
+
if (ratio > 5) {
|
|
558
|
+
cardinality = `many-to-one (\u2248${sourceCount} vs ${targetCount})`;
|
|
559
|
+
} else if (ratio < 1.2 && ratio > 0.8) {
|
|
560
|
+
cardinality = `roughly 1:1 (${sourceCount} vs ${targetCount})`;
|
|
561
|
+
} else if (ratio < 0.2) {
|
|
562
|
+
cardinality = `one-to-many (${sourceCount} vs ${targetCount})`;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const mappings = relationship.from.map((fromCol, idx) => {
|
|
566
|
+
const targetCol = relationship.to[idx] ?? relationship.to[0] ?? fromCol;
|
|
567
|
+
return `${relationship.table}.${fromCol} -> ${relationship.referenced_table}.${targetCol}`;
|
|
568
|
+
}).join(", ");
|
|
569
|
+
return `- ${relationship.table} (${relationship.from.join(", ")}) -> ${relationship.referenced_table} (${relationship.to.join(", ")}) [${cardinality}]`;
|
|
570
|
+
}).join("\n");
|
|
571
|
+
};
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// packages/text2sql/src/lib/adapters/postgres/column-stats.postgres.grounding.ts
|
|
575
|
+
var PostgresColumnStatsGrounding = class extends ColumnStatsGrounding {
|
|
576
|
+
#adapter;
|
|
577
|
+
constructor(adapter, config = {}) {
|
|
578
|
+
super(config);
|
|
579
|
+
this.#adapter = adapter;
|
|
580
|
+
}
|
|
581
|
+
async collectStats(tableName, column) {
|
|
582
|
+
if (!this.#shouldCollectStats(column.type)) {
|
|
583
|
+
return void 0;
|
|
584
|
+
}
|
|
585
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
586
|
+
const tableIdentifier = `${this.#adapter.quoteIdentifier(schema)}.${this.#adapter.quoteIdentifier(table)}`;
|
|
587
|
+
const columnIdentifier = this.#adapter.quoteIdentifier(column.name);
|
|
588
|
+
const sql = `
|
|
589
|
+
SELECT
|
|
590
|
+
MIN(${columnIdentifier})::text AS min_value,
|
|
591
|
+
MAX(${columnIdentifier})::text AS max_value,
|
|
592
|
+
AVG(CASE WHEN ${columnIdentifier} IS NULL THEN 1.0 ELSE 0.0 END) AS null_fraction
|
|
593
|
+
FROM ${tableIdentifier}
|
|
594
|
+
`;
|
|
595
|
+
const rows = await this.#adapter.runQuery(sql);
|
|
596
|
+
if (!rows.length) {
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
const min = rows[0]?.min_value;
|
|
600
|
+
const max = rows[0]?.max_value;
|
|
601
|
+
const nullFraction = this.#adapter.toNumber(rows[0]?.null_fraction);
|
|
602
|
+
if (min == null && max == null && nullFraction == null) {
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
min: min ?? void 0,
|
|
607
|
+
max: max ?? void 0,
|
|
608
|
+
nullFraction: nullFraction != null && Number.isFinite(nullFraction) ? Math.max(0, Math.min(1, nullFraction)) : void 0
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
#shouldCollectStats(type) {
|
|
612
|
+
if (!type) {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
const normalized = type.toLowerCase();
|
|
616
|
+
return /int|real|numeric|double|float|decimal|date|time|bool|serial/.test(
|
|
617
|
+
normalized
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// packages/text2sql/src/lib/adapters/postgres/constraint.postgres.grounding.ts
|
|
623
|
+
var PostgresConstraintGrounding = class extends ConstraintGrounding {
|
|
624
|
+
#adapter;
|
|
625
|
+
constructor(adapter, config = {}) {
|
|
626
|
+
super(config);
|
|
627
|
+
this.#adapter = adapter;
|
|
628
|
+
}
|
|
629
|
+
async getConstraints(tableName) {
|
|
630
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
631
|
+
const constraints2 = [];
|
|
632
|
+
const constraintRows = await this.#adapter.runQuery(`
|
|
633
|
+
SELECT
|
|
634
|
+
con.conname AS constraint_name,
|
|
635
|
+
con.contype AS constraint_type,
|
|
636
|
+
pg_get_constraintdef(con.oid) AS definition,
|
|
637
|
+
a.attname AS column_name,
|
|
638
|
+
ref_nsp.nspname AS ref_schema,
|
|
639
|
+
ref_rel.relname AS ref_table,
|
|
640
|
+
ref_a.attname AS ref_column
|
|
641
|
+
FROM pg_constraint con
|
|
642
|
+
JOIN pg_class rel ON rel.oid = con.conrelid
|
|
643
|
+
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
|
|
644
|
+
LEFT JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS key(attnum, ord) ON TRUE
|
|
645
|
+
LEFT JOIN pg_attribute a ON a.attrelid = rel.oid AND a.attnum = key.attnum
|
|
646
|
+
LEFT JOIN pg_class ref_rel ON ref_rel.oid = con.confrelid
|
|
647
|
+
LEFT JOIN pg_namespace ref_nsp ON ref_nsp.oid = ref_rel.relnamespace
|
|
648
|
+
LEFT JOIN LATERAL unnest(con.confkey) WITH ORDINALITY AS ref_key(attnum, ord) ON key.ord = ref_key.ord
|
|
649
|
+
LEFT JOIN pg_attribute ref_a ON ref_a.attrelid = ref_rel.oid AND ref_a.attnum = ref_key.attnum
|
|
650
|
+
WHERE nsp.nspname = '${this.#adapter.escapeString(schema)}'
|
|
651
|
+
AND rel.relname = '${this.#adapter.escapeString(table)}'
|
|
652
|
+
AND con.contype IN ('p', 'f', 'c', 'u')
|
|
653
|
+
ORDER BY con.conname, key.ord
|
|
654
|
+
`);
|
|
655
|
+
const constraintMap = /* @__PURE__ */ new Map();
|
|
656
|
+
for (const row of constraintRows) {
|
|
657
|
+
const existing = constraintMap.get(row.constraint_name);
|
|
658
|
+
if (existing) {
|
|
659
|
+
if (row.column_name && !existing.columns.includes(row.column_name)) {
|
|
660
|
+
existing.columns.push(row.column_name);
|
|
661
|
+
}
|
|
662
|
+
if (row.ref_column && !existing.refColumns.includes(row.ref_column)) {
|
|
663
|
+
existing.refColumns.push(row.ref_column);
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
constraintMap.set(row.constraint_name, {
|
|
667
|
+
type: row.constraint_type,
|
|
668
|
+
definition: row.definition,
|
|
669
|
+
columns: row.column_name ? [row.column_name] : [],
|
|
670
|
+
refSchema: row.ref_schema,
|
|
671
|
+
refTable: row.ref_table,
|
|
672
|
+
refColumns: row.ref_column ? [row.ref_column] : []
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
for (const [name, data] of constraintMap) {
|
|
677
|
+
if (data.type === "p") {
|
|
678
|
+
constraints2.push({
|
|
679
|
+
name,
|
|
680
|
+
type: "PRIMARY_KEY",
|
|
681
|
+
columns: data.columns
|
|
682
|
+
});
|
|
683
|
+
} else if (data.type === "f") {
|
|
684
|
+
const referencedTable = data.refSchema && data.refTable ? `${data.refSchema}.${data.refTable}` : data.refTable ?? void 0;
|
|
685
|
+
constraints2.push({
|
|
686
|
+
name,
|
|
687
|
+
type: "FOREIGN_KEY",
|
|
688
|
+
columns: data.columns,
|
|
689
|
+
referencedTable,
|
|
690
|
+
referencedColumns: data.refColumns
|
|
691
|
+
});
|
|
692
|
+
} else if (data.type === "c") {
|
|
693
|
+
constraints2.push({
|
|
694
|
+
name,
|
|
695
|
+
type: "CHECK",
|
|
696
|
+
definition: data.definition?.replace(/^CHECK\s*\(/i, "").replace(/\)$/, "") || void 0,
|
|
697
|
+
columns: data.columns.length > 0 ? data.columns : void 0
|
|
698
|
+
});
|
|
699
|
+
} else if (data.type === "u") {
|
|
700
|
+
constraints2.push({
|
|
701
|
+
name,
|
|
702
|
+
type: "UNIQUE",
|
|
703
|
+
columns: data.columns
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const columnRows = await this.#adapter.runQuery(`
|
|
708
|
+
SELECT
|
|
709
|
+
column_name,
|
|
710
|
+
column_default,
|
|
711
|
+
is_nullable
|
|
712
|
+
FROM information_schema.columns
|
|
713
|
+
WHERE table_schema = '${this.#adapter.escapeString(schema)}'
|
|
714
|
+
AND table_name = '${this.#adapter.escapeString(table)}'
|
|
715
|
+
`);
|
|
716
|
+
for (const col of columnRows) {
|
|
717
|
+
const pkConstraint = constraints2.find((c) => c.type === "PRIMARY_KEY");
|
|
718
|
+
const isPkColumn = pkConstraint?.columns?.includes(col.column_name);
|
|
719
|
+
if (col.is_nullable === "NO" && !isPkColumn) {
|
|
720
|
+
constraints2.push({
|
|
721
|
+
name: `${table}_${col.column_name}_notnull`,
|
|
722
|
+
type: "NOT_NULL",
|
|
723
|
+
columns: [col.column_name]
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
if (col.column_default != null) {
|
|
727
|
+
constraints2.push({
|
|
728
|
+
name: `${table}_${col.column_name}_default`,
|
|
729
|
+
type: "DEFAULT",
|
|
730
|
+
columns: [col.column_name],
|
|
731
|
+
defaultValue: col.column_default
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return constraints2;
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// packages/text2sql/src/lib/adapters/postgres/indexes.postgres.grounding.ts
|
|
740
|
+
var PostgresIndexesGrounding = class extends IndexesGrounding {
|
|
741
|
+
#adapter;
|
|
742
|
+
#schemas;
|
|
743
|
+
constructor(adapter, config = {}) {
|
|
744
|
+
super(config);
|
|
745
|
+
this.#adapter = adapter;
|
|
746
|
+
this.#schemas = config.schemas;
|
|
747
|
+
}
|
|
748
|
+
async getIndexes(tableName) {
|
|
749
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
750
|
+
const rows = await this.#adapter.runQuery(`
|
|
751
|
+
SELECT
|
|
752
|
+
i.relname AS index_name,
|
|
753
|
+
a.attname AS column_name,
|
|
754
|
+
ix.indisunique AS is_unique,
|
|
755
|
+
am.amname AS index_type,
|
|
756
|
+
array_position(ix.indkey, a.attnum) AS column_position
|
|
757
|
+
FROM pg_index ix
|
|
758
|
+
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
759
|
+
JOIN pg_class t ON t.oid = ix.indrelid
|
|
760
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
761
|
+
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
|
|
762
|
+
JOIN pg_am am ON am.oid = i.relam
|
|
763
|
+
WHERE n.nspname = '${this.#adapter.escapeString(schema)}'
|
|
764
|
+
AND t.relname = '${this.#adapter.escapeString(table)}'
|
|
765
|
+
ORDER BY i.relname, array_position(ix.indkey, a.attnum)
|
|
766
|
+
`);
|
|
767
|
+
return this.#groupIndexes(rows);
|
|
768
|
+
}
|
|
769
|
+
#groupIndexes(rows) {
|
|
770
|
+
const indexes2 = /* @__PURE__ */ new Map();
|
|
771
|
+
for (const row of rows) {
|
|
772
|
+
if (!row.index_name || !row.column_name) continue;
|
|
773
|
+
const existing = indexes2.get(row.index_name);
|
|
774
|
+
if (existing) {
|
|
775
|
+
existing.columns.push(row.column_name);
|
|
776
|
+
} else {
|
|
777
|
+
indexes2.set(row.index_name, {
|
|
778
|
+
name: row.index_name,
|
|
779
|
+
columns: [row.column_name],
|
|
780
|
+
unique: row.is_unique,
|
|
781
|
+
type: row.index_type?.toUpperCase()
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return Array.from(indexes2.values());
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// packages/text2sql/src/lib/adapters/postgres/info.postgres.grounding.ts
|
|
790
|
+
var PostgresInfoGrounding = class extends InfoGrounding {
|
|
791
|
+
#adapter;
|
|
792
|
+
constructor(adapter, config = {}) {
|
|
793
|
+
super(config);
|
|
794
|
+
this.#adapter = adapter;
|
|
795
|
+
}
|
|
796
|
+
async collectInfo() {
|
|
797
|
+
const [versionRows, dbRows] = await Promise.all([
|
|
798
|
+
this.#adapter.runQuery(
|
|
799
|
+
"SELECT version() AS version"
|
|
800
|
+
),
|
|
801
|
+
this.#adapter.runQuery("SELECT current_database() AS db")
|
|
802
|
+
]);
|
|
803
|
+
return {
|
|
804
|
+
dialect: "postgresql",
|
|
805
|
+
version: versionRows[0]?.version,
|
|
806
|
+
database: dbRows[0]?.db
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// packages/text2sql/src/lib/adapters/postgres/low-cardinality.postgres.grounding.ts
|
|
812
|
+
var LOW_CARDINALITY_LIMIT = 20;
|
|
813
|
+
var PostgresLowCardinalityGrounding = class extends LowCardinalityGrounding {
|
|
814
|
+
#adapter;
|
|
815
|
+
constructor(adapter, config = {}) {
|
|
816
|
+
super(config);
|
|
817
|
+
this.#adapter = adapter;
|
|
818
|
+
}
|
|
819
|
+
async collectLowCardinality(tableName, column) {
|
|
820
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
821
|
+
const tableIdentifier = `${this.#adapter.quoteIdentifier(schema)}.${this.#adapter.quoteIdentifier(table)}`;
|
|
822
|
+
const columnIdentifier = this.#adapter.quoteIdentifier(column.name);
|
|
823
|
+
const limit = LOW_CARDINALITY_LIMIT + 1;
|
|
824
|
+
const sql = `
|
|
825
|
+
SELECT DISTINCT ${columnIdentifier}::text AS value
|
|
826
|
+
FROM ${tableIdentifier}
|
|
827
|
+
WHERE ${columnIdentifier} IS NOT NULL
|
|
828
|
+
LIMIT ${limit}
|
|
829
|
+
`;
|
|
830
|
+
const rows = await this.#adapter.runQuery(sql);
|
|
831
|
+
if (!rows.length || rows.length > LOW_CARDINALITY_LIMIT) {
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
const values = [];
|
|
835
|
+
for (const row of rows) {
|
|
836
|
+
if (row.value == null) {
|
|
837
|
+
return void 0;
|
|
838
|
+
}
|
|
839
|
+
values.push(row.value);
|
|
840
|
+
}
|
|
841
|
+
if (!values.length) {
|
|
842
|
+
return void 0;
|
|
843
|
+
}
|
|
844
|
+
return { kind: "LowCardinality", values };
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
// packages/text2sql/src/lib/adapters/postgres/postgres.ts
|
|
849
|
+
var POSTGRES_ERROR_MAP = {
|
|
850
|
+
"42P01": {
|
|
851
|
+
type: "MISSING_TABLE",
|
|
852
|
+
hint: "Check the database schema for the correct table name. Include the schema prefix if necessary."
|
|
853
|
+
},
|
|
854
|
+
"42703": {
|
|
855
|
+
type: "INVALID_COLUMN",
|
|
856
|
+
hint: "Verify the column exists on the referenced table and use table aliases to disambiguate."
|
|
857
|
+
},
|
|
858
|
+
"42601": {
|
|
859
|
+
type: "SYNTAX_ERROR",
|
|
860
|
+
hint: "There is a SQL syntax error. Review keywords, punctuation, and the overall query shape."
|
|
861
|
+
},
|
|
862
|
+
"42P10": {
|
|
863
|
+
type: "INVALID_COLUMN",
|
|
864
|
+
hint: "Columns referenced in GROUP BY/SELECT must exist. Double-check the column names and aliases."
|
|
865
|
+
},
|
|
866
|
+
"42883": {
|
|
867
|
+
type: "INVALID_FUNCTION",
|
|
868
|
+
hint: "The function or operator you used is not recognized. Confirm its name and argument types."
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
var LOW_CARDINALITY_LIMIT2 = 20;
|
|
872
|
+
function isPostgresError(error) {
|
|
873
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string";
|
|
874
|
+
}
|
|
875
|
+
function formatPostgresError(sql, error) {
|
|
876
|
+
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error occurred";
|
|
877
|
+
if (isPostgresError(error)) {
|
|
878
|
+
const metadata = POSTGRES_ERROR_MAP[error.code ?? ""];
|
|
879
|
+
if (metadata) {
|
|
880
|
+
return {
|
|
881
|
+
error: errorMessage,
|
|
882
|
+
error_type: metadata.type,
|
|
883
|
+
suggestion: metadata.hint,
|
|
884
|
+
sql_attempted: sql
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
error: errorMessage,
|
|
890
|
+
error_type: "UNKNOWN_ERROR",
|
|
891
|
+
suggestion: "Review the query and try again",
|
|
892
|
+
sql_attempted: sql
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
var Postgres = class extends Adapter {
|
|
896
|
+
#options;
|
|
897
|
+
grounding;
|
|
898
|
+
defaultSchema = "public";
|
|
899
|
+
systemSchemas = ["pg_catalog", "information_schema"];
|
|
900
|
+
constructor(options) {
|
|
901
|
+
super();
|
|
902
|
+
if (!options || typeof options.execute !== "function") {
|
|
903
|
+
throw new Error("Postgres adapter requires an execute function.");
|
|
904
|
+
}
|
|
905
|
+
this.#options = {
|
|
906
|
+
...options,
|
|
907
|
+
schemas: options.schemas?.length ? options.schemas : void 0
|
|
908
|
+
};
|
|
909
|
+
this.grounding = options.grounding;
|
|
910
|
+
}
|
|
911
|
+
async execute(sql) {
|
|
912
|
+
return this.#options.execute(sql);
|
|
913
|
+
}
|
|
914
|
+
async validate(sql) {
|
|
915
|
+
const validator = this.#options.validate ?? (async (text) => {
|
|
916
|
+
await this.#options.execute(`EXPLAIN ${text}`);
|
|
917
|
+
});
|
|
918
|
+
try {
|
|
919
|
+
return await validator(sql);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
return JSON.stringify(formatPostgresError(sql, error));
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
async runQuery(sql) {
|
|
925
|
+
return this.#runIntrospectionQuery(sql);
|
|
926
|
+
}
|
|
927
|
+
quoteIdentifier(name) {
|
|
928
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
929
|
+
}
|
|
930
|
+
escape(value) {
|
|
931
|
+
return value.replace(/"/g, '""');
|
|
932
|
+
}
|
|
933
|
+
async #loadTables() {
|
|
934
|
+
const rows = await this.#runIntrospectionQuery(`
|
|
935
|
+
SELECT
|
|
936
|
+
c.table_schema,
|
|
937
|
+
c.table_name,
|
|
938
|
+
c.column_name,
|
|
939
|
+
c.data_type
|
|
940
|
+
FROM information_schema.columns AS c
|
|
941
|
+
JOIN information_schema.tables AS t
|
|
942
|
+
ON c.table_schema = t.table_schema
|
|
943
|
+
AND c.table_name = t.table_name
|
|
944
|
+
WHERE t.table_type = 'BASE TABLE'
|
|
945
|
+
${this.#buildSchemaFilter("c.table_schema")}
|
|
946
|
+
ORDER BY c.table_schema, c.table_name, c.ordinal_position
|
|
947
|
+
`);
|
|
948
|
+
const tables2 = /* @__PURE__ */ new Map();
|
|
949
|
+
for (const row of rows) {
|
|
950
|
+
if (!row.table_name) {
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
const schema = row.table_schema ?? "public";
|
|
954
|
+
const tableName = row.table_name;
|
|
955
|
+
const qualifiedName = `${schema}.${tableName}`;
|
|
956
|
+
const table = tables2.get(qualifiedName) ?? {
|
|
957
|
+
name: qualifiedName,
|
|
958
|
+
schema,
|
|
959
|
+
rawName: tableName,
|
|
960
|
+
columns: []
|
|
961
|
+
};
|
|
962
|
+
table.columns.push({
|
|
963
|
+
name: row.column_name ?? "unknown",
|
|
964
|
+
type: row.data_type ?? "unknown"
|
|
965
|
+
});
|
|
966
|
+
tables2.set(qualifiedName, table);
|
|
967
|
+
}
|
|
968
|
+
return Array.from(tables2.values());
|
|
969
|
+
}
|
|
970
|
+
async #loadRelationships(filterTableNames) {
|
|
971
|
+
const tableFilter = this.#buildTableNamesFilter(filterTableNames);
|
|
972
|
+
const rows = await this.#runIntrospectionQuery(`
|
|
973
|
+
SELECT
|
|
974
|
+
tc.constraint_name,
|
|
975
|
+
tc.table_schema,
|
|
976
|
+
tc.table_name,
|
|
977
|
+
kcu.column_name,
|
|
978
|
+
ccu.table_schema AS foreign_table_schema,
|
|
979
|
+
ccu.table_name AS foreign_table_name,
|
|
980
|
+
ccu.column_name AS foreign_column_name
|
|
981
|
+
FROM information_schema.table_constraints AS tc
|
|
982
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
983
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
984
|
+
AND tc.table_schema = kcu.table_schema
|
|
985
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
986
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
987
|
+
AND ccu.table_schema = tc.table_schema
|
|
988
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
989
|
+
${this.#buildSchemaFilter("tc.table_schema")}
|
|
990
|
+
${tableFilter}
|
|
991
|
+
ORDER BY tc.table_schema, tc.table_name, tc.constraint_name, kcu.ordinal_position
|
|
992
|
+
`);
|
|
993
|
+
const relationships = /* @__PURE__ */ new Map();
|
|
994
|
+
for (const row of rows) {
|
|
995
|
+
if (!row.table_name || !row.foreign_table_name || !row.constraint_name) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const schema = row.table_schema ?? "public";
|
|
999
|
+
const referencedSchema = row.foreign_table_schema ?? "public";
|
|
1000
|
+
const key = `${schema}.${row.table_name}:${row.constraint_name}`;
|
|
1001
|
+
const relationship = relationships.get(key) ?? {
|
|
1002
|
+
table: `${schema}.${row.table_name}`,
|
|
1003
|
+
from: [],
|
|
1004
|
+
referenced_table: `${referencedSchema}.${row.foreign_table_name}`,
|
|
1005
|
+
to: []
|
|
1006
|
+
};
|
|
1007
|
+
relationship.from.push(row.column_name ?? "unknown");
|
|
1008
|
+
relationship.to.push(row.foreign_column_name ?? "unknown");
|
|
1009
|
+
relationships.set(key, relationship);
|
|
1010
|
+
}
|
|
1011
|
+
return Array.from(relationships.values());
|
|
1012
|
+
}
|
|
1013
|
+
async #annotateRowCounts(tables2, onProgress) {
|
|
1014
|
+
const total = tables2.length;
|
|
1015
|
+
for (let i = 0; i < tables2.length; i++) {
|
|
1016
|
+
const table = tables2[i];
|
|
1017
|
+
const tableIdentifier = this.#formatQualifiedTableName(table);
|
|
1018
|
+
onProgress?.({
|
|
1019
|
+
phase: "row_counts",
|
|
1020
|
+
message: `Counting rows in ${table.name}...`,
|
|
1021
|
+
current: i + 1,
|
|
1022
|
+
total
|
|
1023
|
+
});
|
|
1024
|
+
try {
|
|
1025
|
+
const rows = await this.#runIntrospectionQuery(`SELECT COUNT(*) as count FROM ${tableIdentifier}`);
|
|
1026
|
+
const rowCount2 = this.#toNumber(rows[0]?.count);
|
|
1027
|
+
if (rowCount2 != null) {
|
|
1028
|
+
table.rowCount = rowCount2;
|
|
1029
|
+
table.sizeHint = this.#classifyRowCount(rowCount2);
|
|
1030
|
+
}
|
|
1031
|
+
} catch {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* @deprecated Primary keys are now handled via constraints grounding.
|
|
1038
|
+
* This method is kept for backward compatibility but does nothing.
|
|
1039
|
+
*/
|
|
1040
|
+
async #annotatePrimaryKeys(_tables) {
|
|
1041
|
+
}
|
|
1042
|
+
async #annotateIndexes(tables2) {
|
|
1043
|
+
if (!tables2.length) {
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const tableMap = new Map(tables2.map((table) => [table.name, table]));
|
|
1047
|
+
const rows = await this.#runIntrospectionQuery(`
|
|
1048
|
+
SELECT
|
|
1049
|
+
n.nspname AS table_schema,
|
|
1050
|
+
t.relname AS table_name,
|
|
1051
|
+
i.relname AS index_name,
|
|
1052
|
+
a.attname AS column_name,
|
|
1053
|
+
ix.indisunique,
|
|
1054
|
+
ix.indisprimary,
|
|
1055
|
+
ix.indisclustered,
|
|
1056
|
+
am.amname AS method
|
|
1057
|
+
FROM pg_index ix
|
|
1058
|
+
JOIN pg_class t ON t.oid = ix.indrelid
|
|
1059
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
1060
|
+
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
1061
|
+
JOIN pg_am am ON am.oid = i.relam
|
|
1062
|
+
LEFT JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS key(attnum, ordinality) ON TRUE
|
|
1063
|
+
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = key.attnum
|
|
1064
|
+
WHERE t.relkind = 'r'
|
|
1065
|
+
${this.#buildSchemaFilter("n.nspname")}
|
|
1066
|
+
ORDER BY n.nspname, t.relname, i.relname, key.ordinality
|
|
1067
|
+
`);
|
|
1068
|
+
const indexMap = /* @__PURE__ */ new Map();
|
|
1069
|
+
for (const row of rows) {
|
|
1070
|
+
if (!row.table_name || !row.index_name) {
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
const schema = row.table_schema ?? "public";
|
|
1074
|
+
const tableKey = `${schema}.${row.table_name}`;
|
|
1075
|
+
const table = tableMap.get(tableKey);
|
|
1076
|
+
if (!table) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
const indexKey = `${tableKey}:${row.index_name}`;
|
|
1080
|
+
let index = indexMap.get(indexKey);
|
|
1081
|
+
if (!index) {
|
|
1082
|
+
index = {
|
|
1083
|
+
name: row.index_name,
|
|
1084
|
+
columns: [],
|
|
1085
|
+
unique: Boolean(row.indisunique ?? false),
|
|
1086
|
+
type: row.indisclustered ? "clustered" : row.method ?? void 0
|
|
1087
|
+
};
|
|
1088
|
+
indexMap.set(indexKey, index);
|
|
1089
|
+
if (!table.indexes) {
|
|
1090
|
+
table.indexes = [];
|
|
1091
|
+
}
|
|
1092
|
+
table.indexes.push(index);
|
|
1093
|
+
}
|
|
1094
|
+
if (row.column_name) {
|
|
1095
|
+
index.columns.push(row.column_name);
|
|
1096
|
+
const column = table.columns.find(
|
|
1097
|
+
(col) => col.name === row.column_name
|
|
1098
|
+
);
|
|
1099
|
+
if (column) {
|
|
1100
|
+
column.isIndexed = true;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async #annotateColumnStats(tables2, onProgress) {
|
|
1106
|
+
if (!tables2.length) {
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
const total = tables2.length;
|
|
1110
|
+
for (let i = 0; i < tables2.length; i++) {
|
|
1111
|
+
const table = tables2[i];
|
|
1112
|
+
const tableIdentifier = this.#formatQualifiedTableName(table);
|
|
1113
|
+
onProgress?.({
|
|
1114
|
+
phase: "column_stats",
|
|
1115
|
+
message: `Collecting stats for ${table.name}...`,
|
|
1116
|
+
current: i + 1,
|
|
1117
|
+
total
|
|
1118
|
+
});
|
|
1119
|
+
for (const column of table.columns) {
|
|
1120
|
+
if (!this.#shouldCollectStats(column.type)) {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
const columnIdentifier = this.#quoteIdentifier(column.name);
|
|
1124
|
+
const sql = `
|
|
1125
|
+
SELECT
|
|
1126
|
+
MIN(${columnIdentifier})::text AS min_value,
|
|
1127
|
+
MAX(${columnIdentifier})::text AS max_value,
|
|
1128
|
+
AVG(CASE WHEN ${columnIdentifier} IS NULL THEN 1.0 ELSE 0.0 END)::float AS null_fraction
|
|
1129
|
+
FROM ${tableIdentifier}
|
|
1130
|
+
`;
|
|
1131
|
+
try {
|
|
1132
|
+
const rows = await this.#runIntrospectionQuery(sql);
|
|
1133
|
+
if (!rows.length) {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
const min = rows[0]?.min_value ?? void 0;
|
|
1137
|
+
const max = rows[0]?.max_value ?? void 0;
|
|
1138
|
+
const nullFraction = this.#toNumber(rows[0]?.null_fraction);
|
|
1139
|
+
if (min != null || max != null || nullFraction != null) {
|
|
1140
|
+
column.stats = {
|
|
1141
|
+
min,
|
|
1142
|
+
max,
|
|
1143
|
+
nullFraction: nullFraction != null && Number.isFinite(nullFraction) ? Math.max(0, Math.min(1, nullFraction)) : void 0
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
} catch {
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
async #annotateLowCardinalityColumns(tables2, onProgress) {
|
|
1153
|
+
const total = tables2.length;
|
|
1154
|
+
for (let i = 0; i < tables2.length; i++) {
|
|
1155
|
+
const table = tables2[i];
|
|
1156
|
+
const tableIdentifier = this.#formatQualifiedTableName(table);
|
|
1157
|
+
onProgress?.({
|
|
1158
|
+
phase: "low_cardinality",
|
|
1159
|
+
message: `Analyzing cardinality in ${table.name}...`,
|
|
1160
|
+
current: i + 1,
|
|
1161
|
+
total
|
|
1162
|
+
});
|
|
1163
|
+
for (const column of table.columns) {
|
|
1164
|
+
const columnIdentifier = this.#quoteIdentifier(column.name);
|
|
1165
|
+
const limit = LOW_CARDINALITY_LIMIT2 + 1;
|
|
1166
|
+
const sql = `
|
|
1167
|
+
SELECT DISTINCT ${columnIdentifier} AS value
|
|
1168
|
+
FROM ${tableIdentifier}
|
|
1169
|
+
WHERE ${columnIdentifier} IS NOT NULL
|
|
1170
|
+
LIMIT ${limit}
|
|
1171
|
+
`;
|
|
1172
|
+
let rows = [];
|
|
1173
|
+
try {
|
|
1174
|
+
rows = await this.#runIntrospectionQuery(sql);
|
|
1175
|
+
} catch {
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (!rows.length || rows.length > LOW_CARDINALITY_LIMIT2) {
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
const values = [];
|
|
1182
|
+
let shouldSkip = false;
|
|
1183
|
+
for (const row of rows) {
|
|
1184
|
+
const formatted = this.#normalizeValue(row.value);
|
|
1185
|
+
if (formatted == null) {
|
|
1186
|
+
shouldSkip = true;
|
|
1187
|
+
break;
|
|
1188
|
+
}
|
|
1189
|
+
values.push(formatted);
|
|
1190
|
+
}
|
|
1191
|
+
if (shouldSkip || !values.length) {
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
column.kind = "LowCardinality";
|
|
1195
|
+
column.values = values;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
#buildSchemaFilter(columnName) {
|
|
1200
|
+
if (this.#options.schemas && this.#options.schemas.length > 0) {
|
|
1201
|
+
const values = this.#options.schemas.map((schema) => `'${schema.replace(/'/g, "''")}'`).join(", ");
|
|
1202
|
+
return `AND ${columnName} IN (${values})`;
|
|
1203
|
+
}
|
|
1204
|
+
return `AND ${columnName} NOT IN ('pg_catalog', 'information_schema')`;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Build a filter for table names (qualified as schema.table).
|
|
1208
|
+
* Matches if either the source table or referenced table is in the list.
|
|
1209
|
+
*/
|
|
1210
|
+
#buildTableNamesFilter(tableNames) {
|
|
1211
|
+
if (!tableNames || tableNames.length === 0) {
|
|
1212
|
+
return "";
|
|
1213
|
+
}
|
|
1214
|
+
const conditions = [];
|
|
1215
|
+
for (const name of tableNames) {
|
|
1216
|
+
if (name.includes(".")) {
|
|
1217
|
+
const [schema, ...rest] = name.split(".");
|
|
1218
|
+
const tableName = rest.join(".");
|
|
1219
|
+
const escapedSchema = schema.replace(/'/g, "''");
|
|
1220
|
+
const escapedTable = tableName.replace(/'/g, "''");
|
|
1221
|
+
conditions.push(
|
|
1222
|
+
`(tc.table_schema = '${escapedSchema}' AND tc.table_name = '${escapedTable}')`
|
|
1223
|
+
);
|
|
1224
|
+
conditions.push(
|
|
1225
|
+
`(ccu.table_schema = '${escapedSchema}' AND ccu.table_name = '${escapedTable}')`
|
|
1226
|
+
);
|
|
1227
|
+
} else {
|
|
1228
|
+
const escaped = name.replace(/'/g, "''");
|
|
1229
|
+
conditions.push(`tc.table_name = '${escaped}'`);
|
|
1230
|
+
conditions.push(`ccu.table_name = '${escaped}'`);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return `AND (${conditions.join(" OR ")})`;
|
|
1234
|
+
}
|
|
1235
|
+
async #runIntrospectionQuery(sql) {
|
|
1236
|
+
const result = await this.#options.execute(sql);
|
|
1237
|
+
if (Array.isArray(result)) {
|
|
1238
|
+
return result;
|
|
1239
|
+
}
|
|
1240
|
+
if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) {
|
|
1241
|
+
return result.rows;
|
|
1242
|
+
}
|
|
1243
|
+
throw new Error(
|
|
1244
|
+
"Postgres adapter execute() must return an array of rows or an object with a rows array when introspecting."
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
#quoteIdentifier(name) {
|
|
1248
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
1249
|
+
}
|
|
1250
|
+
#formatQualifiedTableName(table) {
|
|
1251
|
+
if (table.schema && table.rawName) {
|
|
1252
|
+
return `${this.#quoteIdentifier(table.schema)}.${this.#quoteIdentifier(table.rawName)}`;
|
|
1253
|
+
}
|
|
1254
|
+
if (table.name.includes(".")) {
|
|
1255
|
+
const [schemaPart, ...rest] = table.name.split(".");
|
|
1256
|
+
const tablePart = rest.join(".") || schemaPart;
|
|
1257
|
+
if (rest.length === 0) {
|
|
1258
|
+
return this.#quoteIdentifier(schemaPart);
|
|
1259
|
+
}
|
|
1260
|
+
return `${this.#quoteIdentifier(schemaPart)}.${this.#quoteIdentifier(tablePart)}`;
|
|
1261
|
+
}
|
|
1262
|
+
return this.#quoteIdentifier(table.name);
|
|
1263
|
+
}
|
|
1264
|
+
#toNumber(value) {
|
|
1265
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1266
|
+
return value;
|
|
1267
|
+
}
|
|
1268
|
+
if (typeof value === "bigint") {
|
|
1269
|
+
return Number(value);
|
|
1270
|
+
}
|
|
1271
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1272
|
+
const parsed = Number(value);
|
|
1273
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
1274
|
+
}
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
#shouldCollectStats(type) {
|
|
1278
|
+
if (!type) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
const normalized = type.toLowerCase();
|
|
1282
|
+
return /int|numeric|decimal|double|real|money|date|time|timestamp|bool/.test(
|
|
1283
|
+
normalized
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
#classifyRowCount(count) {
|
|
1287
|
+
if (count < 100) {
|
|
1288
|
+
return "tiny";
|
|
1289
|
+
}
|
|
1290
|
+
if (count < 1e3) {
|
|
1291
|
+
return "small";
|
|
1292
|
+
}
|
|
1293
|
+
if (count < 1e4) {
|
|
1294
|
+
return "medium";
|
|
1295
|
+
}
|
|
1296
|
+
if (count < 1e5) {
|
|
1297
|
+
return "large";
|
|
1298
|
+
}
|
|
1299
|
+
return "huge";
|
|
1300
|
+
}
|
|
1301
|
+
#normalizeValue(value) {
|
|
1302
|
+
if (value === null || value === void 0) {
|
|
1303
|
+
return null;
|
|
1304
|
+
}
|
|
1305
|
+
if (typeof value === "string") {
|
|
1306
|
+
return value;
|
|
1307
|
+
}
|
|
1308
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
1309
|
+
return String(value);
|
|
1310
|
+
}
|
|
1311
|
+
if (typeof value === "boolean") {
|
|
1312
|
+
return value ? "true" : "false";
|
|
1313
|
+
}
|
|
1314
|
+
if (value instanceof Date) {
|
|
1315
|
+
return value.toISOString();
|
|
1316
|
+
}
|
|
1317
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
|
|
1318
|
+
return value.toString("utf-8");
|
|
1319
|
+
}
|
|
1320
|
+
return null;
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
// packages/text2sql/src/lib/adapters/postgres/row-count.postgres.grounding.ts
|
|
1325
|
+
var PostgresRowCountGrounding = class extends RowCountGrounding {
|
|
1326
|
+
#adapter;
|
|
1327
|
+
constructor(adapter, config = {}) {
|
|
1328
|
+
super(config);
|
|
1329
|
+
this.#adapter = adapter;
|
|
1330
|
+
}
|
|
1331
|
+
async getRowCount(tableName) {
|
|
1332
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
1333
|
+
const tableIdentifier = `${this.#adapter.quoteIdentifier(schema)}.${this.#adapter.quoteIdentifier(table)}`;
|
|
1334
|
+
const rows = await this.#adapter.runQuery(
|
|
1335
|
+
`SELECT COUNT(*) as count FROM ${tableIdentifier}`
|
|
1336
|
+
);
|
|
1337
|
+
return this.#adapter.toNumber(rows[0]?.count);
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
// packages/text2sql/src/lib/adapters/postgres/table.postgres.grounding.ts
|
|
1342
|
+
var PostgresTableGrounding = class extends TableGrounding {
|
|
1343
|
+
#adapter;
|
|
1344
|
+
#schemas;
|
|
1345
|
+
constructor(adapter, config = {}) {
|
|
1346
|
+
super(config);
|
|
1347
|
+
this.#adapter = adapter;
|
|
1348
|
+
this.#schemas = config.schemas;
|
|
1349
|
+
}
|
|
1350
|
+
async getAllTableNames() {
|
|
1351
|
+
const rows = await this.#adapter.runQuery(`
|
|
1352
|
+
SELECT DISTINCT table_schema || '.' || table_name AS name
|
|
1353
|
+
FROM information_schema.tables
|
|
1354
|
+
WHERE table_type = 'BASE TABLE'
|
|
1355
|
+
${this.#adapter.buildSchemaFilter("table_schema", this.#schemas)}
|
|
1356
|
+
ORDER BY name
|
|
1357
|
+
`);
|
|
1358
|
+
return rows.map((r) => r.name);
|
|
1359
|
+
}
|
|
1360
|
+
async getTable(tableName) {
|
|
1361
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
1362
|
+
const columns = await this.#adapter.runQuery(`
|
|
1363
|
+
SELECT column_name, data_type
|
|
1364
|
+
FROM information_schema.columns
|
|
1365
|
+
WHERE table_schema = '${this.#adapter.escapeString(schema)}'
|
|
1366
|
+
AND table_name = '${this.#adapter.escapeString(table)}'
|
|
1367
|
+
ORDER BY ordinal_position
|
|
1368
|
+
`);
|
|
1369
|
+
return {
|
|
1370
|
+
name: tableName,
|
|
1371
|
+
schema,
|
|
1372
|
+
rawName: table,
|
|
1373
|
+
columns: columns.map((col) => ({
|
|
1374
|
+
name: col.column_name ?? "unknown",
|
|
1375
|
+
type: col.data_type ?? "unknown"
|
|
1376
|
+
}))
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
async findOutgoingRelations(tableName) {
|
|
1380
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
1381
|
+
const rows = await this.#adapter.runQuery(`
|
|
1382
|
+
SELECT
|
|
1383
|
+
tc.constraint_name,
|
|
1384
|
+
tc.table_schema,
|
|
1385
|
+
tc.table_name,
|
|
1386
|
+
kcu.column_name,
|
|
1387
|
+
ccu.table_schema AS foreign_table_schema,
|
|
1388
|
+
ccu.table_name AS foreign_table_name,
|
|
1389
|
+
ccu.column_name AS foreign_column_name
|
|
1390
|
+
FROM information_schema.table_constraints AS tc
|
|
1391
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
1392
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
1393
|
+
AND tc.table_schema = kcu.table_schema
|
|
1394
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
1395
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
1396
|
+
AND ccu.table_schema = tc.table_schema
|
|
1397
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
1398
|
+
AND tc.table_schema = '${this.#adapter.escapeString(schema)}'
|
|
1399
|
+
AND tc.table_name = '${this.#adapter.escapeString(table)}'
|
|
1400
|
+
ORDER BY tc.constraint_name, kcu.ordinal_position
|
|
1401
|
+
`);
|
|
1402
|
+
return this.#groupRelationships(rows);
|
|
1403
|
+
}
|
|
1404
|
+
async findIncomingRelations(tableName) {
|
|
1405
|
+
const { schema, table } = this.#adapter.parseTableName(tableName);
|
|
1406
|
+
const rows = await this.#adapter.runQuery(`
|
|
1407
|
+
SELECT
|
|
1408
|
+
tc.constraint_name,
|
|
1409
|
+
tc.table_schema,
|
|
1410
|
+
tc.table_name,
|
|
1411
|
+
kcu.column_name,
|
|
1412
|
+
ccu.table_schema AS foreign_table_schema,
|
|
1413
|
+
ccu.table_name AS foreign_table_name,
|
|
1414
|
+
ccu.column_name AS foreign_column_name
|
|
1415
|
+
FROM information_schema.table_constraints AS tc
|
|
1416
|
+
JOIN information_schema.key_column_usage AS kcu
|
|
1417
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
1418
|
+
AND tc.table_schema = kcu.table_schema
|
|
1419
|
+
JOIN information_schema.constraint_column_usage AS ccu
|
|
1420
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
1421
|
+
AND ccu.table_schema = tc.table_schema
|
|
1422
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
1423
|
+
AND ccu.table_schema = '${this.#adapter.escapeString(schema)}'
|
|
1424
|
+
AND ccu.table_name = '${this.#adapter.escapeString(table)}'
|
|
1425
|
+
ORDER BY tc.constraint_name, kcu.ordinal_position
|
|
1426
|
+
`);
|
|
1427
|
+
return this.#groupRelationships(rows);
|
|
1428
|
+
}
|
|
1429
|
+
#groupRelationships(rows) {
|
|
1430
|
+
const relationships = /* @__PURE__ */ new Map();
|
|
1431
|
+
const defaultSchema = this.#adapter.defaultSchema ?? "public";
|
|
1432
|
+
for (const row of rows) {
|
|
1433
|
+
if (!row.table_name || !row.foreign_table_name || !row.constraint_name) {
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
const schema = row.table_schema ?? defaultSchema;
|
|
1437
|
+
const referencedSchema = row.foreign_table_schema ?? defaultSchema;
|
|
1438
|
+
const key = `${schema}.${row.table_name}:${row.constraint_name}`;
|
|
1439
|
+
const relationship = relationships.get(key) ?? {
|
|
1440
|
+
table: `${schema}.${row.table_name}`,
|
|
1441
|
+
from: [],
|
|
1442
|
+
referenced_table: `${referencedSchema}.${row.foreign_table_name}`,
|
|
1443
|
+
to: []
|
|
1444
|
+
};
|
|
1445
|
+
relationship.from.push(row.column_name ?? "unknown");
|
|
1446
|
+
relationship.to.push(row.foreign_column_name ?? "unknown");
|
|
1447
|
+
relationships.set(key, relationship);
|
|
1448
|
+
}
|
|
1449
|
+
return Array.from(relationships.values());
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
// packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
|
|
1454
|
+
var ViewGrounding = class extends AbstractGrounding {
|
|
1455
|
+
#filter;
|
|
1456
|
+
constructor(config = {}) {
|
|
1457
|
+
super("views");
|
|
1458
|
+
this.#filter = config.filter;
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Execute the grounding process.
|
|
1462
|
+
* Writes discovered views to the context.
|
|
1463
|
+
*/
|
|
1464
|
+
async execute(ctx) {
|
|
1465
|
+
const viewNames = await this.applyFilter();
|
|
1466
|
+
const views2 = await Promise.all(
|
|
1467
|
+
viewNames.map((name) => this.getView(name))
|
|
1468
|
+
);
|
|
1469
|
+
ctx.views.push(...views2);
|
|
1470
|
+
return () => this.#describe(views2);
|
|
1471
|
+
}
|
|
1472
|
+
#describe(views2) {
|
|
1473
|
+
if (!views2.length) {
|
|
1474
|
+
return "No views available.";
|
|
1475
|
+
}
|
|
1476
|
+
return views2.map((view) => {
|
|
1477
|
+
const columns = view.columns.map((column) => {
|
|
1478
|
+
const annotations = [];
|
|
1479
|
+
if (column.kind === "LowCardinality" && column.values?.length) {
|
|
1480
|
+
annotations.push(`LowCardinality: ${column.values.join(", ")}`);
|
|
1481
|
+
}
|
|
1482
|
+
if (column.stats) {
|
|
1483
|
+
const statParts = [];
|
|
1484
|
+
if (column.stats.min != null || column.stats.max != null) {
|
|
1485
|
+
const minText = column.stats.min ?? "n/a";
|
|
1486
|
+
const maxText = column.stats.max ?? "n/a";
|
|
1487
|
+
statParts.push(`range ${minText} \u2192 ${maxText}`);
|
|
1488
|
+
}
|
|
1489
|
+
if (column.stats.nullFraction != null && Number.isFinite(column.stats.nullFraction)) {
|
|
1490
|
+
const percent = Math.round(column.stats.nullFraction * 1e3) / 10;
|
|
1491
|
+
statParts.push(`null\u2248${percent}%`);
|
|
1492
|
+
}
|
|
1493
|
+
if (statParts.length) {
|
|
1494
|
+
annotations.push(statParts.join(", "));
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
const annotationText = annotations.length ? ` [${annotations.join(", ")}]` : "";
|
|
1498
|
+
return ` - ${column.name} (${column.type})${annotationText}`;
|
|
1499
|
+
}).join("\n");
|
|
1500
|
+
const definition = view.definition ? `
|
|
1501
|
+
Definition: ${view.definition.length > 200 ? view.definition.slice(0, 200) + "..." : view.definition}` : "";
|
|
1502
|
+
return `- View: ${view.name}${definition}
|
|
1503
|
+
Columns:
|
|
1504
|
+
${columns}`;
|
|
1505
|
+
}).join("\n\n");
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Apply the filter to get view names.
|
|
1509
|
+
* If filter is an explicit array, skip querying all view names.
|
|
1510
|
+
*/
|
|
1511
|
+
async applyFilter() {
|
|
1512
|
+
const filter = this.#filter;
|
|
1513
|
+
if (Array.isArray(filter)) {
|
|
1514
|
+
return filter;
|
|
1515
|
+
}
|
|
1516
|
+
const names = await this.getAllViewNames();
|
|
1517
|
+
if (!filter) {
|
|
1518
|
+
return names;
|
|
1519
|
+
}
|
|
1520
|
+
if (filter instanceof RegExp) {
|
|
1521
|
+
return names.filter((name) => filter.test(name));
|
|
1522
|
+
}
|
|
1523
|
+
return names.filter(filter);
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
|
|
1527
|
+
// packages/text2sql/src/lib/adapters/postgres/view.postgres.grounding.ts
|
|
1528
|
+
var PostgresViewGrounding = class extends ViewGrounding {
|
|
1529
|
+
#adapter;
|
|
1530
|
+
#schemas;
|
|
1531
|
+
constructor(adapter, config = {}) {
|
|
1532
|
+
super(config);
|
|
1533
|
+
this.#adapter = adapter;
|
|
1534
|
+
this.#schemas = config.schemas;
|
|
1535
|
+
}
|
|
1536
|
+
async getAllViewNames() {
|
|
1537
|
+
const rows = await this.#adapter.runQuery(`
|
|
1538
|
+
SELECT table_schema || '.' || table_name AS name
|
|
1539
|
+
FROM information_schema.views
|
|
1540
|
+
WHERE 1=1
|
|
1541
|
+
${this.#adapter.buildSchemaFilter("table_schema", this.#schemas)}
|
|
1542
|
+
ORDER BY name
|
|
1543
|
+
`);
|
|
1544
|
+
return rows.map((r) => r.name);
|
|
1545
|
+
}
|
|
1546
|
+
async getView(viewName) {
|
|
1547
|
+
const { schema, table: view } = this.#adapter.parseTableName(viewName);
|
|
1548
|
+
const defRows = await this.#adapter.runQuery(`
|
|
1549
|
+
SELECT definition
|
|
1550
|
+
FROM pg_views
|
|
1551
|
+
WHERE schemaname = '${this.#adapter.escapeString(schema)}'
|
|
1552
|
+
AND viewname = '${this.#adapter.escapeString(view)}'
|
|
1553
|
+
`);
|
|
1554
|
+
const columns = await this.#adapter.runQuery(`
|
|
1555
|
+
SELECT column_name, data_type
|
|
1556
|
+
FROM information_schema.columns
|
|
1557
|
+
WHERE table_schema = '${this.#adapter.escapeString(schema)}'
|
|
1558
|
+
AND table_name = '${this.#adapter.escapeString(view)}'
|
|
1559
|
+
ORDER BY ordinal_position
|
|
1560
|
+
`);
|
|
1561
|
+
return {
|
|
1562
|
+
name: viewName,
|
|
1563
|
+
schema,
|
|
1564
|
+
rawName: view,
|
|
1565
|
+
definition: defRows[0]?.definition ?? void 0,
|
|
1566
|
+
columns: columns.map((col) => ({
|
|
1567
|
+
name: col.column_name ?? "unknown",
|
|
1568
|
+
type: col.data_type ?? "unknown"
|
|
1569
|
+
}))
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
// packages/text2sql/src/lib/adapters/postgres/index.ts
|
|
1575
|
+
function tables(config = {}) {
|
|
1576
|
+
return (adapter) => new PostgresTableGrounding(adapter, config);
|
|
1577
|
+
}
|
|
1578
|
+
function info(config = {}) {
|
|
1579
|
+
return (adapter) => new PostgresInfoGrounding(adapter, config);
|
|
1580
|
+
}
|
|
1581
|
+
function views(config = {}) {
|
|
1582
|
+
return (adapter) => {
|
|
1583
|
+
return new PostgresViewGrounding(adapter, config);
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
function columnStats(config = {}) {
|
|
1587
|
+
return (adapter) => {
|
|
1588
|
+
return new PostgresColumnStatsGrounding(adapter, config);
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function lowCardinality(config = {}) {
|
|
1592
|
+
return (adapter) => {
|
|
1593
|
+
return new PostgresLowCardinalityGrounding(adapter, config);
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function indexes(config = {}) {
|
|
1597
|
+
return (adapter) => {
|
|
1598
|
+
return new PostgresIndexesGrounding(adapter, config);
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
function rowCount(config = {}) {
|
|
1602
|
+
return (adapter) => {
|
|
1603
|
+
return new PostgresRowCountGrounding(adapter, config);
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
function constraints(config = {}) {
|
|
1607
|
+
return (adapter) => {
|
|
1608
|
+
return new PostgresConstraintGrounding(adapter, config);
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
function report(config = {}) {
|
|
1612
|
+
return (adapter) => new ReportGrounding(adapter, config);
|
|
1613
|
+
}
|
|
1614
|
+
var postgres_default = {
|
|
1615
|
+
tables,
|
|
1616
|
+
info,
|
|
1617
|
+
views,
|
|
1618
|
+
columnStats,
|
|
1619
|
+
lowCardinality,
|
|
1620
|
+
indexes,
|
|
1621
|
+
rowCount,
|
|
1622
|
+
constraints,
|
|
1623
|
+
report,
|
|
1624
|
+
Postgres
|
|
1625
|
+
};
|
|
1626
|
+
export {
|
|
1627
|
+
Postgres,
|
|
1628
|
+
columnStats,
|
|
1629
|
+
constraints,
|
|
1630
|
+
postgres_default as default,
|
|
1631
|
+
formatPostgresError,
|
|
1632
|
+
indexes,
|
|
1633
|
+
info,
|
|
1634
|
+
lowCardinality,
|
|
1635
|
+
report,
|
|
1636
|
+
rowCount,
|
|
1637
|
+
tables,
|
|
1638
|
+
views
|
|
1639
|
+
};
|
|
1640
|
+
//# sourceMappingURL=index.js.map
|