sqlcipher-mcp-server 1.0.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +433 -210
- package/package.json +1 -1
- package/src/config/constants.js +18 -40
- package/src/definitions/prompts.js +124 -0
- package/src/definitions/tools.js +363 -0
- package/src/handlers/http-handlers.js +576 -4
- package/src/handlers/mcp-handlers.js +558 -69
- package/src/handlers/prompt-handlers.js +601 -0
- package/src/server/http-server.js +52 -2
- package/src/server/mcp-server.js +208 -95
- package/src/services/database-service.js +395 -55
- package/src/utils/database-operations.js +967 -0
- package/src/utils/detectors.js +55 -0
- package/src/utils/formatters.js +470 -64
- package/src/utils/validators.js +147 -58
- package/lib/database.js +0 -216
|
@@ -5,8 +5,32 @@
|
|
|
5
5
|
|
|
6
6
|
import { SERVER_CONFIG } from '../config/constants.js';
|
|
7
7
|
import { getDatabasePassword, isPasswordConfigured } from '../config/environment.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
validateDatabasePath,
|
|
10
|
+
validateQuery,
|
|
11
|
+
validateTableName,
|
|
12
|
+
validateColumnName,
|
|
13
|
+
validatePattern,
|
|
14
|
+
validateNumericParameter,
|
|
15
|
+
resolveDatabasePath
|
|
16
|
+
} from '../utils/validators.js';
|
|
17
|
+
import {
|
|
18
|
+
executeQueryOnDatabase,
|
|
19
|
+
getTableListFromDatabase,
|
|
20
|
+
getTableSchemaFromDatabase,
|
|
21
|
+
getForeignKeysFromDatabase,
|
|
22
|
+
getIndexesFromDatabase,
|
|
23
|
+
getDatabaseInfoFromDatabase,
|
|
24
|
+
getTableInfoFromDatabase,
|
|
25
|
+
testDatabaseConnection,
|
|
26
|
+
explainQueryPlanFromDatabase,
|
|
27
|
+
getTableStatisticsFromDatabase,
|
|
28
|
+
sampleTableDataFromDatabase,
|
|
29
|
+
getColumnStatisticsFromDatabase,
|
|
30
|
+
searchTablesInDatabase,
|
|
31
|
+
searchColumnsInDatabase,
|
|
32
|
+
findRelatedTablesInDatabase
|
|
33
|
+
} from '../services/database-service.js';
|
|
10
34
|
|
|
11
35
|
/**
|
|
12
36
|
* Handle health check endpoint
|
|
@@ -30,12 +54,44 @@ export function handleInfo(req, res) {
|
|
|
30
54
|
res.json({
|
|
31
55
|
name: SERVER_CONFIG.name,
|
|
32
56
|
version: SERVER_CONFIG.version,
|
|
33
|
-
description: 'HTTP wrapper for SQLCipher MCP Server',
|
|
57
|
+
description: 'HTTP wrapper for SQLCipher MCP Server - Full feature parity with MCP server',
|
|
34
58
|
endpoints: {
|
|
59
|
+
// Server Status
|
|
35
60
|
health: 'GET /health',
|
|
36
|
-
query: 'POST /api/query',
|
|
37
61
|
info: 'GET /api/info',
|
|
62
|
+
|
|
63
|
+
// Query Execution (backward compatibility)
|
|
64
|
+
query: 'POST /api/query (execute_query)',
|
|
65
|
+
|
|
66
|
+
// Schema Exploration
|
|
67
|
+
list_tables: 'POST /api/tool/list_tables',
|
|
68
|
+
get_table_schema: 'POST /api/tool/get_table_schema',
|
|
69
|
+
list_columns: 'POST /api/tool/list_columns',
|
|
70
|
+
get_foreign_keys: 'POST /api/tool/get_foreign_keys',
|
|
71
|
+
get_indexes: 'POST /api/tool/get_indexes',
|
|
72
|
+
find_related_tables: 'POST /api/tool/find_related_tables',
|
|
73
|
+
|
|
74
|
+
// Database & Table Info
|
|
75
|
+
get_database_info: 'POST /api/tool/get_database_info',
|
|
76
|
+
get_table_info: 'POST /api/tool/get_table_info',
|
|
77
|
+
test_connection: 'POST /api/tool/test_connection',
|
|
78
|
+
|
|
79
|
+
// Query Helpers
|
|
80
|
+
explain_query: 'POST /api/tool/explain_query',
|
|
81
|
+
validate_query_syntax: 'POST /api/tool/validate_query_syntax',
|
|
82
|
+
suggest_query: 'POST /api/tool/suggest_query',
|
|
83
|
+
|
|
84
|
+
// Data Analysis
|
|
85
|
+
get_table_statistics: 'POST /api/tool/get_table_statistics',
|
|
86
|
+
sample_table_data: 'POST /api/tool/sample_table_data',
|
|
87
|
+
get_column_statistics: 'POST /api/tool/get_column_statistics',
|
|
88
|
+
|
|
89
|
+
// Search
|
|
90
|
+
search_tables: 'POST /api/tool/search_tables',
|
|
91
|
+
search_columns: 'POST /api/tool/search_columns',
|
|
38
92
|
},
|
|
93
|
+
totalTools: 18,
|
|
94
|
+
totalEndpoints: 20,
|
|
39
95
|
passwordConfigured: isPasswordConfigured(),
|
|
40
96
|
});
|
|
41
97
|
}
|
|
@@ -87,3 +143,519 @@ export async function handleQuery(req, res) {
|
|
|
87
143
|
});
|
|
88
144
|
}
|
|
89
145
|
}
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Schema Exploration Handlers
|
|
149
|
+
// ============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Handle list_tables endpoint
|
|
153
|
+
* @param {Object} req - Express request object
|
|
154
|
+
* @param {Object} res - Express response object
|
|
155
|
+
*/
|
|
156
|
+
export async function handleListTables(req, res) {
|
|
157
|
+
try {
|
|
158
|
+
const { database_path } = req.body;
|
|
159
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
160
|
+
const password = getDatabasePassword();
|
|
161
|
+
|
|
162
|
+
const tables = await getTableListFromDatabase(dbPath, password);
|
|
163
|
+
|
|
164
|
+
res.json({
|
|
165
|
+
success: true,
|
|
166
|
+
data: tables,
|
|
167
|
+
message: `Retrieved ${tables.length} table(s) from database.`,
|
|
168
|
+
});
|
|
169
|
+
} catch (error) {
|
|
170
|
+
res.status(400).json({
|
|
171
|
+
success: false,
|
|
172
|
+
error: error.message,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Handle get_table_schema endpoint
|
|
179
|
+
* @param {Object} req - Express request object
|
|
180
|
+
* @param {Object} res - Express response object
|
|
181
|
+
*/
|
|
182
|
+
export async function handleGetTableSchema(req, res) {
|
|
183
|
+
try {
|
|
184
|
+
const { database_path, table_name } = req.body;
|
|
185
|
+
validateTableName(table_name);
|
|
186
|
+
|
|
187
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
188
|
+
const password = getDatabasePassword();
|
|
189
|
+
|
|
190
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
191
|
+
|
|
192
|
+
res.json({
|
|
193
|
+
success: true,
|
|
194
|
+
data: schema,
|
|
195
|
+
message: `Retrieved schema for table "${table_name}".`,
|
|
196
|
+
});
|
|
197
|
+
} catch (error) {
|
|
198
|
+
res.status(400).json({
|
|
199
|
+
success: false,
|
|
200
|
+
error: error.message,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Handle list_columns endpoint
|
|
207
|
+
* @param {Object} req - Express request object
|
|
208
|
+
* @param {Object} res - Express response object
|
|
209
|
+
*/
|
|
210
|
+
export async function handleListColumns(req, res) {
|
|
211
|
+
try {
|
|
212
|
+
const { database_path, table_name } = req.body;
|
|
213
|
+
validateTableName(table_name);
|
|
214
|
+
|
|
215
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
216
|
+
const password = getDatabasePassword();
|
|
217
|
+
|
|
218
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
219
|
+
const columns = schema.map(col => col.name);
|
|
220
|
+
|
|
221
|
+
res.json({
|
|
222
|
+
success: true,
|
|
223
|
+
data: { table_name, columns, schema },
|
|
224
|
+
message: `Retrieved ${columns.length} column(s) from table "${table_name}".`,
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
res.status(400).json({
|
|
228
|
+
success: false,
|
|
229
|
+
error: error.message,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handle get_foreign_keys endpoint
|
|
236
|
+
* @param {Object} req - Express request object
|
|
237
|
+
* @param {Object} res - Express response object
|
|
238
|
+
*/
|
|
239
|
+
export async function handleGetForeignKeys(req, res) {
|
|
240
|
+
try {
|
|
241
|
+
const { database_path, table_name } = req.body;
|
|
242
|
+
validateTableName(table_name);
|
|
243
|
+
|
|
244
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
245
|
+
const password = getDatabasePassword();
|
|
246
|
+
|
|
247
|
+
const foreignKeys = await getForeignKeysFromDatabase(dbPath, password, table_name);
|
|
248
|
+
|
|
249
|
+
res.json({
|
|
250
|
+
success: true,
|
|
251
|
+
data: { table_name, foreign_keys: foreignKeys },
|
|
252
|
+
message: `Retrieved ${foreignKeys.length} foreign key(s) for table "${table_name}".`,
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
res.status(400).json({
|
|
256
|
+
success: false,
|
|
257
|
+
error: error.message,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Handle get_indexes endpoint
|
|
264
|
+
* @param {Object} req - Express request object
|
|
265
|
+
* @param {Object} res - Express response object
|
|
266
|
+
*/
|
|
267
|
+
export async function handleGetIndexes(req, res) {
|
|
268
|
+
try {
|
|
269
|
+
const { database_path, table_name } = req.body;
|
|
270
|
+
validateTableName(table_name);
|
|
271
|
+
|
|
272
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
273
|
+
const password = getDatabasePassword();
|
|
274
|
+
|
|
275
|
+
const indexes = await getIndexesFromDatabase(dbPath, password, table_name);
|
|
276
|
+
|
|
277
|
+
res.json({
|
|
278
|
+
success: true,
|
|
279
|
+
data: { table_name, indexes },
|
|
280
|
+
message: `Retrieved ${indexes.length} index(es) for table "${table_name}".`,
|
|
281
|
+
});
|
|
282
|
+
} catch (error) {
|
|
283
|
+
res.status(400).json({
|
|
284
|
+
success: false,
|
|
285
|
+
error: error.message,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Handle find_related_tables endpoint
|
|
292
|
+
* @param {Object} req - Express request object
|
|
293
|
+
* @param {Object} res - Express response object
|
|
294
|
+
*/
|
|
295
|
+
export async function handleFindRelatedTables(req, res) {
|
|
296
|
+
try {
|
|
297
|
+
const { database_path, table_name } = req.body;
|
|
298
|
+
validateTableName(table_name);
|
|
299
|
+
|
|
300
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
301
|
+
const password = getDatabasePassword();
|
|
302
|
+
|
|
303
|
+
const relationships = await findRelatedTablesInDatabase(dbPath, password, table_name);
|
|
304
|
+
|
|
305
|
+
res.json({
|
|
306
|
+
success: true,
|
|
307
|
+
data: relationships,
|
|
308
|
+
message: `Found relationships for table "${table_name}".`,
|
|
309
|
+
});
|
|
310
|
+
} catch (error) {
|
|
311
|
+
res.status(400).json({
|
|
312
|
+
success: false,
|
|
313
|
+
error: error.message,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// Database & Table Info Handlers
|
|
320
|
+
// ============================================================================
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Handle get_database_info endpoint
|
|
324
|
+
* @param {Object} req - Express request object
|
|
325
|
+
* @param {Object} res - Express response object
|
|
326
|
+
*/
|
|
327
|
+
export async function handleGetDatabaseInfo(req, res) {
|
|
328
|
+
try {
|
|
329
|
+
const { database_path } = req.body;
|
|
330
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
331
|
+
const password = getDatabasePassword();
|
|
332
|
+
|
|
333
|
+
const info = await getDatabaseInfoFromDatabase(dbPath, password);
|
|
334
|
+
|
|
335
|
+
res.json({
|
|
336
|
+
success: true,
|
|
337
|
+
data: info,
|
|
338
|
+
message: 'Retrieved database information.',
|
|
339
|
+
});
|
|
340
|
+
} catch (error) {
|
|
341
|
+
res.status(400).json({
|
|
342
|
+
success: false,
|
|
343
|
+
error: error.message,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Handle get_table_info endpoint
|
|
350
|
+
* @param {Object} req - Express request object
|
|
351
|
+
* @param {Object} res - Express response object
|
|
352
|
+
*/
|
|
353
|
+
export async function handleGetTableInfo(req, res) {
|
|
354
|
+
try {
|
|
355
|
+
const { database_path, table_name } = req.body;
|
|
356
|
+
validateTableName(table_name);
|
|
357
|
+
|
|
358
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
359
|
+
const password = getDatabasePassword();
|
|
360
|
+
|
|
361
|
+
const info = await getTableInfoFromDatabase(dbPath, password, table_name);
|
|
362
|
+
|
|
363
|
+
res.json({
|
|
364
|
+
success: true,
|
|
365
|
+
data: info,
|
|
366
|
+
message: `Retrieved information for table "${table_name}".`,
|
|
367
|
+
});
|
|
368
|
+
} catch (error) {
|
|
369
|
+
res.status(400).json({
|
|
370
|
+
success: false,
|
|
371
|
+
error: error.message,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Handle test_connection endpoint
|
|
378
|
+
* @param {Object} req - Express request object
|
|
379
|
+
* @param {Object} res - Express response object
|
|
380
|
+
*/
|
|
381
|
+
export async function handleTestConnection(req, res) {
|
|
382
|
+
try {
|
|
383
|
+
const { database_path } = req.body;
|
|
384
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
385
|
+
const password = getDatabasePassword();
|
|
386
|
+
|
|
387
|
+
const result = await testDatabaseConnection(dbPath, password);
|
|
388
|
+
|
|
389
|
+
res.json({
|
|
390
|
+
success: true,
|
|
391
|
+
data: result,
|
|
392
|
+
message: result.success ? 'Database connection successful.' : 'Database connection failed.',
|
|
393
|
+
});
|
|
394
|
+
} catch (error) {
|
|
395
|
+
res.status(400).json({
|
|
396
|
+
success: false,
|
|
397
|
+
error: error.message,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ============================================================================
|
|
403
|
+
// Query Helper Handlers
|
|
404
|
+
// ============================================================================
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Handle explain_query endpoint
|
|
408
|
+
* @param {Object} req - Express request object
|
|
409
|
+
* @param {Object} res - Express response object
|
|
410
|
+
*/
|
|
411
|
+
export async function handleExplainQuery(req, res) {
|
|
412
|
+
try {
|
|
413
|
+
const { database_path, query } = req.body;
|
|
414
|
+
validateQuery(query);
|
|
415
|
+
|
|
416
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
417
|
+
const password = getDatabasePassword();
|
|
418
|
+
|
|
419
|
+
const plan = await explainQueryPlanFromDatabase(dbPath, password, query);
|
|
420
|
+
|
|
421
|
+
res.json({
|
|
422
|
+
success: true,
|
|
423
|
+
data: { query, execution_plan: plan },
|
|
424
|
+
message: 'Query execution plan retrieved.',
|
|
425
|
+
});
|
|
426
|
+
} catch (error) {
|
|
427
|
+
res.status(400).json({
|
|
428
|
+
success: false,
|
|
429
|
+
error: error.message,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Handle validate_query_syntax endpoint
|
|
436
|
+
* @param {Object} req - Express request object
|
|
437
|
+
* @param {Object} res - Express response object
|
|
438
|
+
*/
|
|
439
|
+
export async function handleValidateQuerySyntax(req, res) {
|
|
440
|
+
try {
|
|
441
|
+
const { database_path, query } = req.body;
|
|
442
|
+
|
|
443
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
444
|
+
const password = getDatabasePassword();
|
|
445
|
+
|
|
446
|
+
// Try to explain the query - if it fails, syntax is invalid
|
|
447
|
+
try {
|
|
448
|
+
await explainQueryPlanFromDatabase(dbPath, password, query);
|
|
449
|
+
res.json({
|
|
450
|
+
success: true,
|
|
451
|
+
data: { valid: true, query },
|
|
452
|
+
message: 'Query syntax is valid.',
|
|
453
|
+
});
|
|
454
|
+
} catch (error) {
|
|
455
|
+
res.json({
|
|
456
|
+
success: true,
|
|
457
|
+
data: { valid: false, query, error: error.message },
|
|
458
|
+
message: 'Query syntax is invalid.',
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
} catch (error) {
|
|
462
|
+
res.status(400).json({
|
|
463
|
+
success: false,
|
|
464
|
+
error: error.message,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Handle suggest_query endpoint
|
|
471
|
+
* @param {Object} req - Express request object
|
|
472
|
+
* @param {Object} res - Express response object
|
|
473
|
+
*/
|
|
474
|
+
export async function handleSuggestQuery(req, res) {
|
|
475
|
+
try {
|
|
476
|
+
const { database_path, table_name, intent } = req.body;
|
|
477
|
+
validateTableName(table_name);
|
|
478
|
+
|
|
479
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
480
|
+
const password = getDatabasePassword();
|
|
481
|
+
|
|
482
|
+
// Get table schema to build a suggested query
|
|
483
|
+
const schema = await getTableSchemaFromDatabase(dbPath, password, table_name);
|
|
484
|
+
const columns = schema.map(col => col.name).join(', ');
|
|
485
|
+
|
|
486
|
+
let suggestedQuery = `SELECT ${columns} FROM ${table_name}`;
|
|
487
|
+
|
|
488
|
+
if (intent) {
|
|
489
|
+
suggestedQuery += ` -- Intent: ${intent}`;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
suggestedQuery += ' LIMIT 10;';
|
|
493
|
+
|
|
494
|
+
res.json({
|
|
495
|
+
success: true,
|
|
496
|
+
data: {
|
|
497
|
+
table_name,
|
|
498
|
+
intent: intent || 'General query',
|
|
499
|
+
suggested_query: suggestedQuery,
|
|
500
|
+
columns: schema
|
|
501
|
+
},
|
|
502
|
+
message: `Generated query suggestion for table "${table_name}".`,
|
|
503
|
+
});
|
|
504
|
+
} catch (error) {
|
|
505
|
+
res.status(400).json({
|
|
506
|
+
success: false,
|
|
507
|
+
error: error.message,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ============================================================================
|
|
513
|
+
// Data Analysis Handlers
|
|
514
|
+
// ============================================================================
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Handle get_table_statistics endpoint
|
|
518
|
+
* @param {Object} req - Express request object
|
|
519
|
+
* @param {Object} res - Express response object
|
|
520
|
+
*/
|
|
521
|
+
export async function handleGetTableStatistics(req, res) {
|
|
522
|
+
try {
|
|
523
|
+
const { database_path, table_name } = req.body;
|
|
524
|
+
validateTableName(table_name);
|
|
525
|
+
|
|
526
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
527
|
+
const password = getDatabasePassword();
|
|
528
|
+
|
|
529
|
+
const stats = await getTableStatisticsFromDatabase(dbPath, password, table_name);
|
|
530
|
+
|
|
531
|
+
res.json({
|
|
532
|
+
success: true,
|
|
533
|
+
data: stats,
|
|
534
|
+
message: `Retrieved statistics for table "${table_name}".`,
|
|
535
|
+
});
|
|
536
|
+
} catch (error) {
|
|
537
|
+
res.status(400).json({
|
|
538
|
+
success: false,
|
|
539
|
+
error: error.message,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Handle sample_table_data endpoint
|
|
546
|
+
* @param {Object} req - Express request object
|
|
547
|
+
* @param {Object} res - Express response object
|
|
548
|
+
*/
|
|
549
|
+
export async function handleSampleTableData(req, res) {
|
|
550
|
+
try {
|
|
551
|
+
const { database_path, table_name, limit = 10, offset = 0 } = req.body;
|
|
552
|
+
validateTableName(table_name);
|
|
553
|
+
validateNumericParameter(limit, 'limit', 1, 1000);
|
|
554
|
+
validateNumericParameter(offset, 'offset', 0, 1000000);
|
|
555
|
+
|
|
556
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
557
|
+
const password = getDatabasePassword();
|
|
558
|
+
|
|
559
|
+
const sample = await sampleTableDataFromDatabase(dbPath, password, table_name, limit, offset);
|
|
560
|
+
|
|
561
|
+
res.json({
|
|
562
|
+
success: true,
|
|
563
|
+
data: sample,
|
|
564
|
+
message: `Retrieved ${sample.rows.length} sample row(s) from table "${table_name}".`,
|
|
565
|
+
});
|
|
566
|
+
} catch (error) {
|
|
567
|
+
res.status(400).json({
|
|
568
|
+
success: false,
|
|
569
|
+
error: error.message,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Handle get_column_statistics endpoint
|
|
576
|
+
* @param {Object} req - Express request object
|
|
577
|
+
* @param {Object} res - Express response object
|
|
578
|
+
*/
|
|
579
|
+
export async function handleGetColumnStatistics(req, res) {
|
|
580
|
+
try {
|
|
581
|
+
const { database_path, table_name, column_name } = req.body;
|
|
582
|
+
validateTableName(table_name);
|
|
583
|
+
validateColumnName(column_name);
|
|
584
|
+
|
|
585
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
586
|
+
const password = getDatabasePassword();
|
|
587
|
+
|
|
588
|
+
const stats = await getColumnStatisticsFromDatabase(dbPath, password, table_name, column_name);
|
|
589
|
+
|
|
590
|
+
res.json({
|
|
591
|
+
success: true,
|
|
592
|
+
data: stats,
|
|
593
|
+
message: `Retrieved statistics for column "${column_name}" in table "${table_name}".`,
|
|
594
|
+
});
|
|
595
|
+
} catch (error) {
|
|
596
|
+
res.status(400).json({
|
|
597
|
+
success: false,
|
|
598
|
+
error: error.message,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// ============================================================================
|
|
604
|
+
// Search Handlers
|
|
605
|
+
// ============================================================================
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Handle search_tables endpoint
|
|
609
|
+
* @param {Object} req - Express request object
|
|
610
|
+
* @param {Object} res - Express response object
|
|
611
|
+
*/
|
|
612
|
+
export async function handleSearchTables(req, res) {
|
|
613
|
+
try {
|
|
614
|
+
const { database_path, pattern } = req.body;
|
|
615
|
+
validatePattern(pattern);
|
|
616
|
+
|
|
617
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
618
|
+
const password = getDatabasePassword();
|
|
619
|
+
|
|
620
|
+
const results = await searchTablesInDatabase(dbPath, password, pattern);
|
|
621
|
+
|
|
622
|
+
res.json({
|
|
623
|
+
success: true,
|
|
624
|
+
data: { pattern, matches: results },
|
|
625
|
+
message: `Found ${results.length} table(s) matching pattern "${pattern}".`,
|
|
626
|
+
});
|
|
627
|
+
} catch (error) {
|
|
628
|
+
res.status(400).json({
|
|
629
|
+
success: false,
|
|
630
|
+
error: error.message,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Handle search_columns endpoint
|
|
637
|
+
* @param {Object} req - Express request object
|
|
638
|
+
* @param {Object} res - Express response object
|
|
639
|
+
*/
|
|
640
|
+
export async function handleSearchColumns(req, res) {
|
|
641
|
+
try {
|
|
642
|
+
const { database_path, pattern } = req.body;
|
|
643
|
+
validatePattern(pattern);
|
|
644
|
+
|
|
645
|
+
const dbPath = resolveDatabasePath(database_path);
|
|
646
|
+
const password = getDatabasePassword();
|
|
647
|
+
|
|
648
|
+
const results = await searchColumnsInDatabase(dbPath, password, pattern);
|
|
649
|
+
|
|
650
|
+
res.json({
|
|
651
|
+
success: true,
|
|
652
|
+
data: { pattern, matches: results },
|
|
653
|
+
message: `Found ${results.length} column(s) matching pattern "${pattern}".`,
|
|
654
|
+
});
|
|
655
|
+
} catch (error) {
|
|
656
|
+
res.status(400).json({
|
|
657
|
+
success: false,
|
|
658
|
+
error: error.message,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|