@bytebase/dbhub 0.1.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +19 -12
  2. package/dist/index.js +1057 -155
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -121,47 +121,112 @@ var PostgresConnector = class {
121
121
  this.pool = null;
122
122
  }
123
123
  }
124
- async getTables() {
124
+ async getSchemas() {
125
125
  if (!this.pool) {
126
126
  throw new Error("Not connected to database");
127
127
  }
128
128
  const client = await this.pool.connect();
129
129
  try {
130
+ const result = await client.query(`
131
+ SELECT schema_name
132
+ FROM information_schema.schemata
133
+ WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
134
+ ORDER BY schema_name
135
+ `);
136
+ return result.rows.map((row) => row.schema_name);
137
+ } finally {
138
+ client.release();
139
+ }
140
+ }
141
+ async getTables(schema) {
142
+ if (!this.pool) {
143
+ throw new Error("Not connected to database");
144
+ }
145
+ const client = await this.pool.connect();
146
+ try {
147
+ const schemaToUse = schema || "public";
130
148
  const result = await client.query(`
131
149
  SELECT table_name
132
150
  FROM information_schema.tables
133
- WHERE table_schema = 'public'
151
+ WHERE table_schema = $1
134
152
  ORDER BY table_name
135
- `);
153
+ `, [schemaToUse]);
136
154
  return result.rows.map((row) => row.table_name);
137
155
  } finally {
138
156
  client.release();
139
157
  }
140
158
  }
141
- async tableExists(tableName) {
159
+ async tableExists(tableName, schema) {
142
160
  if (!this.pool) {
143
161
  throw new Error("Not connected to database");
144
162
  }
145
163
  const client = await this.pool.connect();
146
164
  try {
165
+ const schemaToUse = schema || "public";
147
166
  const result = await client.query(`
148
167
  SELECT EXISTS (
149
168
  SELECT FROM information_schema.tables
150
- WHERE table_schema = 'public'
151
- AND table_name = $1
169
+ WHERE table_schema = $1
170
+ AND table_name = $2
152
171
  )
153
- `, [tableName]);
172
+ `, [schemaToUse, tableName]);
154
173
  return result.rows[0].exists;
155
174
  } finally {
156
175
  client.release();
157
176
  }
158
177
  }
