@bytebase/dbhub 0.1.2 → 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.
- package/README.md +23 -19
- package/dist/index.js +1037 -138
- 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
|
|
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 =
|
|
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 =
|
|
151
|
-
AND table_name = $
|
|
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
|
|
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 =
|
|
173
|
-
AND table_name = $
|
|
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
|
|
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
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
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
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1016
|
+
${schemaClause}
|
|
607
1017
|
AND table_name = ?
|
|
608
|
-
`,
|
|
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
|
|
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
|
-
|
|
1092
|
+
${schemaClause}
|
|
628
1093
|
AND table_name = ?
|
|
629
1094
|
ORDER BY ordinal_position
|
|
630
|
-
`,
|
|
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
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
-
|
|
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
|
-
`
|
|
1016
|
-
"
|
|
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
|
-
"
|
|
1025
|
-
"db://
|
|
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
|
-
"
|
|
1030
|
-
new ResourceTemplate("db://
|
|
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
|
-
|
|
1893
|
+
schema: z2.string().optional().describe("Optional database schema to use")
|
|
1111
1894
|
};
|
|
1112
|
-
async function sqlGeneratorPromptHandler({ description,
|
|
1895
|
+
async function sqlGeneratorPromptHandler({ description, schema }, _extra) {
|
|
1113
1896
|
try {
|
|
1114
1897
|
const connector = ConnectorManager.getCurrentConnector();
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
)
|
|
1133
|
-
|
|
1134
|
-
|
|
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
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
-
|
|
1176
|
-
|
|
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
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
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
|
-
|
|
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({
|
|
2060
|
+
async function dbExplainerPromptHandler({ schema, table }, _extra) {
|
|
1207
2061
|
try {
|
|
1208
2062
|
const connector = ConnectorManager.getCurrentConnector();
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
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
|
-
|
|
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 (
|
|
1227
|
-
const [tableName, columnName] =
|
|
1228
|
-
|
|
1229
|
-
|
|
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(
|
|
1245
|
-
|
|
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
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
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}`,
|