@deebeetech/sqleasy-engine 0.0.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.
@@ -0,0 +1,413 @@
1
+ //#region src/introspection/build-schema.ts
2
+ /** Stitch flat catalog rows into per-table column/FK/index lists. Shared by every dialect reader. */
3
+ function buildSchema(tables, columns, fks, indexColumns = [], rowCounts = []) {
4
+ const colMap = /* @__PURE__ */ new Map();
5
+ for (const c of columns) {
6
+ const key = `${c.schema}.${c.table}`;
7
+ const list = colMap.get(key) ?? [];
8
+ list.push({
9
+ name: c.name,
10
+ dataType: c.dataType,
11
+ nullable: c.nullable,
12
+ isPrimaryKey: c.isPrimaryKey,
13
+ defaultValue: c.defaultValue
14
+ });
15
+ colMap.set(key, list);
16
+ }
17
+ const fkMap = /* @__PURE__ */ new Map();
18
+ for (const fk of fks) {
19
+ const key = `${fk.schema}.${fk.table}`;
20
+ const list = fkMap.get(key) ?? [];
21
+ list.push({
22
+ columnName: fk.columnName,
23
+ referencedTable: fk.referencedTable,
24
+ referencedColumn: fk.referencedColumn,
25
+ referencedSchema: fk.referencedSchema
26
+ });
27
+ fkMap.set(key, list);
28
+ }
29
+ const idxMap = /* @__PURE__ */ new Map();
30
+ for (const r of indexColumns) {
31
+ const key = `${r.schema}.${r.table}`;
32
+ const byName = idxMap.get(key) ?? /* @__PURE__ */ new Map();
33
+ const idx = byName.get(r.indexName) ?? {
34
+ unique: r.unique,
35
+ cols: []
36
+ };
37
+ idx.cols.push({
38
+ name: r.columnName,
39
+ ordinal: r.ordinal
40
+ });
41
+ byName.set(r.indexName, idx);
42
+ idxMap.set(key, byName);
43
+ }
44
+ const indexesFor = (key) => [...idxMap.get(key)?.entries() ?? []].map(([name, i]) => ({
45
+ name,
46
+ unique: i.unique,
47
+ columns: i.cols.sort((a, b) => a.ordinal - b.ordinal).map((c) => c.name)
48
+ }));
49
+ const rowCountMap = new Map(rowCounts.map((r) => [`${r.schema}.${r.table}`, r.count]));
50
+ return { tables: tables.map((t) => {
51
+ const key = `${t.schema}.${t.name}`;
52
+ return {
53
+ schema: t.schema,
54
+ name: t.name,
55
+ type: t.isView ? "view" : "table",
56
+ columns: colMap.get(key) ?? [],
57
+ foreignKeys: fkMap.get(key) ?? [],
58
+ indexes: indexesFor(key),
59
+ approxRowCount: rowCountMap.get(key)
60
+ };
61
+ }) };
62
+ }
63
+ //#endregion
64
+ //#region src/introspection/mssql.ts
65
+ /** SQL Server catalog reader (INFORMATION_SCHEMA + sys catalog). Pass an executor connected to a
66
+ * SQL Server database (`createMssqlExecutor`). */
67
+ async function introspectMssql(executor, schema) {
68
+ const target = schema || "dbo";
69
+ const tables = await executor.run({
70
+ sql: `SELECT TABLE_NAME AS table_name, TABLE_TYPE AS table_type
71
+ FROM INFORMATION_SCHEMA.TABLES
72
+ WHERE TABLE_SCHEMA = @p0 AND TABLE_TYPE IN ('BASE TABLE', 'VIEW')
73
+ ORDER BY TABLE_TYPE, TABLE_NAME`,
74
+ params: [target]
75
+ });
76
+ const columns = await executor.run({
77
+ sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name, DATA_TYPE AS data_type,
78
+ IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default
79
+ FROM INFORMATION_SCHEMA.COLUMNS
80
+ WHERE TABLE_SCHEMA = @p0
81
+ ORDER BY TABLE_NAME, ORDINAL_POSITION`,
82
+ params: [target]
83
+ });
84
+ const pks = await executor.run({
85
+ sql: `SELECT kcu.TABLE_NAME AS table_name, kcu.COLUMN_NAME AS column_name
86
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
87
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
88
+ ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
89
+ WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.TABLE_SCHEMA = @p0`,
90
+ params: [target]
91
+ });
92
+ const fks = await executor.run({
93
+ sql: `SELECT t1.name AS table_name, c1.name AS column_name,
94
+ s2.name AS referenced_schema, t2.name AS referenced_table, c2.name AS referenced_column
95
+ FROM sys.foreign_key_columns fkc
96
+ JOIN sys.objects t1 ON fkc.parent_object_id = t1.object_id
97
+ JOIN sys.columns c1 ON fkc.parent_object_id = c1.object_id AND fkc.parent_column_id = c1.column_id
98
+ JOIN sys.objects t2 ON fkc.referenced_object_id = t2.object_id
99
+ JOIN sys.columns c2 ON fkc.referenced_object_id = c2.object_id AND fkc.referenced_column_id = c2.column_id
100
+ JOIN sys.schemas s1 ON t1.schema_id = s1.schema_id
101
+ JOIN sys.schemas s2 ON t2.schema_id = s2.schema_id
102
+ WHERE s1.name = @p0`,
103
+ params: [target]
104
+ });
105
+ const indexes = await executor.run({
106
+ sql: `SELECT t.name AS table_name, i.name AS index_name, i.is_unique AS is_unique,
107
+ c.name AS column_name, ic.key_ordinal AS ordinal
108
+ FROM sys.indexes i
109
+ JOIN sys.tables t ON t.object_id = i.object_id
110
+ JOIN sys.schemas s ON s.schema_id = t.schema_id
111
+ JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
112
+ JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
113
+ WHERE s.name = @p0 AND i.type > 0 AND ic.is_included_column = 0
114
+ ORDER BY t.name, i.name, ic.key_ordinal`,
115
+ params: [target]
116
+ });
117
+ const rowCounts = await executor.run({
118
+ sql: `SELECT t.name AS table_name, SUM(p.rows) AS n
119
+ FROM sys.tables t
120
+ JOIN sys.schemas s ON s.schema_id = t.schema_id
121
+ JOIN sys.partitions p ON p.object_id = t.object_id AND p.index_id IN (0, 1)
122
+ WHERE s.name = @p0
123
+ GROUP BY t.name`,
124
+ params: [target]
125
+ });
126
+ const pkSet = new Set(pks.rows.map((p) => `${p.table_name}.${p.column_name}`));
127
+ return buildSchema(tables.rows.map((t) => ({
128
+ schema: target,
129
+ name: t.table_name,
130
+ isView: t.table_type === "VIEW"
131
+ })), columns.rows.map((c) => ({
132
+ schema: target,
133
+ table: c.table_name,
134
+ name: c.column_name,
135
+ dataType: c.data_type,
136
+ nullable: c.is_nullable === "YES",
137
+ isPrimaryKey: pkSet.has(`${c.table_name}.${c.column_name}`),
138
+ defaultValue: c.column_default ?? void 0
139
+ })), fks.rows.map((fk) => ({
140
+ schema: target,
141
+ table: fk.table_name,
142
+ columnName: fk.column_name,
143
+ referencedTable: fk.referenced_table,
144
+ referencedColumn: fk.referenced_column,
145
+ referencedSchema: fk.referenced_schema
146
+ })), indexes.rows.map((r) => ({
147
+ schema: target,
148
+ table: r.table_name,
149
+ indexName: r.index_name,
150
+ unique: Boolean(r.is_unique),
151
+ columnName: r.column_name,
152
+ ordinal: Number(r.ordinal)
153
+ })), rowCounts.rows.map((r) => ({
154
+ schema: target,
155
+ table: r.table_name,
156
+ count: Number(r.n)
157
+ })));
158
+ }
159
+ //#endregion
160
+ //#region src/introspection/mysql.ts
161
+ /** MySQL / MariaDB catalog reader (information_schema). Pass an executor connected to a MySQL
162
+ * database (`createMysqlExecutor`). */
163
+ async function introspectMysql(executor, schema) {
164
+ let target = schema;
165
+ if (!target) target = (await executor.run({ sql: "SELECT DATABASE() AS db" })).rows[0]?.db ?? "";
166
+ const tables = await executor.run({
167
+ sql: `SELECT TABLE_NAME AS table_name, TABLE_TYPE AS table_type
168
+ FROM information_schema.tables
169
+ WHERE table_schema = ? AND table_type IN ('BASE TABLE', 'VIEW')
170
+ ORDER BY table_type, table_name`,
171
+ params: [target]
172
+ });
173
+ const columns = await executor.run({
174
+ sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name, DATA_TYPE AS data_type,
175
+ IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default, COLUMN_KEY AS column_key
176
+ FROM information_schema.columns
177
+ WHERE table_schema = ?
178
+ ORDER BY table_name, ordinal_position`,
179
+ params: [target]
180
+ });
181
+ const fks = await executor.run({
182
+ sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name,
183
+ REFERENCED_TABLE_SCHEMA AS referenced_table_schema,
184
+ REFERENCED_TABLE_NAME AS referenced_table_name,
185
+ REFERENCED_COLUMN_NAME AS referenced_column_name
186
+ FROM information_schema.key_column_usage
187
+ WHERE table_schema = ? AND referenced_table_name IS NOT NULL`,
188
+ params: [target]
189
+ });
190
+ const indexes = await executor.run({
191
+ sql: `SELECT TABLE_NAME AS table_name, INDEX_NAME AS index_name, NON_UNIQUE AS non_unique,
192
+ COLUMN_NAME AS column_name, SEQ_IN_INDEX AS seq
193
+ FROM information_schema.statistics
194
+ WHERE table_schema = ?
195
+ ORDER BY table_name, index_name, seq`,
196
+ params: [target]
197
+ });
198
+ const rowCounts = await executor.run({
199
+ sql: `SELECT TABLE_NAME AS table_name, TABLE_ROWS AS n
200
+ FROM information_schema.tables
201
+ WHERE table_schema = ? AND table_type = 'BASE TABLE'`,
202
+ params: [target]
203
+ });
204
+ return buildSchema(tables.rows.map((t) => ({
205
+ schema: target,
206
+ name: t.table_name,
207
+ isView: t.table_type === "VIEW"
208
+ })), columns.rows.map((c) => ({
209
+ schema: target,
210
+ table: c.table_name,
211
+ name: c.column_name,
212
+ dataType: c.data_type,
213
+ nullable: c.is_nullable === "YES",
214
+ isPrimaryKey: c.column_key === "PRI",
215
+ defaultValue: c.column_default ?? void 0
216
+ })), fks.rows.map((fk) => ({
217
+ schema: target,
218
+ table: fk.table_name,
219
+ columnName: fk.column_name,
220
+ referencedTable: fk.referenced_table_name,
221
+ referencedColumn: fk.referenced_column_name,
222
+ referencedSchema: fk.referenced_table_schema
223
+ })), indexes.rows.filter((r) => r.column_name != null).map((r) => ({
224
+ schema: target,
225
+ table: r.table_name,
226
+ indexName: r.index_name,
227
+ unique: r.non_unique === 0,
228
+ columnName: r.column_name,
229
+ ordinal: Number(r.seq)
230
+ })), rowCounts.rows.map((r) => ({
231
+ schema: target,
232
+ table: r.table_name,
233
+ count: Number(r.n ?? 0)
234
+ })));
235
+ }
236
+ //#endregion
237
+ //#region src/introspection/postgres.ts
238
+ /** Postgres catalog reader (information_schema + pg_catalog). Pass an executor connected to a
239
+ * Postgres database (`createPostgresExecutor`). */
240
+ async function introspectPostgres(executor, schema) {
241
+ const target = schema || "public";
242
+ const tables = await executor.run({
243
+ sql: `SELECT table_name, table_type
244
+ FROM information_schema.tables
245
+ WHERE table_schema = $1 AND table_type IN ('BASE TABLE', 'VIEW')
246
+ ORDER BY table_type, table_name`,
247
+ params: [target]
248
+ });
249
+ const columns = await executor.run({
250
+ sql: `SELECT table_name, column_name, data_type, is_nullable, column_default
251
+ FROM information_schema.columns
252
+ WHERE table_schema = $1
253
+ ORDER BY table_name, ordinal_position`,
254
+ params: [target]
255
+ });
256
+ const pks = await executor.run({
257
+ sql: `SELECT kcu.table_name, kcu.column_name
258
+ FROM information_schema.table_constraints tc
259
+ JOIN information_schema.key_column_usage kcu
260
+ ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
261
+ WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_schema = $1`,
262
+ params: [target]
263
+ });
264
+ const fks = await executor.run({
265
+ sql: `SELECT kcu.table_name, kcu.column_name,
266
+ ccu.table_schema AS referenced_table_schema,
267
+ ccu.table_name AS referenced_table_name,
268
+ ccu.column_name AS referenced_column_name
269
+ FROM information_schema.table_constraints tc
270
+ JOIN information_schema.key_column_usage kcu
271
+ ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
272
+ JOIN information_schema.constraint_column_usage ccu
273
+ ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
274
+ WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = $1`,
275
+ params: [target]
276
+ });
277
+ const indexes = await executor.run({
278
+ sql: `SELECT t.relname AS table_name, i.relname AS index_name, ix.indisunique AS is_unique,
279
+ a.attname AS column_name, k.ord AS ordinal
280
+ FROM pg_index ix
281
+ JOIN pg_class i ON i.oid = ix.indexrelid
282
+ JOIN pg_class t ON t.oid = ix.indrelid
283
+ JOIN pg_namespace n ON n.oid = t.relnamespace
284
+ JOIN LATERAL unnest(string_to_array(ix.indkey::text, ' ')::int[]) WITH ORDINALITY AS k(attnum, ord)
285
+ ON true
286
+ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
287
+ WHERE n.nspname = $1 AND t.relkind = 'r' AND k.attnum > 0
288
+ ORDER BY table_name, index_name, ordinal`,
289
+ params: [target]
290
+ });
291
+ const rowCounts = await executor.run({
292
+ sql: `SELECT c.relname AS table_name, c.reltuples::bigint AS n
293
+ FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
294
+ WHERE n.nspname = $1 AND c.relkind = 'r'`,
295
+ params: [target]
296
+ });
297
+ const pkSet = new Set(pks.rows.map((p) => `${p.table_name}.${p.column_name}`));
298
+ return buildSchema(tables.rows.map((t) => ({
299
+ schema: target,
300
+ name: t.table_name,
301
+ isView: t.table_type === "VIEW"
302
+ })), columns.rows.map((c) => ({
303
+ schema: target,
304
+ table: c.table_name,
305
+ name: c.column_name,
306
+ dataType: c.data_type,
307
+ nullable: c.is_nullable === "YES",
308
+ isPrimaryKey: pkSet.has(`${c.table_name}.${c.column_name}`),
309
+ defaultValue: c.column_default ?? void 0
310
+ })), fks.rows.map((fk) => ({
311
+ schema: target,
312
+ table: fk.table_name,
313
+ columnName: fk.column_name,
314
+ referencedTable: fk.referenced_table_name,
315
+ referencedColumn: fk.referenced_column_name,
316
+ referencedSchema: fk.referenced_table_schema
317
+ })), indexes.rows.map((r) => ({
318
+ schema: target,
319
+ table: r.table_name,
320
+ indexName: r.index_name,
321
+ unique: r.is_unique,
322
+ columnName: r.column_name,
323
+ ordinal: Number(r.ordinal)
324
+ })), rowCounts.rows.map((r) => ({
325
+ schema: target,
326
+ table: r.table_name,
327
+ count: Number(r.n)
328
+ })).filter((r) => r.count >= 0));
329
+ }
330
+ //#endregion
331
+ //#region src/introspection/sqlite.ts
332
+ /** SQLite catalog reader (sqlite_master + pragma table-valued functions). Also serves libsql/turso —
333
+ * they are all SQLite engines with the same catalog. Pass an executor from `createSqliteExecutor`. */
334
+ async function introspectSqlite(executor) {
335
+ const tables = await executor.run({ sql: `SELECT name, type FROM sqlite_master
336
+ WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
337
+ ORDER BY type, name` });
338
+ const columns = [];
339
+ const fks = [];
340
+ const indexColumns = [];
341
+ for (const t of tables.rows) {
342
+ const cols = await executor.run({
343
+ sql: "SELECT name, type, \"notnull\", dflt_value, pk FROM pragma_table_info(?)",
344
+ params: [t.name]
345
+ });
346
+ for (const c of cols.rows) columns.push({
347
+ schema: "main",
348
+ table: t.name,
349
+ name: c.name,
350
+ dataType: c.type || "TEXT",
351
+ nullable: c.notnull === 0 && c.pk === 0,
352
+ isPrimaryKey: c.pk > 0,
353
+ defaultValue: c.dflt_value ?? void 0
354
+ });
355
+ const fkRows = await executor.run({
356
+ sql: "SELECT \"from\", \"table\", \"to\" FROM pragma_foreign_key_list(?)",
357
+ params: [t.name]
358
+ });
359
+ for (const fk of fkRows.rows) fks.push({
360
+ schema: "main",
361
+ table: t.name,
362
+ columnName: fk.from,
363
+ referencedTable: fk.table,
364
+ referencedColumn: fk.to
365
+ });
366
+ const idxList = await executor.run({
367
+ sql: "SELECT name, \"unique\" FROM pragma_index_list(?)",
368
+ params: [t.name]
369
+ });
370
+ for (const idx of idxList.rows) {
371
+ const idxCols = await executor.run({
372
+ sql: "SELECT seqno, name FROM pragma_index_info(?)",
373
+ params: [idx.name]
374
+ });
375
+ for (const ic of idxCols.rows) {
376
+ if (ic.name == null) continue;
377
+ indexColumns.push({
378
+ schema: "main",
379
+ table: t.name,
380
+ indexName: idx.name,
381
+ unique: idx.unique === 1,
382
+ columnName: ic.name,
383
+ ordinal: ic.seqno
384
+ });
385
+ }
386
+ }
387
+ }
388
+ return buildSchema(tables.rows.map((t) => ({
389
+ schema: "main",
390
+ name: t.name,
391
+ isView: t.type === "view"
392
+ })), columns, fks, indexColumns);
393
+ }
394
+ //#endregion
395
+ //#region src/introspection/index.ts
396
+ /**
397
+ * Read a database's catalog as a {@link SchemaData}, through a supplied {@link DbExecutor}. Choose
398
+ * the `dialect` matching the executor you built — the reader runs that dialect's catalog queries.
399
+ * `schema` scopes the namespace, defaulting per dialect (postgres `public`, mysql the current
400
+ * database, mssql `dbo`, sqlite `main` — sqlite ignores it).
401
+ */
402
+ function introspectSchema(executor, dialect, schema) {
403
+ switch (dialect) {
404
+ case "postgres": return introspectPostgres(executor, schema);
405
+ case "mysql": return introspectMysql(executor, schema);
406
+ case "mssql": return introspectMssql(executor, schema);
407
+ case "sqlite": return introspectSqlite(executor);
408
+ }
409
+ }
410
+ //#endregion
411
+ export { buildSchema, introspectMssql, introspectMysql, introspectPostgres, introspectSchema, introspectSqlite };
412
+
413
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/introspection/build-schema.ts","../../src/introspection/mssql.ts","../../src/introspection/mysql.ts","../../src/introspection/postgres.ts","../../src/introspection/sqlite.ts","../../src/introspection/index.ts"],"sourcesContent":["import type {\n SchemaColumn,\n SchemaData,\n SchemaForeignKey,\n SchemaIndex,\n SchemaTable,\n} from './schema';\n\n/** A table row as read from the catalog, before assembly. */\nexport type RawTable = {\n schema: string;\n name: string;\n isView: boolean;\n};\n\n/** One (index, column) pair as read from the catalog, before grouping into per-table indexes. */\nexport type IndexColumnRow = {\n schema: string;\n table: string;\n indexName: string;\n unique: boolean;\n columnName: string;\n ordinal: number;\n};\n\n/** Stitch flat catalog rows into per-table column/FK/index lists. Shared by every dialect reader. */\nexport function buildSchema(\n tables: RawTable[],\n columns: (SchemaColumn & { schema: string; table: string })[],\n fks: (SchemaForeignKey & { schema: string; table: string })[],\n indexColumns: IndexColumnRow[] = [],\n rowCounts: { schema: string; table: string; count: number }[] = [],\n): SchemaData {\n const colMap = new Map<string, SchemaColumn[]>();\n for (const c of columns) {\n const key = `${c.schema}.${c.table}`;\n const list = colMap.get(key) ?? [];\n list.push({\n name: c.name,\n dataType: c.dataType,\n nullable: c.nullable,\n isPrimaryKey: c.isPrimaryKey,\n defaultValue: c.defaultValue,\n });\n colMap.set(key, list);\n }\n\n const fkMap = new Map<string, SchemaForeignKey[]>();\n for (const fk of fks) {\n const key = `${fk.schema}.${fk.table}`;\n const list = fkMap.get(key) ?? [];\n list.push({\n columnName: fk.columnName,\n referencedTable: fk.referencedTable,\n referencedColumn: fk.referencedColumn,\n referencedSchema: fk.referencedSchema,\n });\n fkMap.set(key, list);\n }\n\n // Group flat (index, column) rows into per-table indexes, columns ordered within each index.\n const idxMap = new Map<\n string,\n Map<string, { unique: boolean; cols: { name: string; ordinal: number }[] }>\n >();\n for (const r of indexColumns) {\n const key = `${r.schema}.${r.table}`;\n const byName = idxMap.get(key) ?? new Map();\n const idx = byName.get(r.indexName) ?? { unique: r.unique, cols: [] };\n idx.cols.push({ name: r.columnName, ordinal: r.ordinal });\n byName.set(r.indexName, idx);\n idxMap.set(key, byName);\n }\n const indexesFor = (key: string): SchemaIndex[] =>\n [...(idxMap.get(key)?.entries() ?? [])].map(([name, i]) => ({\n name,\n unique: i.unique,\n columns: i.cols.sort((a, b) => a.ordinal - b.ordinal).map((c) => c.name),\n }));\n\n const rowCountMap = new Map(rowCounts.map((r) => [`${r.schema}.${r.table}`, r.count]));\n\n const result: SchemaTable[] = tables.map((t) => {\n const key = `${t.schema}.${t.name}`;\n return {\n schema: t.schema,\n name: t.name,\n type: t.isView ? 'view' : 'table',\n columns: colMap.get(key) ?? [],\n foreignKeys: fkMap.get(key) ?? [],\n indexes: indexesFor(key),\n approxRowCount: rowCountMap.get(key),\n };\n });\n\n return { tables: result };\n}\n","import type { DbExecutor } from '../index';\nimport { buildSchema } from './build-schema';\nimport type { SchemaData } from './schema';\n\n/** SQL Server catalog reader (INFORMATION_SCHEMA + sys catalog). Pass an executor connected to a\n * SQL Server database (`createMssqlExecutor`). */\nexport async function introspectMssql(executor: DbExecutor, schema?: string): Promise<SchemaData> {\n const target = schema || 'dbo';\n\n const tables = await executor.run<{ table_name: string; table_type: string }>({\n sql: `SELECT TABLE_NAME AS table_name, TABLE_TYPE AS table_type\n FROM INFORMATION_SCHEMA.TABLES\n WHERE TABLE_SCHEMA = @p0 AND TABLE_TYPE IN ('BASE TABLE', 'VIEW')\n ORDER BY TABLE_TYPE, TABLE_NAME`,\n params: [target],\n });\n\n const columns = await executor.run<{\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n }>({\n sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name, DATA_TYPE AS data_type,\n IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default\n FROM INFORMATION_SCHEMA.COLUMNS\n WHERE TABLE_SCHEMA = @p0\n ORDER BY TABLE_NAME, ORDINAL_POSITION`,\n params: [target],\n });\n\n const pks = await executor.run<{ table_name: string; column_name: string }>({\n sql: `SELECT kcu.TABLE_NAME AS table_name, kcu.COLUMN_NAME AS column_name\n FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc\n JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu\n ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME AND tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA\n WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.TABLE_SCHEMA = @p0`,\n params: [target],\n });\n\n const fks = await executor.run<{\n table_name: string;\n column_name: string;\n referenced_schema: string;\n referenced_table: string;\n referenced_column: string;\n }>({\n sql: `SELECT t1.name AS table_name, c1.name AS column_name,\n s2.name AS referenced_schema, t2.name AS referenced_table, c2.name AS referenced_column\n FROM sys.foreign_key_columns fkc\n JOIN sys.objects t1 ON fkc.parent_object_id = t1.object_id\n JOIN sys.columns c1 ON fkc.parent_object_id = c1.object_id AND fkc.parent_column_id = c1.column_id\n JOIN sys.objects t2 ON fkc.referenced_object_id = t2.object_id\n JOIN sys.columns c2 ON fkc.referenced_object_id = c2.object_id AND fkc.referenced_column_id = c2.column_id\n JOIN sys.schemas s1 ON t1.schema_id = s1.schema_id\n JOIN sys.schemas s2 ON t2.schema_id = s2.schema_id\n WHERE s1.name = @p0`,\n params: [target],\n });\n\n // Secondary indexes (sys.index_columns, key columns only in key_ordinal order; `i.type > 0` skips\n // heaps) + an approximate row count from partition stats.\n const indexes = await executor.run<{\n table_name: string;\n index_name: string;\n is_unique: boolean | number;\n column_name: string;\n ordinal: number;\n }>({\n sql: `SELECT t.name AS table_name, i.name AS index_name, i.is_unique AS is_unique,\n c.name AS column_name, ic.key_ordinal AS ordinal\n FROM sys.indexes i\n JOIN sys.tables t ON t.object_id = i.object_id\n JOIN sys.schemas s ON s.schema_id = t.schema_id\n JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id\n JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id\n WHERE s.name = @p0 AND i.type > 0 AND ic.is_included_column = 0\n ORDER BY t.name, i.name, ic.key_ordinal`,\n params: [target],\n });\n\n const rowCounts = await executor.run<{ table_name: string; n: number }>({\n sql: `SELECT t.name AS table_name, SUM(p.rows) AS n\n FROM sys.tables t\n JOIN sys.schemas s ON s.schema_id = t.schema_id\n JOIN sys.partitions p ON p.object_id = t.object_id AND p.index_id IN (0, 1)\n WHERE s.name = @p0\n GROUP BY t.name`,\n params: [target],\n });\n\n const pkSet = new Set(pks.rows.map((p) => `${p.table_name}.${p.column_name}`));\n\n return buildSchema(\n tables.rows.map((t) => ({\n schema: target,\n name: t.table_name,\n isView: t.table_type === 'VIEW',\n })),\n columns.rows.map((c) => ({\n schema: target,\n table: c.table_name,\n name: c.column_name,\n dataType: c.data_type,\n nullable: c.is_nullable === 'YES',\n isPrimaryKey: pkSet.has(`${c.table_name}.${c.column_name}`),\n defaultValue: c.column_default ?? undefined,\n })),\n fks.rows.map((fk) => ({\n schema: target,\n table: fk.table_name,\n columnName: fk.column_name,\n referencedTable: fk.referenced_table,\n referencedColumn: fk.referenced_column,\n referencedSchema: fk.referenced_schema,\n })),\n indexes.rows.map((r) => ({\n schema: target,\n table: r.table_name,\n indexName: r.index_name,\n unique: Boolean(r.is_unique),\n columnName: r.column_name,\n ordinal: Number(r.ordinal),\n })),\n rowCounts.rows.map((r) => ({ schema: target, table: r.table_name, count: Number(r.n) })),\n );\n}\n","import type { DbExecutor } from '../index';\nimport { buildSchema } from './build-schema';\nimport type { SchemaData } from './schema';\n\n/** MySQL / MariaDB catalog reader (information_schema). Pass an executor connected to a MySQL\n * database (`createMysqlExecutor`). */\nexport async function introspectMysql(executor: DbExecutor, schema?: string): Promise<SchemaData> {\n let target = schema;\n if (!target) {\n const r = await executor.run<{ db: string }>({ sql: 'SELECT DATABASE() AS db' });\n target = r.rows[0]?.db ?? '';\n }\n\n // Explicit lowercase aliases: MySQL 8's information_schema returns UPPERCASE column headers unless\n // aliased (5.7/MariaDB return them as written) — without them every row property reads undefined.\n const tables = await executor.run<{ table_name: string; table_type: string }>({\n sql: `SELECT TABLE_NAME AS table_name, TABLE_TYPE AS table_type\n FROM information_schema.tables\n WHERE table_schema = ? AND table_type IN ('BASE TABLE', 'VIEW')\n ORDER BY table_type, table_name`,\n params: [target],\n });\n\n const columns = await executor.run<{\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n column_key: string;\n }>({\n sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name, DATA_TYPE AS data_type,\n IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default, COLUMN_KEY AS column_key\n FROM information_schema.columns\n WHERE table_schema = ?\n ORDER BY table_name, ordinal_position`,\n params: [target],\n });\n\n const fks = await executor.run<{\n table_name: string;\n column_name: string;\n referenced_table_schema: string;\n referenced_table_name: string;\n referenced_column_name: string;\n }>({\n sql: `SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name,\n REFERENCED_TABLE_SCHEMA AS referenced_table_schema,\n REFERENCED_TABLE_NAME AS referenced_table_name,\n REFERENCED_COLUMN_NAME AS referenced_column_name\n FROM information_schema.key_column_usage\n WHERE table_schema = ? AND referenced_table_name IS NOT NULL`,\n params: [target],\n });\n\n // Secondary indexes (one row per index column, ordered by SEQ_IN_INDEX) + approx row count.\n const indexes = await executor.run<{\n table_name: string;\n index_name: string;\n non_unique: number;\n column_name: string | null;\n seq: number;\n }>({\n sql: `SELECT TABLE_NAME AS table_name, INDEX_NAME AS index_name, NON_UNIQUE AS non_unique,\n COLUMN_NAME AS column_name, SEQ_IN_INDEX AS seq\n FROM information_schema.statistics\n WHERE table_schema = ?\n ORDER BY table_name, index_name, seq`,\n params: [target],\n });\n\n const rowCounts = await executor.run<{ table_name: string; n: number | null }>({\n sql: `SELECT TABLE_NAME AS table_name, TABLE_ROWS AS n\n FROM information_schema.tables\n WHERE table_schema = ? AND table_type = 'BASE TABLE'`,\n params: [target],\n });\n\n return buildSchema(\n tables.rows.map((t) => ({\n schema: target,\n name: t.table_name,\n isView: t.table_type === 'VIEW',\n })),\n columns.rows.map((c) => ({\n schema: target,\n table: c.table_name,\n name: c.column_name,\n dataType: c.data_type,\n nullable: c.is_nullable === 'YES',\n isPrimaryKey: c.column_key === 'PRI',\n defaultValue: c.column_default ?? undefined,\n })),\n fks.rows.map((fk) => ({\n schema: target,\n table: fk.table_name,\n columnName: fk.column_name,\n referencedTable: fk.referenced_table_name,\n referencedColumn: fk.referenced_column_name,\n referencedSchema: fk.referenced_table_schema,\n })),\n indexes.rows\n .filter((r) => r.column_name != null)\n .map((r) => ({\n schema: target,\n table: r.table_name,\n indexName: r.index_name,\n unique: r.non_unique === 0,\n columnName: r.column_name!,\n ordinal: Number(r.seq),\n })),\n rowCounts.rows.map((r) => ({ schema: target, table: r.table_name, count: Number(r.n ?? 0) })),\n );\n}\n","import type { DbExecutor } from '../index';\nimport { buildSchema } from './build-schema';\nimport type { SchemaData } from './schema';\n\n/** Postgres catalog reader (information_schema + pg_catalog). Pass an executor connected to a\n * Postgres database (`createPostgresExecutor`). */\nexport async function introspectPostgres(\n executor: DbExecutor,\n schema?: string,\n): Promise<SchemaData> {\n const target = schema || 'public';\n\n const tables = await executor.run<{ table_name: string; table_type: string }>({\n sql: `SELECT table_name, table_type\n FROM information_schema.tables\n WHERE table_schema = $1 AND table_type IN ('BASE TABLE', 'VIEW')\n ORDER BY table_type, table_name`,\n params: [target],\n });\n\n const columns = await executor.run<{\n table_name: string;\n column_name: string;\n data_type: string;\n is_nullable: string;\n column_default: string | null;\n }>({\n sql: `SELECT table_name, column_name, data_type, is_nullable, column_default\n FROM information_schema.columns\n WHERE table_schema = $1\n ORDER BY table_name, ordinal_position`,\n params: [target],\n });\n\n const pks = await executor.run<{ table_name: string; column_name: string }>({\n sql: `SELECT kcu.table_name, kcu.column_name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema\n WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_schema = $1`,\n params: [target],\n });\n\n const fks = await executor.run<{\n table_name: string;\n column_name: string;\n referenced_table_schema: string;\n referenced_table_name: string;\n referenced_column_name: string;\n }>({\n sql: `SELECT kcu.table_name, kcu.column_name,\n ccu.table_schema AS referenced_table_schema,\n ccu.table_name AS referenced_table_name,\n ccu.column_name AS referenced_column_name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema\n JOIN information_schema.constraint_column_usage ccu\n ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema\n WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = $1`,\n params: [target],\n });\n\n // Secondary indexes: columns unnested in key order (WITH ORDINALITY), `attnum > 0` drops\n // expression-index entries. Plus an approximate row count from planner stats (reltuples).\n const indexes = await executor.run<{\n table_name: string;\n index_name: string;\n is_unique: boolean;\n column_name: string;\n ordinal: number;\n }>({\n sql: `SELECT t.relname AS table_name, i.relname AS index_name, ix.indisunique AS is_unique,\n a.attname AS column_name, k.ord AS ordinal\n FROM pg_index ix\n JOIN pg_class i ON i.oid = ix.indexrelid\n JOIN pg_class t ON t.oid = ix.indrelid\n JOIN pg_namespace n ON n.oid = t.relnamespace\n JOIN LATERAL unnest(string_to_array(ix.indkey::text, ' ')::int[]) WITH ORDINALITY AS k(attnum, ord)\n ON true\n JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum\n WHERE n.nspname = $1 AND t.relkind = 'r' AND k.attnum > 0\n ORDER BY table_name, index_name, ordinal`,\n params: [target],\n });\n\n const rowCounts = await executor.run<{ table_name: string; n: string }>({\n sql: `SELECT c.relname AS table_name, c.reltuples::bigint AS n\n FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE n.nspname = $1 AND c.relkind = 'r'`,\n params: [target],\n });\n\n const pkSet = new Set(pks.rows.map((p) => `${p.table_name}.${p.column_name}`));\n\n return buildSchema(\n tables.rows.map((t) => ({\n schema: target,\n name: t.table_name,\n isView: t.table_type === 'VIEW',\n })),\n columns.rows.map((c) => ({\n schema: target,\n table: c.table_name,\n name: c.column_name,\n dataType: c.data_type,\n nullable: c.is_nullable === 'YES',\n isPrimaryKey: pkSet.has(`${c.table_name}.${c.column_name}`),\n defaultValue: c.column_default ?? undefined,\n })),\n fks.rows.map((fk) => ({\n schema: target,\n table: fk.table_name,\n columnName: fk.column_name,\n referencedTable: fk.referenced_table_name,\n referencedColumn: fk.referenced_column_name,\n referencedSchema: fk.referenced_table_schema,\n })),\n indexes.rows.map((r) => ({\n schema: target,\n table: r.table_name,\n indexName: r.index_name,\n unique: r.is_unique,\n columnName: r.column_name,\n ordinal: Number(r.ordinal),\n })),\n rowCounts.rows\n .map((r) => ({ schema: target, table: r.table_name, count: Number(r.n) }))\n .filter((r) => r.count >= 0),\n );\n}\n","import type { DbExecutor } from '../index';\nimport { buildSchema, type IndexColumnRow } from './build-schema';\nimport type { SchemaColumn, SchemaData, SchemaForeignKey } from './schema';\n\n/** SQLite catalog reader (sqlite_master + pragma table-valued functions). Also serves libsql/turso —\n * they are all SQLite engines with the same catalog. Pass an executor from `createSqliteExecutor`. */\nexport async function introspectSqlite(executor: DbExecutor): Promise<SchemaData> {\n const tables = await executor.run<{ name: string; type: string }>({\n sql: `SELECT name, type FROM sqlite_master\n WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'\n ORDER BY type, name`,\n });\n\n const columns: (SchemaColumn & { schema: string; table: string })[] = [];\n const fks: (SchemaForeignKey & { schema: string; table: string })[] = [];\n const indexColumns: IndexColumnRow[] = [];\n\n for (const t of tables.rows) {\n // Table-valued pragma functions accept the table name as a bound parameter, so no identifier\n // interpolation is needed.\n const cols = await executor.run<{\n name: string;\n type: string;\n notnull: number;\n dflt_value: string | null;\n pk: number;\n }>({\n sql: 'SELECT name, type, \"notnull\", dflt_value, pk FROM pragma_table_info(?)',\n params: [t.name],\n });\n\n for (const c of cols.rows) {\n columns.push({\n schema: 'main',\n table: t.name,\n name: c.name,\n dataType: c.type || 'TEXT',\n nullable: c.notnull === 0 && c.pk === 0,\n isPrimaryKey: c.pk > 0,\n defaultValue: c.dflt_value ?? undefined,\n });\n }\n\n const fkRows = await executor.run<{ from: string; table: string; to: string }>({\n sql: 'SELECT \"from\", \"table\", \"to\" FROM pragma_foreign_key_list(?)',\n params: [t.name],\n });\n for (const fk of fkRows.rows) {\n fks.push({\n schema: 'main',\n table: t.name,\n columnName: fk.from,\n referencedTable: fk.table,\n referencedColumn: fk.to,\n });\n }\n\n // Indexes: list them, then read each index's columns (pragma_index_info, ordered by seqno).\n const idxList = await executor.run<{ name: string; unique: number }>({\n sql: 'SELECT name, \"unique\" FROM pragma_index_list(?)',\n params: [t.name],\n });\n for (const idx of idxList.rows) {\n const idxCols = await executor.run<{ seqno: number; name: string | null }>({\n sql: 'SELECT seqno, name FROM pragma_index_info(?)',\n params: [idx.name],\n });\n for (const ic of idxCols.rows) {\n if (ic.name == null) continue; // expression-index columns have no name\n indexColumns.push({\n schema: 'main',\n table: t.name,\n indexName: idx.name,\n unique: idx.unique === 1,\n columnName: ic.name,\n ordinal: ic.seqno,\n });\n }\n }\n }\n\n return buildSchema(\n tables.rows.map((t) => ({ schema: 'main', name: t.name, isView: t.type === 'view' })),\n columns,\n fks,\n indexColumns,\n );\n}\n","/**\n * Schema introspection: read a database's catalog (tables, views, columns, primary keys, foreign\n * keys, indexes, approximate row counts) into a normalized {@link SchemaData}.\n *\n * This entry point pulls in NO driver — it runs entirely through a {@link DbExecutor} you supply, so\n * it reuses that executor's connection, credentials, and placeholder convention rather than opening\n * its own. Build the executor from the matching dialect subpath and pass it in.\n */\nimport type { DbExecutor } from '../index';\nimport { introspectMssql } from './mssql';\nimport { introspectMysql } from './mysql';\nimport { introspectPostgres } from './postgres';\nimport type { SchemaData } from './schema';\nimport { introspectSqlite } from './sqlite';\n\nexport type {\n SchemaColumn,\n SchemaForeignKey,\n SchemaIndex,\n SchemaTable,\n SchemaData,\n} from './schema';\nexport { buildSchema, type RawTable, type IndexColumnRow } from './build-schema';\nexport { introspectPostgres } from './postgres';\nexport { introspectMysql } from './mysql';\nexport { introspectMssql } from './mssql';\nexport { introspectSqlite } from './sqlite';\n\n/** The dialects whose catalog this package can read. libsql and turso use `'sqlite'`. */\nexport type IntrospectDialect = 'postgres' | 'mysql' | 'mssql' | 'sqlite';\n\n/**\n * Read a database's catalog as a {@link SchemaData}, through a supplied {@link DbExecutor}. Choose\n * the `dialect` matching the executor you built — the reader runs that dialect's catalog queries.\n * `schema` scopes the namespace, defaulting per dialect (postgres `public`, mysql the current\n * database, mssql `dbo`, sqlite `main` — sqlite ignores it).\n */\nexport function introspectSchema(\n executor: DbExecutor,\n dialect: IntrospectDialect,\n schema?: string,\n): Promise<SchemaData> {\n switch (dialect) {\n case 'postgres':\n return introspectPostgres(executor, schema);\n case 'mysql':\n return introspectMysql(executor, schema);\n case 'mssql':\n return introspectMssql(executor, schema);\n case 'sqlite':\n return introspectSqlite(executor);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let mssql = require("mssql");
25
+ mssql = __toESM(mssql, 1);
26
+ //#region src/mssql/index.ts
27
+ const { ConnectionPool, Transaction, Request } = mssql.default;
28
+ /** Read the T-SQL string literal starting at `from` (just past the opening quote), honouring `''`
29
+ * escapes. Returns the unescaped text and the index just past the closing quote. */
30
+ function readLiteral(sql, from) {
31
+ let out = "";
32
+ for (let i = from; i < sql.length; i++) {
33
+ if (sql[i] !== "'") {
34
+ out += sql[i];
35
+ continue;
36
+ }
37
+ if (sql[i + 1] === "'") {
38
+ out += "'";
39
+ i++;
40
+ continue;
41
+ }
42
+ return {
43
+ text: out,
44
+ end: i + 1
45
+ };
46
+ }
47
+ }
48
+ /**
49
+ * Turn the mssql dialect's output into something SHOWPLAN can actually cost.
50
+ *
51
+ * It emits `SET NOCOUNT ON; exec sp_executesql N'<select>', N'<decls>'[, @p0 = …];`. SHOWPLAN does
52
+ * NOT compile dynamic SQL, so explaining the EXEC yields a plan with no cost — the inner statement
53
+ * must be lifted out. When it has parameters, that inner statement references `@p0`, undeclared
54
+ * outside sp_executesql, so re-declare them. The assigned VALUES are dropped on purpose: a cost
55
+ * estimate doesn't need them. Not a wrapped statement ⇒ returned unchanged.
56
+ */
57
+ function toExplainableBatch(sql) {
58
+ const m = /exec\s+sp_executesql\s+N'/i.exec(sql);
59
+ if (!m) return sql;
60
+ const inner = readLiteral(sql, m.index + m[0].length);
61
+ if (!inner) return sql;
62
+ const decl = /^\s*,\s*N'/.exec(sql.slice(inner.end));
63
+ const decls = decl ? readLiteral(sql, inner.end + decl[0].length)?.text.trim() : "";
64
+ return decls ? `DECLARE ${decls};\n${inner.text}` : inner.text;
65
+ }
66
+ /** Parse a SHOWPLAN_XML document into the normalized estimate. Exported for unit tests. */
67
+ function parsePlanXml(xml) {
68
+ let best;
69
+ for (const [tag] of xml.matchAll(/<StmtSimple\b[^>]*>/g)) {
70
+ const cost = Number(/StatementSubTreeCost="([\d.eE+-]+)"/.exec(tag)?.[1]);
71
+ if (!Number.isFinite(cost) || best && cost <= best.cost) continue;
72
+ const rows = Number(/StatementEstRows="([\d.eE+-]+)"/.exec(tag)?.[1]);
73
+ best = {
74
+ cost,
75
+ rows: Number.isFinite(rows) ? rows : void 0
76
+ };
77
+ }
78
+ return {
79
+ cost: best?.cost,
80
+ rows: best?.rows,
81
+ fullScan: /PhysicalOp="(?:Table Scan|Clustered Index Scan|Index Scan)"/.test(xml),
82
+ plan: xml.slice(0, 500)
83
+ };
84
+ }
85
+ const toResult = (result) => ({
86
+ rows: result.recordset ?? [],
87
+ rowCount: result.recordset ? result.recordset.length : result.rowsAffected?.[0] ?? 0
88
+ });
89
+ const bindParams = (request, params) => {
90
+ (params ?? []).forEach((value, i) => request.input(`p${i}`, value));
91
+ return request;
92
+ };
93
+ /**
94
+ * A SQL Server executor backed by an `mssql` connection pool. Feed it the self-contained
95
+ * `sp_executesql` batch a `MssqlQuery` builder emits (its `params` is always `[]`).
96
+ */
97
+ function createMssqlExecutor(config) {
98
+ const makePool = () => new ConnectionPool("connectionString" in config ? config.connectionString : config);
99
+ let pool = makePool();
100
+ let ready;
101
+ const ensureReady = () => ready ??= pool.connect().catch((e) => {
102
+ ready = void 0;
103
+ const dead = pool;
104
+ pool = makePool();
105
+ dead.close().catch(() => {});
106
+ throw e;
107
+ });
108
+ return {
109
+ async run(prepared) {
110
+ await ensureReady();
111
+ const request = bindParams(pool.request(), prepared.params);
112
+ return toResult(await request.query(prepared.sql));
113
+ },
114
+ async transaction(statements) {
115
+ await ensureReady();
116
+ const tx = new Transaction(pool);
117
+ await tx.begin();
118
+ try {
119
+ const results = [];
120
+ for (const s of statements) {
121
+ const request = bindParams(new Request(tx), s.params);
122
+ results.push(toResult(await request.query(s.sql)));
123
+ }
124
+ await tx.commit();
125
+ return results;
126
+ } catch (err) {
127
+ await tx.rollback().catch(() => {});
128
+ throw err;
129
+ }
130
+ },
131
+ async explain(prepared) {
132
+ await ensureReady();
133
+ const tx = new Transaction(pool);
134
+ await tx.begin();
135
+ try {
136
+ await new Request(tx).batch("SET SHOWPLAN_XML ON");
137
+ const first = (await new Request(tx).batch(toExplainableBatch(prepared.sql))).recordset?.[0];
138
+ return parsePlanXml(String(first ? Object.values(first)[0] : ""));
139
+ } finally {
140
+ await new Request(tx).batch("SET SHOWPLAN_XML OFF").catch(() => {});
141
+ await tx.rollback().catch(() => {});
142
+ }
143
+ },
144
+ async close() {
145
+ await (ready?.catch(() => {}) ?? Promise.resolve());
146
+ await pool.close();
147
+ }
148
+ };
149
+ }
150
+ //#endregion
151
+ exports.createMssqlExecutor = createMssqlExecutor;
152
+ exports.parsePlanXml = parsePlanXml;
153
+ exports.toExplainableBatch = toExplainableBatch;
154
+
155
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../src/mssql/index.ts"],"sourcesContent":["// `mssql` is CommonJS and Node's ESM loader can't see its named exports — import the default and\n// destructure. (pg/mysql2/@libsql expose named exports fine.)\nimport mssql from 'mssql';\nimport type { config as MssqlDriverConfig, IResult } from 'mssql';\nimport type { DbExecutor, ExplainEstimate, PreparedSql, QueryResult, Row } from '../index';\n\nconst { ConnectionPool, Transaction, Request } = mssql;\n\n/** Connection settings — any `mssql` config object, or a raw connection string. */\nexport type MssqlConfig = MssqlDriverConfig | { connectionString: string };\n\n/** Read the T-SQL string literal starting at `from` (just past the opening quote), honouring `''`\n * escapes. Returns the unescaped text and the index just past the closing quote. */\nfunction readLiteral(sql: string, from: number): { text: string; end: number } | undefined {\n let out = '';\n for (let i = from; i < sql.length; i++) {\n if (sql[i] !== \"'\") {\n out += sql[i];\n continue;\n }\n if (sql[i + 1] === \"'\") {\n out += \"'\";\n i++;\n continue;\n }\n return { text: out, end: i + 1 };\n }\n return undefined; // unterminated — caller falls back to the raw batch\n}\n\n/**\n * Turn the mssql dialect's output into something SHOWPLAN can actually cost.\n *\n * It emits `SET NOCOUNT ON; exec sp_executesql N'<select>', N'<decls>'[, @p0 = …];`. SHOWPLAN does\n * NOT compile dynamic SQL, so explaining the EXEC yields a plan with no cost — the inner statement\n * must be lifted out. When it has parameters, that inner statement references `@p0`, undeclared\n * outside sp_executesql, so re-declare them. The assigned VALUES are dropped on purpose: a cost\n * estimate doesn't need them. Not a wrapped statement ⇒ returned unchanged.\n */\nexport function toExplainableBatch(sql: string): string {\n const m = /exec\\s+sp_executesql\\s+N'/i.exec(sql);\n if (!m) return sql;\n const inner = readLiteral(sql, m.index + m[0].length);\n if (!inner) return sql;\n const decl = /^\\s*,\\s*N'/.exec(sql.slice(inner.end));\n const decls = decl ? readLiteral(sql, inner.end + decl[0].length)?.text.trim() : '';\n return decls ? `DECLARE ${decls};\\n${inner.text}` : inner.text;\n}\n\n/** Parse a SHOWPLAN_XML document into the normalized estimate. Exported for unit tests. */\nexport function parsePlanXml(xml: string): ExplainEstimate {\n // A batch holds one <StmtSimple> per statement (the injected DECLARE, SET NOCOUNT ON, the SELECT).\n // Take the most expensive — never blindly the first, which is usually a costless preamble.\n let best: { cost: number; rows?: number } | undefined;\n for (const [tag] of xml.matchAll(/<StmtSimple\\b[^>]*>/g)) {\n const cost = Number(/StatementSubTreeCost=\"([\\d.eE+-]+)\"/.exec(tag)?.[1]);\n if (!Number.isFinite(cost) || (best && cost <= best.cost)) continue;\n const rows = Number(/StatementEstRows=\"([\\d.eE+-]+)\"/.exec(tag)?.[1]);\n best = { cost, rows: Number.isFinite(rows) ? rows : undefined };\n }\n return {\n cost: best?.cost,\n rows: best?.rows,\n fullScan: /PhysicalOp=\"(?:Table Scan|Clustered Index Scan|Index Scan)\"/.test(xml),\n plan: xml.slice(0, 500),\n };\n}\n\nconst toResult = <T>(result: IResult<unknown>): QueryResult<T> => ({\n rows: (result.recordset ?? []) as unknown as T[],\n rowCount: result.recordset ? result.recordset.length : (result.rowsAffected?.[0] ?? 0),\n});\n\n// Bind params (if any) as @p0..@pN. SQLEasy's mssql dialect inlines its values into the\n// sp_executesql batch and passes `params: []`, so nothing binds on that path; a caller passing\n// bound values must reference @p0.. in their SQL (mssql has no positional `?`). No `?`→`@p`\n// rewriting — that scan corrupts a `?` inside a string literal.\ntype BindableRequest = { input(name: string, value: unknown): unknown };\nconst bindParams = <R extends BindableRequest>(request: R, params?: readonly unknown[]): R => {\n (params ?? []).forEach((value, i) => request.input(`p${i}`, value));\n return request;\n};\n\n/**\n * A SQL Server executor backed by an `mssql` connection pool. Feed it the self-contained\n * `sp_executesql` batch a `MssqlQuery` builder emits (its `params` is always `[]`).\n */\nexport function createMssqlExecutor(config: MssqlConfig): DbExecutor {\n const makePool = () =>\n new ConnectionPool('connectionString' in config ? config.connectionString : config);\n let pool = makePool();\n\n // Single-flight connect that RECOVERS. Caching a rejected `pool.connect()` promise would brick the\n // pool forever (a DB restart / blip): every later query awaits the same settled rejection. So reset\n // the gate and rebuild the pool on failure, and the next call retries. (pg/mysql self-heal\n // per-acquire; mssql caches connect, so it alone needs this.)\n let ready: Promise<unknown> | undefined;\n const ensureReady = () =>\n (ready ??= pool.connect().catch((e: unknown) => {\n ready = undefined;\n const dead = pool;\n pool = makePool();\n void dead.close().catch(() => {});\n throw e;\n }));\n\n return {\n async run<T = Row>(prepared: PreparedSql): Promise<QueryResult<T>> {\n await ensureReady();\n const request = bindParams(pool.request(), prepared.params);\n return toResult<T>(await request.query(prepared.sql));\n },\n\n async transaction(statements: readonly PreparedSql[]): Promise<QueryResult[]> {\n await ensureReady();\n const tx = new Transaction(pool);\n await tx.begin();\n try {\n const results: QueryResult[] = [];\n for (const s of statements) {\n const request = bindParams(new Request(tx), s.params);\n results.push(toResult(await request.query(s.sql)));\n }\n await tx.commit();\n return results;\n } catch (err) {\n await tx.rollback().catch(() => {});\n throw err;\n }\n },\n\n async explain(prepared: PreparedSql): Promise<ExplainEstimate> {\n await ensureReady();\n // SQL Server has no EXPLAIN. The estimated plan comes from SET SHOWPLAN_XML, which (a) must be\n // the ONLY statement in its batch and (b) is SESSION state — so the SET and the query must run\n // on the SAME connection. A transaction pins one connection for both; the finally block always\n // clears the flag and releases it, so SHOWPLAN never leaks onto a connection serving reads.\n const tx = new Transaction(pool);\n await tx.begin();\n try {\n await new Request(tx).batch('SET SHOWPLAN_XML ON');\n const res = await new Request(tx).batch(toExplainableBatch(prepared.sql));\n const first = res.recordset?.[0] as Row | undefined;\n return parsePlanXml(String(first ? Object.values(first)[0] : ''));\n } finally {\n await new Request(tx).batch('SET SHOWPLAN_XML OFF').catch(() => {});\n await tx.rollback().catch(() => {});\n }\n },\n\n async close(): Promise<void> {\n await (ready?.catch(() => {}) ?? Promise.resolve());\n await pool.close();\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAM,EAAE,gBAAgB,aAAa,YAAY,MAAA;;;AAOjD,SAAS,YAAY,KAAa,MAAyD;CACzF,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,QAAQ,KAAK;EACtC,IAAI,IAAI,OAAO,KAAK;GAClB,OAAO,IAAI;GACX;EACF;EACA,IAAI,IAAI,IAAI,OAAO,KAAK;GACtB,OAAO;GACP;GACA;EACF;EACA,OAAO;GAAE,MAAM;GAAK,KAAK,IAAI;EAAE;CACjC;AAEF;;;;;;;;;;AAWA,SAAgB,mBAAmB,KAAqB;CACtD,MAAM,IAAI,6BAA6B,KAAK,GAAG;CAC/C,IAAI,CAAC,GAAG,OAAO;CACf,MAAM,QAAQ,YAAY,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM;CACpD,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,OAAO,aAAa,KAAK,IAAI,MAAM,MAAM,GAAG,CAAC;CACnD,MAAM,QAAQ,OAAO,YAAY,KAAK,MAAM,MAAM,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,IAAI;CACjF,OAAO,QAAQ,WAAW,MAAM,KAAK,MAAM,SAAS,MAAM;AAC5D;;AAGA,SAAgB,aAAa,KAA8B;CAGzD,IAAI;CACJ,KAAK,MAAM,CAAC,QAAQ,IAAI,SAAS,sBAAsB,GAAG;EACxD,MAAM,OAAO,OAAO,sCAAsC,KAAK,GAAG,CAAC,GAAG,EAAE;EACxE,IAAI,CAAC,OAAO,SAAS,IAAI,KAAM,QAAQ,QAAQ,KAAK,MAAO;EAC3D,MAAM,OAAO,OAAO,kCAAkC,KAAK,GAAG,CAAC,GAAG,EAAE;EACpE,OAAO;GAAE;GAAM,MAAM,OAAO,SAAS,IAAI,IAAI,OAAO,KAAA;EAAU;CAChE;CACA,OAAO;EACL,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,UAAU,8DAA8D,KAAK,GAAG;EAChF,MAAM,IAAI,MAAM,GAAG,GAAG;CACxB;AACF;AAEA,MAAM,YAAe,YAA8C;CACjE,MAAO,OAAO,aAAa,CAAC;CAC5B,UAAU,OAAO,YAAY,OAAO,UAAU,SAAU,OAAO,eAAe,MAAM;AACtF;AAOA,MAAM,cAAyC,SAAY,WAAmC;CAC5F,CAAC,UAAU,CAAC,EAAA,CAAG,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,KAAK,KAAK,CAAC;CAClE,OAAO;AACT;;;;;AAMA,SAAgB,oBAAoB,QAAiC;CACnE,MAAM,iBACJ,IAAI,eAAe,sBAAsB,SAAS,OAAO,mBAAmB,MAAM;CACpF,IAAI,OAAO,SAAS;CAMpB,IAAI;CACJ,MAAM,oBACH,UAAU,KAAK,QAAQ,CAAC,CAAC,OAAO,MAAe;EAC9C,QAAQ,KAAA;EACR,MAAM,OAAO;EACb,OAAO,SAAS;EAChB,KAAU,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAChC,MAAM;CACR,CAAC;CAEH,OAAO;EACL,MAAM,IAAa,UAAgD;GACjE,MAAM,YAAY;GAClB,MAAM,UAAU,WAAW,KAAK,QAAQ,GAAG,SAAS,MAAM;GAC1D,OAAO,SAAY,MAAM,QAAQ,MAAM,SAAS,GAAG,CAAC;EACtD;EAEA,MAAM,YAAY,YAA4D;GAC5E,MAAM,YAAY;GAClB,MAAM,KAAK,IAAI,YAAY,IAAI;GAC/B,MAAM,GAAG,MAAM;GACf,IAAI;IACF,MAAM,UAAyB,CAAC;IAChC,KAAK,MAAM,KAAK,YAAY;KAC1B,MAAM,UAAU,WAAW,IAAI,QAAQ,EAAE,GAAG,EAAE,MAAM;KACpD,QAAQ,KAAK,SAAS,MAAM,QAAQ,MAAM,EAAE,GAAG,CAAC,CAAC;IACnD;IACA,MAAM,GAAG,OAAO;IAChB,OAAO;GACT,SAAS,KAAK;IACZ,MAAM,GAAG,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC;IAClC,MAAM;GACR;EACF;EAEA,MAAM,QAAQ,UAAiD;GAC7D,MAAM,YAAY;GAKlB,MAAM,KAAK,IAAI,YAAY,IAAI;GAC/B,MAAM,GAAG,MAAM;GACf,IAAI;IACF,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,qBAAqB;IAEjD,MAAM,SAAQ,MADI,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,mBAAmB,SAAS,GAAG,CAAC,EAAA,CACtD,YAAY;IAC9B,OAAO,aAAa,OAAO,QAAQ,OAAO,OAAO,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;GAClE,UAAU;IACR,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,sBAAsB,CAAC,CAAC,YAAY,CAAC,CAAC;IAClE,MAAM,GAAG,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC;GACpC;EACF;EAEA,MAAM,QAAuB;GAC3B,OAAO,OAAO,YAAY,CAAC,CAAC,KAAK,QAAQ,QAAQ;GACjD,MAAM,KAAK,MAAM;EACnB;CACF;AACF"}