159
- async getTableSchema(tableName) {
178
+ async getTableIndexes(tableName, schema) {
179
+ if (!this.pool) {
180
+ throw new Error("Not connected to database");
181
+ }
182
+ const client = await this.pool.connect();
183
+ try {
184
+ const schemaToUse = schema || "public";
185
+ const result = await client.query(`
186
+ SELECT
187
+ i.relname as index_name,
188
+ array_agg(a.attname) as column_names,
189
+ ix.indisunique as is_unique,
190
+ ix.indisprimary as is_primary
191
+ FROM
192
+ pg_class t,
193
+ pg_class i,
194
+ pg_index ix,
195
+ pg_attribute a,
196
+ pg_namespace ns
197
+ WHERE
198
+ t.oid = ix.indrelid
199
+ AND i.oid = ix.indexrelid
200
+ AND a.attrelid = t.oid
201
+ AND a.attnum = ANY(ix.indkey)
202
+ AND t.relkind = 'r'
203
+ AND t.relname = $1
204
+ AND ns.oid = t.relnamespace
205
+ AND ns.nspname = $2
206
+ GROUP BY
207
+ i.relname,
208
+ ix.indisunique,
209
+ ix.indisprimary
210
+ ORDER BY
211
+ i.relname
212
+ `, [tableName, schemaToUse]);
213
+ return result.rows.map((row) => ({
214
+ index_name: row.index_name,
215
+ column_names: row.column_names,
216
+ is_unique: row.is_unique,
217
+ is_primary: row.is_primary
218
+ }));
219
+ } finally {
220
+ client.release();
221
+ }
222
+ }
223
+ async getTableSchema(tableName, schema) {
160
224
  if (!this.pool) {
161
225
  throw new Error("Not connected to database");
162
226
  }
163
227
  const client = await this.pool.connect();
164
228
  try {
229
+ const schemaToUse = schema || "public";
165
230
  const result = await client.query(`
166
231
  SELECT
167
232
  column_name,
@@ -169,15 +234,104 @@ var PostgresConnector = class {
169
234
  is_nullable,
170
235
  column_default
171
236
  FROM information_schema.columns
172
- WHERE table_schema = 'public'
173
- AND table_name = $1
237
+ WHERE table_schema = $1
238
+ AND table_name = $2
174
239
  ORDER BY ordinal_position
175
- `, [tableName]);
240
+ `, [schemaToUse, tableName]);
176
241
  return result.rows;
177
242
  } finally {
178
243
  client.release();
179
244
  }
180
245
  }
246
+ async getStoredProcedures(schema) {
247
+ if (!this.pool) {
248
+ throw new Error("Not connected to database");
249
+ }
250
+ const client = await this.pool.connect();
251
+ try {
252
+ const schemaToUse = schema || "public";
253
+ const result = await client.query(`
254
+ SELECT
255
+ routine_name
256
+ FROM information_schema.routines
257
+ WHERE routine_schema = $1
258
+ ORDER BY routine_name
259
+ `, [schemaToUse]);
260
+ return result.rows.map((row) => row.routine_name);
261
+ } finally {
262
+ client.release();
263
+ }
264
+ }
265
+ async getStoredProcedureDetail(procedureName, schema) {
266
+ if (!this.pool) {
267
+ throw new Error("Not connected to database");
268
+ }
269
+ const client = await this.pool.connect();
270
+ try {
271
+ const schemaToUse = schema || "public";
272
+ const result = await client.query(`
273
+ SELECT
274
+ routine_name as procedure_name,
275
+ routine_type,
276
+ CASE WHEN routine_type = 'PROCEDURE' THEN 'procedure' ELSE 'function' END as procedure_type,
277
+ external_language as language,
278
+ data_type as return_type,
279
+ routine_definition as definition,
280
+ (
281
+ SELECT string_agg(
282
+ parameter_name || ' ' ||
283
+ parameter_mode || ' ' ||
284
+ data_type,
285
+ ', '
286
+ )
287
+ FROM information_schema.parameters
288
+ WHERE specific_schema = $1
289
+ AND specific_name = $2
290
+ AND parameter_name IS NOT NULL
291
+ ) as parameter_list
292
+ FROM information_schema.routines
293
+ WHERE routine_schema = $1
294
+ AND routine_name = $2
295
+ `, [schemaToUse, procedureName]);
296
+ if (result.rows.length === 0) {
297
+ throw new Error(`Stored procedure '${procedureName}' not found in schema '${schemaToUse}'`);
298
+ }
299
+ const procedure = result.rows[0];
300
+ let definition = procedure.definition;
301
+ try {
302
+ const oidResult = await client.query(`
303
+ SELECT p.oid, p.prosrc
304
+ FROM pg_proc p
305
+ JOIN pg_namespace n ON p.pronamespace = n.oid
306
+ WHERE p.proname = $1
307
+ AND n.nspname = $2
308
+ `, [procedureName, schemaToUse]);
309
+ if (oidResult.rows.length > 0) {
310
+ if (!definition) {
311
+ const oid = oidResult.rows[0].oid;
312
+ const defResult = await client.query(`SELECT pg_get_functiondef($1)`, [oid]);
313
+ if (defResult.rows.length > 0) {
314
+ definition = defResult.rows[0].pg_get_functiondef;
315
+ } else {
316
+ definition = oidResult.rows[0].prosrc;
317
+ }
318
+ }
319
+ }
320
+ } catch (err) {
321
+ console.error(`Error getting procedure definition: ${err}`);
322
+ }
323
+ return {
324
+ procedure_name: procedure.procedure_name,
325
+ procedure_type: procedure.procedure_type,
326
+ language: procedure.language || "sql",
327
+ parameter_list: procedure.parameter_list || "",
328
+ return_type: procedure.return_type !== "void" ? procedure.return_type : void 0,
329
+ definition: definition || void 0
330
+ };
331
+ } finally {
332
+ client.release();
333
+ }
334
+ }
181
335
  async executeQuery(query) {
182
336
  if (!this.pool) {
183
337
  throw new Error("Not connected to database");
@@ -283,56 +437,232 @@ var SQLServerConnector = class {
283
437
  this.connection = void 0;
284
438
  }
285
439
  }
286
- async getTables() {
440
+ async getSchemas() {
287
441
  if (!this.connection) {
288
442
  throw new Error("Not connected to SQL Server database");
289
443
  }
290
444
  try {
291
445
  const result = await this.connection.request().query(`
446
+ SELECT SCHEMA_NAME
447
+ FROM INFORMATION_SCHEMA.SCHEMATA
448
+ ORDER BY SCHEMA_NAME
449
+ `);
450
+ return result.recordset.map((row) => row.SCHEMA_NAME);
451
+ } catch (error) {
452
+ throw new Error(`Failed to get schemas: ${error.message}`);
453
+ }
454
+ }
455
+ async getTables(schema) {
456
+ if (!this.connection) {
457
+ throw new Error("Not connected to SQL Server database");
458
+ }
459
+ try {
460
+ const schemaToUse = schema || "dbo";
461
+ const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
462
+ const query = `
292
463
  SELECT TABLE_NAME
293
464
  FROM INFORMATION_SCHEMA.TABLES
465
+ WHERE TABLE_SCHEMA = @schema
294
466
  ORDER BY TABLE_NAME
295
- `);
467
+ `;
468
+ const result = await request.query(query);
296
469
  return result.recordset.map((row) => row.TABLE_NAME);
297
470
  } catch (error) {
298
471
  throw new Error(`Failed to get tables: ${error.message}`);
299
472
  }
300
473
  }
301
- async tableExists(tableName) {
474
+ async tableExists(tableName, schema) {
302
475
  if (!this.connection) {
303
476
  throw new Error("Not connected to SQL Server database");
304
477
  }
305
478
  try {
306
- const result = await this.connection.request().input("tableName", sql.VarChar, tableName).query(`
307
- SELECT COUNT(*) as count
308
- FROM INFORMATION_SCHEMA.TABLES
309
- WHERE TABLE_NAME = @tableName
310
- `);
479
+ const schemaToUse = schema || "dbo";
480
+ const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
481
+ const query = `
482
+ SELECT COUNT(*) as count
483
+ FROM INFORMATION_SCHEMA.TABLES
484
+ WHERE TABLE_NAME = @tableName
485
+ AND TABLE_SCHEMA = @schema
486
+ `;
487
+ const result = await request.query(query);
311
488
  return result.recordset[0].count > 0;
312
489
  } catch (error) {
313
490
  throw new Error(`Failed to check if table exists: ${error.message}`);
314
491
  }
315
492
  }
316
- async getTableSchema(tableName) {
493
+ async getTableIndexes(tableName, schema) {
317
494
  if (!this.connection) {
318
495
  throw new Error("Not connected to SQL Server database");
319
496
  }
320
497
  try {
321
- const result = await this.connection.request().input("tableName", sql.VarChar, tableName).query(`
322
- SELECT
323
- COLUMN_NAME as column_name,
324
- DATA_TYPE as data_type,
325
- IS_NULLABLE as is_nullable,
326
- COLUMN_DEFAULT as column_default
327
- FROM INFORMATION_SCHEMA.COLUMNS
328
- WHERE TABLE_NAME = @tableName
329
- ORDER BY ORDINAL_POSITION
330
- `);
498
+ const schemaToUse = schema || "dbo";
499
+ const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
500
+ const query = `
501
+ SELECT
502
+ i.name AS index_name,
503
+ i.is_unique,
504
+ i.is_primary_key,
505
+ c.name AS column_name,
506
+ ic.key_ordinal
507
+ FROM
508
+ sys.indexes i
509
+ INNER JOIN
510
+ sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
511
+ INNER JOIN
512
+ sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
513
+ INNER JOIN
514
+ sys.tables t ON i.object_id = t.object_id
515
+ INNER JOIN
516
+ sys.schemas s ON t.schema_id = s.schema_id
517
+ WHERE
518
+ t.name = @tableName
519
+ AND s.name = @schema
520
+ ORDER BY
521
+ i.name,
522
+ ic.key_ordinal
523
+ `;
524
+ const result = await request.query(query);
525
+ const indexMap = /* @__PURE__ */ new Map();
526
+ for (const row of result.recordset) {
527
+ const indexName = row.index_name;
528
+ const columnName = row.column_name;
529
+ const isUnique = !!row.is_unique;
530
+ const isPrimary = !!row.is_primary_key;
531
+ if (!indexMap.has(indexName)) {
532
+ indexMap.set(indexName, {
533
+ columns: [],
534
+ is_unique: isUnique,
535
+ is_primary: isPrimary
536
+ });
537
+ }
538
+ const indexInfo = indexMap.get(indexName);
539
+ indexInfo.columns.push(columnName);
540
+ }
541
+ const indexes = [];
542
+ indexMap.forEach((info, name) => {
543
+ indexes.push({
544
+ index_name: name,
545
+ column_names: info.columns,
546
+ is_unique: info.is_unique,
547
+ is_primary: info.is_primary
548
+ });
549
+ });
550
+ return indexes;
551
+ } catch (error) {
552
+ throw new Error(`Failed to get indexes for table ${tableName}: ${error.message}`);
553
+ }
554
+ }
555
+ async getTableSchema(tableName, schema) {
556
+ if (!this.connection) {
557
+ throw new Error("Not connected to SQL Server database");
558
+ }
559
+ try {
560
+ const schemaToUse = schema || "dbo";
561
+ const request = this.connection.request().input("tableName", sql.VarChar, tableName).input("schema", sql.VarChar, schemaToUse);
562
+ const query = `
563
+ SELECT
564
+ COLUMN_NAME as column_name,
565
+ DATA_TYPE as data_type,
566
+ IS_NULLABLE as is_nullable,
567
+ COLUMN_DEFAULT as column_default
568
+ FROM INFORMATION_SCHEMA.COLUMNS
569
+ WHERE TABLE_NAME = @tableName
570
+ AND TABLE_SCHEMA = @schema
571
+ ORDER BY ORDINAL_POSITION
572
+ `;
573
+ const result = await request.query(query);
331
574
  return result.recordset;
332
575
  } catch (error) {
333
576
  throw new Error(`Failed to get schema for table ${tableName}: ${error.message}`);
334
577
  }
335
578
  }
579
+ async getStoredProcedures(schema) {
580
+ if (!this.connection) {
581
+ throw new Error("Not connected to SQL Server database");
582
+ }
583
+ try {
584
+ const schemaToUse = schema || "dbo";
585
+ const request = this.connection.request().input("schema", sql.VarChar, schemaToUse);
586
+ const query = `
587
+ SELECT ROUTINE_NAME
588
+ FROM INFORMATION_SCHEMA.ROUTINES
589
+ WHERE ROUTINE_SCHEMA = @schema
590
+ AND (ROUTINE_TYPE = 'PROCEDURE' OR ROUTINE_TYPE = 'FUNCTION')
591
+ ORDER BY ROUTINE_NAME
592
+ `;
593
+ const result = await request.query(query);
594
+ return result.recordset.map((row) => row.ROUTINE_NAME);
595
+ } catch (error) {
596
+ throw new Error(`Failed to get stored procedures: ${error.message}`);
597
+ }
598
+ }
599
+ async getStoredProcedureDetail(procedureName, schema) {
600
+ if (!this.connection) {
601
+ throw new Error("Not connected to SQL Server database");
602
+ }
603
+ try {
604
+ const schemaToUse = schema || "dbo";
605
+ const request = this.connection.request().input("procedureName", sql.VarChar, procedureName).input("schema", sql.VarChar, schemaToUse);
606
+ const routineQuery = `
607
+ SELECT
608
+ ROUTINE_NAME as procedure_name,
609
+ ROUTINE_TYPE,
610
+ DATA_TYPE as return_data_type
611
+ FROM INFORMATION_SCHEMA.ROUTINES
612
+ WHERE ROUTINE_NAME = @procedureName
613
+ AND ROUTINE_SCHEMA = @schema
614
+ `;
615
+ const routineResult = await request.query(routineQuery);
616
+ if (routineResult.recordset.length === 0) {
617
+ throw new Error(`Stored procedure '${procedureName}' not found in schema '${schemaToUse}'`);
618
+ }
619
+ const routine = routineResult.recordset[0];
620
+ const parameterQuery = `
621
+ SELECT
622
+ PARAMETER_NAME,
623
+ PARAMETER_MODE,
624
+ DATA_TYPE,
625
+ CHARACTER_MAXIMUM_LENGTH,
626
+ ORDINAL_POSITION
627
+ FROM INFORMATION_SCHEMA.PARAMETERS
628
+ WHERE SPECIFIC_NAME = @procedureName
629
+ AND SPECIFIC_SCHEMA = @schema
630
+ ORDER BY ORDINAL_POSITION
631
+ `;
632
+ const parameterResult = await request.query(parameterQuery);
633
+ let parameterList = "";
634
+ if (parameterResult.recordset.length > 0) {
635
+ parameterList = parameterResult.recordset.map((param) => {
636
+ const lengthStr = param.CHARACTER_MAXIMUM_LENGTH > 0 ? `(${param.CHARACTER_MAXIMUM_LENGTH})` : "";
637
+ return `${param.PARAMETER_NAME} ${param.PARAMETER_MODE} ${param.DATA_TYPE}${lengthStr}`;
638
+ }).join(", ");
639
+ }
640
+ const definitionQuery = `
641
+ SELECT definition
642
+ FROM sys.sql_modules sm
643
+ JOIN sys.objects o ON sm.object_id = o.object_id
644
+ JOIN sys.schemas s ON o.schema_id = s.schema_id
645
+ WHERE o.name = @procedureName
646
+ AND s.name = @schema
647
+ `;
648
+ const definitionResult = await request.query(definitionQuery);
649
+ let definition = void 0;
650
+ if (definitionResult.recordset.length > 0) {
651
+ definition = definitionResult.recordset[0].definition;
652
+ }
653
+ return {
654
+ procedure_name: routine.procedure_name,
655
+ procedure_type: routine.ROUTINE_TYPE === "PROCEDURE" ? "procedure" : "function",
656
+ language: "sql",
657
+ // SQL Server procedures are typically in T-SQL
658
+ parameter_list: parameterList,
659
+ return_type: routine.ROUTINE_TYPE === "FUNCTION" ? routine.return_data_type : void 0,
660
+ definition
661
+ };
662
+ } catch (error) {
663
+ throw new Error(`Failed to get stored procedure details: ${error.message}`);
664
+ }
665
+ }
336
666
  async executeQuery(query) {
337
667
  if (!this.connection) {
338
668
  throw new Error("Not connected to SQL Server database");
@@ -439,7 +769,13 @@ var SQLiteConnector = class {
439
769
  }
440
770
  return Promise.resolve();
441
771
  }
442
- async getTables() {
772
+ async getSchemas() {
773
+ if (!this.db) {
774
+ throw new Error("Not connected to SQLite database");
775
+ }
776
+ return [this.dbPath === ":memory:" ? "main" : this.dbPath];
777
+ }
778
+ async getTables(schema) {
443
779
  if (!this.db) {
444
780
  throw new Error("Not connected to SQLite database");
445
781
  }
@@ -454,7 +790,7 @@ var SQLiteConnector = class {
454
790
  throw error;
455
791
  }
456
792
  }
457
- async tableExists(tableName) {
793
+ async tableExists(tableName, schema) {
458
794
  if (!this.db) {
459
795
  throw new Error("Not connected to SQLite database");
460
796
  }
@@ -468,7 +804,49 @@ var SQLiteConnector = class {
468
804
  throw error;
469
805
  }
470
806
  }
471
- async getTableSchema(tableName) {
807
+ async getTableIndexes(tableName, schema) {
808
+ if (!this.db) {
809
+ throw new Error("Not connected to SQLite database");
810
+ }
811
+ try {
812
+ const indexInfoRows = this.db.prepare(`
813
+ SELECT
814
+ name as index_name,
815
+ CASE
816
+ WHEN "unique" = 1 THEN 1
817
+ ELSE 0
818
+ END as is_unique
819
+ FROM sqlite_master
820
+ WHERE type = 'index'
821
+ AND tbl_name = ?
822
+ `).all(tableName);
823
+ const tableInfo = this.db.prepare(`PRAGMA table_info(${tableName})`).all();
824
+ const pkColumns = tableInfo.filter((col) => col.pk > 0).map((col) => col.name);
825
+ const results = [];
826
+ for (const indexInfo of indexInfoRows) {
827
+ const indexDetailRows = this.db.prepare(`PRAGMA index_info(${indexInfo.index_name})`).all();
828
+ const columnNames = indexDetailRows.map((row) => row.name);
829
+ results.push({
830
+ index_name: indexInfo.index_name,
831
+ column_names: columnNames,
832
+ is_unique: indexInfo.is_unique === 1,
833
+ is_primary: false
834
+ });
835
+ }
836
+ if (pkColumns.length > 0) {
837
+ results.push({
838
+ index_name: "PRIMARY",
839
+ column_names: pkColumns,
840
+ is_unique: true,
841
+ is_primary: true
842
+ });
843
+ }
844
+ return results;
845
+ } catch (error) {
846
+ throw error;
847
+ }
848
+ }
849
+ async getTableSchema(tableName, schema) {
472
850
  if (!this.db) {
473
851
  throw new Error("Not connected to SQLite database");
474
852
  }
@@ -486,6 +864,18 @@ var SQLiteConnector = class {
486
864
  throw error;
487
865
  }
488
866
  }
867
+ async getStoredProcedures(schema) {
868
+ if (!this.db) {
869
+ throw new Error("Not connected to SQLite database");
870
+ }
871
+ return [];
872
+ }
873
+ async getStoredProcedureDetail(procedureName, schema) {
874
+ if (!this.db) {
875
+ throw new Error("Not connected to SQLite database");
876
+ }
877
+ throw new Error("SQLite does not support stored procedures. Functions are defined programmatically through the SQLite API, not stored in the database.");
878
+ }
489
879
  async executeQuery(query) {
490
880
  if (!this.db) {
491
881
  throw new Error("Not connected to SQLite database");
@@ -578,62 +968,255 @@ var MySQLConnector = class {
578
968
  this.pool = null;
579
969
  }
580
970
  }
581
- async getTables() {
971
+ async getSchemas() {
582
972
  if (!this.pool) {
583
973
  throw new Error("Not connected to database");
584
974
  }
585
975
  try {
586
976
  const [rows] = await this.pool.query(`
587
- SELECT table_name
588
- FROM information_schema.tables
589
- WHERE table_schema = DATABASE()
590
- ORDER BY table_name
977
+ SELECT SCHEMA_NAME
978
+ FROM INFORMATION_SCHEMA.SCHEMATA
979
+ ORDER BY SCHEMA_NAME
591
980
  `);
592
- return rows.map((row) => row.table_name);
981
+ return rows.map((row) => row.SCHEMA_NAME);
982
+ } catch (error) {
983
+ console.error("Error getting schemas:", error);
984
+ throw error;
985
+ }
986
+ }
987
+ async getTables(schema) {
988
+ if (!this.pool) {
989
+ throw new Error("Not connected to database");
990
+ }
991
+ try {
992
+ const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
993
+ const queryParams = schema ? [schema] : [];
994
+ const [rows] = await this.pool.query(`
995
+ SELECT TABLE_NAME
996
+ FROM INFORMATION_SCHEMA.TABLES
997
+ ${schemaClause}
998
+ ORDER BY TABLE_NAME
999
+ `, queryParams);
1000
+ return rows.map((row) => row.TABLE_NAME);
593
1001
  } catch (error) {
594
1002
  console.error("Error getting tables:", error);
595
1003
  throw error;
596
1004
  }
597
1005
  }
598
- async tableExists(tableName) {
1006
+ async tableExists(tableName, schema) {
599
1007
  if (!this.pool) {
600
1008
  throw new Error("Not connected to database");
601
1009
  }
602
1010
  try {
1011
+ const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1012
+ const queryParams = schema ? [schema, tableName] : [tableName];
603
1013
  const [rows] = await this.pool.query(`
604
- SELECT COUNT(*) as count
605
- FROM information_schema.tables
606
- WHERE table_schema = DATABASE()
607
- AND table_name = ?
608
- `, [tableName]);
609
- return rows[0].count > 0;
1014
+ SELECT COUNT(*) AS COUNT
1015
+ FROM INFORMATION_SCHEMA.TABLES
1016
+ ${schemaClause}
1017
+ AND TABLE_NAME = ?
1018
+ `, queryParams);
1019
+ return rows[0].COUNT > 0;
610
1020
  } catch (error) {
611
1021
  console.error("Error checking if table exists:", error);
612
1022
  throw error;
613
1023
  }
614
1024
  }
615
- async getTableSchema(tableName) {
1025
+ async getTableIndexes(tableName, schema) {
616
1026
  if (!this.pool) {
617
1027
  throw new Error("Not connected to database");
618
1028
  }
619
1029
  try {
1030
+ const schemaClause = schema ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
1031
+ const queryParams = schema ? [schema, tableName] : [tableName];
1032
+ const [indexRows] = await this.pool.query(`
1033
+ SELECT
1034
+ INDEX_NAME,
1035
+ COLUMN_NAME,
1036
+ NON_UNIQUE,
1037
+ SEQ_IN_INDEX
1038
+ FROM
1039
+ INFORMATION_SCHEMA.STATISTICS
1040
+ WHERE
1041
+ ${schemaClause}
1042
+ AND TABLE_NAME = ?
1043
+ ORDER BY
1044
+ INDEX_NAME,
1045
+ SEQ_IN_INDEX
1046
+ `, queryParams);
1047
+ const indexMap = /* @__PURE__ */ new Map();
1048
+ for (const row of indexRows) {
1049
+ const indexName = row.INDEX_NAME;
1050
+ const columnName = row.COLUMN_NAME;
1051
+ const isUnique = row.NON_UNIQUE === 0;
1052
+ const isPrimary = indexName === "PRIMARY";
1053
+ if (!indexMap.has(indexName)) {
1054
+ indexMap.set(indexName, {
1055
+ columns: [],
1056
+ is_unique: isUnique,
1057
+ is_primary: isPrimary
1058
+ });
1059
+ }
1060
+ const indexInfo = indexMap.get(indexName);
1061
+ indexInfo.columns.push(columnName);
1062
+ }
1063
+ const results = [];
1064
+ indexMap.forEach((indexInfo, indexName) => {
1065
+ results.push({
1066
+ index_name: indexName,
1067
+ column_names: indexInfo.columns,
1068
+ is_unique: indexInfo.is_unique,
1069
+ is_primary: indexInfo.is_primary
1070
+ });
1071
+ });
1072
+ return results;
1073
+ } catch (error) {
1074
+ console.error("Error getting table indexes:", error);
1075
+ throw error;
1076
+ }
1077
+ }
1078
+ async getTableSchema(tableName, schema) {
1079
+ if (!this.pool) {
1080
+ throw new Error("Not connected to database");
1081
+ }
1082
+ try {
1083
+ const schemaClause = schema ? "WHERE TABLE_SCHEMA = ?" : "WHERE TABLE_SCHEMA = DATABASE()";
1084
+ const queryParams = schema ? [schema, tableName] : [tableName];
620
1085
  const [rows] = await this.pool.query(`
621
1086
  SELECT
622
- column_name,
623
- data_type,
624
- is_nullable,
625
- column_default
626
- FROM information_schema.columns
627
- WHERE table_schema = DATABASE()
628
- AND table_name = ?
629
- ORDER BY ordinal_position
630
- `, [tableName]);
1087
+ COLUMN_NAME as column_name,
1088
+ DATA_TYPE as data_type,
1089
+ IS_NULLABLE as is_nullable,
1090
+ COLUMN_DEFAULT as column_default
1091
+ FROM INFORMATION_SCHEMA.COLUMNS
1092
+ ${schemaClause}
1093
+ AND TABLE_NAME = ?
1094
+ ORDER BY ORDINAL_POSITION
1095
+ `, queryParams);
631
1096
  return rows;
632
1097
  } catch (error) {
633
1098
  console.error("Error getting table schema:", error);
634
1099
  throw error;
635
1100
  }
636
1101
  }
1102
+ async getStoredProcedures(schema) {
1103
+ if (!this.pool) {
1104
+ throw new Error("Not connected to database");
1105
+ }
1106
+ try {
1107
+ const schemaClause = schema ? "WHERE ROUTINE_SCHEMA = ?" : "WHERE ROUTINE_SCHEMA = DATABASE()";
1108
+ const queryParams = schema ? [schema] : [];
1109
+ const [rows] = await this.pool.query(`
1110
+ SELECT ROUTINE_NAME
1111
+ FROM INFORMATION_SCHEMA.ROUTINES
1112
+ ${schemaClause}
1113
+ ORDER BY ROUTINE_NAME
1114
+ `, queryParams);
1115
+ return rows.map((row) => row.ROUTINE_NAME);
1116
+ } catch (error) {
1117
+ console.error("Error getting stored procedures:", error);
1118
+ throw error;
1119
+ }
1120
+ }
1121
+ async getStoredProcedureDetail(procedureName, schema) {
1122
+ if (!this.pool) {
1123
+ throw new Error("Not connected to database");
1124
+ }
1125
+ try {
1126
+ const schemaClause = schema ? "WHERE r.ROUTINE_SCHEMA = ?" : "WHERE r.ROUTINE_SCHEMA = DATABASE()";
1127
+ const queryParams = schema ? [schema, procedureName] : [procedureName];
1128
+ const [rows] = await this.pool.query(`
1129
+ SELECT
1130
+ r.ROUTINE_NAME AS procedure_name,
1131
+ CASE
1132
+ WHEN r.ROUTINE_TYPE = 'PROCEDURE' THEN 'procedure'
1133
+ ELSE 'function'
1134
+ END AS procedure_type,
1135
+ LOWER(r.ROUTINE_TYPE) AS routine_type,
1136
+ r.ROUTINE_DEFINITION,
1137
+ r.DTD_IDENTIFIER AS return_type,
1138
+ (
1139
+ SELECT GROUP_CONCAT(
1140
+ CONCAT(p.PARAMETER_NAME, ' ', p.PARAMETER_MODE, ' ', p.DATA_TYPE)
1141
+ ORDER BY p.ORDINAL_POSITION
1142
+ SEPARATOR ', '
1143
+ )
1144
+ FROM INFORMATION_SCHEMA.PARAMETERS p
1145
+ WHERE p.SPECIFIC_SCHEMA = r.ROUTINE_SCHEMA
1146
+ AND p.SPECIFIC_NAME = r.ROUTINE_NAME
1147
+ AND p.PARAMETER_NAME IS NOT NULL
1148
+ ) AS parameter_list
1149
+ FROM INFORMATION_SCHEMA.ROUTINES r
1150
+ ${schemaClause}
1151
+ AND r.ROUTINE_NAME = ?
1152
+ `, queryParams);
1153
+ if (rows.length === 0) {
1154
+ const schemaName = schema || "current schema";
1155
+ throw new Error(`Stored procedure '${procedureName}' not found in ${schemaName}`);
1156
+ }
1157
+ const procedure = rows[0];
1158
+ let definition = procedure.ROUTINE_DEFINITION;
1159
+ try {
1160
+ const schemaValue = schema || await this.getCurrentSchema();
1161
+ if (procedure.procedure_type === "procedure") {
1162
+ try {
1163
+ const [defRows] = await this.pool.query(`
1164
+ SHOW CREATE PROCEDURE ${schemaValue}.${procedureName}
1165
+ `);
1166
+ if (defRows && defRows.length > 0) {
1167
+ definition = defRows[0]["Create Procedure"];
1168
+ }
1169
+ } catch (err) {
1170
+ console.error(`Error getting procedure definition with SHOW CREATE: ${err}`);
1171
+ }
1172
+ } else {
1173
+ try {
1174
+ const [defRows] = await this.pool.query(`
1175
+ SHOW CREATE FUNCTION ${schemaValue}.${procedureName}
1176
+ `);
1177
+ if (defRows && defRows.length > 0) {
1178
+ definition = defRows[0]["Create Function"];
1179
+ }
1180
+ } catch (innerErr) {
1181
+ console.error(`Error getting function definition with SHOW CREATE: ${innerErr}`);
1182
+ }
1183
+ }
1184
+ if (!definition) {
1185
+ const [bodyRows] = await this.pool.query(`
1186
+ SELECT ROUTINE_DEFINITION, ROUTINE_BODY
1187
+ FROM INFORMATION_SCHEMA.ROUTINES
1188
+ WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ?
1189
+ `, [schemaValue, procedureName]);
1190
+ if (bodyRows && bodyRows.length > 0) {
1191
+ if (bodyRows[0].ROUTINE_DEFINITION) {
1192
+ definition = bodyRows[0].ROUTINE_DEFINITION;
1193
+ } else if (bodyRows[0].ROUTINE_BODY) {
1194
+ definition = bodyRows[0].ROUTINE_BODY;
1195
+ }
1196
+ }
1197
+ }
1198
+ } catch (error) {
1199
+ console.error(`Error getting procedure/function details: ${error}`);
1200
+ }
1201
+ return {
1202
+ procedure_name: procedure.procedure_name,
1203
+ procedure_type: procedure.procedure_type,
1204
+ language: "sql",
1205
+ // MySQL procedures are generally in SQL
1206
+ parameter_list: procedure.parameter_list || "",
1207
+ return_type: procedure.routine_type === "function" ? procedure.return_type : void 0,
1208
+ definition: definition || void 0
1209
+ };
1210
+ } catch (error) {
1211
+ console.error("Error getting stored procedure detail:", error);
1212
+ throw error;
1213
+ }
1214
+ }
1215
+ // Helper method to get current schema (database) name
1216
+ async getCurrentSchema() {
1217
+ const [rows] = await this.pool.query("SELECT DATABASE() AS DB");
1218
+ return rows[0].DB;
1219
+ }
637
1220
  async executeQuery(query) {
638
1221
  if (!this.pool) {
639
1222
  throw new Error("Not connected to database");
@@ -981,22 +1564,62 @@ function formatPromptErrorResponse(error, code = "ERROR") {
981
1564
  }
982
1565
 
983
1566
  // src/resources/tables.ts
984
- async function tablesResourceHandler(uri, _extra) {
1567
+ async function tablesResourceHandler(uri, variables, _extra) {
985
1568
  const connector = ConnectorManager.getCurrentConnector();
986
- const tableNames = await connector.getTables();
987
- const responseData = {
988
- tables: tableNames,
989
- count: tableNames.length
990
- };
991
- return createResourceSuccessResponse(uri.href, responseData);
1569
+ const schemaName = variables && variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
1570
+ try {
1571
+ if (schemaName) {
1572
+ const availableSchemas = await connector.getSchemas();
1573
+ if (!availableSchemas.includes(schemaName)) {
1574
+ return createResourceErrorResponse(
1575
+ uri.href,
1576
+ `Schema '${schemaName}' does not exist or cannot be accessed`,
1577
+ "SCHEMA_NOT_FOUND"
1578
+ );
1579
+ }
1580
+ }
1581
+ const tableNames = await connector.getTables(schemaName);
1582
+ const responseData = {
1583
+ tables: tableNames,
1584
+ count: tableNames.length,
1585
+ schema: schemaName
1586
+ };
1587
+ return createResourceSuccessResponse(uri.href, responseData);
1588
+ } catch (error) {
1589
+ return createResourceErrorResponse(
1590
+ uri.href,
1591
+ `Error retrieving tables: ${error.message}`,
1592
+ "TABLES_RETRIEVAL_ERROR"
1593
+ );
1594
+ }
992
1595
  }
993
1596
 
994
1597
  // src/resources/schema.ts
995
- async function schemaResourceHandler(uri, variables, _extra) {
1598
+ async function tableStructureResourceHandler(uri, variables, _extra) {
996
1599
  const connector = ConnectorManager.getCurrentConnector();
997
1600
  const tableName = Array.isArray(variables.tableName) ? variables.tableName[0] : variables.tableName;
1601
+ const schemaName = variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
998
1602
  try {
999
- const columns = await connector.getTableSchema(tableName);
1603
+ if (schemaName) {
1604
+ const availableSchemas = await connector.getSchemas();
1605
+ if (!availableSchemas.includes(schemaName)) {
1606
+ return createResourceErrorResponse(
1607
+ uri.href,
1608
+ `Schema '${schemaName}' does not exist or cannot be accessed`,
1609
+ "SCHEMA_NOT_FOUND"
1610
+ );
1611
+ }
1612
+ }
1613
+ const tableExists = await connector.tableExists(tableName, schemaName);
1614
+ if (!tableExists) {
1615
+ const schemaInfo = schemaName ? ` in schema '${schemaName}'` : "";
1616
+ return createResourceErrorResponse(
1617
+ uri.href,
1618
+ `Table '${tableName}'${schemaInfo} does not exist or cannot be accessed`,
1619
+ "TABLE_NOT_FOUND"
1620
+ );
1621
+ }
1622
+ const columns = await connector.getTableSchema(tableName, schemaName);
1000
1623
  const formattedColumns = columns.map((col) => ({
1001
1624
  name: col.column_name,
1002
1625
  type: col.data_type,
@@ -1005,6 +1628,7 @@ async function schemaResourceHandler(uri, variables, _extra) {
1005
1628
  }));
1006
1629
  const responseData = {
1007
1630
  table: tableName,
1631
+ schema: schemaName,
1008
1632
  columns: formattedColumns,
1009
1633
  count: formattedColumns.length
1010
1634
  };
@@ -1012,8 +1636,147 @@ async function schemaResourceHandler(uri, variables, _extra) {
1012
1636
  } catch (error) {
1013
1637
  return createResourceErrorResponse(
1014
1638
  uri.href,
1015
- `Table '${tableName}' does not exist or cannot be accessed`,
1016
- "TABLE_NOT_FOUND"
1639
+ `Error retrieving schema: ${error.message}`,
1640
+ "SCHEMA_RETRIEVAL_ERROR"
1641
+ );
1642
+ }
1643
+ }
1644
+
1645
+ // src/resources/schemas.ts
1646
+ async function schemasResourceHandler(uri, _extra) {
1647
+ const connector = ConnectorManager.getCurrentConnector();
1648
+ try {
1649
+ const schemas = await connector.getSchemas();
1650
+ const responseData = {
1651
+ schemas,
1652
+ count: schemas.length
1653
+ };
1654
+ return createResourceSuccessResponse(uri.href, responseData);
1655
+ } catch (error) {
1656
+ return createResourceErrorResponse(
1657
+ uri.href,
1658
+ `Error retrieving database schemas: ${error.message}`,
1659
+ "SCHEMAS_RETRIEVAL_ERROR"
1660
+ );
1661
+ }
1662
+ }
1663
+
1664
+ // src/resources/indexes.ts
1665
+ async function indexesResourceHandler(uri, variables, _extra) {
1666
+ const connector = ConnectorManager.getCurrentConnector();
1667
+ const schemaName = variables && variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
1668
+ const tableName = variables && variables.tableName ? Array.isArray(variables.tableName) ? variables.tableName[0] : variables.tableName : void 0;
1669
+ if (!tableName) {
1670
+ return createResourceErrorResponse(
1671
+ uri.href,
1672
+ "Table name is required",
1673
+ "MISSING_TABLE_NAME"
1674
+ );
1675
+ }
1676
+ try {
1677
+ if (schemaName) {
1678
+ const availableSchemas = await connector.getSchemas();
1679
+ if (!availableSchemas.includes(schemaName)) {
1680
+ return createResourceErrorResponse(
1681
+ uri.href,
1682
+ `Schema '${schemaName}' does not exist or cannot be accessed`,
1683
+ "SCHEMA_NOT_FOUND"
1684
+ );
1685
+ }
1686
+ }
1687
+ const tableExists = await connector.tableExists(tableName, schemaName);
1688
+ if (!tableExists) {
1689
+ return createResourceErrorResponse(
1690
+ uri.href,
1691
+ `Table '${tableName}' does not exist in schema '${schemaName || "default"}'`,
1692
+ "TABLE_NOT_FOUND"
1693
+ );
1694
+ }
1695
+ const indexes = await connector.getTableIndexes(tableName, schemaName);
1696
+ const responseData = {
1697
+ table: tableName,
1698
+ schema: schemaName,
1699
+ indexes,
1700
+ count: indexes.length
1701
+ };
1702
+ return createResourceSuccessResponse(uri.href, responseData);
1703
+ } catch (error) {
1704
+ return createResourceErrorResponse(
1705
+ uri.href,
1706
+ `Error retrieving indexes: ${error.message}`,
1707
+ "INDEXES_RETRIEVAL_ERROR"
1708
+ );
1709
+ }
1710
+ }
1711
+
1712
+ // src/resources/procedures.ts
1713
+ async function proceduresResourceHandler(uri, variables, _extra) {
1714
+ const connector = ConnectorManager.getCurrentConnector();
1715
+ const schemaName = variables && variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
1716
+ try {
1717
+ if (schemaName) {
1718
+ const availableSchemas = await connector.getSchemas();
1719
+ if (!availableSchemas.includes(schemaName)) {
1720
+ return createResourceErrorResponse(
1721
+ uri.href,
1722
+ `Schema '${schemaName}' does not exist or cannot be accessed`,
1723
+ "SCHEMA_NOT_FOUND"
1724
+ );
1725
+ }
1726
+ }
1727
+ const procedureNames = await connector.getStoredProcedures(schemaName);
1728
+ const responseData = {
1729
+ procedures: procedureNames,
1730
+ count: procedureNames.length,
1731
+ schema: schemaName
1732
+ };
1733
+ return createResourceSuccessResponse(uri.href, responseData);
1734
+ } catch (error) {
1735
+ return createResourceErrorResponse(
1736
+ uri.href,
1737
+ `Error retrieving stored procedures: ${error.message}`,
1738
+ "PROCEDURES_RETRIEVAL_ERROR"
1739
+ );
1740
+ }
1741
+ }
1742
+ async function procedureDetailResourceHandler(uri, variables, _extra) {
1743
+ const connector = ConnectorManager.getCurrentConnector();
1744
+ const schemaName = variables && variables.schemaName ? Array.isArray(variables.schemaName) ? variables.schemaName[0] : variables.schemaName : void 0;
1745
+ const procedureName = variables && variables.procedureName ? Array.isArray(variables.procedureName) ? variables.procedureName[0] : variables.procedureName : void 0;
1746
+ if (!procedureName) {
1747
+ return createResourceErrorResponse(
1748
+ uri.href,
1749
+ "Procedure name is required",
1750
+ "MISSING_PARAMETER"
1751
+ );
1752
+ }
1753
+ try {
1754
+ if (schemaName) {
1755
+ const availableSchemas = await connector.getSchemas();
1756
+ if (!availableSchemas.includes(schemaName)) {
1757
+ return createResourceErrorResponse(
1758
+ uri.href,
1759
+ `Schema '${schemaName}' does not exist or cannot be accessed`,
1760
+ "SCHEMA_NOT_FOUND"
1761
+ );
1762
+ }
1763
+ }
1764
+ const procedureDetails = await connector.getStoredProcedureDetail(procedureName, schemaName);
1765
+ const responseData = {
1766
+ procedureName: procedureDetails.procedure_name,
1767
+ procedureType: procedureDetails.procedure_type,
1768
+ language: procedureDetails.language,
1769
+ parameters: procedureDetails.parameter_list,
1770
+ returnType: procedureDetails.return_type,
1771
+ definition: procedureDetails.definition,
1772
+ schema: schemaName
1773
+ };
1774
+ return createResourceSuccessResponse(uri.href, responseData);
1775
+ } catch (error) {
1776
+ return createResourceErrorResponse(
1777
+ uri.href,
1778
+ `Error retrieving procedure details: ${error.message}`,
1779
+ "PROCEDURE_DETAILS_ERROR"
1017
1780
  );
1018
1781
  }
1019
1782
  }
@@ -1021,14 +1784,34 @@ async function schemaResourceHandler(uri, variables, _extra) {
1021
1784
  // src/resources/index.ts
1022
1785
  function registerResources(server) {
1023
1786
  server.resource(
1024
- "tables",
1025
- "db://tables",
1787
+ "schemas",
1788
+ "db://schemas",
1789
+ schemasResourceHandler
1790
+ );
1791
+ server.resource(
1792
+ "tables_in_schema",
1793
+ new ResourceTemplate("db://schemas/{schemaName}/tables", { list: void 0 }),
1026
1794
  tablesResourceHandler
1027
1795
  );
1028
1796
  server.resource(
1029
- "schema",
1030
- new ResourceTemplate("db://schema/{tableName}", { list: void 0 }),
1031
- schemaResourceHandler
1797
+ "table_structure_in_schema",
1798
+ new ResourceTemplate("db://schemas/{schemaName}/tables/{tableName}", { list: void 0 }),
1799
+ tableStructureResourceHandler
1800
+ );
1801
+ server.resource(
1802
+ "indexes_in_table",
1803
+ new ResourceTemplate("db://schemas/{schemaName}/tables/{tableName}/indexes", { list: void 0 }),
1804
+ indexesResourceHandler
1805
+ );
1806
+ server.resource(
1807
+ "procedures_in_schema",
1808
+ new ResourceTemplate("db://schemas/{schemaName}/procedures", { list: void 0 }),
1809
+ proceduresResourceHandler
1810
+ );
1811
+ server.resource(
1812
+ "procedure_detail_in_schema",
1813
+ new ResourceTemplate("db://schemas/{schemaName}/procedures/{procedureName}", { list: void 0 }),
1814
+ procedureDetailResourceHandler
1032
1815
  );
1033
1816
  }
1034
1817
 
@@ -1063,7 +1846,6 @@ async function runQueryToolHandler({ query }, _extra) {
1063
1846
 
1064
1847
  // src/tools/list-connectors.ts
1065
1848
  async function listConnectorsToolHandler(_args, _extra) {
1066
- const connectors = ConnectorManager.getAvailableConnectors();
1067
1849
  const samples = ConnectorRegistry.getAllSampleDSNs();
1068
1850
  let activeConnectorId = null;
1069
1851
  try {
@@ -1107,52 +1889,108 @@ function registerTools(server) {
1107
1889
  import { z as z2 } from "zod";
1108
1890
  var sqlGeneratorSchema = {
1109
1891
  description: z2.string().describe("Natural language description of the SQL query to generate"),
1110
- dialect: z2.enum(["postgres", "sqlite"]).optional().describe("SQL dialect to use (optional)")
1892
+ schema: z2.string().optional().describe("Optional database schema to use")
1111
1893
  };
1112
- async function sqlGeneratorPromptHandler({ description, dialect }, _extra) {
1894
+ async function sqlGeneratorPromptHandler({ description, schema }, _extra) {
1113
1895
  try {
1114
1896
  const connector = ConnectorManager.getCurrentConnector();
1115
- const sqlDialect = dialect || (connector.id === "postgres" ? "postgres" : connector.id === "sqlite" ? "sqlite" : "postgres");
1116
- const tables = await connector.getTables();
1117
- const tableSchemas = await Promise.all(
1118
- tables.map(async (table) => {
1119
- try {
1120
- const columns = await connector.getTableSchema(table);
1121
- return {
1122
- table,
1123
- columns: columns.map((col) => ({
1124
- name: col.column_name,
1125
- type: col.data_type
1126
- }))
1127
- };
1128
- } catch (error) {
1129
- return null;
1130
- }
1131
- })
1132
- );
1133
- const accessibleSchemas = tableSchemas.filter((schema) => schema !== null);
1134
- const schemaContext = accessibleSchemas.length > 0 ? `Available tables and their columns:
1897
+ let sqlDialect;
1898
+ switch (connector.id) {
1899
+ case "postgres":
1900
+ sqlDialect = "postgres";
1901
+ break;
1902
+ case "sqlite":
1903
+ sqlDialect = "sqlite";
1904
+ break;
1905
+ case "mysql":
1906
+ sqlDialect = "mysql";
1907
+ break;
1908
+ case "sqlserver":
1909
+ sqlDialect = "mssql";
1910
+ break;
1911
+ default:
1912
+ sqlDialect = "ansi";
1913
+ }
1914
+ if (schema) {
1915
+ const availableSchemas = await connector.getSchemas();
1916
+ if (!availableSchemas.includes(schema)) {
1917
+ return formatPromptErrorResponse(
1918
+ `Schema '${schema}' does not exist or cannot be accessed. Available schemas: ${availableSchemas.join(", ")}`,
1919
+ "SCHEMA_NOT_FOUND"
1920
+ );
1921
+ }
1922
+ }
1923
+ try {
1924
+ const tables = await connector.getTables(schema);
1925
+ if (tables.length === 0) {
1926
+ const schemaInfo2 = schema ? `in schema '${schema}'` : "in the database";
1927
+ return formatPromptErrorResponse(
1928
+ `No tables found ${schemaInfo2}. Please check your database connection or schema name.`,
1929
+ "NO_TABLES_FOUND"
1930
+ );
1931
+ }
1932
+ const tableSchemas = await Promise.all(
1933
+ tables.map(async (table) => {
1934
+ try {
1935
+ const columns = await connector.getTableSchema(table, schema);
1936
+ return {
1937
+ table,
1938
+ columns: columns.map((col) => ({
1939
+ name: col.column_name,
1940
+ type: col.data_type
1941
+ }))
1942
+ };
1943
+ } catch (error) {
1944
+ return null;
1945
+ }
1946
+ })
1947
+ );
1948
+ const accessibleSchemas = tableSchemas.filter((schema2) => schema2 !== null);
1949
+ if (accessibleSchemas.length === 0) {
1950
+ return formatPromptErrorResponse(
1951
+ `No accessible tables found. You may not have sufficient permissions to access table schemas.`,
1952
+ "NO_ACCESSIBLE_TABLES"
1953
+ );
1954
+ }
1955
+ const schemaContext = accessibleSchemas.length > 0 ? `Available tables and their columns:
1135
1956
  ${accessibleSchemas.map(
1136
- (schema) => `- ${schema.table}: ${schema.columns.map(
1137
- (col) => `${col.name} (${col.type})`
1138
- ).join(", ")}`
1139
- ).join("\n")}` : "No schema information available.";
1140
- const dialectExamples = {
1141
- postgres: [
1142
- "SELECT * FROM users WHERE created_at > NOW() - INTERVAL '1 day'",
1143
- "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1144
- "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1145
- ],
1146
- sqlite: [
1147
- "SELECT * FROM users WHERE created_at > datetime('now', '-1 day')",
1148
- "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1149
- "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1150
- ]
1151
- };
1152
- const prompt = `
1957
+ (schema2) => `- ${schema2.table}: ${schema2.columns.map(
1958
+ (col) => `${col.name} (${col.type})`
1959
+ ).join(", ")}`
1960
+ ).join("\n")}` : "No schema information available.";
1961
+ const dialectExamples = {
1962
+ postgres: [
1963
+ "SELECT * FROM users WHERE created_at > NOW() - INTERVAL '1 day'",
1964
+ "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1965
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1966
+ ],
1967
+ sqlite: [
1968
+ "SELECT * FROM users WHERE created_at > datetime('now', '-1 day')",
1969
+ "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1970
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1971
+ ],
1972
+ mysql: [
1973
+ "SELECT * FROM users WHERE created_at > NOW() - INTERVAL 1 DAY",
1974
+ "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1975
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1976
+ ],
1977
+ mssql: [
1978
+ "SELECT * FROM users WHERE created_at > DATEADD(day, -1, GETDATE())",
1979
+ "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1980
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1981
+ ],
1982
+ ansi: [
1983
+ "SELECT * FROM users WHERE created_at > CURRENT_TIMESTAMP - INTERVAL '1' DAY",
1984
+ "SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.name HAVING COUNT(o.id) > 5",
1985
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1986
+ ]
1987
+ };
1988
+ const schemaInfo = schema ? `in schema '${schema}'` : "across all schemas";
1989
+ const prompt = `
1153
1990
  Generate a ${sqlDialect} SQL query based on this description: "${description}"
1154
1991
 
1155
1992
  ${schemaContext}
1993
+ Working ${schemaInfo}
1156
1994
 
1157
1995
  The query should:
1158
1996
  1. Be written for ${sqlDialect} dialect
@@ -1161,35 +1999,49 @@ The query should:
1161
1999
  4. Include appropriate comments
1162
2000
  5. Be compatible with ${sqlDialect} syntax
1163
2001
  `;
1164
- let generatedSQL;
1165
- if (description.toLowerCase().includes("count")) {
1166
- generatedSQL = `-- Count query generated from: "${description}"
2002
+ let generatedSQL;
2003
+ if (description.toLowerCase().includes("count")) {
2004
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2005
+ ` : "";
2006
+ generatedSQL = `${schemaPrefix}-- Count query generated from: "${description}"
1167
2007
  SELECT COUNT(*) AS count
1168
2008
  FROM ${accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name"};`;
1169
- } else if (description.toLowerCase().includes("average") || description.toLowerCase().includes("avg")) {
1170
- const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
1171
- const numericColumn = accessibleSchemas.length > 0 ? accessibleSchemas[0].columns.find((col) => ["int", "numeric", "decimal", "float", "real", "double"].some((t) => col.type.includes(t)))?.name || "numeric_column" : "numeric_column";
1172
- generatedSQL = `-- Average query generated from: "${description}"
2009
+ } else if (description.toLowerCase().includes("average") || description.toLowerCase().includes("avg")) {
2010
+ const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
2011
+ const numericColumn = accessibleSchemas.length > 0 ? accessibleSchemas[0].columns.find((col) => ["int", "numeric", "decimal", "float", "real", "double"].some((t) => col.type.includes(t)))?.name || "numeric_column" : "numeric_column";
2012
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2013
+ ` : "";
2014
+ generatedSQL = `${schemaPrefix}-- Average query generated from: "${description}"
1173
2015
  SELECT AVG(${numericColumn}) AS average
1174
2016
  FROM ${table};`;
1175
- } else if (description.toLowerCase().includes("join")) {
1176
- generatedSQL = `-- Join query generated from: "${description}"
2017
+ } else if (description.toLowerCase().includes("join")) {
2018
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2019
+ ` : "";
2020
+ generatedSQL = `${schemaPrefix}-- Join query generated from: "${description}"
1177
2021
  SELECT t1.*, t2.*
1178
2022
  FROM ${accessibleSchemas.length > 0 ? accessibleSchemas[0]?.table : "table1"} t1
1179
2023
  JOIN ${accessibleSchemas.length > 1 ? accessibleSchemas[1]?.table : "table2"} t2
1180
2024
  ON t1.id = t2.${accessibleSchemas.length > 0 ? accessibleSchemas[0]?.table : "table1"}_id;`;
1181
- } else {
1182
- const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
1183
- generatedSQL = `-- Query generated from: "${description}"
2025
+ } else {
2026
+ const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
2027
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2028
+ ` : "";
2029
+ generatedSQL = `${schemaPrefix}-- Query generated from: "${description}"
1184
2030
  SELECT *
1185
2031
  FROM ${table}
1186
2032
  LIMIT 10;`;
2033
+ }
2034
+ return formatPromptSuccessResponse(
2035
+ generatedSQL,
2036
+ // Add references to example queries that could help
2037
+ dialectExamples[sqlDialect]
2038
+ );
2039
+ } catch (error) {
2040
+ return formatPromptErrorResponse(
2041
+ `Error generating SQL query schema information: ${error.message}`,
2042
+ "SCHEMA_RETRIEVAL_ERROR"
2043
+ );
1187
2044
  }
1188
- return formatPromptSuccessResponse(
1189
- generatedSQL,
1190
- // Add references to example queries that could help
1191
- dialectExamples[sqlDialect]
1192
- );
1193
2045
  } catch (error) {
1194
2046
  return formatPromptErrorResponse(
1195
2047
  `Failed to generate SQL: ${error.message}`,
@@ -1201,17 +2053,35 @@ LIMIT 10;`;
1201
2053
  // src/prompts/db-explainer.ts
1202
2054
  import { z as z3 } from "zod";
1203
2055
  var dbExplainerSchema = {
1204
- target: z3.string().describe("Name of the table, column, or database to explain")
2056
+ schema: z3.string().optional().describe("Optional database schema to use"),
2057
+ table: z3.string().optional().describe("Optional specific table to explain")
1205
2058
  };
1206
- async function dbExplainerPromptHandler({ target }, _extra) {
2059
+ async function dbExplainerPromptHandler({ schema, table }, _extra) {
1207
2060
  try {
1208
2061
  const connector = ConnectorManager.getCurrentConnector();
1209
- const tables = await connector.getTables();
1210
- const normalizedTarget = target.toLowerCase();
1211
- const matchingTable = tables.find((t) => t.toLowerCase() === normalizedTarget);
1212
- if (matchingTable) {
1213
- const columns = await connector.getTableSchema(matchingTable);
1214
- const tableDescription = `Table: ${matchingTable}
2062
+ if (schema) {
2063
+ const availableSchemas = await connector.getSchemas();
2064
+ if (!availableSchemas.includes(schema)) {
2065
+ return formatPromptErrorResponse(
2066
+ `Schema '${schema}' does not exist or cannot be accessed. Available schemas: ${availableSchemas.join(", ")}`,
2067
+ "SCHEMA_NOT_FOUND"
2068
+ );
2069
+ }
2070
+ }
2071
+ const tables = await connector.getTables(schema);
2072
+ const normalizedTable = table?.toLowerCase() || "";
2073
+ const matchingTable = tables.find((t) => t.toLowerCase() === normalizedTable);
2074
+ if (matchingTable && table) {
2075
+ try {
2076
+ const columns = await connector.getTableSchema(matchingTable, schema);
2077
+ if (columns.length === 0) {
2078
+ return formatPromptErrorResponse(
2079
+ `Table '${matchingTable}' exists but has no columns or cannot be accessed.`,
2080
+ "EMPTY_TABLE_SCHEMA"
2081
+ );
2082
+ }
2083
+ const schemaInfo = schema ? ` in schema '${schema}'` : "";
2084
+ const tableDescription = `Table: ${matchingTable}${schemaInfo}
1215
2085
 
1216
2086
  Structure:
1217
2087
  ${columns.map((col) => `- ${col.column_name} (${col.data_type})${col.is_nullable === "YES" ? ", nullable" : ""}${col.column_default ? `, default: ${col.column_default}` : ""}`).join("\n")}
@@ -1221,12 +2091,25 @@ This table appears to store ${determineTablePurpose(matchingTable, columns)}
1221
2091
 
1222
2092
  Relationships:
1223
2093
  ${determineRelationships(matchingTable, columns)}`;
1224
- return formatPromptSuccessResponse(tableDescription);
2094
+ return formatPromptSuccessResponse(tableDescription);
2095
+ } catch (error) {
2096
+ return formatPromptErrorResponse(
2097
+ `Error retrieving schema for table '${matchingTable}': ${error.message}`,
2098
+ "TABLE_SCHEMA_ERROR"
2099
+ );
2100
+ }
1225
2101
  }
1226
- if (target.includes(".")) {
1227
- const [tableName, columnName] = target.split(".");
1228
- if (tables.find((t) => t.toLowerCase() === tableName.toLowerCase())) {
1229
- const columns = await connector.getTableSchema(tableName);
2102
+ if (table && table.includes(".")) {
2103
+ const [tableName, columnName] = table.split(".");
2104
+ const tableExists = tables.find((t) => t.toLowerCase() === tableName.toLowerCase());
2105
+ if (!tableExists) {
2106
+ return formatPromptErrorResponse(
2107
+ `Table '${tableName}' does not exist in schema '${schema || "default"}'. Available tables: ${tables.slice(0, 10).join(", ")}${tables.length > 10 ? "..." : ""}`,
2108
+ "TABLE_NOT_FOUND"
2109
+ );
2110
+ }
2111
+ try {
2112
+ const columns = await connector.getTableSchema(tableName, schema);
1230
2113
  const column = columns.find((c) => c.column_name.toLowerCase() === columnName.toLowerCase());
1231
2114
  if (column) {
1232
2115
  const columnDescription = `Column: ${tableName}.${column.column_name}
@@ -1238,11 +2121,22 @@ Default: ${column.column_default || "None"}
1238
2121
  Purpose:
1239
2122
  ${determineColumnPurpose(column.column_name, column.data_type)}`;
1240
2123
  return formatPromptSuccessResponse(columnDescription);
2124
+ } else {
2125
+ return formatPromptErrorResponse(
2126
+ `Column '${columnName}' does not exist in table '${tableName}'. Available columns: ${columns.map((c) => c.column_name).join(", ")}`,
2127
+ "COLUMN_NOT_FOUND"
2128
+ );
1241
2129
  }
2130
+ } catch (error) {
2131
+ return formatPromptErrorResponse(
2132
+ `Error accessing table schema: ${error.message}`,
2133
+ "SCHEMA_ACCESS_ERROR"
2134
+ );
1242
2135
  }
1243
2136
  }
1244
- if (["database", "db", "schema", "overview", "all"].includes(normalizedTarget)) {
1245
- let dbOverview = `Database Overview
2137
+ if (!table || ["database", "db", "schema", "overview", "all"].includes(normalizedTable)) {
2138
+ const schemaInfo = schema ? `in schema '${schema}'` : "across all schemas";
2139
+ let dbOverview = `Database Overview ${schemaInfo}
1246
2140
 
1247
2141
  Tables: ${tables.length}
1248
2142
  ${tables.map((t) => `- ${t}`).join("\n")}
@@ -1250,26 +2144,34 @@ ${tables.map((t) => `- ${t}`).join("\n")}
1250
2144
  This database ${describeDatabasePurpose(tables)}`;
1251
2145
  return formatPromptSuccessResponse(dbOverview);
1252
2146
  }
1253
- const possibleTableMatches = tables.filter(
1254
- (t) => t.toLowerCase().includes(normalizedTarget) || normalizedTarget.includes(t.toLowerCase())
1255
- );
1256
- if (possibleTableMatches.length > 0) {
1257
- return formatPromptSuccessResponse(
1258
- `Could not find exact match for "${target}". Did you mean one of these tables?
2147
+ if (table && !normalizedTable.includes(".")) {
2148
+ const possibleTableMatches = tables.filter(
2149
+ (t) => t.toLowerCase().includes(normalizedTable) || normalizedTable.includes(t.toLowerCase())
2150
+ );
2151
+ if (possibleTableMatches.length > 0) {
2152
+ return formatPromptSuccessResponse(
2153
+ `Table "${table}" not found. Did you mean one of these tables?
1259
2154
 
1260
2155
  ${possibleTableMatches.join("\n")}`
1261
- );
2156
+ );
2157
+ } else {
2158
+ const schemaInfo = schema ? `in schema '${schema}'` : "in the database";
2159
+ return formatPromptErrorResponse(
2160
+ `Table "${table}" does not exist ${schemaInfo}. Available tables: ${tables.slice(0, 10).join(", ")}${tables.length > 10 ? "..." : ""}`,
2161
+ "TABLE_NOT_FOUND"
2162
+ );
2163
+ }
1262
2164
  }
1263
- return formatPromptErrorResponse(
1264
- `Could not find a table, column, or database feature matching "${target}"`,
1265
- "NOT_FOUND"
1266
- );
1267
2165
  } catch (error) {
1268
2166
  return formatPromptErrorResponse(
1269
2167
  `Error explaining database: ${error.message}`,
1270
2168
  "EXPLANATION_ERROR"
1271
2169
  );
1272
2170
  }
2171
+ return formatPromptErrorResponse(
2172
+ `Unable to process request for schema: ${schema}, table: ${table}`,
2173
+ "UNHANDLED_REQUEST"
2174
+ );
1273
2175
  }
1274
2176
  function determineTablePurpose(tableName, columns) {
1275
2177
  const lowerTableName = tableName.toLowerCase();