@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.
Files changed (139) hide show
  1. package/dist/index.d.ts +7 -9
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +882 -2730
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/adapter.d.ts +67 -10
  6. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  7. package/dist/lib/adapters/grounding.ticket.d.ts +21 -0
  8. package/dist/lib/adapters/grounding.ticket.d.ts.map +1 -0
  9. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts +32 -0
  10. package/dist/lib/adapters/groundings/column-stats.grounding.d.ts.map +1 -0
  11. package/dist/lib/adapters/groundings/constraint.grounding.d.ts +31 -0
  12. package/dist/lib/adapters/groundings/constraint.grounding.d.ts.map +1 -0
  13. package/dist/lib/adapters/groundings/context.d.ts +41 -0
  14. package/dist/lib/adapters/groundings/context.d.ts.map +1 -0
  15. package/dist/lib/adapters/groundings/grounding.d.ts +8 -0
  16. package/dist/lib/adapters/groundings/grounding.d.ts.map +1 -0
  17. package/dist/lib/adapters/groundings/grounding.js +507 -0
  18. package/dist/lib/adapters/groundings/grounding.js.map +7 -0
  19. package/dist/lib/adapters/groundings/indexes.grounding.d.ts +30 -0
  20. package/dist/lib/adapters/groundings/indexes.grounding.d.ts.map +1 -0
  21. package/dist/lib/adapters/groundings/info.grounding.d.ts +29 -0
  22. package/dist/lib/adapters/groundings/info.grounding.d.ts.map +1 -0
  23. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts +35 -0
  24. package/dist/lib/adapters/groundings/low-cardinality.grounding.d.ts.map +1 -0
  25. package/dist/lib/adapters/groundings/report.grounding.d.ts +38 -0
  26. package/dist/lib/adapters/groundings/report.grounding.d.ts.map +1 -0
  27. package/dist/lib/adapters/groundings/row-count.grounding.d.ts +30 -0
  28. package/dist/lib/adapters/groundings/row-count.grounding.d.ts.map +1 -0
  29. package/dist/lib/adapters/groundings/table.grounding.d.ts +61 -0
  30. package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -0
  31. package/dist/lib/adapters/groundings/view.grounding.d.ts +57 -0
  32. package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -0
  33. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts +12 -0
  34. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -0
  35. package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts +11 -0
  36. package/dist/lib/adapters/postgres/constraint.postgres.grounding.d.ts.map +1 -0
  37. package/dist/lib/adapters/postgres/index.d.ts +43 -0
  38. package/dist/lib/adapters/postgres/index.d.ts.map +1 -0
  39. package/dist/lib/adapters/postgres/index.js +1640 -0
  40. package/dist/lib/adapters/postgres/index.js.map +7 -0
  41. package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts +15 -0
  42. package/dist/lib/adapters/postgres/indexes.postgres.grounding.d.ts.map +1 -0
  43. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts +11 -0
  44. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -0
  45. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts +14 -0
  46. package/dist/lib/adapters/postgres/low-cardinality.postgres.grounding.d.ts.map +1 -0
  47. package/dist/lib/adapters/postgres/postgres.d.ts +26 -0
  48. package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -0
  49. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts +11 -0
  50. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts.map +1 -0
  51. package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts +21 -0
  52. package/dist/lib/adapters/postgres/table.postgres.grounding.d.ts.map +1 -0
  53. package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts +16 -0
  54. package/dist/lib/adapters/postgres/view.postgres.grounding.d.ts.map +1 -0
  55. package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts +12 -0
  56. package/dist/lib/adapters/sqlite/column-stats.sqlite.grounding.d.ts.map +1 -0
  57. package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts +15 -0
  58. package/dist/lib/adapters/sqlite/constraint.sqlite.grounding.d.ts.map +1 -0
  59. package/dist/lib/adapters/sqlite/index.d.ts +43 -0
  60. package/dist/lib/adapters/sqlite/index.d.ts.map +1 -0
  61. package/dist/lib/adapters/sqlite/index.js +1215 -0
  62. package/dist/lib/adapters/sqlite/index.js.map +7 -0
  63. package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts +11 -0
  64. package/dist/lib/adapters/sqlite/indexes.sqlite.grounding.d.ts.map +1 -0
  65. package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts +11 -0
  66. package/dist/lib/adapters/sqlite/info.sqlite.grounding.d.ts.map +1 -0
  67. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts +14 -0
  68. package/dist/lib/adapters/sqlite/low-cardinality.sqlite.grounding.d.ts.map +1 -0
  69. package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts +11 -0
  70. package/dist/lib/adapters/sqlite/row-count.sqlite.grounding.d.ts.map +1 -0
  71. package/dist/lib/adapters/sqlite/sqlite.d.ts +25 -0
  72. package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -0
  73. package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts +17 -0
  74. package/dist/lib/adapters/sqlite/table.sqlite.grounding.d.ts.map +1 -0
  75. package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts +12 -0
  76. package/dist/lib/adapters/sqlite/view.sqlite.grounding.d.ts.map +1 -0
  77. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts +12 -0
  78. package/dist/lib/adapters/sqlserver/column-stats.sqlserver.grounding.d.ts.map +1 -0
  79. package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts +11 -0
  80. package/dist/lib/adapters/sqlserver/constraint.sqlserver.grounding.d.ts.map +1 -0
  81. package/dist/lib/adapters/sqlserver/index.d.ts +43 -0
  82. package/dist/lib/adapters/sqlserver/index.d.ts.map +1 -0
  83. package/dist/lib/adapters/sqlserver/index.js +1693 -0
  84. package/dist/lib/adapters/sqlserver/index.js.map +7 -0
  85. package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts +15 -0
  86. package/dist/lib/adapters/sqlserver/indexes.sqlserver.grounding.d.ts.map +1 -0
  87. package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts +11 -0
  88. package/dist/lib/adapters/sqlserver/info.sqlserver.grounding.d.ts.map +1 -0
  89. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts +14 -0
  90. package/dist/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.d.ts.map +1 -0
  91. package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts +11 -0
  92. package/dist/lib/adapters/sqlserver/row-count.sqlserver.grounding.d.ts.map +1 -0
  93. package/dist/lib/adapters/sqlserver/sqlserver.d.ts +26 -0
  94. package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -0
  95. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts +21 -0
  96. package/dist/lib/adapters/sqlserver/table.sqlserver.grounding.d.ts.map +1 -0
  97. package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts +16 -0
  98. package/dist/lib/adapters/sqlserver/view.sqlserver.grounding.d.ts.map +1 -0
  99. package/dist/lib/agents/suggestions.agents.d.ts +0 -2
  100. package/dist/lib/agents/suggestions.agents.d.ts.map +1 -1
  101. package/dist/lib/agents/teachables.agent.d.ts +0 -3
  102. package/dist/lib/agents/teachables.agent.d.ts.map +1 -1
  103. package/dist/lib/agents/text2sql.agent.d.ts +69 -29
  104. package/dist/lib/agents/text2sql.agent.d.ts.map +1 -1
  105. package/dist/lib/file-cache.d.ts +12 -0
  106. package/dist/lib/file-cache.d.ts.map +1 -0
  107. package/dist/lib/instructions.d.ts +3 -0
  108. package/dist/lib/instructions.d.ts.map +1 -0
  109. package/dist/lib/instructions.js +386 -0
  110. package/dist/lib/instructions.js.map +7 -0
  111. package/dist/lib/memory/memory.prompt.d.ts +3 -0
  112. package/dist/lib/memory/memory.prompt.d.ts.map +1 -0
  113. package/dist/lib/memory/memory.store.d.ts +5 -0
  114. package/dist/lib/memory/memory.store.d.ts.map +1 -0
  115. package/dist/lib/memory/sqlite.store.d.ts +14 -0
  116. package/dist/lib/memory/sqlite.store.d.ts.map +1 -0
  117. package/dist/lib/memory/store.d.ts +40 -0
  118. package/dist/lib/memory/store.d.ts.map +1 -0
  119. package/dist/lib/prompt.d.ts +1 -6
  120. package/dist/lib/prompt.d.ts.map +1 -1
  121. package/dist/lib/sql.d.ts +26 -35
  122. package/dist/lib/sql.d.ts.map +1 -1
  123. package/dist/lib/teach/teachables.d.ts +184 -13
  124. package/dist/lib/teach/teachables.d.ts.map +1 -1
  125. package/dist/lib/teach/teachings.d.ts.map +1 -1
  126. package/dist/lib/teach/xml.d.ts.map +1 -1
  127. package/package.json +38 -4
  128. package/dist/lib/adapters/postgres.d.ts +0 -31
  129. package/dist/lib/adapters/postgres.d.ts.map +0 -1
  130. package/dist/lib/adapters/resolveTables.spec.d.ts +0 -2
  131. package/dist/lib/adapters/resolveTables.spec.d.ts.map +0 -1
  132. package/dist/lib/adapters/sqlite.d.ts +0 -30
  133. package/dist/lib/adapters/sqlite.d.ts.map +0 -1
  134. package/dist/lib/adapters/sqlserver.d.ts +0 -31
  135. package/dist/lib/adapters/sqlserver.d.ts.map +0 -1
  136. package/dist/lib/agents/brief.agent.d.ts +0 -21
  137. package/dist/lib/agents/brief.agent.d.ts.map +0 -1
  138. package/dist/lib/memory/user-profile.d.ts +0 -39
  139. 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