@bytebase/dbhub 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,2367 +3,28 @@ import {
3
3
  BUILTIN_TOOL_EXECUTE_SQL,
4
4
  BUILTIN_TOOL_SEARCH_OBJECTS,
5
5
  ConnectorManager,
6
- ConnectorRegistry,
7
- SafeURL,
8
- getDatabaseTypeFromDSN,
9
- getDefaultPortForType,
10
- getToolRegistry,
11
- initializeToolRegistry,
12
- isDemoMode,
13
- loadTomlConfig,
14
- mapArgumentsToArray,
15
- obfuscateDSNPassword,
16
- parseConnectionInfoFromDSN,
17
- resolvePort,
18
- resolveSourceConfigs,
19
- resolveTomlConfigPath,
20
- resolveTransport,
21
- splitSQLStatements,
22
- stripCommentsAndStrings
23
- } from "./chunk-LUNM7TUY.js";
24
-
25
- // src/connectors/postgres/index.ts
26
- import pg from "pg";
27
-
28
- // src/utils/sql-row-limiter.ts
29
- var SQLRowLimiter = class {
30
- /**
31
- * Check if a SQL statement is a SELECT query that can benefit from row limiting
32
- * Only handles SELECT queries
33
- */
34
- static isSelectQuery(sql2) {
35
- const trimmed = sql2.trim().toLowerCase();
36
- return trimmed.startsWith("select");
37
- }
38
- /**
39
- * Check if a SQL statement already has a LIMIT clause.
40
- * Strips comments and string literals first to avoid false positives.
41
- */
42
- static hasLimitClause(sql2) {
43
- const cleanedSQL = stripCommentsAndStrings(sql2);
44
- const limitRegex = /\blimit\s+(?:\d+|\$\d+|\?|@p\d+)/i;
45
- return limitRegex.test(cleanedSQL);
46
- }
47
- /**
48
- * Check if a SQL statement already has a TOP clause (SQL Server).
49
- * Strips comments and string literals first to avoid false positives.
50
- */
51
- static hasTopClause(sql2) {
52
- const cleanedSQL = stripCommentsAndStrings(sql2);
53
- const topRegex = /\bselect\s+top\s+\d+/i;
54
- return topRegex.test(cleanedSQL);
55
- }
56
- /**
57
- * Extract existing LIMIT value from SQL if present.
58
- * Strips comments and string literals first to avoid false positives.
59
- */
60
- static extractLimitValue(sql2) {
61
- const cleanedSQL = stripCommentsAndStrings(sql2);
62
- const limitMatch = cleanedSQL.match(/\blimit\s+(\d+)/i);
63
- if (limitMatch) {
64
- return parseInt(limitMatch[1], 10);
65
- }
66
- return null;
67
- }
68
- /**
69
- * Extract existing TOP value from SQL if present (SQL Server).
70
- * Strips comments and string literals first to avoid false positives.
71
- */
72
- static extractTopValue(sql2) {
73
- const cleanedSQL = stripCommentsAndStrings(sql2);
74
- const topMatch = cleanedSQL.match(/\bselect\s+top\s+(\d+)/i);
75
- if (topMatch) {
76
- return parseInt(topMatch[1], 10);
77
- }
78
- return null;
79
- }
80
- /**
81
- * Add or modify LIMIT clause in a SQL statement
82
- */
83
- static applyLimitToQuery(sql2, maxRows) {
84
- const existingLimit = this.extractLimitValue(sql2);
85
- if (existingLimit !== null) {
86
- const effectiveLimit = Math.min(existingLimit, maxRows);
87
- return sql2.replace(/\blimit\s+\d+/i, `LIMIT ${effectiveLimit}`);
88
- } else {
89
- const trimmed = sql2.trim();
90
- const hasSemicolon = trimmed.endsWith(";");
91
- const sqlWithoutSemicolon = hasSemicolon ? trimmed.slice(0, -1) : trimmed;
92
- return `${sqlWithoutSemicolon} LIMIT ${maxRows}${hasSemicolon ? ";" : ""}`;
93
- }
94
- }
95
- /**
96
- * Add or modify TOP clause in a SQL statement (SQL Server)
97
- */
98
- static applyTopToQuery(sql2, maxRows) {
99
- const existingTop = this.extractTopValue(sql2);
100
- if (existingTop !== null) {
101
- const effectiveTop = Math.min(existingTop, maxRows);
102
- return sql2.replace(/\bselect\s+top\s+\d+/i, `SELECT TOP ${effectiveTop}`);
103
- } else {
104
- return sql2.replace(/\bselect\s+/i, `SELECT TOP ${maxRows} `);
105
- }
106
- }
107
- /**
108
- * Check if a LIMIT clause uses a parameter placeholder (not a literal number).
109
- * Strips comments and string literals first to avoid false positives.
110
- */
111
- static hasParameterizedLimit(sql2) {
112
- const cleanedSQL = stripCommentsAndStrings(sql2);
113
- const parameterizedLimitRegex = /\blimit\s+(?:\$\d+|\?|@p\d+)/i;
114
- return parameterizedLimitRegex.test(cleanedSQL);
115
- }
116
- /**
117
- * Apply maxRows limit to a SELECT query only
118
- *
119
- * This method is used by PostgreSQL, MySQL, MariaDB, and SQLite connectors which all support
120
- * the LIMIT clause syntax. SQL Server uses applyMaxRowsForSQLServer() instead with TOP syntax.
121
- *
122
- * For parameterized LIMIT clauses (e.g., LIMIT $1 or LIMIT ?), we wrap the query in a subquery
123
- * to enforce max_rows as a hard cap, since the parameter value is not known until runtime.
124
- */
125
- static applyMaxRows(sql2, maxRows) {
126
- if (!maxRows || !this.isSelectQuery(sql2)) {
127
- return sql2;
128
- }
129
- if (this.hasParameterizedLimit(sql2)) {
130
- const trimmed = sql2.trim();
131
- const hasSemicolon = trimmed.endsWith(";");
132
- const sqlWithoutSemicolon = hasSemicolon ? trimmed.slice(0, -1) : trimmed;
133
- return `SELECT * FROM (${sqlWithoutSemicolon}) AS subq LIMIT ${maxRows}${hasSemicolon ? ";" : ""}`;
134
- }
135
- return this.applyLimitToQuery(sql2, maxRows);
136
- }
137
- /**
138
- * Apply maxRows limit to a SELECT query using SQL Server TOP syntax
139
- */
140
- static applyMaxRowsForSQLServer(sql2, maxRows) {
141
- if (!maxRows || !this.isSelectQuery(sql2)) {
142
- return sql2;
143
- }
144
- return this.applyTopToQuery(sql2, maxRows);
145
- }
146
- };
147
-
148
- // src/utils/identifier-quoter.ts
149
- function quoteIdentifier(identifier, dbType) {
150
- if (/[\0\x08\x09\x1a\n\r]/.test(identifier)) {
151
- throw new Error(`Invalid identifier: contains control characters: ${identifier}`);
152
- }
153
- if (!identifier) {
154
- throw new Error("Identifier cannot be empty");
155
- }
156
- switch (dbType) {
157
- case "postgres":
158
- case "sqlite":
159
- return `"${identifier.replace(/"/g, '""')}"`;
160
- case "mysql":
161
- case "mariadb":
162
- return `\`${identifier.replace(/`/g, "``")}\``;
163
- case "sqlserver":
164
- return `[${identifier.replace(/]/g, "]]")}]`;
165
- default:
166
- return `"${identifier.replace(/"/g, '""')}"`;
167
- }
168
- }
169
- function quoteQualifiedIdentifier(tableName, schemaName, dbType) {
170
- const quotedTable = quoteIdentifier(tableName, dbType);
171
- if (schemaName) {
172
- const quotedSchema = quoteIdentifier(schemaName, dbType);
173
- return `${quotedSchema}.${quotedTable}`;
174
- }
175
- return quotedTable;
176
- }
177
-
178
- // src/connectors/postgres/index.ts
179
- var { Pool } = pg;
180
- var PostgresDSNParser = class {
181
- async parse(dsn, config) {
182
- const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
183
- const queryTimeoutSeconds = config?.queryTimeoutSeconds;
184
- if (!this.isValidDSN(dsn)) {
185
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
186
- const expectedFormat = this.getSampleDSN();
187
- throw new Error(
188
- `Invalid PostgreSQL DSN format.
189
- Provided: ${obfuscatedDSN}
190
- Expected: ${expectedFormat}`
191
- );
192
- }
193
- try {
194
- const url = new SafeURL(dsn);
195
- const poolConfig = {
196
- host: url.hostname,
197
- port: url.port ? parseInt(url.port) : 5432,
198
- database: url.pathname ? url.pathname.substring(1) : "",
199
- // Remove leading '/' if exists
200
- user: url.username,
201
- password: url.password
202
- };
203
- url.forEachSearchParam((value, key) => {
204
- if (key === "sslmode") {
205
- if (value === "disable") {
206
- poolConfig.ssl = false;
207
- } else if (value === "require") {
208
- poolConfig.ssl = { rejectUnauthorized: false };
209
- } else {
210
- poolConfig.ssl = true;
211
- }
212
- }
213
- });
214
- if (connectionTimeoutSeconds !== void 0) {
215
- poolConfig.connectionTimeoutMillis = connectionTimeoutSeconds * 1e3;
216
- }
217
- if (queryTimeoutSeconds !== void 0) {
218
- poolConfig.query_timeout = queryTimeoutSeconds * 1e3;
219
- }
220
- return poolConfig;
221
- } catch (error) {
222
- throw new Error(
223
- `Failed to parse PostgreSQL DSN: ${error instanceof Error ? error.message : String(error)}`
224
- );
225
- }
226
- }
227
- getSampleDSN() {
228
- return "postgres://postgres:password@localhost:5432/postgres?sslmode=require";
229
- }
230
- isValidDSN(dsn) {
231
- try {
232
- return dsn.startsWith("postgres://") || dsn.startsWith("postgresql://");
233
- } catch (error) {
234
- return false;
235
- }
236
- }
237
- };
238
- var PostgresConnector = class _PostgresConnector {
239
- constructor() {
240
- this.id = "postgres";
241
- this.name = "PostgreSQL";
242
- this.dsnParser = new PostgresDSNParser();
243
- this.pool = null;
244
- // Source ID is set by ConnectorManager after cloning
245
- this.sourceId = "default";
246
- // Default schema for discovery methods (first entry from search_path, or "public")
247
- this.defaultSchema = "public";
248
- }
249
- getId() {
250
- return this.sourceId;
251
- }
252
- clone() {
253
- return new _PostgresConnector();
254
- }
255
- async connect(dsn, initScript, config) {
256
- this.defaultSchema = "public";
257
- try {
258
- const poolConfig = await this.dsnParser.parse(dsn, config);
259
- if (config?.readonly) {
260
- poolConfig.options = (poolConfig.options || "") + " -c default_transaction_read_only=on";
261
- }
262
- if (config?.searchPath) {
263
- const schemas = config.searchPath.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
264
- if (schemas.length > 0) {
265
- this.defaultSchema = schemas[0];
266
- const quotedSchemas = schemas.map((s) => quoteIdentifier(s, "postgres"));
267
- const optionsValue = quotedSchemas.join(",").replace(/\\/g, "\\\\").replace(/ /g, "\\ ");
268
- poolConfig.options = (poolConfig.options || "") + ` -c search_path=${optionsValue}`;
269
- }
270
- }
271
- this.pool = new Pool(poolConfig);
272
- const client = await this.pool.connect();
273
- client.release();
274
- } catch (err) {
275
- console.error("Failed to connect to PostgreSQL database:", err);
276
- throw err;
277
- }
278
- }
279
- async disconnect() {
280
- if (this.pool) {
281
- await this.pool.end();
282
- this.pool = null;
283
- }
284
- }
285
- async getSchemas() {
286
- if (!this.pool) {
287
- throw new Error("Not connected to database");
288
- }
289
- const client = await this.pool.connect();
290
- try {
291
- const result = await client.query(`
292
- SELECT schema_name
293
- FROM information_schema.schemata
294
- WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
295
- ORDER BY schema_name
296
- `);
297
- return result.rows.map((row) => row.schema_name);
298
- } finally {
299
- client.release();
300
- }
301
- }
302
- async getTables(schema) {
303
- if (!this.pool) {
304
- throw new Error("Not connected to database");
305
- }
306
- const client = await this.pool.connect();
307
- try {
308
- const schemaToUse = schema || this.defaultSchema;
309
- const result = await client.query(
310
- `
311
- SELECT table_name
312
- FROM information_schema.tables
313
- WHERE table_schema = $1
314
- ORDER BY table_name
315
- `,
316
- [schemaToUse]
317
- );
318
- return result.rows.map((row) => row.table_name);
319
- } finally {
320
- client.release();
321
- }
322
- }
323
- async tableExists(tableName, schema) {
324
- if (!this.pool) {
325
- throw new Error("Not connected to database");
326
- }
327
- const client = await this.pool.connect();
328
- try {
329
- const schemaToUse = schema || this.defaultSchema;
330
- const result = await client.query(
331
- `
332
- SELECT EXISTS (
333
- SELECT FROM information_schema.tables
334
- WHERE table_schema = $1
335
- AND table_name = $2
336
- )
337
- `,
338
- [schemaToUse, tableName]
339
- );
340
- return result.rows[0].exists;
341
- } finally {
342
- client.release();
343
- }
344
- }
345
- async getTableIndexes(tableName, schema) {
346
- if (!this.pool) {
347
- throw new Error("Not connected to database");
348
- }
349
- const client = await this.pool.connect();
350
- try {
351
- const schemaToUse = schema || this.defaultSchema;
352
- const result = await client.query(
353
- `
354
- SELECT
355
- i.relname as index_name,
356
- array_agg(a.attname) as column_names,
357
- ix.indisunique as is_unique,
358
- ix.indisprimary as is_primary
359
- FROM
360
- pg_class t,
361
- pg_class i,
362
- pg_index ix,
363
- pg_attribute a,
364
- pg_namespace ns
365
- WHERE
366
- t.oid = ix.indrelid
367
- AND i.oid = ix.indexrelid
368
- AND a.attrelid = t.oid
369
- AND a.attnum = ANY(ix.indkey)
370
- AND t.relkind = 'r'
371
- AND t.relname = $1
372
- AND ns.oid = t.relnamespace
373
- AND ns.nspname = $2
374
- GROUP BY
375
- i.relname,
376
- ix.indisunique,
377
- ix.indisprimary
378
- ORDER BY
379
- i.relname
380
- `,
381
- [tableName, schemaToUse]
382
- );
383
- return result.rows.map((row) => ({
384
- index_name: row.index_name,
385
- column_names: row.column_names,
386
- is_unique: row.is_unique,
387
- is_primary: row.is_primary
388
- }));
389
- } finally {
390
- client.release();
391
- }
392
- }
393
- async getTableSchema(tableName, schema) {
394
- if (!this.pool) {
395
- throw new Error("Not connected to database");
396
- }
397
- const client = await this.pool.connect();
398
- try {
399
- const schemaToUse = schema || this.defaultSchema;
400
- const result = await client.query(
401
- `
402
- SELECT
403
- c.column_name,
404
- c.data_type,
405
- c.is_nullable,
406
- c.column_default,
407
- pgd.description
408
- FROM information_schema.columns c
409
- LEFT JOIN pg_catalog.pg_namespace nsp
410
- ON nsp.nspname = c.table_schema
411
- LEFT JOIN pg_catalog.pg_class cls
412
- ON cls.relnamespace = nsp.oid
413
- AND cls.relname = c.table_name
414
- LEFT JOIN pg_catalog.pg_description pgd
415
- ON pgd.objoid = cls.oid
416
- AND pgd.objsubid = c.ordinal_position
417
- WHERE c.table_schema = $1
418
- AND c.table_name = $2
419
- ORDER BY c.ordinal_position
420
- `,
421
- [schemaToUse, tableName]
422
- );
423
- return result.rows;
424
- } finally {
425
- client.release();
426
- }
427
- }
428
- async getTableRowCount(tableName, schema) {
429
- if (!this.pool) {
430
- throw new Error("Not connected to database");
431
- }
432
- const client = await this.pool.connect();
433
- try {
434
- const schemaToUse = schema || this.defaultSchema;
435
- const result = await client.query(
436
- `
437
- SELECT c.reltuples::bigint as count
438
- FROM pg_class c
439
- JOIN pg_namespace n ON n.oid = c.relnamespace
440
- WHERE c.relname = $1
441
- AND n.nspname = $2
442
- AND c.relkind IN ('r','p','m','f')
443
- `,
444
- [tableName, schemaToUse]
445
- );
446
- if (result.rows.length > 0) {
447
- const count = Number(result.rows[0].count);
448
- return count >= 0 ? count : null;
449
- }
450
- return null;
451
- } finally {
452
- client.release();
453
- }
454
- }
455
- async getTableComment(tableName, schema) {
456
- if (!this.pool) {
457
- throw new Error("Not connected to database");
458
- }
459
- const client = await this.pool.connect();
460
- try {
461
- const schemaToUse = schema || this.defaultSchema;
462
- const result = await client.query(
463
- `
464
- SELECT obj_description(c.oid) as table_comment
465
- FROM pg_catalog.pg_class c
466
- JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
467
- WHERE c.relname = $1
468
- AND n.nspname = $2
469
- AND c.relkind IN ('r','p','m','f')
470
- `,
471
- [tableName, schemaToUse]
472
- );
473
- if (result.rows.length > 0) {
474
- return result.rows[0].table_comment || null;
475
- }
476
- return null;
477
- } finally {
478
- client.release();
479
- }
480
- }
481
- async getStoredProcedures(schema, routineType) {
482
- if (!this.pool) {
483
- throw new Error("Not connected to database");
484
- }
485
- const client = await this.pool.connect();
486
- try {
487
- const schemaToUse = schema || this.defaultSchema;
488
- const params = [schemaToUse];
489
- let typeFilter = "";
490
- if (routineType === "function") {
491
- typeFilter = " AND routine_type = 'FUNCTION'";
492
- } else if (routineType === "procedure") {
493
- typeFilter = " AND routine_type = 'PROCEDURE'";
494
- }
495
- const result = await client.query(
496
- `
497
- SELECT
498
- routine_name
499
- FROM information_schema.routines
500
- WHERE routine_schema = $1${typeFilter}
501
- ORDER BY routine_name
502
- `,
503
- params
504
- );
505
- return result.rows.map((row) => row.routine_name);
506
- } finally {
507
- client.release();
508
- }
509
- }
510
- async getStoredProcedureDetail(procedureName, schema) {
511
- if (!this.pool) {
512
- throw new Error("Not connected to database");
513
- }
514
- const client = await this.pool.connect();
515
- try {
516
- const schemaToUse = schema || this.defaultSchema;
517
- const result = await client.query(
518
- `
519
- SELECT
520
- routine_name as procedure_name,
521
- routine_type,
522
- CASE WHEN routine_type = 'PROCEDURE' THEN 'procedure' ELSE 'function' END as procedure_type,
523
- external_language as language,
524
- data_type as return_type,
525
- routine_definition as definition,
526
- (
527
- SELECT string_agg(
528
- parameter_name || ' ' ||
529
- parameter_mode || ' ' ||
530
- data_type,
531
- ', '
532
- )
533
- FROM information_schema.parameters
534
- WHERE specific_schema = $1
535
- AND specific_name = $2
536
- AND parameter_name IS NOT NULL
537
- ) as parameter_list
538
- FROM information_schema.routines
539
- WHERE routine_schema = $1
540
- AND routine_name = $2
541
- `,
542
- [schemaToUse, procedureName]
543
- );
544
- if (result.rows.length === 0) {
545
- throw new Error(`Stored procedure '${procedureName}' not found in schema '${schemaToUse}'`);
546
- }
547
- const procedure = result.rows[0];
548
- let definition = procedure.definition;
549
- try {
550
- const oidResult = await client.query(
551
- `
552
- SELECT p.oid, p.prosrc
553
- FROM pg_proc p
554
- JOIN pg_namespace n ON p.pronamespace = n.oid
555
- WHERE p.proname = $1
556
- AND n.nspname = $2
557
- `,
558
- [procedureName, schemaToUse]
559
- );
560
- if (oidResult.rows.length > 0) {
561
- if (!definition) {
562
- const oid = oidResult.rows[0].oid;
563
- const defResult = await client.query(`SELECT pg_get_functiondef($1)`, [oid]);
564
- if (defResult.rows.length > 0) {
565
- definition = defResult.rows[0].pg_get_functiondef;
566
- } else {
567
- definition = oidResult.rows[0].prosrc;
568
- }
569
- }
570
- }
571
- } catch (err) {
572
- console.error(`Error getting procedure definition: ${err}`);
573
- }
574
- return {
575
- procedure_name: procedure.procedure_name,
576
- procedure_type: procedure.procedure_type,
577
- language: procedure.language || "sql",
578
- parameter_list: procedure.parameter_list || "",
579
- return_type: procedure.return_type !== "void" ? procedure.return_type : void 0,
580
- definition: definition || void 0
581
- };
582
- } finally {
583
- client.release();
584
- }
585
- }
586
- async executeSQL(sql2, options, parameters) {
587
- if (!this.pool) {
588
- throw new Error("Not connected to database");
589
- }
590
- const client = await this.pool.connect();
591
- try {
592
- const statements = splitSQLStatements(sql2, "postgres");
593
- if (statements.length === 1) {
594
- const processedStatement = SQLRowLimiter.applyMaxRows(statements[0], options.maxRows);
595
- let result;
596
- if (parameters && parameters.length > 0) {
597
- try {
598
- result = await client.query(processedStatement, parameters);
599
- } catch (error) {
600
- console.error(`[PostgreSQL executeSQL] ERROR: ${error.message}`);
601
- console.error(`[PostgreSQL executeSQL] SQL: ${processedStatement}`);
602
- console.error(`[PostgreSQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
603
- throw error;
604
- }
605
- } else {
606
- result = await client.query(processedStatement);
607
- }
608
- return { rows: result.rows, rowCount: result.rowCount ?? result.rows.length };
609
- } else {
610
- if (parameters && parameters.length > 0) {
611
- throw new Error("Parameters are not supported for multi-statement queries in PostgreSQL");
612
- }
613
- let allRows = [];
614
- let totalRowCount = 0;
615
- await client.query("BEGIN");
616
- try {
617
- for (let statement of statements) {
618
- const processedStatement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
619
- const result = await client.query(processedStatement);
620
- if (result.rows && result.rows.length > 0) {
621
- allRows.push(...result.rows);
622
- }
623
- if (result.rowCount) {
624
- totalRowCount += result.rowCount;
625
- }
626
- }
627
- await client.query("COMMIT");
628
- } catch (error) {
629
- await client.query("ROLLBACK");
630
- throw error;
631
- }
632
- return { rows: allRows, rowCount: totalRowCount };
633
- }
634
- } finally {
635
- client.release();
636
- }
637
- }
638
- };
639
- var postgresConnector = new PostgresConnector();
640
- ConnectorRegistry.register(postgresConnector);
641
-
642
- // src/connectors/sqlserver/index.ts
643
- import sql from "mssql";
644
- import { DefaultAzureCredential } from "@azure/identity";
645
- var SQLServerDSNParser = class {
646
- async parse(dsn, config) {
647
- const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
648
- const queryTimeoutSeconds = config?.queryTimeoutSeconds;
649
- if (!this.isValidDSN(dsn)) {
650
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
651
- const expectedFormat = this.getSampleDSN();
652
- throw new Error(
653
- `Invalid SQL Server DSN format.
654
- Provided: ${obfuscatedDSN}
655
- Expected: ${expectedFormat}`
656
- );
657
- }
658
- try {
659
- const url = new SafeURL(dsn);
660
- const options = {};
661
- url.forEachSearchParam((value, key) => {
662
- if (key === "authentication") {
663
- options.authentication = value;
664
- } else if (key === "sslmode") {
665
- options.sslmode = value;
666
- } else if (key === "instanceName") {
667
- options.instanceName = value;
668
- } else if (key === "domain") {
669
- options.domain = value;
670
- }
671
- });
672
- if (options.authentication === "ntlm" && !options.domain) {
673
- throw new Error("NTLM authentication requires 'domain' parameter");
674
- }
675
- if (options.domain && options.authentication !== "ntlm") {
676
- throw new Error("Parameter 'domain' requires 'authentication=ntlm'");
677
- }
678
- if (options.sslmode) {
679
- if (options.sslmode === "disable") {
680
- options.encrypt = false;
681
- options.trustServerCertificate = false;
682
- } else if (options.sslmode === "require") {
683
- options.encrypt = true;
684
- options.trustServerCertificate = true;
685
- }
686
- }
687
- const config2 = {
688
- server: url.hostname,
689
- port: url.port ? parseInt(url.port) : 1433,
690
- // Default SQL Server port
691
- database: url.pathname ? url.pathname.substring(1) : "",
692
- // Remove leading slash
693
- options: {
694
- encrypt: options.encrypt ?? false,
695
- // Default to unencrypted for development
696
- trustServerCertificate: options.trustServerCertificate ?? false,
697
- ...connectionTimeoutSeconds !== void 0 && {
698
- connectTimeout: connectionTimeoutSeconds * 1e3
699
- },
700
- ...queryTimeoutSeconds !== void 0 && {
701
- requestTimeout: queryTimeoutSeconds * 1e3
702
- },
703
- instanceName: options.instanceName
704
- // Add named instance support
705
- }
706
- };
707
- switch (options.authentication) {
708
- case "azure-active-directory-access-token": {
709
- try {
710
- const credential = new DefaultAzureCredential();
711
- const token = await credential.getToken("https://database.windows.net/");
712
- config2.authentication = {
713
- type: "azure-active-directory-access-token",
714
- options: {
715
- token: token.token
716
- }
717
- };
718
- } catch (error) {
719
- const errorMessage = error instanceof Error ? error.message : String(error);
720
- throw new Error(`Failed to get Azure AD token: ${errorMessage}`);
721
- }
722
- break;
723
- }
724
- case "ntlm":
725
- config2.authentication = {
726
- type: "ntlm",
727
- options: {
728
- domain: options.domain,
729
- userName: url.username,
730
- password: url.password
731
- }
732
- };
733
- break;
734
- default:
735
- config2.user = url.username;
736
- config2.password = url.password;
737
- break;
738
- }
739
- return config2;
740
- } catch (error) {
741
- throw new Error(
742
- `Failed to parse SQL Server DSN: ${error instanceof Error ? error.message : String(error)}`
743
- );
744
- }
745
- }
746
- getSampleDSN() {
747
- return "sqlserver://username:password@localhost:1433/database?sslmode=disable&instanceName=INSTANCE1";
748
- }
749
- isValidDSN(dsn) {
750
- try {
751
- return dsn.startsWith("sqlserver://");
752
- } catch (error) {
753
- return false;
754
- }
755
- }
756
- };
757
- var SQLServerConnector = class _SQLServerConnector {
758
- constructor() {
759
- this.id = "sqlserver";
760
- this.name = "SQL Server";
761
- this.dsnParser = new SQLServerDSNParser();
762
- // Source ID is set by ConnectorManager after cloning
763
- this.sourceId = "default";
764
- }
765
- getId() {
766
- return this.sourceId;
767
- }
768
- clone() {
769
- return new _SQLServerConnector();
770
- }
771
- async connect(dsn, initScript, config) {
772
- try {
773
- this.config = await this.dsnParser.parse(dsn, config);
774
- if (!this.config.options) {
775
- this.config.options = {};
776
- }
777
- this.connection = await new sql.ConnectionPool(this.config).connect();
778
- } catch (error) {
779
- throw error;
780
- }
781
- }
782
- async disconnect() {
783
- if (this.connection) {
784
- await this.connection.close();
785
- this.connection = void 0;
786
- }
787
- }
788
- async getSchemas() {
789
- if (!this.connection) {
790
- throw new Error("Not connected to SQL Server database");
791
- }
792
- try {
793
- const result = await this.connection.request().query(`
794
- SELECT SCHEMA_NAME
795
- FROM INFORMATION_SCHEMA.SCHEMATA
796
- ORDER BY SCHEMA_NAME
797
- `);
798
- return result.recordset.map((row) => row.SCHEMA_NAME);
799
- } catch (error) {
800
- throw new Error(`Failed to get schemas: ${error.message}`);
801
- }
802
- }
803
- async getTables(schema) {
804
- if (!this.connection) {
805
- throw new Error("Not connected to SQL Server database");
806
- }
807
- try {
808
- const schemaToUse = schema || "dbo";
809
- const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
810
- const query = `
811
- SELECT TABLE_NAME
812
- FROM INFORMATION_SCHEMA.TABLES
813
- WHERE TABLE_SCHEMA = @schema
814
- ORDER BY TABLE_NAME
815
- `;
816
- const result = await request.query(query);
817
- return result.recordset.map((row) => row.TABLE_NAME);
818
- } catch (error) {
819
- throw new Error(`Failed to get tables: ${error.message}`);
820
- }
821
- }
822
- async tableExists(tableName, schema) {
823
- if (!this.connection) {
824
- throw new Error("Not connected to SQL Server database");
825
- }
826
- try {
827
- const schemaToUse = schema || "dbo";
828
- const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
829
- const query = `
830
- SELECT COUNT(*) as count
831
- FROM INFORMATION_SCHEMA.TABLES
832
- WHERE TABLE_NAME = @tableName
833
- AND TABLE_SCHEMA = @schema
834
- `;
835
- const result = await request.query(query);
836
- return result.recordset[0].count > 0;
837
- } catch (error) {
838
- throw new Error(`Failed to check if table exists: ${error.message}`);
839
- }
840
- }
841
- async getTableIndexes(tableName, schema) {
842
- if (!this.connection) {
843
- throw new Error("Not connected to SQL Server database");
844
- }
845
- try {
846
- const schemaToUse = schema || "dbo";
847
- const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
848
- const query = `
849
- SELECT i.name AS index_name,
850
- i.is_unique,
851
- i.is_primary_key,
852
- c.name AS column_name,
853
- ic.key_ordinal
854
- FROM sys.indexes i
855
- INNER JOIN
856
- sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
857
- INNER JOIN
858
- sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
859
- INNER JOIN
860
- sys.tables t ON i.object_id = t.object_id
861
- INNER JOIN
862
- sys.schemas s ON t.schema_id = s.schema_id
863
- WHERE t.name = @tableName
864
- AND s.name = @schema
865
- ORDER BY i.name,
866
- ic.key_ordinal
867
- `;
868
- const result = await request.query(query);
869
- const indexMap = /* @__PURE__ */ new Map();
870
- for (const row of result.recordset) {
871
- const indexName = row.index_name;
872
- const columnName = row.column_name;
873
- const isUnique = !!row.is_unique;
874
- const isPrimary = !!row.is_primary_key;
875
- if (!indexMap.has(indexName)) {
876
- indexMap.set(indexName, {
877
- columns: [],
878
- is_unique: isUnique,
879
- is_primary: isPrimary
880
- });
881
- }
882
- const indexInfo = indexMap.get(indexName);
883
- indexInfo.columns.push(columnName);
884
- }
885
- const indexes = [];
886
- indexMap.forEach((info, name) => {
887
- indexes.push({
888
- index_name: name,
889
- column_names: info.columns,
890
- is_unique: info.is_unique,
891
- is_primary: info.is_primary
892
- });
893
- });
894
- return indexes;
895
- } catch (error) {
896
- throw new Error(`Failed to get indexes for table ${tableName}: ${error.message}`);
897
- }
898
- }
899
- async getTableSchema(tableName, schema) {
900
- if (!this.connection) {
901
- throw new Error("Not connected to SQL Server database");
902
- }
903
- try {
904
- const schemaToUse = schema || "dbo";
905
- const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
906
- const query = `
907
- SELECT c.COLUMN_NAME as column_name,
908
- c.DATA_TYPE as data_type,
909
- c.IS_NULLABLE as is_nullable,
910
- c.COLUMN_DEFAULT as column_default,
911
- ep.value as description
912
- FROM INFORMATION_SCHEMA.COLUMNS c
913
- LEFT JOIN sys.columns sc
914
- ON sc.name = c.COLUMN_NAME
915
- AND sc.object_id = OBJECT_ID(QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME))
916
- LEFT JOIN sys.extended_properties ep
917
- ON ep.major_id = sc.object_id
918
- AND ep.minor_id = sc.column_id
919
- AND ep.name = 'MS_Description'
920
- WHERE c.TABLE_NAME = @tableName
921
- AND c.TABLE_SCHEMA = @schema
922
- ORDER BY c.ORDINAL_POSITION
923
- `;
924
- const result = await request.query(query);
925
- return result.recordset.map((row) => ({
926
- ...row,
927
- description: row.description || null
928
- }));
929
- } catch (error) {
930
- throw new Error(`Failed to get schema for table ${tableName}: ${error.message}`);
931
- }
932
- }
933
- async getTableComment(tableName, schema) {
934
- if (!this.connection) {
935
- throw new Error("Not connected to SQL Server database");
936
- }
937
- try {
938
- const schemaToUse = schema || "dbo";
939
- const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
940
- const query = `
941
- SELECT ep.value as table_comment
942
- FROM sys.extended_properties ep
943
- JOIN sys.tables t ON ep.major_id = t.object_id
944
- JOIN sys.schemas s ON t.schema_id = s.schema_id
945
- WHERE ep.minor_id = 0
946
- AND ep.name = 'MS_Description'
947
- AND t.name = @tableName
948
- AND s.name = @schema
949
- `;
950
- const result = await request.query(query);
951
- if (result.recordset.length > 0) {
952
- return result.recordset[0].table_comment || null;
953
- }
954
- return null;
955
- } catch (error) {
956
- return null;
957
- }
958
- }
959
- async getStoredProcedures(schema, routineType) {
960
- if (!this.connection) {
961
- throw new Error("Not connected to SQL Server database");
962
- }
963
- try {
964
- const schemaToUse = schema || "dbo";
965
- const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
966
- let typeFilter;
967
- if (routineType === "function") {
968
- typeFilter = "AND ROUTINE_TYPE = 'FUNCTION'";
969
- } else if (routineType === "procedure") {
970
- typeFilter = "AND ROUTINE_TYPE = 'PROCEDURE'";
971
- } else {
972
- typeFilter = "AND (ROUTINE_TYPE = 'PROCEDURE' OR ROUTINE_TYPE = 'FUNCTION')";
973
- }
974
- const query = `
975
- SELECT ROUTINE_NAME
976
- FROM INFORMATION_SCHEMA.ROUTINES
977
- WHERE ROUTINE_SCHEMA = @schema
978
- ${typeFilter}
979
- ORDER BY ROUTINE_NAME
980
- `;
981
- const result = await request.query(query);
982
- return result.recordset.map((row) => row.ROUTINE_NAME);
983
- } catch (error) {
984
- throw new Error(`Failed to get stored procedures: ${error.message}`);
985
- }
986
- }
987
- async getStoredProcedureDetail(procedureName, schema) {
988
- if (!this.connection) {
989
- throw new Error("Not connected to SQL Server database");
990
- }
991
- try {
992
- const schemaToUse = schema || "dbo";
993
- const request = this.connection.request().input("procedureName", sql.VarChar, procedureName).input("schema", sql.VarChar, schemaToUse);
994
- const routineQuery = `
995
- SELECT ROUTINE_NAME as procedure_name,
996
- ROUTINE_TYPE,
997
- DATA_TYPE as return_data_type
998
- FROM INFORMATION_SCHEMA.ROUTINES
999
- WHERE ROUTINE_NAME = @procedureName
1000
- AND ROUTINE_SCHEMA = @schema
1001
- `;
1002
- const routineResult = await request.query(routineQuery);
1003
- if (routineResult.recordset.length === 0) {
1004
- throw new Error(`Stored procedure '${procedureName}' not found in schema '${schemaToUse}'`);
1005
- }
1006
- const routine = routineResult.recordset[0];
1007
- const parameterQuery = `
1008
- SELECT PARAMETER_NAME,
1009
- PARAMETER_MODE,
1010
- DATA_TYPE,
1011
- CHARACTER_MAXIMUM_LENGTH,
1012
- ORDINAL_POSITION
1013
- FROM INFORMATION_SCHEMA.PARAMETERS
1014
- WHERE SPECIFIC_NAME = @procedureName
1015
- AND SPECIFIC_SCHEMA = @schema
1016
- ORDER BY ORDINAL_POSITION
1017
- `;
1018
- const parameterResult = await request.query(parameterQuery);
1019
- let parameterList = "";
1020
- if (parameterResult.recordset.length > 0) {
1021
- parameterList = parameterResult.recordset.map(
1022
- (param) => {
1023
- const lengthStr = param.CHARACTER_MAXIMUM_LENGTH > 0 ? `(${param.CHARACTER_MAXIMUM_LENGTH})` : "";
1024
- return `${param.PARAMETER_NAME} ${param.PARAMETER_MODE} ${param.DATA_TYPE}${lengthStr}`;
1025
- }
1026
- ).join(", ");
1027
- }
1028
- const definitionQuery = `
1029
- SELECT definition
1030
- FROM sys.sql_modules sm
1031
- JOIN sys.objects o ON sm.object_id = o.object_id
1032
- JOIN sys.schemas s ON o.schema_id = s.schema_id
1033
- WHERE o.name = @procedureName
1034
- AND s.name = @schema
1035
- `;
1036
- const definitionResult = await request.query(definitionQuery);
1037
- let definition = void 0;
1038
- if (definitionResult.recordset.length > 0) {
1039
- definition = definitionResult.recordset[0].definition;
1040
- }
1041
- return {
1042
- procedure_name: routine.procedure_name,
1043
- procedure_type: routine.ROUTINE_TYPE === "PROCEDURE" ? "procedure" : "function",
1044
- language: "sql",
1045
- // SQL Server procedures are typically in T-SQL
1046
- parameter_list: parameterList,
1047
- return_type: routine.ROUTINE_TYPE === "FUNCTION" ? routine.return_data_type : void 0,
1048
- definition
1049
- };
1050
- } catch (error) {
1051
- throw new Error(`Failed to get stored procedure details: ${error.message}`);
1052
- }
1053
- }
1054
- async executeSQL(sqlQuery, options, parameters) {
1055
- if (!this.connection) {
1056
- throw new Error("Not connected to SQL Server database");
1057
- }
1058
- try {
1059
- let processedSQL = sqlQuery;
1060
- if (options.maxRows) {
1061
- processedSQL = SQLRowLimiter.applyMaxRowsForSQLServer(sqlQuery, options.maxRows);
1062
- }
1063
- const request = this.connection.request();
1064
- if (parameters && parameters.length > 0) {
1065
- parameters.forEach((param, index) => {
1066
- const paramName = `p${index + 1}`;
1067
- if (typeof param === "string") {
1068
- request.input(paramName, sql.VarChar, param);
1069
- } else if (typeof param === "number") {
1070
- if (Number.isInteger(param)) {
1071
- request.input(paramName, sql.Int, param);
1072
- } else {
1073
- request.input(paramName, sql.Float, param);
1074
- }
1075
- } else if (typeof param === "boolean") {
1076
- request.input(paramName, sql.Bit, param);
1077
- } else if (param === null || param === void 0) {
1078
- request.input(paramName, sql.VarChar, param);
1079
- } else if (Array.isArray(param)) {
1080
- request.input(paramName, sql.VarChar, JSON.stringify(param));
1081
- } else {
1082
- request.input(paramName, sql.VarChar, JSON.stringify(param));
1083
- }
1084
- });
1085
- }
1086
- let result;
1087
- try {
1088
- result = await request.query(processedSQL);
1089
- } catch (error) {
1090
- if (parameters && parameters.length > 0) {
1091
- console.error(`[SQL Server executeSQL] ERROR: ${error.message}`);
1092
- console.error(`[SQL Server executeSQL] SQL: ${processedSQL}`);
1093
- console.error(`[SQL Server executeSQL] Parameters: ${JSON.stringify(parameters)}`);
1094
- }
1095
- throw error;
1096
- }
1097
- return {
1098
- rows: result.recordset || [],
1099
- rowCount: result.rowsAffected[0] || 0
1100
- };
1101
- } catch (error) {
1102
- throw new Error(`Failed to execute query: ${error.message}`);
1103
- }
1104
- }
1105
- };
1106
- var sqlServerConnector = new SQLServerConnector();
1107
- ConnectorRegistry.register(sqlServerConnector);
1108
-
1109
- // src/connectors/sqlite/index.ts
1110
- import Database from "better-sqlite3";
1111
- var SQLiteDSNParser = class {
1112
- async parse(dsn, config) {
1113
- if (!this.isValidDSN(dsn)) {
1114
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
1115
- const expectedFormat = this.getSampleDSN();
1116
- throw new Error(
1117
- `Invalid SQLite DSN format.
1118
- Provided: ${obfuscatedDSN}
1119
- Expected: ${expectedFormat}`
1120
- );
1121
- }
1122
- try {
1123
- const url = new SafeURL(dsn);
1124
- let dbPath;
1125
- if (url.hostname === "" && url.pathname === "/:memory:") {
1126
- dbPath = ":memory:";
1127
- } else {
1128
- if (url.pathname.startsWith("//")) {
1129
- dbPath = url.pathname.substring(2);
1130
- } else if (url.pathname.match(/^\/[A-Za-z]:\//)) {
1131
- dbPath = url.pathname.substring(1);
1132
- } else {
1133
- dbPath = url.pathname;
1134
- }
1135
- }
1136
- return { dbPath };
1137
- } catch (error) {
1138
- throw new Error(
1139
- `Failed to parse SQLite DSN: ${error instanceof Error ? error.message : String(error)}`
1140
- );
1141
- }
1142
- }
1143
- getSampleDSN() {
1144
- return "sqlite:///path/to/database.db";
1145
- }
1146
- isValidDSN(dsn) {
1147
- try {
1148
- return dsn.startsWith("sqlite://");
1149
- } catch (error) {
1150
- return false;
1151
- }
1152
- }
1153
- };
1154
- var SQLiteConnector = class _SQLiteConnector {
1155
- constructor() {
1156
- this.id = "sqlite";
1157
- this.name = "SQLite";
1158
- this.dsnParser = new SQLiteDSNParser();
1159
- this.db = null;
1160
- this.dbPath = ":memory:";
1161
- // Default to in-memory database
1162
- // Source ID is set by ConnectorManager after cloning
1163
- this.sourceId = "default";
1164
- }
1165
- getId() {
1166
- return this.sourceId;
1167
- }
1168
- clone() {
1169
- return new _SQLiteConnector();
1170
- }
1171
- /**
1172
- * Connect to SQLite database
1173
- * Note: SQLite does not support connection timeouts as it's a local file-based database.
1174
- * The config parameter is accepted for interface compliance but ignored.
1175
- */
1176
- async connect(dsn, initScript, config) {
1177
- const parsedConfig = await this.dsnParser.parse(dsn, config);
1178
- this.dbPath = parsedConfig.dbPath;
1179
- try {
1180
- const dbOptions = {};
1181
- if (config?.readonly && this.dbPath !== ":memory:") {
1182
- dbOptions.readonly = true;
1183
- }
1184
- this.db = new Database(this.dbPath, dbOptions);
1185
- if (initScript) {
1186
- this.db.exec(initScript);
1187
- }
1188
- } catch (error) {
1189
- console.error("Failed to connect to SQLite database:", error);
1190
- throw error;
1191
- }
1192
- }
1193
- async disconnect() {
1194
- if (this.db) {
1195
- try {
1196
- if (!this.db.inTransaction) {
1197
- this.db.close();
1198
- } else {
1199
- try {
1200
- this.db.exec("ROLLBACK");
1201
- } catch (rollbackError) {
1202
- }
1203
- this.db.close();
1204
- }
1205
- this.db = null;
1206
- } catch (error) {
1207
- console.error("Error during SQLite disconnect:", error);
1208
- this.db = null;
1209
- }
1210
- }
1211
- return Promise.resolve();
1212
- }
1213
- async getSchemas() {
1214
- if (!this.db) {
1215
- throw new Error("Not connected to SQLite database");
1216
- }
1217
- return ["main"];
1218
- }
1219
- async getTables(schema) {
1220
- if (!this.db) {
1221
- throw new Error("Not connected to SQLite database");
1222
- }
1223
- try {
1224
- const rows = this.db.prepare(
1225
- `
1226
- SELECT name FROM sqlite_master
1227
- WHERE type='table' AND name NOT LIKE 'sqlite_%'
1228
- ORDER BY name
1229
- `
1230
- ).all();
1231
- return rows.map((row) => row.name);
1232
- } catch (error) {
1233
- throw error;
1234
- }
1235
- }
1236
- async tableExists(tableName, schema) {
1237
- if (!this.db) {
1238
- throw new Error("Not connected to SQLite database");
1239
- }
1240
- try {
1241
- const row = this.db.prepare(
1242
- `
1243
- SELECT name FROM sqlite_master
1244
- WHERE type='table' AND name = ?
1245
- `
1246
- ).get(tableName);
1247
- return !!row;
1248
- } catch (error) {
1249
- throw error;
1250
- }
1251
- }
1252
- async getTableIndexes(tableName, schema) {
1253
- if (!this.db) {
1254
- throw new Error("Not connected to SQLite database");
1255
- }
1256
- try {
1257
- const indexInfoRows = this.db.prepare(
1258
- `
1259
- SELECT
1260
- name as index_name,
1261
- 0 as is_unique
1262
- FROM sqlite_master
1263
- WHERE type = 'index'
1264
- AND tbl_name = ?
1265
- `
1266
- ).all(tableName);
1267
- const quotedTableName = quoteIdentifier(tableName, "sqlite");
1268
- const indexListRows = this.db.prepare(`PRAGMA index_list(${quotedTableName})`).all();
1269
- const indexUniqueMap = /* @__PURE__ */ new Map();
1270
- for (const indexListRow of indexListRows) {
1271
- indexUniqueMap.set(indexListRow.name, indexListRow.unique === 1);
1272
- }
1273
- const tableInfo = this.db.prepare(`PRAGMA table_info(${quotedTableName})`).all();
1274
- const pkColumns = tableInfo.filter((col) => col.pk > 0).map((col) => col.name);
1275
- const results = [];
1276
- for (const indexInfo of indexInfoRows) {
1277
- const quotedIndexName = quoteIdentifier(indexInfo.index_name, "sqlite");
1278
- const indexDetailRows = this.db.prepare(`PRAGMA index_info(${quotedIndexName})`).all();
1279
- const columnNames = indexDetailRows.map((row) => row.name);
1280
- results.push({
1281
- index_name: indexInfo.index_name,
1282
- column_names: columnNames,
1283
- is_unique: indexUniqueMap.get(indexInfo.index_name) || false,
1284
- is_primary: false
1285
- });
1286
- }
1287
- if (pkColumns.length > 0) {
1288
- results.push({
1289
- index_name: "PRIMARY",
1290
- column_names: pkColumns,
1291
- is_unique: true,
1292
- is_primary: true
1293
- });
1294
- }
1295
- return results;
1296
- } catch (error) {
1297
- throw error;
1298
- }
1299
- }
1300
- async getTableSchema(tableName, schema) {
1301
- if (!this.db) {
1302
- throw new Error("Not connected to SQLite database");
1303
- }
1304
- try {
1305
- const quotedTableName = quoteIdentifier(tableName, "sqlite");
1306
- const rows = this.db.prepare(`PRAGMA table_info(${quotedTableName})`).all();
1307
- const columns = rows.map((row) => ({
1308
- column_name: row.name,
1309
- data_type: row.type,
1310
- // In SQLite, primary key columns are automatically NOT NULL even if notnull=0
1311
- is_nullable: row.notnull === 1 || row.pk > 0 ? "NO" : "YES",
1312
- column_default: row.dflt_value,
1313
- description: null
1314
- }));
1315
- return columns;
1316
- } catch (error) {
1317
- throw error;
1318
- }
1319
- }
1320
- async getStoredProcedures(schema, routineType) {
1321
- if (!this.db) {
1322
- throw new Error("Not connected to SQLite database");
1323
- }
1324
- return [];
1325
- }
1326
- async getStoredProcedureDetail(procedureName, schema) {
1327
- if (!this.db) {
1328
- throw new Error("Not connected to SQLite database");
1329
- }
1330
- throw new Error(
1331
- "SQLite does not support stored procedures. Functions are defined programmatically through the SQLite API, not stored in the database."
1332
- );
1333
- }
1334
- async executeSQL(sql2, options, parameters) {
1335
- if (!this.db) {
1336
- throw new Error("Not connected to SQLite database");
1337
- }
1338
- try {
1339
- const statements = splitSQLStatements(sql2, "sqlite");
1340
- if (statements.length === 1) {
1341
- let processedStatement = statements[0];
1342
- const trimmedStatement = statements[0].toLowerCase().trim();
1343
- const isReadStatement = trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"));
1344
- if (options.maxRows) {
1345
- processedStatement = SQLRowLimiter.applyMaxRows(processedStatement, options.maxRows);
1346
- }
1347
- if (isReadStatement) {
1348
- if (parameters && parameters.length > 0) {
1349
- try {
1350
- const rows = this.db.prepare(processedStatement).all(...parameters);
1351
- return { rows, rowCount: rows.length };
1352
- } catch (error) {
1353
- console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
1354
- console.error(`[SQLite executeSQL] SQL: ${processedStatement}`);
1355
- console.error(`[SQLite executeSQL] Parameters: ${JSON.stringify(parameters)}`);
1356
- throw error;
1357
- }
1358
- } else {
1359
- const rows = this.db.prepare(processedStatement).all();
1360
- return { rows, rowCount: rows.length };
1361
- }
1362
- } else {
1363
- let result;
1364
- if (parameters && parameters.length > 0) {
1365
- try {
1366
- result = this.db.prepare(processedStatement).run(...parameters);
1367
- } catch (error) {
1368
- console.error(`[SQLite executeSQL] ERROR: ${error.message}`);
1369
- console.error(`[SQLite executeSQL] SQL: ${processedStatement}`);
1370
- console.error(`[SQLite executeSQL] Parameters: ${JSON.stringify(parameters)}`);
1371
- throw error;
1372
- }
1373
- } else {
1374
- result = this.db.prepare(processedStatement).run();
1375
- }
1376
- return { rows: [], rowCount: result.changes };
1377
- }
1378
- } else {
1379
- if (parameters && parameters.length > 0) {
1380
- throw new Error("Parameters are not supported for multi-statement queries in SQLite");
1381
- }
1382
- const readStatements = [];
1383
- const writeStatements = [];
1384
- for (const statement of statements) {
1385
- const trimmedStatement = statement.toLowerCase().trim();
1386
- if (trimmedStatement.startsWith("select") || trimmedStatement.startsWith("with") || trimmedStatement.startsWith("explain") || trimmedStatement.startsWith("analyze") || trimmedStatement.startsWith("pragma") && (trimmedStatement.includes("table_info") || trimmedStatement.includes("index_info") || trimmedStatement.includes("index_list") || trimmedStatement.includes("foreign_key_list"))) {
1387
- readStatements.push(statement);
1388
- } else {
1389
- writeStatements.push(statement);
1390
- }
1391
- }
1392
- let totalChanges = 0;
1393
- for (const statement of writeStatements) {
1394
- const result = this.db.prepare(statement).run();
1395
- totalChanges += result.changes;
1396
- }
1397
- let allRows = [];
1398
- for (let statement of readStatements) {
1399
- statement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
1400
- const result = this.db.prepare(statement).all();
1401
- allRows.push(...result);
1402
- }
1403
- return { rows: allRows, rowCount: totalChanges + allRows.length };
1404
- }
1405
- } catch (error) {
1406
- throw error;
1407
- }
1408
- }
1409
- };
1410
- var sqliteConnector = new SQLiteConnector();
1411
- ConnectorRegistry.register(sqliteConnector);
1412
-
1413
- // src/connectors/mysql/index.ts
1414
- import mysql from "mysql2/promise";
1415
-
1416
- // src/utils/multi-statement-result-parser.ts
1417
- function isMetadataObject(element) {
1418
- if (!element || typeof element !== "object" || Array.isArray(element)) {
1419
- return false;
1420
- }
1421
- return "affectedRows" in element || "insertId" in element || "fieldCount" in element || "warningStatus" in element;
1422
- }
1423
- function isMultiStatementResult(results) {
1424
- if (!Array.isArray(results) || results.length === 0) {
1425
- return false;
1426
- }
1427
- const firstElement = results[0];
1428
- return isMetadataObject(firstElement) || Array.isArray(firstElement);
1429
- }
1430
- function extractRowsFromMultiStatement(results) {
1431
- if (!Array.isArray(results)) {
1432
- return [];
1433
- }
1434
- const allRows = [];
1435
- for (const result of results) {
1436
- if (Array.isArray(result)) {
1437
- allRows.push(...result);
1438
- }
1439
- }
1440
- return allRows;
1441
- }
1442
- function extractAffectedRows(results) {
1443
- if (isMetadataObject(results)) {
1444
- return results.affectedRows || 0;
1445
- }
1446
- if (!Array.isArray(results)) {
1447
- return 0;
1448
- }
1449
- if (isMultiStatementResult(results)) {
1450
- let totalAffected = 0;
1451
- for (const result of results) {
1452
- if (isMetadataObject(result)) {
1453
- totalAffected += result.affectedRows || 0;
1454
- } else if (Array.isArray(result)) {
1455
- totalAffected += result.length;
1456
- }
1457
- }
1458
- return totalAffected;
1459
- }
1460
- return results.length;
1461
- }
1462
- function parseQueryResults(results) {
1463
- if (!Array.isArray(results)) {
1464
- return [];
1465
- }
1466
- if (isMultiStatementResult(results)) {
1467
- return extractRowsFromMultiStatement(results);
1468
- }
1469
- return results;
1470
- }
1471
-
1472
- // src/connectors/mysql/index.ts
1473
- var MySQLDSNParser = class {
1474
- async parse(dsn, config) {
1475
- const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
1476
- if (!this.isValidDSN(dsn)) {
1477
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
1478
- const expectedFormat = this.getSampleDSN();
1479
- throw new Error(
1480
- `Invalid MySQL DSN format.
1481
- Provided: ${obfuscatedDSN}
1482
- Expected: ${expectedFormat}`
1483
- );
1484
- }
1485
- try {
1486
- const url = new SafeURL(dsn);
1487
- const config2 = {
1488
- host: url.hostname,
1489
- port: url.port ? parseInt(url.port) : 3306,
1490
- database: url.pathname ? url.pathname.substring(1) : "",
1491
- // Remove leading '/' if exists
1492
- user: url.username,
1493
- password: url.password,
1494
- multipleStatements: true
1495
- // Enable native multi-statement support
1496
- };
1497
- url.forEachSearchParam((value, key) => {
1498
- if (key === "sslmode") {
1499
- if (value === "disable") {
1500
- config2.ssl = void 0;
1501
- } else if (value === "require") {
1502
- config2.ssl = { rejectUnauthorized: false };
1503
- } else {
1504
- config2.ssl = {};
1505
- }
1506
- }
1507
- });
1508
- if (connectionTimeoutSeconds !== void 0) {
1509
- config2.connectTimeout = connectionTimeoutSeconds * 1e3;
1510
- }
1511
- if (url.password && url.password.includes("X-Amz-Credential")) {
1512
- config2.authPlugins = {
1513
- mysql_clear_password: () => () => {
1514
- return Buffer.from(url.password + "\0");
1515
- }
1516
- };
1517
- if (config2.ssl === void 0) {
1518
- config2.ssl = { rejectUnauthorized: false };
1519
- }
1520
- }
1521
- return config2;
1522
- } catch (error) {
1523
- throw new Error(
1524
- `Failed to parse MySQL DSN: ${error instanceof Error ? error.message : String(error)}`
1525
- );
1526
- }
1527
- }
1528
- getSampleDSN() {
1529
- return "mysql://root:password@localhost:3306/mysql?sslmode=require";
1530
- }
1531
- isValidDSN(dsn) {
1532
- try {
1533
- return dsn.startsWith("mysql://");
1534
- } catch (error) {
1535
- return false;
1536
- }
1537
- }
1538
- };
1539
- var MySQLConnector = class _MySQLConnector {
1540
- constructor() {
1541
- this.id = "mysql";
1542
- this.name = "MySQL";
1543
- this.dsnParser = new MySQLDSNParser();
1544
- this.pool = null;
1545
- // Source ID is set by ConnectorManager after cloning
1546
- this.sourceId = "default";
1547
- }
1548
- getId() {
1549
- return this.sourceId;
1550
- }
1551
- clone() {
1552
- return new _MySQLConnector();
1553
- }
1554
- async connect(dsn, initScript, config) {
1555
- try {
1556
- const connectionOptions = await this.dsnParser.parse(dsn, config);
1557
- this.pool = mysql.createPool(connectionOptions);
1558
- if (config?.queryTimeoutSeconds !== void 0) {
1559
- this.queryTimeoutMs = config.queryTimeoutSeconds * 1e3;
1560
- }
1561
- const [rows] = await this.pool.query("SELECT 1");
1562
- } catch (err) {
1563
- console.error("Failed to connect to MySQL database:", err);
1564
- throw err;
1565
- }
1566
- }
1567
- async disconnect() {
1568
- if (this.pool) {
1569
- await this.pool.end();
1570
- this.pool = null;
1571
- }
1572
- }
1573
- async getSchemas() {
1574
- if (!this.pool) {
1575
- throw new Error("Not connected to database");
1576
- }
1577
- try {
1578
- const [rows] = await this.pool.query(`
1579
- SELECT SCHEMA_NAME
1580
- FROM INFORMATION_SCHEMA.SCHEMATA
1581
- ORDER BY SCHEMA_NAME
1582
- `);
1583
- return rows.map((row) => row.SCHEMA_NAME);
1584
- } catch (error) {
1585
- console.error("Error getting schemas:", error);
1586
- throw error;
1587
- }
1588
- }
1589
- async getTables(schema) {
1590
- if (!this.pool) {
1591
- throw new Error("Not connected to database");
1592
- }
1593
- try {
1594
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1595
- const queryParams = schema ? [schema] : [];
1596
- const [rows] = await this.pool.query(
1597
- `
1598
- SELECT TABLE_NAME
1599
- FROM INFORMATION_SCHEMA.TABLES
1600
- ${schemaClause}
1601
- ORDER BY TABLE_NAME
1602
- `,
1603
- queryParams
1604
- );
1605
- return rows.map((row) => row.TABLE_NAME);
1606
- } catch (error) {
1607
- console.error("Error getting tables:", error);
1608
- throw error;
1609
- }
1610
- }
1611
- async tableExists(tableName, schema) {
1612
- if (!this.pool) {
1613
- throw new Error("Not connected to database");
1614
- }
1615
- try {
1616
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1617
- const queryParams = schema ? [schema, tableName] : [tableName];
1618
- const [rows] = await this.pool.query(
1619
- `
1620
- SELECT COUNT(*) AS COUNT
1621
- FROM INFORMATION_SCHEMA.TABLES
1622
- ${schemaClause}
1623
- AND TABLE_NAME = ?
1624
- `,
1625
- queryParams
1626
- );
1627
- return rows[0].COUNT > 0;
1628
- } catch (error) {
1629
- console.error("Error checking if table exists:", error);
1630
- throw error;
1631
- }
1632
- }
1633
- async getTableIndexes(tableName, schema) {
1634
- if (!this.pool) {
1635
- throw new Error("Not connected to database");
1636
- }
1637
- try {
1638
- const schemaClause = schema ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
1639
- const queryParams = schema ? [schema, tableName] : [tableName];
1640
- const [indexRows] = await this.pool.query(
1641
- `
1642
- SELECT
1643
- INDEX_NAME,
1644
- COLUMN_NAME,
1645
- NON_UNIQUE,
1646
- SEQ_IN_INDEX
1647
- FROM
1648
- INFORMATION_SCHEMA.STATISTICS
1649
- WHERE
1650
- ${schemaClause}
1651
- AND TABLE_NAME = ?
1652
- ORDER BY
1653
- INDEX_NAME,
1654
- SEQ_IN_INDEX
1655
- `,
1656
- queryParams
1657
- );
1658
- const indexMap = /* @__PURE__ */ new Map();
1659
- for (const row of indexRows) {
1660
- const indexName = row.INDEX_NAME;
1661
- const columnName = row.COLUMN_NAME;
1662
- const isUnique = row.NON_UNIQUE === 0;
1663
- const isPrimary = indexName === "PRIMARY";
1664
- if (!indexMap.has(indexName)) {
1665
- indexMap.set(indexName, {
1666
- columns: [],
1667
- is_unique: isUnique,
1668
- is_primary: isPrimary
1669
- });
1670
- }
1671
- const indexInfo = indexMap.get(indexName);
1672
- indexInfo.columns.push(columnName);
1673
- }
1674
- const results = [];
1675
- indexMap.forEach((indexInfo, indexName) => {
1676
- results.push({
1677
- index_name: indexName,
1678
- column_names: indexInfo.columns,
1679
- is_unique: indexInfo.is_unique,
1680
- is_primary: indexInfo.is_primary
1681
- });
1682
- });
1683
- return results;
1684
- } catch (error) {
1685
- console.error("Error getting table indexes:", error);
1686
- throw error;
1687
- }
1688
- }
1689
- async getTableSchema(tableName, schema) {
1690
- if (!this.pool) {
1691
- throw new Error("Not connected to database");
1692
- }
1693
- try {
1694
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1695
- const queryParams = schema ? [schema, tableName] : [tableName];
1696
- const [rows] = await this.pool.query(
1697
- `
1698
- SELECT
1699
- COLUMN_NAME as column_name,
1700
- DATA_TYPE as data_type,
1701
- IS_NULLABLE as is_nullable,
1702
- COLUMN_DEFAULT as column_default,
1703
- COLUMN_COMMENT as description
1704
- FROM INFORMATION_SCHEMA.COLUMNS
1705
- ${schemaClause}
1706
- AND TABLE_NAME = ?
1707
- ORDER BY ORDINAL_POSITION
1708
- `,
1709
- queryParams
1710
- );
1711
- return rows.map((row) => ({
1712
- ...row,
1713
- description: row.description || null
1714
- }));
1715
- } catch (error) {
1716
- console.error("Error getting table schema:", error);
1717
- throw error;
1718
- }
1719
- }
1720
- async getTableComment(tableName, schema) {
1721
- if (!this.pool) {
1722
- throw new Error("Not connected to database");
1723
- }
1724
- try {
1725
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1726
- const queryParams = schema ? [schema, tableName] : [tableName];
1727
- const [rows] = await this.pool.query(
1728
- `
1729
- SELECT TABLE_COMMENT
1730
- FROM INFORMATION_SCHEMA.TABLES
1731
- ${schemaClause}
1732
- AND TABLE_NAME = ?
1733
- `,
1734
- queryParams
1735
- );
1736
- if (rows.length > 0) {
1737
- return rows[0].TABLE_COMMENT || null;
1738
- }
1739
- return null;
1740
- } catch (error) {
1741
- return null;
1742
- }
1743
- }
1744
- async getStoredProcedures(schema, routineType) {
1745
- if (!this.pool) {
1746
- throw new Error("Not connected to database");
1747
- }
1748
- try {
1749
- const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
1750
- const queryParams = schema ? [schema] : [];
1751
- let typeFilter = "";
1752
- if (routineType === "function") {
1753
- typeFilter = " AND ROUTINE_TYPE = 'FUNCTION'";
1754
- } else if (routineType === "procedure") {
1755
- typeFilter = " AND ROUTINE_TYPE = 'PROCEDURE'";
1756
- }
1757
- const [rows] = await this.pool.query(
1758
- `
1759
- SELECT ROUTINE_NAME
1760
- FROM INFORMATION_SCHEMA.ROUTINES
1761
- ${schemaClause}${typeFilter}
1762
- ORDER BY ROUTINE_NAME
1763
- `,
1764
- queryParams
1765
- );
1766
- return rows.map((row) => row.ROUTINE_NAME);
1767
- } catch (error) {
1768
- console.error("Error getting stored procedures:", error);
1769
- throw error;
1770
- }
1771
- }
1772
- async getStoredProcedureDetail(procedureName, schema) {
1773
- if (!this.pool) {
1774
- throw new Error("Not connected to database");
1775
- }
1776
- try {
1777
- const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
1778
- const queryParams = schema ? [schema, procedureName] : [procedureName];
1779
- const [rows] = await this.pool.query(
1780
- `
1781
- SELECT
1782
- r.ROUTINE_NAME AS procedure_name,
1783
- CASE
1784
- WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
1785
- ELSE 'function'
1786
- END AS procedure_type,
1787
- LOWER(r.ROUTINE_TYPE) AS routine_type,
1788
- r.ROUTINE_DEFINITION,
1789
- r.DTD_IDENTIFIER AS return_type,
1790
- (
1791
- SELECT GROUP_CONCAT(
1792
- CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
1793
- ORDER BY p.ORDINAL_POSITION
1794
- SEPARATOR ', '
1795
- )
1796
- FROM INFORMATION_SCHEMA.PARAMETERS p
1797
- WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
1798
- AND p.SPECIFIC_NAME = r.ROUTINE_NAME
1799
- AND p.PARAMETER_NAME IS NOT NULL
1800
- ) AS parameter_list
1801
- FROM INFORMATION_SCHEMA.ROUTINES r
1802
- ${schemaClause}
1803
- AND r.ROUTINE_NAME = ?
1804
- `,
1805
- queryParams
1806
- );
1807
- if (rows.length === 0) {
1808
- const schemaName = schema || "current schema";
1809
- throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
1810
- }
1811
- const procedure = rows[0];
1812
- let definition = procedure.ROUTINE_DEFINITION;
1813
- try {
1814
- const schemaValue = schema || await this.getCurrentSchema();
1815
- if (procedure.procedure_type === "procedure") {
1816
- try {
1817
- const [defRows] = await this.pool.query(`
1818
- SHOW CREATE PROCEDURE ${schemaValue}.${procedureName}
1819
- `);
1820
- if (defRows && defRows.length > 0) {
1821
- definition = defRows[0]["Create Procedure"];
1822
- }
1823
- } catch (err) {
1824
- console.error(`Error getting procedure definition with SHOW CREATE: ${err}`);
1825
- }
1826
- } else {
1827
- try {
1828
- const [defRows] = await this.pool.query(`
1829
- SHOW CREATE FUNCTION ${schemaValue}.${procedureName}
1830
- `);
1831
- if (defRows && defRows.length > 0) {
1832
- definition = defRows[0]["Create Function"];
1833
- }
1834
- } catch (innerErr) {
1835
- console.error(`Error getting function definition with SHOW CREATE: ${innerErr}`);
1836
- }
1837
- }
1838
- if (!definition) {
1839
- const [bodyRows] = await this.pool.query(
1840
- `
1841
- SELECT ROUTINE_DEFINITION, ROUTINE_BODY
1842
- FROM INFORMATION_SCHEMA.ROUTINES
1843
- WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
1844
- `,
1845
- [schemaValue, procedureName]
1846
- );
1847
- if (bodyRows && bodyRows.length > 0) {
1848
- if (bodyRows[0].ROUTINE_DEFINITION) {
1849
- definition = bodyRows[0].ROUTINE_DEFINITION;
1850
- } else if (bodyRows[0].ROUTINE_BODY) {
1851
- definition = bodyRows[0].ROUTINE_BODY;
1852
- }
1853
- }
1854
- }
1855
- } catch (error) {
1856
- console.error(`Error getting procedure/function details: ${error}`);
1857
- }
1858
- return {
1859
- procedure_name: procedure.procedure_name,
1860
- procedure_type: procedure.procedure_type,
1861
- language: "sql",
1862
- // MySQL procedures are generally in SQL
1863
- parameter_list: procedure.parameter_list || "",
1864
- return_type: procedure.routine_type === "function" ? procedure.return_type : void 0,
1865
- definition: definition || void 0
1866
- };
1867
- } catch (error) {
1868
- console.error("Error getting stored procedure detail:", error);
1869
- throw error;
1870
- }
1871
- }
1872
- // Helper method to get current schema (database) name
1873
- async getCurrentSchema() {
1874
- const [rows] = await this.pool.query("SELECT DATABASE() AS DB");
1875
- return rows[0].DB;
1876
- }
1877
- async executeSQL(sql2, options, parameters) {
1878
- if (!this.pool) {
1879
- throw new Error("Not connected to database");
1880
- }
1881
- const conn = await this.pool.getConnection();
1882
- try {
1883
- let processedSQL = sql2;
1884
- if (options.maxRows) {
1885
- const statements = splitSQLStatements(sql2, "mysql");
1886
- const processedStatements = statements.map(
1887
- (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
1888
- );
1889
- processedSQL = processedStatements.join("; ");
1890
- if (sql2.trim().endsWith(";")) {
1891
- processedSQL += ";";
1892
- }
1893
- }
1894
- let results;
1895
- if (parameters && parameters.length > 0) {
1896
- try {
1897
- results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs }, parameters);
1898
- } catch (error) {
1899
- console.error(`[MySQL executeSQL] ERROR: ${error.message}`);
1900
- console.error(`[MySQL executeSQL] SQL: ${processedSQL}`);
1901
- console.error(`[MySQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
1902
- throw error;
1903
- }
1904
- } else {
1905
- results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs });
1906
- }
1907
- const [firstResult] = results;
1908
- const rows = parseQueryResults(firstResult);
1909
- const rowCount = extractAffectedRows(firstResult);
1910
- return { rows, rowCount };
1911
- } catch (error) {
1912
- console.error("Error executing query:", error);
1913
- throw error;
1914
- } finally {
1915
- conn.release();
1916
- }
1917
- }
1918
- };
1919
- var mysqlConnector = new MySQLConnector();
1920
- ConnectorRegistry.register(mysqlConnector);
1921
-
1922
- // src/connectors/mariadb/index.ts
1923
- import * as mariadb from "mariadb";
1924
- var MariadbDSNParser = class {
1925
- async parse(dsn, config) {
1926
- const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
1927
- const queryTimeoutSeconds = config?.queryTimeoutSeconds;
1928
- if (!this.isValidDSN(dsn)) {
1929
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
1930
- const expectedFormat = this.getSampleDSN();
1931
- throw new Error(
1932
- `Invalid MariaDB DSN format.
1933
- Provided: ${obfuscatedDSN}
1934
- Expected: ${expectedFormat}`
1935
- );
1936
- }
1937
- try {
1938
- const url = new SafeURL(dsn);
1939
- const connectionConfig = {
1940
- host: url.hostname,
1941
- port: url.port ? parseInt(url.port) : 3306,
1942
- database: url.pathname ? url.pathname.substring(1) : "",
1943
- // Remove leading '/' if exists
1944
- user: url.username,
1945
- password: url.password,
1946
- multipleStatements: true,
1947
- // Enable native multi-statement support
1948
- ...connectionTimeoutSeconds !== void 0 && {
1949
- connectTimeout: connectionTimeoutSeconds * 1e3
1950
- },
1951
- ...queryTimeoutSeconds !== void 0 && {
1952
- queryTimeout: queryTimeoutSeconds * 1e3
1953
- }
1954
- };
1955
- url.forEachSearchParam((value, key) => {
1956
- if (key === "sslmode") {
1957
- if (value === "disable") {
1958
- connectionConfig.ssl = void 0;
1959
- } else if (value === "require") {
1960
- connectionConfig.ssl = { rejectUnauthorized: false };
1961
- } else {
1962
- connectionConfig.ssl = {};
1963
- }
1964
- }
1965
- });
1966
- if (url.password && url.password.includes("X-Amz-Credential")) {
1967
- if (connectionConfig.ssl === void 0) {
1968
- connectionConfig.ssl = { rejectUnauthorized: false };
1969
- }
1970
- }
1971
- return connectionConfig;
1972
- } catch (error) {
1973
- throw new Error(
1974
- `Failed to parse MariaDB DSN: ${error instanceof Error ? error.message : String(error)}`
1975
- );
1976
- }
1977
- }
1978
- getSampleDSN() {
1979
- return "mariadb://root:password@localhost:3306/db?sslmode=require";
1980
- }
1981
- isValidDSN(dsn) {
1982
- try {
1983
- return dsn.startsWith("mariadb://");
1984
- } catch (error) {
1985
- return false;
1986
- }
1987
- }
1988
- };
1989
- var MariaDBConnector = class _MariaDBConnector {
1990
- constructor() {
1991
- this.id = "mariadb";
1992
- this.name = "MariaDB";
1993
- this.dsnParser = new MariadbDSNParser();
1994
- this.pool = null;
1995
- // Source ID is set by ConnectorManager after cloning
1996
- this.sourceId = "default";
1997
- }
1998
- getId() {
1999
- return this.sourceId;
2000
- }
2001
- clone() {
2002
- return new _MariaDBConnector();
2003
- }
2004
- async connect(dsn, initScript, config) {
2005
- try {
2006
- const connectionConfig = await this.dsnParser.parse(dsn, config);
2007
- this.pool = mariadb.createPool(connectionConfig);
2008
- await this.pool.query("SELECT 1");
2009
- } catch (err) {
2010
- console.error("Failed to connect to MariaDB database:", err);
2011
- throw err;
2012
- }
2013
- }
2014
- async disconnect() {
2015
- if (this.pool) {
2016
- await this.pool.end();
2017
- this.pool = null;
2018
- }
2019
- }
2020
- async getSchemas() {
2021
- if (!this.pool) {
2022
- throw new Error("Not connected to database");
2023
- }
2024
- try {
2025
- const rows = await this.pool.query(`
2026
- SELECT SCHEMA_NAME
2027
- FROM INFORMATION_SCHEMA.SCHEMATA
2028
- ORDER BY SCHEMA_NAME
2029
- `);
2030
- return rows.map((row) => row.SCHEMA_NAME);
2031
- } catch (error) {
2032
- console.error("Error getting schemas:", error);
2033
- throw error;
2034
- }
2035
- }
2036
- async getTables(schema) {
2037
- if (!this.pool) {
2038
- throw new Error("Not connected to database");
2039
- }
2040
- try {
2041
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
2042
- const queryParams = schema ? [schema] : [];
2043
- const rows = await this.pool.query(
2044
- `
2045
- SELECT TABLE_NAME
2046
- FROM INFORMATION_SCHEMA.TABLES
2047
- ${schemaClause}
2048
- ORDER BY TABLE_NAME
2049
- `,
2050
- queryParams
2051
- );
2052
- return rows.map((row) => row.TABLE_NAME);
2053
- } catch (error) {
2054
- console.error("Error getting tables:", error);
2055
- throw error;
2056
- }
2057
- }
2058
- async tableExists(tableName, schema) {
2059
- if (!this.pool) {
2060
- throw new Error("Not connected to database");
2061
- }
2062
- try {
2063
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
2064
- const queryParams = schema ? [schema, tableName] : [tableName];
2065
- const rows = await this.pool.query(
2066
- `
2067
- SELECT COUNT(*) AS COUNT
2068
- FROM INFORMATION_SCHEMA.TABLES
2069
- ${schemaClause}
2070
- AND TABLE_NAME = ?
2071
- `,
2072
- queryParams
2073
- );
2074
- return rows[0].COUNT > 0;
2075
- } catch (error) {
2076
- console.error("Error checking if table exists:", error);
2077
- throw error;
2078
- }
2079
- }
2080
- async getTableIndexes(tableName, schema) {
2081
- if (!this.pool) {
2082
- throw new Error("Not connected to database");
2083
- }
2084
- try {
2085
- const schemaClause = schema ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
2086
- const queryParams = schema ? [schema, tableName] : [tableName];
2087
- const indexRows = await this.pool.query(
2088
- `
2089
- SELECT
2090
- INDEX_NAME,
2091
- COLUMN_NAME,
2092
- NON_UNIQUE,
2093
- SEQ_IN_INDEX
2094
- FROM
2095
- INFORMATION_SCHEMA.STATISTICS
2096
- WHERE
2097
- ${schemaClause}
2098
- AND TABLE_NAME = ?
2099
- ORDER BY
2100
- INDEX_NAME,
2101
- SEQ_IN_INDEX
2102
- `,
2103
- queryParams
2104
- );
2105
- const indexMap = /* @__PURE__ */ new Map();
2106
- for (const row of indexRows) {
2107
- const indexName = row.INDEX_NAME;
2108
- const columnName = row.COLUMN_NAME;
2109
- const isUnique = row.NON_UNIQUE === 0;
2110
- const isPrimary = indexName === "PRIMARY";
2111
- if (!indexMap.has(indexName)) {
2112
- indexMap.set(indexName, {
2113
- columns: [],
2114
- is_unique: isUnique,
2115
- is_primary: isPrimary
2116
- });
2117
- }
2118
- const indexInfo = indexMap.get(indexName);
2119
- indexInfo.columns.push(columnName);
2120
- }
2121
- const results = [];
2122
- indexMap.forEach((indexInfo, indexName) => {
2123
- results.push({
2124
- index_name: indexName,
2125
- column_names: indexInfo.columns,
2126
- is_unique: indexInfo.is_unique,
2127
- is_primary: indexInfo.is_primary
2128
- });
2129
- });
2130
- return results;
2131
- } catch (error) {
2132
- console.error("Error getting table indexes:", error);
2133
- throw error;
2134
- }
2135
- }
2136
- async getTableSchema(tableName, schema) {
2137
- if (!this.pool) {
2138
- throw new Error("Not connected to database");
2139
- }
2140
- try {
2141
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
2142
- const queryParams = schema ? [schema, tableName] : [tableName];
2143
- const rows = await this.pool.query(
2144
- `
2145
- SELECT
2146
- COLUMN_NAME as column_name,
2147
- DATA_TYPE as data_type,
2148
- IS_NULLABLE as is_nullable,
2149
- COLUMN_DEFAULT as column_default,
2150
- COLUMN_COMMENT as description
2151
- FROM INFORMATION_SCHEMA.COLUMNS
2152
- ${schemaClause}
2153
- AND TABLE_NAME = ?
2154
- ORDER BY ORDINAL_POSITION
2155
- `,
2156
- queryParams
2157
- );
2158
- return rows.map((row) => ({
2159
- ...row,
2160
- description: row.description || null
2161
- }));
2162
- } catch (error) {
2163
- console.error("Error getting table schema:", error);
2164
- throw error;
2165
- }
2166
- }
2167
- async getTableComment(tableName, schema) {
2168
- if (!this.pool) {
2169
- throw new Error("Not connected to database");
2170
- }
2171
- try {
2172
- const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
2173
- const queryParams = schema ? [schema, tableName] : [tableName];
2174
- const rows = await this.pool.query(
2175
- `
2176
- SELECT TABLE_COMMENT
2177
- FROM INFORMATION_SCHEMA.TABLES
2178
- ${schemaClause}
2179
- AND TABLE_NAME = ?
2180
- `,
2181
- queryParams
2182
- );
2183
- if (rows.length > 0) {
2184
- return rows[0].TABLE_COMMENT || null;
2185
- }
2186
- return null;
2187
- } catch (error) {
2188
- return null;
2189
- }
2190
- }
2191
- async getStoredProcedures(schema, routineType) {
2192
- if (!this.pool) {
2193
- throw new Error("Not connected to database");
2194
- }
2195
- try {
2196
- const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
2197
- const queryParams = schema ? [schema] : [];
2198
- let typeFilter = "";
2199
- if (routineType === "function") {
2200
- typeFilter = " AND ROUTINE_TYPE = 'FUNCTION'";
2201
- } else if (routineType === "procedure") {
2202
- typeFilter = " AND ROUTINE_TYPE = 'PROCEDURE'";
2203
- }
2204
- const rows = await this.pool.query(
2205
- `
2206
- SELECT ROUTINE_NAME
2207
- FROM INFORMATION_SCHEMA.ROUTINES
2208
- ${schemaClause}${typeFilter}
2209
- ORDER BY ROUTINE_NAME
2210
- `,
2211
- queryParams
2212
- );
2213
- return rows.map((row) => row.ROUTINE_NAME);
2214
- } catch (error) {
2215
- console.error("Error getting stored procedures:", error);
2216
- throw error;
2217
- }
2218
- }
2219
- async getStoredProcedureDetail(procedureName, schema) {
2220
- if (!this.pool) {
2221
- throw new Error("Not connected to database");
2222
- }
2223
- try {
2224
- const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
2225
- const queryParams = schema ? [schema, procedureName] : [procedureName];
2226
- const rows = await this.pool.query(
2227
- `
2228
- SELECT
2229
- r.ROUTINE_NAME AS procedure_name,
2230
- CASE
2231
- WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
2232
- ELSE 'function'
2233
- END AS procedure_type,
2234
- LOWER(r.ROUTINE_TYPE) AS routine_type,
2235
- r.ROUTINE_DEFINITION,
2236
- r.DTD_IDENTIFIER AS return_type,
2237
- (
2238
- SELECT GROUP_CONCAT(
2239
- CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
2240
- ORDER BY p.ORDINAL_POSITION
2241
- SEPARATOR ', '
2242
- )
2243
- FROM INFORMATION_SCHEMA.PARAMETERS p
2244
- WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
2245
- AND p.SPECIFIC_NAME = r.ROUTINE_NAME
2246
- AND p.PARAMETER_NAME IS NOT NULL
2247
- ) AS parameter_list
2248
- FROM INFORMATION_SCHEMA.ROUTINES r
2249
- ${schemaClause}
2250
- AND r.ROUTINE_NAME = ?
2251
- `,
2252
- queryParams
2253
- );
2254
- if (rows.length === 0) {
2255
- const schemaName = schema || "current schema";
2256
- throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
2257
- }
2258
- const procedure = rows[0];
2259
- let definition = procedure.ROUTINE_DEFINITION;
2260
- try {
2261
- const schemaValue = schema || await this.getCurrentSchema();
2262
- if (procedure.procedure_type === "procedure") {
2263
- try {
2264
- const defRows = await this.pool.query(`
2265
- SHOW CREATE PROCEDURE ${schemaValue}.${procedureName}
2266
- `);
2267
- if (defRows && defRows.length > 0) {
2268
- definition = defRows[0]["Create Procedure"];
2269
- }
2270
- } catch (err) {
2271
- console.error(`Error getting procedure definition with SHOW CREATE: ${err}`);
2272
- }
2273
- } else {
2274
- try {
2275
- const defRows = await this.pool.query(`
2276
- SHOW CREATE FUNCTION ${schemaValue}.${procedureName}
2277
- `);
2278
- if (defRows && defRows.length > 0) {
2279
- definition = defRows[0]["Create Function"];
2280
- }
2281
- } catch (innerErr) {
2282
- console.error(`Error getting function definition with SHOW CREATE: ${innerErr}`);
2283
- }
2284
- }
2285
- if (!definition) {
2286
- const bodyRows = await this.pool.query(
2287
- `
2288
- SELECT ROUTINE_DEFINITION, ROUTINE_BODY
2289
- FROM INFORMATION_SCHEMA.ROUTINES
2290
- WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
2291
- `,
2292
- [schemaValue, procedureName]
2293
- );
2294
- if (bodyRows && bodyRows.length > 0) {
2295
- if (bodyRows[0].ROUTINE_DEFINITION) {
2296
- definition = bodyRows[0].ROUTINE_DEFINITION;
2297
- } else if (bodyRows[0].ROUTINE_BODY) {
2298
- definition = bodyRows[0].ROUTINE_BODY;
2299
- }
2300
- }
2301
- }
2302
- } catch (error) {
2303
- console.error(`Error getting procedure/function details: ${error}`);
2304
- }
2305
- return {
2306
- procedure_name: procedure.procedure_name,
2307
- procedure_type: procedure.procedure_type,
2308
- language: "sql",
2309
- // MariaDB procedures are generally in SQL
2310
- parameter_list: procedure.parameter_list || "",
2311
- return_type: procedure.routine_type === "function" ? procedure.return_type : void 0,
2312
- definition: definition || void 0
2313
- };
2314
- } catch (error) {
2315
- console.error("Error getting stored procedure detail:", error);
2316
- throw error;
2317
- }
2318
- }
2319
- // Helper method to get current schema (database) name
2320
- async getCurrentSchema() {
2321
- const rows = await this.pool.query("SELECT DATABASE() AS DB");
2322
- return rows[0].DB;
2323
- }
2324
- async executeSQL(sql2, options, parameters) {
2325
- if (!this.pool) {
2326
- throw new Error("Not connected to database");
2327
- }
2328
- const conn = await this.pool.getConnection();
2329
- try {
2330
- let processedSQL = sql2;
2331
- if (options.maxRows) {
2332
- const statements = splitSQLStatements(sql2, "mariadb");
2333
- const processedStatements = statements.map(
2334
- (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
2335
- );
2336
- processedSQL = processedStatements.join("; ");
2337
- if (sql2.trim().endsWith(";")) {
2338
- processedSQL += ";";
2339
- }
2340
- }
2341
- let results;
2342
- if (parameters && parameters.length > 0) {
2343
- try {
2344
- results = await conn.query(processedSQL, parameters);
2345
- } catch (error) {
2346
- console.error(`[MariaDB executeSQL] ERROR: ${error.message}`);
2347
- console.error(`[MariaDB executeSQL] SQL: ${processedSQL}`);
2348
- console.error(`[MariaDB executeSQL] Parameters: ${JSON.stringify(parameters)}`);
2349
- throw error;
2350
- }
2351
- } else {
2352
- results = await conn.query(processedSQL);
2353
- }
2354
- const rows = parseQueryResults(results);
2355
- const rowCount = extractAffectedRows(results);
2356
- return { rows, rowCount };
2357
- } catch (error) {
2358
- console.error("Error executing query:", error);
2359
- throw error;
2360
- } finally {
2361
- conn.release();
2362
- }
2363
- }
2364
- };
2365
- var mariadbConnector = new MariaDBConnector();
2366
- ConnectorRegistry.register(mariadbConnector);
6
+ getToolRegistry,
7
+ initializeToolRegistry,
8
+ isDemoMode,
9
+ loadTomlConfig,
10
+ mapArgumentsToArray,
11
+ resolvePort,
12
+ resolveSourceConfigs,
13
+ resolveTomlConfigPath,
14
+ resolveTransport
15
+ } from "./chunk-25VMLRAQ.js";
16
+ import {
17
+ quoteQualifiedIdentifier
18
+ } from "./chunk-JFWX35TB.js";
19
+ import {
20
+ ConnectorRegistry,
21
+ getDatabaseTypeFromDSN,
22
+ getDefaultPortForType,
23
+ parseConnectionInfoFromDSN,
24
+ splitSQLStatements,
25
+ stripCommentsAndStrings
26
+ } from "./chunk-C7WEAPX4.js";
27
+ import "./chunk-WWAWV7DQ.js";
2367
28
 
