@deepagents/text2sql 0.2.2 → 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 +929 -2746
  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 +27 -34
  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 -16
  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,1693 @@
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/sqlserver/column-stats.sqlserver.grounding.ts
575
+ var SqlServerColumnStatsGrounding = 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.escape(schema)}].[${this.#adapter.escape(table)}]`;
587
+ const columnIdentifier = `[${this.#adapter.escape(column.name)}]`;
588
+ const sql = `
589
+ SELECT
590
+ CAST(MIN(${columnIdentifier}) AS NVARCHAR(MAX)) AS min_value,
591
+ CAST(MAX(${columnIdentifier}) AS NVARCHAR(MAX)) 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|float|decimal|date|time|bit|money/.test(
617
+ normalized
618
+ );
619
+ }
620
+ };
621
+
622
+ // packages/text2sql/src/lib/adapters/sqlserver/constraint.sqlserver.grounding.ts
623
+ var SqlServerConstraintGrounding = 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 pkRows = await this.#adapter.runQuery(`
633
+ SELECT
634
+ kc.name AS constraint_name,
635
+ COL_NAME(ic.object_id, ic.column_id) AS column_name
636
+ FROM sys.key_constraints kc
637
+ JOIN sys.index_columns ic ON kc.parent_object_id = ic.object_id AND kc.unique_index_id = ic.index_id
638
+ JOIN sys.tables t ON kc.parent_object_id = t.object_id
639
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
640
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
641
+ AND t.name = '${this.#adapter.escapeString(table)}'
642
+ AND kc.type = 'PK'
643
+ ORDER BY ic.key_ordinal
644
+ `);
645
+ if (pkRows.length > 0) {
646
+ constraints2.push({
647
+ name: pkRows[0].constraint_name,
648
+ type: "PRIMARY_KEY",
649
+ columns: pkRows.map((r) => r.column_name)
650
+ });
651
+ }
652
+ const fkRows = await this.#adapter.runQuery(`
653
+ SELECT
654
+ fk.name AS constraint_name,
655
+ COL_NAME(fkc.parent_object_id, fkc.parent_column_id) AS column_name,
656
+ ref_s.name AS ref_schema,
657
+ ref_t.name AS ref_table,
658
+ COL_NAME(fkc.referenced_object_id, fkc.referenced_column_id) AS ref_column
659
+ FROM sys.foreign_keys fk
660
+ JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
661
+ JOIN sys.tables t ON fk.parent_object_id = t.object_id
662
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
663
+ JOIN sys.tables ref_t ON fk.referenced_object_id = ref_t.object_id
664
+ JOIN sys.schemas ref_s ON ref_t.schema_id = ref_s.schema_id
665
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
666
+ AND t.name = '${this.#adapter.escapeString(table)}'
667
+ ORDER BY fk.name, fkc.constraint_column_id
668
+ `);
669
+ const fkMap = /* @__PURE__ */ new Map();
670
+ for (const row of fkRows) {
671
+ const existing = fkMap.get(row.constraint_name);
672
+ if (existing) {
673
+ existing.columns.push(row.column_name);
674
+ existing.refColumns.push(row.ref_column);
675
+ } else {
676
+ fkMap.set(row.constraint_name, {
677
+ columns: [row.column_name],
678
+ refSchema: row.ref_schema,
679
+ refTable: row.ref_table,
680
+ refColumns: [row.ref_column]
681
+ });
682
+ }
683
+ }
684
+ for (const [name, data] of fkMap) {
685
+ constraints2.push({
686
+ name,
687
+ type: "FOREIGN_KEY",
688
+ columns: data.columns,
689
+ referencedTable: `${data.refSchema}.${data.refTable}`,
690
+ referencedColumns: data.refColumns
691
+ });
692
+ }
693
+ const checkRows = await this.#adapter.runQuery(`
694
+ SELECT
695
+ cc.name AS constraint_name,
696
+ cc.definition
697
+ FROM sys.check_constraints cc
698
+ JOIN sys.tables t ON cc.parent_object_id = t.object_id
699
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
700
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
701
+ AND t.name = '${this.#adapter.escapeString(table)}'
702
+ `);
703
+ for (const row of checkRows) {
704
+ constraints2.push({
705
+ name: row.constraint_name,
706
+ type: "CHECK",
707
+ definition: row.definition?.replace(/^\(/, "").replace(/\)$/, "")
708
+ });
709
+ }
710
+ const uniqueRows = await this.#adapter.runQuery(`
711
+ SELECT
712
+ i.name AS constraint_name,
713
+ COL_NAME(ic.object_id, ic.column_id) AS column_name
714
+ FROM sys.indexes i
715
+ JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
716
+ JOIN sys.tables t ON i.object_id = t.object_id
717
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
718
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
719
+ AND t.name = '${this.#adapter.escapeString(table)}'
720
+ AND i.is_unique_constraint = 1
721
+ ORDER BY i.name, ic.key_ordinal
722
+ `);
723
+ const uniqueMap = /* @__PURE__ */ new Map();
724
+ for (const row of uniqueRows) {
725
+ const existing = uniqueMap.get(row.constraint_name);
726
+ if (existing) {
727
+ existing.push(row.column_name);
728
+ } else {
729
+ uniqueMap.set(row.constraint_name, [row.column_name]);
730
+ }
731
+ }
732
+ for (const [name, columns] of uniqueMap) {
733
+ constraints2.push({
734
+ name,
735
+ type: "UNIQUE",
736
+ columns
737
+ });
738
+ }
739
+ const columnRows = await this.#adapter.runQuery(`
740
+ SELECT
741
+ c.name AS column_name,
742
+ dc.definition AS default_definition,
743
+ c.is_nullable
744
+ FROM sys.columns c
745
+ JOIN sys.tables t ON c.object_id = t.object_id
746
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
747
+ LEFT JOIN sys.default_constraints dc ON c.default_object_id = dc.object_id
748
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
749
+ AND t.name = '${this.#adapter.escapeString(table)}'
750
+ `);
751
+ const pkConstraint = constraints2.find((c) => c.type === "PRIMARY_KEY");
752
+ const pkColumns = new Set(pkConstraint?.columns ?? []);
753
+ for (const col of columnRows) {
754
+ if (col.is_nullable === 0 && !pkColumns.has(col.column_name)) {
755
+ constraints2.push({
756
+ name: `${table}_${col.column_name}_notnull`,
757
+ type: "NOT_NULL",
758
+ columns: [col.column_name]
759
+ });
760
+ }
761
+ if (col.default_definition != null) {
762
+ constraints2.push({
763
+ name: `${table}_${col.column_name}_default`,
764
+ type: "DEFAULT",
765
+ columns: [col.column_name],
766
+ defaultValue: col.default_definition.replace(/^\(/, "").replace(/\)$/, "")
767
+ });
768
+ }
769
+ }
770
+ return constraints2;
771
+ }
772
+ };
773
+
774
+ // packages/text2sql/src/lib/adapters/sqlserver/indexes.sqlserver.grounding.ts
775
+ var SqlServerIndexesGrounding = class extends IndexesGrounding {
776
+ #adapter;
777
+ #schemas;
778
+ constructor(adapter, config = {}) {
779
+ super(config);
780
+ this.#adapter = adapter;
781
+ this.#schemas = config.schemas;
782
+ }
783
+ async getIndexes(tableName) {
784
+ const { schema, table } = this.#adapter.parseTableName(tableName);
785
+ const rows = await this.#adapter.runQuery(`
786
+ SELECT
787
+ i.name AS index_name,
788
+ COL_NAME(ic.object_id, ic.column_id) AS column_name,
789
+ i.is_unique,
790
+ i.type_desc AS index_type,
791
+ ic.key_ordinal
792
+ FROM sys.indexes i
793
+ JOIN sys.index_columns ic
794
+ ON i.object_id = ic.object_id
795
+ AND i.index_id = ic.index_id
796
+ JOIN sys.tables t
797
+ ON i.object_id = t.object_id
798
+ JOIN sys.schemas s
799
+ ON t.schema_id = s.schema_id
800
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
801
+ AND t.name = '${this.#adapter.escapeString(table)}'
802
+ AND i.name IS NOT NULL
803
+ AND ic.is_included_column = 0
804
+ ORDER BY i.name, ic.key_ordinal
805
+ `);
806
+ return this.#groupIndexes(rows);
807
+ }
808
+ #groupIndexes(rows) {
809
+ const indexes2 = /* @__PURE__ */ new Map();
810
+ for (const row of rows) {
811
+ if (!row.index_name || !row.column_name) continue;
812
+ const existing = indexes2.get(row.index_name);
813
+ if (existing) {
814
+ existing.columns.push(row.column_name);
815
+ } else {
816
+ indexes2.set(row.index_name, {
817
+ name: row.index_name,
818
+ columns: [row.column_name],
819
+ unique: row.is_unique,
820
+ type: row.index_type
821
+ });
822
+ }
823
+ }
824
+ return Array.from(indexes2.values());
825
+ }
826
+ };
827
+
828
+ // packages/text2sql/src/lib/adapters/sqlserver/info.sqlserver.grounding.ts
829
+ var SqlServerInfoGrounding = class extends InfoGrounding {
830
+ #adapter;
831
+ constructor(adapter, config = {}) {
832
+ super(config);
833
+ this.#adapter = adapter;
834
+ }
835
+ async collectInfo() {
836
+ const [versionRows, dbRows] = await Promise.all([
837
+ this.#adapter.runQuery(
838
+ "SELECT @@VERSION AS version"
839
+ ),
840
+ this.#adapter.runQuery("SELECT DB_NAME() AS db")
841
+ ]);
842
+ return {
843
+ dialect: "sqlserver",
844
+ version: versionRows[0]?.version,
845
+ database: dbRows[0]?.db
846
+ };
847
+ }
848
+ };
849
+
850
+ // packages/text2sql/src/lib/adapters/sqlserver/low-cardinality.sqlserver.grounding.ts
851
+ var LOW_CARDINALITY_LIMIT = 20;
852
+ var SqlServerLowCardinalityGrounding = class extends LowCardinalityGrounding {
853
+ #adapter;
854
+ constructor(adapter, config = {}) {
855
+ super(config);
856
+ this.#adapter = adapter;
857
+ }
858
+ async collectLowCardinality(tableName, column) {
859
+ const { schema, table } = this.#adapter.parseTableName(tableName);
860
+ const tableIdentifier = `[${this.#adapter.escape(schema)}].[${this.#adapter.escape(table)}]`;
861
+ const columnIdentifier = `[${this.#adapter.escape(column.name)}]`;
862
+ const limit = LOW_CARDINALITY_LIMIT + 1;
863
+ const sql = `
864
+ SELECT DISTINCT TOP ${limit} CAST(${columnIdentifier} AS NVARCHAR(MAX)) AS value
865
+ FROM ${tableIdentifier}
866
+ WHERE ${columnIdentifier} IS NOT NULL
867
+ `;
868
+ const rows = await this.#adapter.runQuery(sql);
869
+ if (!rows.length || rows.length > LOW_CARDINALITY_LIMIT) {
870
+ return void 0;
871
+ }
872
+ const values = [];
873
+ for (const row of rows) {
874
+ if (row.value == null) {
875
+ return void 0;
876
+ }
877
+ values.push(row.value);
878
+ }
879
+ if (!values.length) {
880
+ return void 0;
881
+ }
882
+ return { kind: "LowCardinality", values };
883
+ }
884
+ };
885
+
886
+ // packages/text2sql/src/lib/adapters/sqlserver/row-count.sqlserver.grounding.ts
887
+ var SqlServerRowCountGrounding = class extends RowCountGrounding {
888
+ #adapter;
889
+ constructor(adapter, config = {}) {
890
+ super(config);
891
+ this.#adapter = adapter;
892
+ }
893
+ async getRowCount(tableName) {
894
+ const { schema, table } = this.#adapter.parseTableName(tableName);
895
+ const tableIdentifier = `[${this.#adapter.escape(schema)}].[${this.#adapter.escape(table)}]`;
896
+ const rows = await this.#adapter.runQuery(
897
+ `SELECT COUNT(*) as count FROM ${tableIdentifier}`
898
+ );
899
+ return this.#adapter.toNumber(rows[0]?.count);
900
+ }
901
+ };
902
+
903
+ // packages/text2sql/src/lib/adapters/sqlserver/sqlserver.ts
904
+ var SQL_SERVER_ERROR_MAP = {
905
+ "208": {
906
+ type: "MISSING_TABLE",
907
+ hint: "Check that the table exists and include the schema prefix (e.g., dbo.TableName)."
908
+ },
909
+ "207": {
910
+ type: "INVALID_COLUMN",
911
+ hint: "Verify the column exists on the table and that any aliases are referenced correctly."
912
+ },
913
+ "156": {
914
+ type: "SYNTAX_ERROR",
915
+ hint: "There is a SQL syntax error. Review keywords, punctuation, and clauses such as GROUP BY."
916
+ },
917
+ "4104": {
918
+ type: "INVALID_COLUMN",
919
+ hint: "Columns must be qualified with table aliases when ambiguous. Double-check join aliases."
920
+ },
921
+ "1934": {
922
+ type: "CONSTRAINT_ERROR",
923
+ hint: "The query violates a constraint. Re-check join logic and filtering."
924
+ }
925
+ };
926
+ var LOW_CARDINALITY_LIMIT2 = 20;
927
+ function getErrorCode(error) {
928
+ if (typeof error === "object" && error !== null && "number" in error && typeof error.number === "number") {
929
+ return String(error.number);
930
+ }
931
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
932
+ return error.code;
933
+ }
934
+ return null;
935
+ }
936
+ function formatSqlServerError(sql, error) {
937
+ const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error occurred";
938
+ const code = getErrorCode(error);
939
+ const metadata = code ? SQL_SERVER_ERROR_MAP[code] : void 0;
940
+ if (metadata) {
941
+ return {
942
+ error: errorMessage,
943
+ error_type: metadata.type,
944
+ suggestion: metadata.hint,
945
+ sql_attempted: sql
946
+ };
947
+ }
948
+ return {
949
+ error: errorMessage,
950
+ error_type: "UNKNOWN_ERROR",
951
+ suggestion: "Review the query and try again",
952
+ sql_attempted: sql
953
+ };
954
+ }
955
+ var SqlServer = class extends Adapter {
956
+ #options;
957
+ grounding;
958
+ defaultSchema = "dbo";
959
+ systemSchemas = ["INFORMATION_SCHEMA", "sys"];
960
+ constructor(options) {
961
+ super();
962
+ if (!options || typeof options.execute !== "function") {
963
+ throw new Error("SqlServer adapter requires an execute function.");
964
+ }
965
+ this.#options = {
966
+ ...options,
967
+ schemas: options.schemas?.length ? options.schemas : void 0
968
+ };
969
+ this.grounding = options.grounding;
970
+ }
971
+ async execute(sql) {
972
+ return this.#options.execute(sql);
973
+ }
974
+ async validate(sql) {
975
+ const validator = this.#options.validate ?? (async (text) => {
976
+ await this.#options.execute(
977
+ `SET PARSEONLY ON; ${text}; SET PARSEONLY OFF;`
978
+ );
979
+ });
980
+ try {
981
+ return await validator(sql);
982
+ } catch (error) {
983
+ return JSON.stringify(formatSqlServerError(sql, error));
984
+ }
985
+ }
986
+ async runQuery(sql) {
987
+ return this.#runIntrospectionQuery(sql);
988
+ }
989
+ quoteIdentifier(name) {
990
+ return `[${name.replace(/]/g, "]]")}]`;
991
+ }
992
+ escape(value) {
993
+ return value.replace(/]/g, "]]");
994
+ }
995
+ async #loadTables() {
996
+ const rows = await this.#runIntrospectionQuery(`
997
+ SELECT
998
+ c.TABLE_SCHEMA AS table_schema,
999
+ c.TABLE_NAME AS table_name,
1000
+ c.COLUMN_NAME AS column_name,
1001
+ c.DATA_TYPE AS data_type
1002
+ FROM INFORMATION_SCHEMA.COLUMNS AS c
1003
+ JOIN INFORMATION_SCHEMA.TABLES AS t
1004
+ ON c.TABLE_SCHEMA = t.TABLE_SCHEMA
1005
+ AND c.TABLE_NAME = t.TABLE_NAME
1006
+ WHERE t.TABLE_TYPE = 'BASE TABLE'
1007
+ ${this.#buildSchemaFilter("c.TABLE_SCHEMA")}
1008
+ ORDER BY c.TABLE_SCHEMA, c.TABLE_NAME, c.ORDINAL_POSITION
1009
+ `);
1010
+ const tables2 = /* @__PURE__ */ new Map();
1011
+ for (const row of rows) {
1012
+ if (!row.table_name) {
1013
+ continue;
1014
+ }
1015
+ const schema = row.table_schema ?? "dbo";
1016
+ const tableName = row.table_name;
1017
+ const qualifiedName = `${schema}.${tableName}`;
1018
+ const table = tables2.get(qualifiedName) ?? {
1019
+ name: qualifiedName,
1020
+ schema,
1021
+ rawName: tableName,
1022
+ columns: []
1023
+ };
1024
+ table.columns.push({
1025
+ name: row.column_name ?? "unknown",
1026
+ type: row.data_type ?? "unknown"
1027
+ });
1028
+ tables2.set(qualifiedName, table);
1029
+ }
1030
+ return Array.from(tables2.values());
1031
+ }
1032
+ async #loadRelationships(filterTableNames) {
1033
+ const tableFilter = this.#buildTableNamesFilter(filterTableNames);
1034
+ const rows = await this.#runIntrospectionQuery(`
1035
+ SELECT
1036
+ fk.CONSTRAINT_NAME AS constraint_name,
1037
+ fk.TABLE_SCHEMA AS table_schema,
1038
+ fk.TABLE_NAME AS table_name,
1039
+ fk.COLUMN_NAME AS column_name,
1040
+ pk.TABLE_SCHEMA AS referenced_table_schema,
1041
+ pk.TABLE_NAME AS referenced_table_name,
1042
+ pk.COLUMN_NAME AS referenced_column_name
1043
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
1044
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS fk
1045
+ ON fk.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
1046
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS pk
1047
+ ON pk.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
1048
+ AND pk.ORDINAL_POSITION = fk.ORDINAL_POSITION
1049
+ WHERE 1 = 1
1050
+ ${this.#buildSchemaFilter("fk.TABLE_SCHEMA")}
1051
+ ${tableFilter}
1052
+ ORDER BY fk.TABLE_SCHEMA, fk.TABLE_NAME, fk.CONSTRAINT_NAME, fk.ORDINAL_POSITION
1053
+ `);
1054
+ const relationships = /* @__PURE__ */ new Map();
1055
+ for (const row of rows) {
1056
+ if (!row.constraint_name || !row.table_name || !row.referenced_table_name) {
1057
+ continue;
1058
+ }
1059
+ const schema = row.table_schema ?? "dbo";
1060
+ const referencedSchema = row.referenced_table_schema ?? "dbo";
1061
+ const key = `${schema}.${row.table_name}:${row.constraint_name}`;
1062
+ const relationship = relationships.get(key) ?? {
1063
+ table: `${schema}.${row.table_name}`,
1064
+ from: [],
1065
+ referenced_table: `${referencedSchema}.${row.referenced_table_name}`,
1066
+ to: []
1067
+ };
1068
+ relationship.from.push(row.column_name ?? "unknown");
1069
+ relationship.to.push(row.referenced_column_name ?? "unknown");
1070
+ relationships.set(key, relationship);
1071
+ }
1072
+ return Array.from(relationships.values());
1073
+ }
1074
+ async #annotateRowCounts(tables2, onProgress) {
1075
+ const total = tables2.length;
1076
+ for (let i = 0; i < tables2.length; i++) {
1077
+ const table = tables2[i];
1078
+ const tableIdentifier = this.#formatQualifiedTableName(table);
1079
+ onProgress?.({
1080
+ phase: "row_counts",
1081
+ message: `Counting rows in ${table.name}...`,
1082
+ current: i + 1,
1083
+ total
1084
+ });
1085
+ try {
1086
+ const rows = await this.#runIntrospectionQuery(`SELECT COUNT(*) as count FROM ${tableIdentifier}`);
1087
+ const rowCount2 = this.#toNumber(rows[0]?.count);
1088
+ if (rowCount2 != null) {
1089
+ table.rowCount = rowCount2;
1090
+ table.sizeHint = this.#classifyRowCount(rowCount2);
1091
+ }
1092
+ } catch {
1093
+ continue;
1094
+ }
1095
+ }
1096
+ }
1097
+ /**
1098
+ * @deprecated Primary keys are now handled via constraints grounding.
1099
+ * This method is kept for backward compatibility but does nothing.
1100
+ */
1101
+ async #annotatePrimaryKeys(_tables) {
1102
+ }
1103
+ async #annotateIndexes(tables2) {
1104
+ if (!tables2.length) {
1105
+ return;
1106
+ }
1107
+ const tableMap = new Map(tables2.map((table) => [table.name, table]));
1108
+ const rows = await this.#runIntrospectionQuery(`
1109
+ SELECT
1110
+ sch.name AS schema_name,
1111
+ t.name AS table_name,
1112
+ ind.name AS index_name,
1113
+ col.name AS column_name,
1114
+ ind.is_unique,
1115
+ ind.is_primary_key,
1116
+ ind.type_desc,
1117
+ ic.is_included_column
1118
+ FROM sys.indexes AS ind
1119
+ JOIN sys.tables AS t ON ind.object_id = t.object_id
1120
+ JOIN sys.schemas AS sch ON t.schema_id = sch.schema_id
1121
+ JOIN sys.index_columns AS ic
1122
+ ON ind.object_id = ic.object_id
1123
+ AND ind.index_id = ic.index_id
1124
+ JOIN sys.columns AS col
1125
+ ON ic.object_id = col.object_id
1126
+ AND ic.column_id = col.column_id
1127
+ WHERE ind.is_hypothetical = 0
1128
+ AND ind.name IS NOT NULL
1129
+ ${this.#buildSchemaFilter("sch.name")}
1130
+ ORDER BY sch.name, t.name, ind.name, ic.key_ordinal
1131
+ `);
1132
+ const indexMap = /* @__PURE__ */ new Map();
1133
+ for (const row of rows) {
1134
+ if (!row.table_name || !row.index_name) {
1135
+ continue;
1136
+ }
1137
+ const schema = row.schema_name ?? "dbo";
1138
+ const tableKey = `${schema}.${row.table_name}`;
1139
+ const table = tableMap.get(tableKey);
1140
+ if (!table) {
1141
+ continue;
1142
+ }
1143
+ const indexKey = `${tableKey}:${row.index_name}`;
1144
+ let index = indexMap.get(indexKey);
1145
+ if (!index) {
1146
+ index = {
1147
+ name: row.index_name,
1148
+ columns: [],
1149
+ unique: Boolean(row.is_unique),
1150
+ type: row.type_desc ?? void 0
1151
+ };
1152
+ indexMap.set(indexKey, index);
1153
+ if (!table.indexes) {
1154
+ table.indexes = [];
1155
+ }
1156
+ table.indexes.push(index);
1157
+ }
1158
+ if (row.is_included_column) {
1159
+ continue;
1160
+ }
1161
+ if (row.column_name) {
1162
+ index.columns.push(row.column_name);
1163
+ const column = table.columns.find(
1164
+ (col) => col.name === row.column_name
1165
+ );
1166
+ if (column) {
1167
+ column.isIndexed = true;
1168
+ }
1169
+ }
1170
+ }
1171
+ }
1172
+ async #annotateColumnStats(tables2, onProgress) {
1173
+ if (!tables2.length) {
1174
+ return;
1175
+ }
1176
+ const total = tables2.length;
1177
+ for (let i = 0; i < tables2.length; i++) {
1178
+ const table = tables2[i];
1179
+ const tableIdentifier = this.#formatQualifiedTableName(table);
1180
+ onProgress?.({
1181
+ phase: "column_stats",
1182
+ message: `Collecting stats for ${table.name}...`,
1183
+ current: i + 1,
1184
+ total
1185
+ });
1186
+ for (const column of table.columns) {
1187
+ if (!this.#shouldCollectStats(column.type)) {
1188
+ continue;
1189
+ }
1190
+ const columnIdentifier = this.#quoteIdentifier(column.name);
1191
+ const sql = `
1192
+ SELECT
1193
+ CONVERT(NVARCHAR(4000), MIN(${columnIdentifier})) AS min_value,
1194
+ CONVERT(NVARCHAR(4000), MAX(${columnIdentifier})) AS max_value,
1195
+ AVG(CASE WHEN ${columnIdentifier} IS NULL THEN 1.0 ELSE 0.0 END) AS null_fraction
1196
+ FROM ${tableIdentifier}
1197
+ `;
1198
+ try {
1199
+ const rows = await this.#runIntrospectionQuery(sql);
1200
+ if (!rows.length) {
1201
+ continue;
1202
+ }
1203
+ const nullFraction = this.#toNumber(rows[0]?.null_fraction);
1204
+ if (rows[0]?.min_value != null || rows[0]?.max_value != null || nullFraction != null) {
1205
+ column.stats = {
1206
+ min: rows[0]?.min_value ?? void 0,
1207
+ max: rows[0]?.max_value ?? void 0,
1208
+ nullFraction: nullFraction != null && Number.isFinite(nullFraction) ? Math.max(0, Math.min(1, nullFraction)) : void 0
1209
+ };
1210
+ }
1211
+ } catch {
1212
+ continue;
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+ async #annotateLowCardinalityColumns(tables2, onProgress) {
1218
+ const total = tables2.length;
1219
+ for (let i = 0; i < tables2.length; i++) {
1220
+ const table = tables2[i];
1221
+ const tableIdentifier = this.#formatQualifiedTableName(table);
1222
+ onProgress?.({
1223
+ phase: "low_cardinality",
1224
+ message: `Analyzing cardinality in ${table.name}...`,
1225
+ current: i + 1,
1226
+ total
1227
+ });
1228
+ for (const column of table.columns) {
1229
+ const columnIdentifier = this.#quoteIdentifier(column.name);
1230
+ const limit = LOW_CARDINALITY_LIMIT2 + 1;
1231
+ const sql = `
1232
+ SELECT DISTINCT TOP (${limit}) ${columnIdentifier} AS value
1233
+ FROM ${tableIdentifier}
1234
+ WHERE ${columnIdentifier} IS NOT NULL
1235
+ `;
1236
+ let rows = [];
1237
+ try {
1238
+ rows = await this.#runIntrospectionQuery(sql);
1239
+ } catch {
1240
+ continue;
1241
+ }
1242
+ if (!rows.length || rows.length > LOW_CARDINALITY_LIMIT2) {
1243
+ continue;
1244
+ }
1245
+ const values = [];
1246
+ let shouldSkip = false;
1247
+ for (const row of rows) {
1248
+ const formatted = this.#normalizeValue(row.value);
1249
+ if (formatted == null) {
1250
+ shouldSkip = true;
1251
+ break;
1252
+ }
1253
+ values.push(formatted);
1254
+ }
1255
+ if (shouldSkip || !values.length) {
1256
+ continue;
1257
+ }
1258
+ column.kind = "LowCardinality";
1259
+ column.values = values;
1260
+ }
1261
+ }
1262
+ }
1263
+ #buildSchemaFilter(columnName) {
1264
+ if (this.#options.schemas && this.#options.schemas.length > 0) {
1265
+ const values = this.#options.schemas.map((schema) => `'${schema.replace(/'/g, "''")}'`).join(", ");
1266
+ return `AND ${columnName} IN (${values})`;
1267
+ }
1268
+ return `AND ${columnName} NOT IN ('INFORMATION_SCHEMA', 'sys')`;
1269
+ }
1270
+ /**
1271
+ * Build a filter for table names (qualified as schema.table).
1272
+ * Matches if either the source table or referenced table is in the list.
1273
+ */
1274
+ #buildTableNamesFilter(tableNames) {
1275
+ if (!tableNames || tableNames.length === 0) {
1276
+ return "";
1277
+ }
1278
+ const conditions = [];
1279
+ for (const name of tableNames) {
1280
+ const escaped = name.replace(/'/g, "''");
1281
+ if (name.includes(".")) {
1282
+ const [schema, ...rest] = name.split(".");
1283
+ const tableName = rest.join(".");
1284
+ const escapedSchema = schema.replace(/'/g, "''");
1285
+ const escapedTable = tableName.replace(/'/g, "''");
1286
+ conditions.push(
1287
+ `(fk.TABLE_SCHEMA = '${escapedSchema}' AND fk.TABLE_NAME = '${escapedTable}')`
1288
+ );
1289
+ conditions.push(
1290
+ `(pk.TABLE_SCHEMA = '${escapedSchema}' AND pk.TABLE_NAME = '${escapedTable}')`
1291
+ );
1292
+ } else {
1293
+ conditions.push(`fk.TABLE_NAME = '${escaped}'`);
1294
+ conditions.push(`pk.TABLE_NAME = '${escaped}'`);
1295
+ }
1296
+ }
1297
+ return `AND (${conditions.join(" OR ")})`;
1298
+ }
1299
+ async #runIntrospectionQuery(sql) {
1300
+ const result = await this.#options.execute(sql);
1301
+ if (Array.isArray(result)) {
1302
+ return result;
1303
+ }
1304
+ if (result && typeof result === "object") {
1305
+ if ("rows" in result && Array.isArray(result.rows)) {
1306
+ return result.rows;
1307
+ }
1308
+ if ("recordset" in result && Array.isArray(result.recordset)) {
1309
+ return result.recordset;
1310
+ }
1311
+ if ("recordsets" in result && Array.isArray(result.recordsets) && Array.isArray(result.recordsets[0])) {
1312
+ return result.recordsets[0];
1313
+ }
1314
+ }
1315
+ throw new Error(
1316
+ "SqlServer adapter execute() must return an array of rows or an object with rows/recordset properties when introspecting."
1317
+ );
1318
+ }
1319
+ #quoteIdentifier(name) {
1320
+ return `[${name.replace(/]/g, "]]")}]`;
1321
+ }
1322
+ #formatQualifiedTableName(table) {
1323
+ if (table.schema && table.rawName) {
1324
+ return `${this.#quoteIdentifier(table.schema)}.${this.#quoteIdentifier(table.rawName)}`;
1325
+ }
1326
+ if (table.name.includes(".")) {
1327
+ const [schemaPart, ...rest] = table.name.split(".");
1328
+ const tablePart = rest.join(".") || schemaPart;
1329
+ if (rest.length === 0) {
1330
+ return this.#quoteIdentifier(schemaPart);
1331
+ }
1332
+ return `${this.#quoteIdentifier(schemaPart)}.${this.#quoteIdentifier(tablePart)}`;
1333
+ }
1334
+ return this.#quoteIdentifier(table.name);
1335
+ }
1336
+ #toNumber(value) {
1337
+ if (typeof value === "number" && Number.isFinite(value)) {
1338
+ return value;
1339
+ }
1340
+ if (typeof value === "bigint") {
1341
+ return Number(value);
1342
+ }
1343
+ if (typeof value === "string" && value.trim() !== "") {
1344
+ const parsed = Number(value);
1345
+ return Number.isFinite(parsed) ? parsed : null;
1346
+ }
1347
+ return null;
1348
+ }
1349
+ #classifyRowCount(count) {
1350
+ if (count < 100) {
1351
+ return "tiny";
1352
+ }
1353
+ if (count < 1e3) {
1354
+ return "small";
1355
+ }
1356
+ if (count < 1e4) {
1357
+ return "medium";
1358
+ }
1359
+ if (count < 1e5) {
1360
+ return "large";
1361
+ }
1362
+ return "huge";
1363
+ }
1364
+ #shouldCollectStats(type) {
1365
+ if (!type) {
1366
+ return false;
1367
+ }
1368
+ const normalized = type.toLowerCase();
1369
+ return /int|numeric|decimal|float|real|money|date|time|timestamp|bool/.test(
1370
+ normalized
1371
+ );
1372
+ }
1373
+ #normalizeValue(value) {
1374
+ if (value === null || value === void 0) {
1375
+ return null;
1376
+ }
1377
+ if (typeof value === "string") {
1378
+ return value;
1379
+ }
1380
+ if (typeof value === "number" || typeof value === "bigint") {
1381
+ return String(value);
1382
+ }
1383
+ if (typeof value === "boolean") {
1384
+ return value ? "true" : "false";
1385
+ }
1386
+ if (value instanceof Date) {
1387
+ return value.toISOString();
1388
+ }
1389
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
1390
+ return value.toString("utf-8");
1391
+ }
1392
+ return null;
1393
+ }
1394
+ };
1395
+
1396
+ // packages/text2sql/src/lib/adapters/sqlserver/table.sqlserver.grounding.ts
1397
+ var SqlServerTableGrounding = class extends TableGrounding {
1398
+ #adapter;
1399
+ #schemas;
1400
+ constructor(adapter, config = {}) {
1401
+ super(config);
1402
+ this.#adapter = adapter;
1403
+ this.#schemas = config.schemas;
1404
+ }
1405
+ async getAllTableNames() {
1406
+ const rows = await this.#adapter.runQuery(`
1407
+ SELECT TABLE_SCHEMA + '.' + TABLE_NAME AS name
1408
+ FROM INFORMATION_SCHEMA.TABLES
1409
+ WHERE TABLE_TYPE = 'BASE TABLE'
1410
+ ${this.#adapter.buildSchemaFilter("TABLE_SCHEMA", this.#schemas)}
1411
+ ORDER BY name
1412
+ `);
1413
+ return rows.map((r) => r.name);
1414
+ }
1415
+ async getTable(tableName) {
1416
+ const { schema, table } = this.#adapter.parseTableName(tableName);
1417
+ const columns = await this.#adapter.runQuery(`
1418
+ SELECT COLUMN_NAME AS column_name, DATA_TYPE AS data_type
1419
+ FROM INFORMATION_SCHEMA.COLUMNS
1420
+ WHERE TABLE_SCHEMA = '${this.#adapter.escapeString(schema)}'
1421
+ AND TABLE_NAME = '${this.#adapter.escapeString(table)}'
1422
+ ORDER BY ORDINAL_POSITION
1423
+ `);
1424
+ return {
1425
+ name: tableName,
1426
+ schema,
1427
+ rawName: table,
1428
+ columns: columns.map((col) => ({
1429
+ name: col.column_name ?? "unknown",
1430
+ type: col.data_type ?? "unknown"
1431
+ }))
1432
+ };
1433
+ }
1434
+ async findOutgoingRelations(tableName) {
1435
+ const { schema, table } = this.#adapter.parseTableName(tableName);
1436
+ const rows = await this.#adapter.runQuery(`
1437
+ SELECT
1438
+ fk.CONSTRAINT_NAME AS constraint_name,
1439
+ fk.TABLE_SCHEMA AS table_schema,
1440
+ fk.TABLE_NAME AS table_name,
1441
+ fk.COLUMN_NAME AS column_name,
1442
+ pk.TABLE_SCHEMA AS referenced_table_schema,
1443
+ pk.TABLE_NAME AS referenced_table_name,
1444
+ pk.COLUMN_NAME AS referenced_column_name
1445
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
1446
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS fk
1447
+ ON fk.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
1448
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS pk
1449
+ ON pk.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
1450
+ AND pk.ORDINAL_POSITION = fk.ORDINAL_POSITION
1451
+ WHERE fk.TABLE_SCHEMA = '${this.#adapter.escapeString(schema)}'
1452
+ AND fk.TABLE_NAME = '${this.#adapter.escapeString(table)}'
1453
+ ORDER BY fk.CONSTRAINT_NAME, fk.ORDINAL_POSITION
1454
+ `);
1455
+ return this.#groupRelationships(rows);
1456
+ }
1457
+ async findIncomingRelations(tableName) {
1458
+ const { schema, table } = this.#adapter.parseTableName(tableName);
1459
+ const rows = await this.#adapter.runQuery(`
1460
+ SELECT
1461
+ fk.CONSTRAINT_NAME AS constraint_name,
1462
+ fk.TABLE_SCHEMA AS table_schema,
1463
+ fk.TABLE_NAME AS table_name,
1464
+ fk.COLUMN_NAME AS column_name,
1465
+ pk.TABLE_SCHEMA AS referenced_table_schema,
1466
+ pk.TABLE_NAME AS referenced_table_name,
1467
+ pk.COLUMN_NAME AS referenced_column_name
1468
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS rc
1469
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS fk
1470
+ ON fk.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
1471
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS pk
1472
+ ON pk.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
1473
+ AND pk.ORDINAL_POSITION = fk.ORDINAL_POSITION
1474
+ WHERE pk.TABLE_SCHEMA = '${this.#adapter.escapeString(schema)}'
1475
+ AND pk.TABLE_NAME = '${this.#adapter.escapeString(table)}'
1476
+ ORDER BY fk.CONSTRAINT_NAME, fk.ORDINAL_POSITION
1477
+ `);
1478
+ return this.#groupRelationships(rows);
1479
+ }
1480
+ #groupRelationships(rows) {
1481
+ const relationships = /* @__PURE__ */ new Map();
1482
+ const defaultSchema = this.#adapter.defaultSchema ?? "dbo";
1483
+ for (const row of rows) {
1484
+ if (!row.constraint_name || !row.table_name || !row.referenced_table_name) {
1485
+ continue;
1486
+ }
1487
+ const schema = row.table_schema ?? defaultSchema;
1488
+ const referencedSchema = row.referenced_table_schema ?? defaultSchema;
1489
+ const key = `${schema}.${row.table_name}:${row.constraint_name}`;
1490
+ const relationship = relationships.get(key) ?? {
1491
+ table: `${schema}.${row.table_name}`,
1492
+ from: [],
1493
+ referenced_table: `${referencedSchema}.${row.referenced_table_name}`,
1494
+ to: []
1495
+ };
1496
+ relationship.from.push(row.column_name ?? "unknown");
1497
+ relationship.to.push(row.referenced_column_name ?? "unknown");
1498
+ relationships.set(key, relationship);
1499
+ }
1500
+ return Array.from(relationships.values());
1501
+ }
1502
+ };
1503
+
1504
+ // packages/text2sql/src/lib/adapters/groundings/view.grounding.ts
1505
+ var ViewGrounding = class extends AbstractGrounding {
1506
+ #filter;
1507
+ constructor(config = {}) {
1508
+ super("views");
1509
+ this.#filter = config.filter;
1510
+ }
1511
+ /**
1512
+ * Execute the grounding process.
1513
+ * Writes discovered views to the context.
1514
+ */
1515
+ async execute(ctx) {
1516
+ const viewNames = await this.applyFilter();
1517
+ const views2 = await Promise.all(
1518
+ viewNames.map((name) => this.getView(name))
1519
+ );
1520
+ ctx.views.push(...views2);
1521
+ return () => this.#describe(views2);
1522
+ }
1523
+ #describe(views2) {
1524
+ if (!views2.length) {
1525
+ return "No views available.";
1526
+ }
1527
+ return views2.map((view) => {
1528
+ const columns = view.columns.map((column) => {
1529
+ const annotations = [];
1530
+ if (column.kind === "LowCardinality" && column.values?.length) {
1531
+ annotations.push(`LowCardinality: ${column.values.join(", ")}`);
1532
+ }
1533
+ if (column.stats) {
1534
+ const statParts = [];
1535
+ if (column.stats.min != null || column.stats.max != null) {
1536
+ const minText = column.stats.min ?? "n/a";
1537
+ const maxText = column.stats.max ?? "n/a";
1538
+ statParts.push(`range ${minText} \u2192 ${maxText}`);
1539
+ }
1540
+ if (column.stats.nullFraction != null && Number.isFinite(column.stats.nullFraction)) {
1541
+ const percent = Math.round(column.stats.nullFraction * 1e3) / 10;
1542
+ statParts.push(`null\u2248${percent}%`);
1543
+ }
1544
+ if (statParts.length) {
1545
+ annotations.push(statParts.join(", "));
1546
+ }
1547
+ }
1548
+ const annotationText = annotations.length ? ` [${annotations.join(", ")}]` : "";
1549
+ return ` - ${column.name} (${column.type})${annotationText}`;
1550
+ }).join("\n");
1551
+ const definition = view.definition ? `
1552
+ Definition: ${view.definition.length > 200 ? view.definition.slice(0, 200) + "..." : view.definition}` : "";
1553
+ return `- View: ${view.name}${definition}
1554
+ Columns:
1555
+ ${columns}`;
1556
+ }).join("\n\n");
1557
+ }
1558
+ /**
1559
+ * Apply the filter to get view names.
1560
+ * If filter is an explicit array, skip querying all view names.
1561
+ */
1562
+ async applyFilter() {
1563
+ const filter = this.#filter;
1564
+ if (Array.isArray(filter)) {
1565
+ return filter;
1566
+ }
1567
+ const names = await this.getAllViewNames();
1568
+ if (!filter) {
1569
+ return names;
1570
+ }
1571
+ if (filter instanceof RegExp) {
1572
+ return names.filter((name) => filter.test(name));
1573
+ }
1574
+ return names.filter(filter);
1575
+ }
1576
+ };
1577
+
1578
+ // packages/text2sql/src/lib/adapters/sqlserver/view.sqlserver.grounding.ts
1579
+ var SqlServerViewGrounding = class extends ViewGrounding {
1580
+ #adapter;
1581
+ #schemas;
1582
+ constructor(adapter, config = {}) {
1583
+ super(config);
1584
+ this.#adapter = adapter;
1585
+ this.#schemas = config.schemas;
1586
+ }
1587
+ async getAllViewNames() {
1588
+ const rows = await this.#adapter.runQuery(`
1589
+ SELECT TABLE_SCHEMA + '.' + TABLE_NAME AS name
1590
+ FROM INFORMATION_SCHEMA.VIEWS
1591
+ WHERE 1=1
1592
+ ${this.#adapter.buildSchemaFilter("TABLE_SCHEMA", this.#schemas)}
1593
+ ORDER BY name
1594
+ `);
1595
+ return rows.map((r) => r.name);
1596
+ }
1597
+ async getView(viewName) {
1598
+ const { schema, table: view } = this.#adapter.parseTableName(viewName);
1599
+ const defRows = await this.#adapter.runQuery(`
1600
+ SELECT m.definition
1601
+ FROM sys.views v
1602
+ JOIN sys.schemas s ON v.schema_id = s.schema_id
1603
+ JOIN sys.sql_modules m ON v.object_id = m.object_id
1604
+ WHERE s.name = '${this.#adapter.escapeString(schema)}'
1605
+ AND v.name = '${this.#adapter.escapeString(view)}'
1606
+ `);
1607
+ const columns = await this.#adapter.runQuery(`
1608
+ SELECT COLUMN_NAME AS column_name, DATA_TYPE AS data_type
1609
+ FROM INFORMATION_SCHEMA.COLUMNS
1610
+ WHERE TABLE_SCHEMA = '${this.#adapter.escapeString(schema)}'
1611
+ AND TABLE_NAME = '${this.#adapter.escapeString(view)}'
1612
+ ORDER BY ORDINAL_POSITION
1613
+ `);
1614
+ return {
1615
+ name: viewName,
1616
+ schema,
1617
+ rawName: view,
1618
+ definition: defRows[0]?.definition ?? void 0,
1619
+ columns: columns.map((col) => ({
1620
+ name: col.column_name ?? "unknown",
1621
+ type: col.data_type ?? "unknown"
1622
+ }))
1623
+ };
1624
+ }
1625
+ };
1626
+
1627
+ // packages/text2sql/src/lib/adapters/sqlserver/index.ts
1628
+ function tables(config = {}) {
1629
+ return (adapter) => new SqlServerTableGrounding(adapter, config);
1630
+ }
1631
+ function info(config = {}) {
1632
+ return (adapter) => new SqlServerInfoGrounding(adapter, config);
1633
+ }
1634
+ function views(config = {}) {
1635
+ return (adapter) => {
1636
+ return new SqlServerViewGrounding(adapter, config);
1637
+ };
1638
+ }
1639
+ function columnStats(config = {}) {
1640
+ return (adapter) => {
1641
+ return new SqlServerColumnStatsGrounding(adapter, config);
1642
+ };
1643
+ }
1644
+ function lowCardinality(config = {}) {
1645
+ return (adapter) => {
1646
+ return new SqlServerLowCardinalityGrounding(adapter, config);
1647
+ };
1648
+ }
1649
+ function indexes(config = {}) {
1650
+ return (adapter) => {
1651
+ return new SqlServerIndexesGrounding(adapter, config);
1652
+ };
1653
+ }
1654
+ function rowCount(config = {}) {
1655
+ return (adapter) => {
1656
+ return new SqlServerRowCountGrounding(adapter, config);
1657
+ };
1658
+ }
1659
+ function constraints(config = {}) {
1660
+ return (adapter) => {
1661
+ return new SqlServerConstraintGrounding(adapter, config);
1662
+ };
1663
+ }
1664
+ function report(config = {}) {
1665
+ return (adapter) => new ReportGrounding(adapter, config);
1666
+ }
1667
+ var sqlserver_default = {
1668
+ tables,
1669
+ info,
1670
+ views,
1671
+ columnStats,
1672
+ lowCardinality,
1673
+ indexes,
1674
+ rowCount,
1675
+ constraints,
1676
+ report,
1677
+ SqlServer
1678
+ };
1679
+ export {
1680
+ SqlServer,
1681
+ columnStats,
1682
+ constraints,
1683
+ sqlserver_default as default,
1684
+ formatSqlServerError,
1685
+ indexes,
1686
+ info,
1687
+ lowCardinality,
1688
+ report,
1689
+ rowCount,
1690
+ tables,
1691
+ views
1692
+ };
1693
+ //# sourceMappingURL=index.js.map