@bytebase/dbhub 0.1.1 → 0.2.2

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 +23 -19
  2. package/dist/index.js +1038 -139
  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) {
494
+ if (!this.connection) {
495
+ throw new Error("Not connected to SQL Server database");
496
+ }
497
+ try {
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) {
317
556
  if (!this.connection) {
318
557
  throw new Error("Not connected to SQL Server database");
319
558
  }
320
559
  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
- `);
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,45 +968,120 @@ 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 {
976
+ const [rows] = await this.pool.query(`
977
+ SELECT schema_name
978
+ FROM information_schema.schemata
979
+ ORDER BY schema_name
980
+ `);
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] : [];
586
994
  const [rows] = await this.pool.query(`
587
995
  SELECT table_name
588
996
  FROM information_schema.tables
589
- WHERE table_schema = DATABASE()
997
+ ${schemaClause}
590
998
  ORDER BY table_name
591
- `);
999
+ `, queryParams);
592
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
1014
  SELECT COUNT(*) as count
605
1015
  FROM information_schema.tables
606
- WHERE table_schema = DATABASE()
1016
+ ${schemaClause}
607
1017
  AND table_name = ?
608
- `, [tableName]);
1018
+ `, queryParams);
609
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
1087
  column_name,
@@ -624,16 +1089,134 @@ var MySQLConnector = class {
624
1089
  is_nullable,
625
1090
  column_default
626
1091
  FROM information_schema.columns
627
- WHERE table_schema = DATABASE()
1092
+ ${schemaClause}
628
1093
  AND table_name = ?
629
1094
  ORDER BY ordinal_position
630
- `, [tableName]);
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");
@@ -864,7 +1447,7 @@ var __dirname2 = path2.dirname(__filename2);
864
1447
  var DEMO_DATA_DIR;
865
1448
  var projectRootPath = path2.join(__dirname2, "..", "..", "..");
866
1449
  var projectResourcesPath = path2.join(projectRootPath, "resources", "employee-sqlite");
867
- var distPath = path2.join(__dirname2, "..", "resources", "employee-sqlite");
1450
+ var distPath = path2.join(__dirname2, "resources", "employee-sqlite");
868
1451
  if (fs2.existsSync(projectResourcesPath)) {
869
1452
  DEMO_DATA_DIR = projectResourcesPath;
870
1453
  } else if (fs2.existsSync(distPath)) {
@@ -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
1598
  async function schemaResourceHandler(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,15 +1784,35 @@ 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 }),
1797
+ "table_structure_in_schema",
1798
+ new ResourceTemplate("db://schemas/{schemaName}/tables/{tableName}", { list: void 0 }),
1031
1799
  schemaResourceHandler
1032
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
1815
+ );
1033
1816
  }
1034
1817
 
1035
1818
  // src/tools/run-query.ts
@@ -1107,52 +1890,108 @@ function registerTools(server) {
1107
1890
  import { z as z2 } from "zod";
1108
1891
  var sqlGeneratorSchema = {
1109
1892
  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)")
1893
+ schema: z2.string().optional().describe("Optional database schema to use")
1111
1894
  };
1112
- async function sqlGeneratorPromptHandler({ description, dialect }, _extra) {
1895
+ async function sqlGeneratorPromptHandler({ description, schema }, _extra) {
1113
1896
  try {
1114
1897
  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:
1898
+ let sqlDialect;
1899
+ switch (connector.id) {
1900
+ case "postgres":
1901
+ sqlDialect = "postgres";
1902
+ break;
1903
+ case "sqlite":
1904
+ sqlDialect = "sqlite";
1905
+ break;
1906
+ case "mysql":
1907
+ sqlDialect = "mysql";
1908
+ break;
1909
+ case "sqlserver":
1910
+ sqlDialect = "mssql";
1911
+ break;
1912
+ default:
1913
+ sqlDialect = "ansi";
1914
+ }
1915
+ if (schema) {
1916
+ const availableSchemas = await connector.getSchemas();
1917
+ if (!availableSchemas.includes(schema)) {
1918
+ return formatPromptErrorResponse(
1919
+ `Schema '${schema}' does not exist or cannot be accessed. Available schemas: ${availableSchemas.join(", ")}`,
1920
+ "SCHEMA_NOT_FOUND"
1921
+ );
1922
+ }
1923
+ }
1924
+ try {
1925
+ const tables = await connector.getTables(schema);
1926
+ if (tables.length === 0) {
1927
+ const schemaInfo2 = schema ? `in schema '${schema}'` : "in the database";
1928
+ return formatPromptErrorResponse(
1929
+ `No tables found ${schemaInfo2}. Please check your database connection or schema name.`,
1930
+ "NO_TABLES_FOUND"
1931
+ );
1932
+ }
1933
+ const tableSchemas = await Promise.all(
1934
+ tables.map(async (table) => {
1935
+ try {
1936
+ const columns = await connector.getTableSchema(table, schema);
1937
+ return {
1938
+ table,
1939
+ columns: columns.map((col) => ({
1940
+ name: col.column_name,
1941
+ type: col.data_type
1942
+ }))
1943
+ };
1944
+ } catch (error) {
1945
+ return null;
1946
+ }
1947
+ })
1948
+ );
1949
+ const accessibleSchemas = tableSchemas.filter((schema2) => schema2 !== null);
1950
+ if (accessibleSchemas.length === 0) {
1951
+ return formatPromptErrorResponse(
1952
+ `No accessible tables found. You may not have sufficient permissions to access table schemas.`,
1953
+ "NO_ACCESSIBLE_TABLES"
1954
+ );
1955
+ }
1956
+ const schemaContext = accessibleSchemas.length > 0 ? `Available tables and their columns:
1135
1957
  ${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 = `
1958
+ (schema2) => `- ${schema2.table}: ${schema2.columns.map(
1959
+ (col) => `${col.name} (${col.type})`
1960
+ ).join(", ")}`
1961
+ ).join("\n")}` : "No schema information available.";
1962
+ const dialectExamples = {
1963
+ postgres: [
1964
+ "SELECT * FROM users WHERE created_at > NOW() - INTERVAL '1 day'",
1965
+ "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",
1966
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1967
+ ],
1968
+ sqlite: [
1969
+ "SELECT * FROM users WHERE created_at > datetime('now', '-1 day')",
1970
+ "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",
1971
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1972
+ ],
1973
+ mysql: [
1974
+ "SELECT * FROM users WHERE created_at > NOW() - INTERVAL 1 DAY",
1975
+ "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",
1976
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1977
+ ],
1978
+ mssql: [
1979
+ "SELECT * FROM users WHERE created_at > DATEADD(day, -1, GETDATE())",
1980
+ "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",
1981
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1982
+ ],
1983
+ ansi: [
1984
+ "SELECT * FROM users WHERE created_at > CURRENT_TIMESTAMP - INTERVAL '1' DAY",
1985
+ "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",
1986
+ "SELECT product_name, price FROM products WHERE price > (SELECT AVG(price) FROM products)"
1987
+ ]
1988
+ };
1989
+ const schemaInfo = schema ? `in schema '${schema}'` : "across all schemas";
1990
+ const prompt = `
1153
1991
  Generate a ${sqlDialect} SQL query based on this description: "${description}"
1154
1992
 
1155
1993
  ${schemaContext}
1994
+ Working ${schemaInfo}
1156
1995
 
1157
1996
  The query should:
1158
1997
  1. Be written for ${sqlDialect} dialect
@@ -1161,35 +2000,49 @@ The query should:
1161
2000
  4. Include appropriate comments
1162
2001
  5. Be compatible with ${sqlDialect} syntax
1163
2002
  `;
1164
- let generatedSQL;
1165
- if (description.toLowerCase().includes("count")) {
1166
- generatedSQL = `-- Count query generated from: "${description}"
2003
+ let generatedSQL;
2004
+ if (description.toLowerCase().includes("count")) {
2005
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2006
+ ` : "";
2007
+ generatedSQL = `${schemaPrefix}-- Count query generated from: "${description}"
1167
2008
  SELECT COUNT(*) AS count
1168
2009
  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}"
2010
+ } else if (description.toLowerCase().includes("average") || description.toLowerCase().includes("avg")) {
2011
+ const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
2012
+ 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";
2013
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2014
+ ` : "";
2015
+ generatedSQL = `${schemaPrefix}-- Average query generated from: "${description}"
1173
2016
  SELECT AVG(${numericColumn}) AS average
1174
2017
  FROM ${table};`;
1175
- } else if (description.toLowerCase().includes("join")) {
1176
- generatedSQL = `-- Join query generated from: "${description}"
2018
+ } else if (description.toLowerCase().includes("join")) {
2019
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2020
+ ` : "";
2021
+ generatedSQL = `${schemaPrefix}-- Join query generated from: "${description}"
1177
2022
  SELECT t1.*, t2.*
1178
2023
  FROM ${accessibleSchemas.length > 0 ? accessibleSchemas[0]?.table : "table1"} t1
1179
2024
  JOIN ${accessibleSchemas.length > 1 ? accessibleSchemas[1]?.table : "table2"} t2
1180
2025
  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}"
2026
+ } else {
2027
+ const table = accessibleSchemas.length > 0 ? accessibleSchemas[0].table : "table_name";
2028
+ const schemaPrefix = schema ? `-- Schema: ${schema}
2029
+ ` : "";
2030
+ generatedSQL = `${schemaPrefix}-- Query generated from: "${description}"
1184
2031
  SELECT *
1185
2032
  FROM ${table}
1186
2033
  LIMIT 10;`;
2034
+ }
2035
+ return formatPromptSuccessResponse(
2036
+ generatedSQL,
2037
+ // Add references to example queries that could help
2038
+ dialectExamples[sqlDialect]
2039
+ );
2040
+ } catch (error) {
2041
+ return formatPromptErrorResponse(
2042
+ `Error generating SQL query schema information: ${error.message}`,
2043
+ "SCHEMA_RETRIEVAL_ERROR"
2044
+ );
1187
2045
  }
1188
- return formatPromptSuccessResponse(
1189
- generatedSQL,
1190
- // Add references to example queries that could help
1191
- dialectExamples[sqlDialect]
1192
- );
1193
2046
  } catch (error) {
1194
2047
  return formatPromptErrorResponse(
1195
2048
  `Failed to generate SQL: ${error.message}`,
@@ -1201,17 +2054,35 @@ LIMIT 10;`;
1201
2054
  // src/prompts/db-explainer.ts
1202
2055
  import { z as z3 } from "zod";
1203
2056
  var dbExplainerSchema = {
1204
- target: z3.string().describe("Name of the table, column, or database to explain")
2057
+ schema: z3.string().optional().describe("Optional database schema to use"),
2058
+ table: z3.string().optional().describe("Optional specific table to explain")
1205
2059
  };
1206
- async function dbExplainerPromptHandler({ target }, _extra) {
2060
+ async function dbExplainerPromptHandler({ schema, table }, _extra) {
1207
2061
  try {
1208
2062
  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}
2063
+ if (schema) {
2064
+ const availableSchemas = await connector.getSchemas();
2065
+ if (!availableSchemas.includes(schema)) {
2066
+ return formatPromptErrorResponse(
2067
+ `Schema '${schema}' does not exist or cannot be accessed. Available schemas: ${availableSchemas.join(", ")}`,
2068
+ "SCHEMA_NOT_FOUND"
2069
+ );
2070
+ }
2071
+ }
2072
+ const tables = await connector.getTables(schema);
2073
+ const normalizedTable = table?.toLowerCase() || "";
2074
+ const matchingTable = tables.find((t) => t.toLowerCase() === normalizedTable);
2075
+ if (matchingTable && table) {
2076
+ try {
2077
+ const columns = await connector.getTableSchema(matchingTable, schema);
2078
+ if (columns.length === 0) {
2079
+ return formatPromptErrorResponse(
2080
+ `Table '${matchingTable}' exists but has no columns or cannot be accessed.`,
2081
+ "EMPTY_TABLE_SCHEMA"
2082
+ );
2083
+ }
2084
+ const schemaInfo = schema ? ` in schema '${schema}'` : "";
2085
+ const tableDescription = `Table: ${matchingTable}${schemaInfo}
1215
2086
 
1216
2087
  Structure:
1217
2088
  ${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 +2092,25 @@ This table appears to store ${determineTablePurpose(matchingTable, columns)}
1221
2092
 
1222
2093
  Relationships:
1223
2094
  ${determineRelationships(matchingTable, columns)}`;
1224
- return formatPromptSuccessResponse(tableDescription);
2095
+ return formatPromptSuccessResponse(tableDescription);
2096
+ } catch (error) {
2097
+ return formatPromptErrorResponse(
2098
+ `Error retrieving schema for table '${matchingTable}': ${error.message}`,
2099
+ "TABLE_SCHEMA_ERROR"
2100
+ );
2101
+ }
1225
2102
  }
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);
2103
+ if (table && table.includes(".")) {
2104
+ const [tableName, columnName] = table.split(".");
2105
+ const tableExists = tables.find((t) => t.toLowerCase() === tableName.toLowerCase());
2106
+ if (!tableExists) {
2107
+ return formatPromptErrorResponse(
2108
+ `Table '${tableName}' does not exist in schema '${schema || "default"}'. Available tables: ${tables.slice(0, 10).join(", ")}${tables.length > 10 ? "..." : ""}`,
2109
+ "TABLE_NOT_FOUND"
2110
+ );
2111
+ }
2112
+ try {
2113
+ const columns = await connector.getTableSchema(tableName, schema);
1230
2114
  const column = columns.find((c) => c.column_name.toLowerCase() === columnName.toLowerCase());
1231
2115
  if (column) {
1232
2116
  const columnDescription = `Column: ${tableName}.${column.column_name}
@@ -1238,11 +2122,22 @@ Default: ${column.column_default || "None"}
1238
2122
  Purpose:
1239
2123
  ${determineColumnPurpose(column.column_name, column.data_type)}`;
1240
2124
  return formatPromptSuccessResponse(columnDescription);
2125
+ } else {
2126
+ return formatPromptErrorResponse(
2127
+ `Column '${columnName}' does not exist in table '${tableName}'. Available columns: ${columns.map((c) => c.column_name).join(", ")}`,
2128
+ "COLUMN_NOT_FOUND"
2129
+ );
1241
2130
  }
2131
+ } catch (error) {
2132
+ return formatPromptErrorResponse(
2133
+ `Error accessing table schema: ${error.message}`,
2134
+ "SCHEMA_ACCESS_ERROR"
2135
+ );
1242
2136
  }
1243
2137
  }
1244
- if (["database", "db", "schema", "overview", "all"].includes(normalizedTarget)) {
1245
- let dbOverview = `Database Overview
2138
+ if (!table || ["database", "db", "schema", "overview", "all"].includes(normalizedTable)) {
2139
+ const schemaInfo = schema ? `in schema '${schema}'` : "across all schemas";
2140
+ let dbOverview = `Database Overview ${schemaInfo}
1246
2141
 
1247
2142
  Tables: ${tables.length}
1248
2143
  ${tables.map((t) => `- ${t}`).join("\n")}
@@ -1250,20 +2145,24 @@ ${tables.map((t) => `- ${t}`).join("\n")}
1250
2145
  This database ${describeDatabasePurpose(tables)}`;
1251
2146
  return formatPromptSuccessResponse(dbOverview);
1252
2147
  }
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?
2148
+ if (table && !normalizedTable.includes(".")) {
2149
+ const possibleTableMatches = tables.filter(
2150
+ (t) => t.toLowerCase().includes(normalizedTable) || normalizedTable.includes(t.toLowerCase())
2151
+ );
2152
+ if (possibleTableMatches.length > 0) {
2153
+ return formatPromptSuccessResponse(
2154
+ `Table "${table}" not found. Did you mean one of these tables?
1259
2155
 
1260
2156
  ${possibleTableMatches.join("\n")}`
1261
- );
2157
+ );
2158
+ } else {
2159
+ const schemaInfo = schema ? `in schema '${schema}'` : "in the database";
2160
+ return formatPromptErrorResponse(
2161
+ `Table "${table}" does not exist ${schemaInfo}. Available tables: ${tables.slice(0, 10).join(", ")}${tables.length > 10 ? "..." : ""}`,
2162
+ "TABLE_NOT_FOUND"
2163
+ );
2164
+ }
1262
2165
  }
1263
- return formatPromptErrorResponse(
1264
- `Could not find a table, column, or database feature matching "${target}"`,
1265
- "NOT_FOUND"
1266
- );
1267
2166
  } catch (error) {
1268
2167
  return formatPromptErrorResponse(
1269
2168
  `Error explaining database: ${error.message}`,