2368
29
  // src/server.ts
2369
30
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -2378,8 +39,13 @@ import { fileURLToPath } from "url";
2378
39
  import { z } from "zod";
2379
40
 
2380
41
  // src/utils/response-formatter.ts
42
+ var MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
43
+ var MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
2381
44
  function bigIntReplacer(_key, value) {
2382
45
  if (typeof value === "bigint") {
46
+ if (value >= MIN_SAFE_BIGINT && value <= MAX_SAFE_BIGINT) {
47
+ return Number(value);
48
+ }
2383
49
  return value.toString();
2384
50
  }
2385
51
  return value;
@@ -2431,10 +97,10 @@ var allowedKeywords = {
2431
97
  sqlite: ["select", "with", "explain", "analyze", "pragma"],
2432
98
  sqlserver: ["select", "with", "explain", "showplan"]
2433
99
  };
2434
- function isReadOnlySQL(sql2, connectorType) {
2435
- const cleanedSQL = stripCommentsAndStrings(sql2, connectorType).trim().toLowerCase();
100
+ function isReadOnlySQL(sql, connectorType) {
101
+ const cleanedSQL = stripCommentsAndStrings(sql, connectorType).trim().toLowerCase();
2436
102
  if (!cleanedSQL) {
2437
- return true;
103
+ return false;
2438
104
  }
2439
105
  const firstWord = cleanedSQL.split(/\s+/)[0];
2440
106
  const keywordList = allowedKeywords[connectorType] || [];
@@ -2525,13 +191,13 @@ function trackToolRequest(metadata, startTime, extra, success, error) {
2525
191
  var executeSqlSchema = {
2526
192
  sql: z.string().describe("SQL to execute (multiple statements separated by ;)")
2527
193
  };
2528
- function areAllStatementsReadOnly(sql2, connectorType) {
2529
- const statements = splitSQLStatements(sql2, connectorType);
194
+ function areAllStatementsReadOnly(sql, connectorType) {
195
+ const statements = splitSQLStatements(sql, connectorType);
2530
196
  return statements.every((statement) => isReadOnlySQL(statement, connectorType));
2531
197
  }
2532
198
  function createExecuteSqlToolHandler(sourceId) {
2533
199
  return async (args, extra) => {
2534
- const { sql: sql2 } = args;
200
+ const { sql } = args;
2535
201
  const startTime = Date.now();
2536
202
  const effectiveSourceId = getEffectiveSourceId(sourceId);
2537
203
  let success = true;
@@ -2544,7 +210,7 @@ function createExecuteSqlToolHandler(sourceId) {
2544
210
  const registry = getToolRegistry();
2545
211
  const toolConfig = registry.getBuiltinToolConfig(BUILTIN_TOOL_EXECUTE_SQL, actualSourceId);
2546
212
  const isReadonly = toolConfig?.readonly === true;
2547
- if (isReadonly && !areAllStatementsReadOnly(sql2, connector.id)) {
213
+ if (isReadonly && !areAllStatementsReadOnly(sql, connector.id)) {
2548
214
  errorMessage = `Read-only mode is enabled. Only the following SQL operations are allowed: ${allowedKeywords[connector.id]?.join(", ") || "none"}`;
2549
215
  success = false;
2550
216
  return createToolErrorResponse(errorMessage, "READONLY_VIOLATION");
@@ -2553,7 +219,7 @@ function createExecuteSqlToolHandler(sourceId) {
2553
219
  readonly: toolConfig?.readonly,
2554
220
  maxRows: toolConfig?.max_rows
2555
221
  };
2556
- result = await connector.executeSQL(sql2, executeOptions);
222
+ result = await connector.executeSQL(sql, executeOptions);
2557
223
  const responseData = {
2558
224
  rows: result.rows,
2559
225
  count: result.rowCount,
@@ -2569,7 +235,7 @@ function createExecuteSqlToolHandler(sourceId) {
2569
235
  {
2570
236
  sourceId: effectiveSourceId,
2571
237
  toolName: effectiveSourceId === "default" ? "execute_sql" : `execute_sql_${effectiveSourceId}`,
2572
- sql: sql2
238
+ sql
2573
239
  },
2574
240
  startTime,
2575
241
  extra,
@@ -3697,7 +1363,7 @@ See documentation for more details on configuring database connections.
3697
1363
  const sources = sourceConfigsData.sources;
3698
1364
  console.error(`Configuration source: ${sourceConfigsData.source}`);
3699
1365
  await connectorManager.connectWithSources(sources);
3700
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-6VNMKD6G.js");
1366
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-FOASCI6Y.js");
3701
1367
  initializeToolRegistry2({
3702
1368
  sources: sourceConfigsData.sources,
3703
1369
  tools: sourceConfigsData.tools
@@ -3804,11 +1470,18 @@ See documentation for more details on configuring database connections.
3804
1470
  const transport = new StdioServerTransport();
3805
1471
  await server.connect(transport);
3806
1472
  console.error("MCP server running on stdio");
3807
- process.on("SIGINT", async () => {
1473
+ let isShuttingDown = false;
1474
+ const shutdown = async () => {
1475
+ if (isShuttingDown) return;
1476
+ isShuttingDown = true;
3808
1477
  console.error("Shutting down...");
3809
1478
  await transport.close();
1479
+ await connectorManager.disconnect();
3810
1480
  process.exit(0);
3811
- });
1481
+ };
1482
+ process.on("SIGINT", shutdown);
1483
+ process.on("SIGTERM", shutdown);
1484
+ process.stdin.on("end", shutdown);
3812
1485
  }
3813
1486
  } catch (err) {
3814
1487
  console.error("Fatal error:", err);
@@ -3816,8 +1489,46 @@ See documentation for more details on configuring database connections.
3816
1489
  }
3817
1490
  }
3818
1491
 
1492
+ // src/utils/module-loader.ts
1493
+ var MISSING_MODULE_RE = /Cannot find (?:package|module) '([^']+)'/;
1494
+ function isDriverNotInstalled(err, driver) {
1495
+ if (!(err instanceof Error) || !("code" in err) || err.code !== "ERR_MODULE_NOT_FOUND") {
1496
+ return false;
1497
+ }
1498
+ const match = err.message.match(MISSING_MODULE_RE);
1499
+ if (!match) {
1500
+ return false;
1501
+ }
1502
+ const missingSpecifier = match[1];
1503
+ return missingSpecifier === driver || missingSpecifier.startsWith(`${driver}/`);
1504
+ }
1505
+ async function loadConnectors(connectorModules2) {
1506
+ await Promise.all(
1507
+ connectorModules2.map(async ({ load, name, driver }) => {
1508
+ try {
1509
+ await load();
1510
+ } catch (err) {
1511
+ if (isDriverNotInstalled(err, driver)) {
1512
+ console.error(
1513
+ `Skipping ${name} connector: driver package "${driver}" not installed.`
1514
+ );
1515
+ } else {
1516
+ throw err;
1517
+ }
1518
+ }
1519
+ })
1520
+ );
1521
+ }
1522
+
3819
1523
  // src/index.ts
3820
- main().catch((error) => {
1524
+ var connectorModules = [
1525
+ { load: () => import("./postgres-JB3LPXGR.js"), name: "PostgreSQL", driver: "pg" },
1526
+ { load: () => import("./sqlserver-LGFLHJHL.js"), name: "SQL Server", driver: "mssql" },
1527
+ { load: () => import("./sqlite-5LT56F5B.js"), name: "SQLite", driver: "better-sqlite3" },
1528
+ { load: () => import("./mysql-FOCVUTPX.js"), name: "MySQL", driver: "mysql2" },
1529
+ { load: () => import("./mariadb-L3YMONWJ.js"), name: "MariaDB", driver: "mariadb" }
1530
+ ];
1531
+ loadConnectors(connectorModules).then(() => main()).catch((error) => {
3821
1532
  console.error("Fatal error:", error);
3822
1533
  process.exit(1);
3823
1534
  });