sequelae-mcp
Version:
Let Claude, Cursor, and other AI agents run real SQL queries on live Postgres databases. No more copy-pasting SQL, stale schema docs, or hallucinated DB adapters ā just raw, real-time access. Now with MCP support!
901 lines (900 loc) ⢠35.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlAgentError = void 0;
exports.handleVersion = handleVersion;
exports.handleHelp = handleHelp;
exports.formatError = formatError;
exports.handleExit = handleExit;
exports.parseArguments = parseArguments;
exports.validateDatabaseUrl = validateDatabaseUrl;
exports.createPool = createPool;
exports.getCommandInfo = getCommandInfo;
exports.validateCommandArgument = validateCommandArgument;
exports.validateFile = validateFile;
exports.readSqlFile = readSqlFile;
exports.formatQueryResultsJson = formatQueryResultsJson;
exports.createNoCommandError = createNoCommandError;
exports.createNoSqlQueryError = createNoSqlQueryError;
exports.createNoFilePathError = createNoFilePathError;
exports.createFileNotFoundError = createFileNotFoundError;
exports.formatSqlError = formatSqlError;
exports.formatCommandResult = formatCommandResult;
exports.buildSchemaCondition = buildSchemaCondition;
exports.buildTableList = buildTableList;
exports.isDirectSqlCommand = isDirectSqlCommand;
exports.main = main;
const pg_1 = require("pg");
const pool_manager_1 = require("./core/pool-manager");
const dotenv_1 = require("dotenv");
const fs_1 = require("fs");
const path_1 = require("path");
const packageJson = __importStar(require("../package.json"));
const sql_executor_1 = require("./core/sql-executor");
const logger_1 = require("./utils/logger");
const jsonb_analyzer_1 = require("./jsonb-analyzer");
// Load .env from the package root (handles both root and subdirectory execution)
const envPath = (0, path_1.resolve)(__dirname, '../.env');
(0, dotenv_1.config)({ path: envPath });
// CLI output helpers that maintain console output but also log
const cliOutput = {
log: (message) => {
console.log(message);
logger_1.logger.debug('CLI output:', { message });
},
error: (message) => {
console.error(message);
logger_1.logger.error('CLI error:', { message });
},
table: (data) => {
console.table(data);
logger_1.logger.debug('CLI table output:', { rowCount: Array.isArray(data) ? data.length : 'N/A' });
},
json: (data) => {
const json = JSON.stringify(data);
console.log(json);
logger_1.logger.debug('CLI JSON output:', { data });
},
};
function handleVersion(jsonMode) {
if (jsonMode) {
return JSON.stringify({ version: packageJson.version });
}
else {
return `sequelae-mcp v${packageJson.version}`;
}
}
function handleHelp(jsonMode) {
if (jsonMode) {
return JSON.stringify({
usage: [
'sequelae exec "SQL query" Execute a SQL query',
'sequelae file path/to/query.sql Execute SQL from file',
'sequelae schema Show all tables in public schema',
'sequelae schema [tables] Show specific table(s) - comma separated',
'sequelae schema --all Show all schemas including system tables',
'sequelae backup Create a database backup',
'sequelae exit Exit sequelae',
'sequelae --json Output results in JSON format',
],
examples: [
'sequelae exec "SELECT * FROM users"',
'sequelae exec "CREATE TABLE posts (id serial primary key, title text)"',
'sequelae file migrations/001_init.sql',
'sequelae schema',
'sequelae schema users,posts',
'sequelae backup --output db_backup.sql',
'sequelae backup --tables users,posts --format custom',
'sequelae --json exec "SELECT * FROM users"',
],
});
}
else {
return `
Usage:
sequelae exec "SQL query" Execute a SQL query
sequelae file path/to/query.sql Execute SQL from file
sequelae schema Show all tables in public schema
sequelae schema [tables] Show specific table(s) - comma separated
sequelae schema --all Show all schemas including system tables
sequelae backup Create a database backup
sequelae exit Exit sequelae
sequelae --json Output results in JSON format
sequelae --no-transaction Disable automatic transactions
sequelae --timeout <ms> Set query timeout in milliseconds
Examples:
sequelae exec "SELECT * FROM users"
sequelae exec "CREATE TABLE posts (id serial primary key, title text)"
sequelae file migrations/001_init.sql
sequelae schema
sequelae schema users,posts
sequelae backup --output db_backup.sql
sequelae backup --tables users,posts --format custom
sequelae --json exec "SELECT * FROM users"
`;
}
}
function formatError(error, jsonMode, hint) {
if (jsonMode) {
return JSON.stringify({ error, ...(hint && { hint }) });
}
else {
let output = `Error: ${error}`;
if (hint) {
output += `\n${hint}`;
}
return output;
}
}
function handleExit(jsonMode) {
if (jsonMode) {
return JSON.stringify({ message: 'Goodbye!' });
}
else {
return 'Goodbye!';
}
}
function parseArguments(args) {
const jsonMode = args.includes('--json');
const allSchemas = args.includes('--all');
const noTransaction = args.includes('--no-transaction');
// Extract timeout value
let timeout;
let timeoutValueIndex;
const timeoutIndex = args.indexOf('--timeout');
if (timeoutIndex !== -1 && timeoutIndex + 1 < args.length) {
const possibleValue = args[timeoutIndex + 1];
// Only treat as timeout value if it's a valid number and not another flag/command
if (!possibleValue.startsWith('-')) {
const timeoutValue = parseInt(possibleValue);
if (!isNaN(timeoutValue) && timeoutValue > 0) {
timeout = timeoutValue;
}
// Always mark the next value for removal if it doesn't start with '-'
timeoutValueIndex = timeoutIndex + 1;
}
}
const filteredArgs = args.filter((arg, index) => {
// Remove flags
if (arg === '--json' || arg === '--all' || arg === '--no-transaction') {
return false;
}
// Remove --timeout and its value
if (arg === '--timeout') {
return false;
}
if (timeoutValueIndex !== undefined && index === timeoutValueIndex) {
return false;
}
return true;
});
return {
jsonMode,
allSchemas,
noTransaction,
timeout,
filteredArgs,
};
}
function validateDatabaseUrl(databaseUrl, jsonMode) {
if (!databaseUrl) {
return formatError('DATABASE_URL environment variable is not set', jsonMode, 'Make sure you have a .env file with DATABASE_URL from your Supabase project');
}
return null;
}
function createPool(connectionString) {
return new pg_1.Pool({
connectionString,
ssl: { rejectUnauthorized: false },
});
}
function getCommandInfo(command) {
const commands = {
exec: { command: 'exec', needsArgument: true, argumentName: 'SQL query' },
file: { command: 'file', needsArgument: true, argumentName: 'file path' },
schema: { command: 'schema', needsArgument: false },
};
return commands[command] || null;
}
function validateCommandArgument(commandInfo, argument, jsonMode) {
if (commandInfo.needsArgument && !argument) {
return formatError(`No ${commandInfo.argumentName} provided`, jsonMode);
}
return null;
}
function validateFile(filepath, jsonMode) {
if (!(0, fs_1.existsSync)(filepath)) {
return formatError(`File not found: ${filepath}`, jsonMode);
}
return null;
}
function readSqlFile(filepath) {
return (0, fs_1.readFileSync)(filepath, 'utf8');
}
function formatQueryResultsJson(result, duration) {
const output = {
success: true,
command: result.command || 'Query executed',
rowCount: result.rowCount || 0,
rows: result.rows || [],
duration: duration,
};
return JSON.stringify(output);
}
class SqlAgentError extends Error {
constructor(message, code, hint) {
super(message);
this.code = code;
this.hint = hint;
this.name = 'SqlAgentError';
}
}
exports.SqlAgentError = SqlAgentError;
function createNoCommandError() {
return new SqlAgentError('No command provided', 'NO_COMMAND', 'Run sequelae --help for usage information');
}
function createNoSqlQueryError() {
return new SqlAgentError('No SQL query provided', 'NO_SQL_QUERY');
}
function createNoFilePathError() {
return new SqlAgentError('No file path provided', 'NO_FILE_PATH');
}
function createFileNotFoundError(filepath) {
return new SqlAgentError(`File not found: ${filepath}`, 'FILE_NOT_FOUND');
}
function formatSqlError(error, jsonMode) {
if (jsonMode) {
const errorOutput = {
success: false,
error: error.message,
position: error.position,
};
return JSON.stringify(errorOutput);
}
else {
let output = `\nError: ${error.message}`;
if (error.position) {
output += `\nPosition: ${error.position}`;
}
return output;
}
}
function formatCommandResult(command, rowCount, duration) {
const commandText = command || 'Query executed';
const rowCountText = rowCount ? `(${rowCount} rows)` : '';
return `\nā ${commandText} ${rowCountText} - ${duration}ms`;
}
function buildSchemaCondition(allSchemas) {
return allSchemas
? "table_schema NOT IN ('pg_catalog', 'information_schema')"
: "table_schema = 'public'";
}
async function cleanupPool(pool) {
try {
await pool.end();
}
catch (error) {
cliOutput.error(`Error closing database pool: ${error}`);
}
}
// Cleanup function for shared pool
// Currently not used as the pool is shared across all SqlExecutor instances
// and will be cleaned up when the process exits
async function _cleanupSharedPool() {
const poolManager = pool_manager_1.PoolManager.getInstance();
await poolManager.close();
}
function buildTableList(tables) {
return tables
.split(',')
.map(t => t.trim())
.filter(t => t.length > 0);
}
function isDirectSqlCommand(command) {
const sqlKeywords = [
'SELECT',
'INSERT',
'UPDATE',
'DELETE',
'CREATE',
'DROP',
'ALTER',
'TRUNCATE',
];
const upperCommand = command.toUpperCase();
return sqlKeywords.some(keyword => upperCommand.startsWith(keyword));
}
async function main() {
const args = process.argv.slice(2);
// Parse arguments
const { jsonMode, allSchemas, noTransaction, timeout, filteredArgs } = parseArguments(args);
// Skip header when running in Jest or JSON mode
if (typeof jest === 'undefined' && !jsonMode) {
cliOutput.log('š sequelae-mcp - PostgreSQL SQL executor\n');
}
// Handle no arguments
if (filteredArgs.length === 0) {
const error = createNoCommandError();
const output = formatError(error.message, jsonMode, error.hint);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.error(output);
}
process.exit(1);
}
// Handle help
if (filteredArgs[0] === '--help' || filteredArgs[0] === '-h') {
const output = handleHelp(jsonMode);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.log(output);
}
process.exit(0);
}
// Handle version
if (filteredArgs[0] === '--version' || filteredArgs[0] === '-v') {
const output = handleVersion(jsonMode);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.log(output);
}
process.exit(0);
}
// Handle exit command
if (filteredArgs[0] === 'exit' || filteredArgs[0] === 'quit') {
const output = handleExit(jsonMode);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.log(output);
}
process.exit(0);
}
const databaseUrl = process.env.DATABASE_URL;
// Set timeout environment variable if provided
if (timeout) {
process.env.POSTGRES_STATEMENT_TIMEOUT = timeout.toString();
}
const dbError = validateDatabaseUrl(databaseUrl, jsonMode);
if (dbError) {
if (jsonMode) {
cliOutput.json(JSON.parse(dbError));
}
else {
cliOutput.error(dbError);
}
process.exit(1);
}
// At this point, databaseUrl is guaranteed to be defined
const pool = createPool(databaseUrl);
try {
let sql;
if (filteredArgs[0] === 'exec') {
if (!filteredArgs[1]) {
const error = createNoSqlQueryError();
const output = formatError(error.message, jsonMode, error.hint);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.error(output);
}
await cleanupPool(pool);
process.exit(1);
}
sql = filteredArgs[1];
}
else if (filteredArgs[0] === 'file') {
if (!filteredArgs[1]) {
const error = createNoFilePathError();
const output = formatError(error.message, jsonMode, error.hint);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.error(output);
}
await cleanupPool(pool);
process.exit(1);
}
const filepath = (0, path_1.resolve)(process.cwd(), filteredArgs[1]);
// Check if file exists
if (!(0, fs_1.existsSync)(filepath)) {
const error = createFileNotFoundError(filepath);
const output = formatError(error.message, jsonMode, error.hint);
if (jsonMode) {
cliOutput.json(JSON.parse(output));
}
else {
cliOutput.error(output);
}
await cleanupPool(pool);
process.exit(1);
}
sql = (0, fs_1.readFileSync)(filepath, 'utf8');
}
else if (filteredArgs[0] === 'schema') {
// Schema command - show database structure
// Join all remaining arguments as they might be space-separated table names
const specificTables = filteredArgs.slice(1).join(' '); // Could be comma-separated list
if (specificTables) {
// Schema for specific tables
const tableList = specificTables
.split(',')
.map(t => t.trim())
.filter(t => t.length > 0); // Remove empty strings
if (tableList.length === 0) {
if (jsonMode) {
cliOutput.json({ error: 'No table names provided' });
}
else {
cliOutput.error('Error: No table names provided');
}
await cleanupPool(pool);
process.exit(1);
}
const tableCondition = tableList.map(t => `'${t}'`).join(',');
sql = `
WITH requested_tables AS (
SELECT unnest(ARRAY[${tableCondition}]) as table_name
),
existing_tables AS (
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
AND ${allSchemas ? "table_schema NOT IN ('pg_catalog', 'information_schema')" : "table_schema = 'public'"}
),
table_info AS (
SELECT
t.table_schema,
t.table_name,
json_agg(
json_build_object(
'column_name', c.column_name,
'data_type', c.data_type,
'is_nullable', c.is_nullable,
'column_default', c.column_default,
'character_maximum_length', c.character_maximum_length
) ORDER BY c.ordinal_position
)::text as columns
FROM information_schema.tables t
JOIN information_schema.columns c
ON t.table_schema = c.table_schema
AND t.table_name = c.table_name
JOIN requested_tables rt ON t.table_name = rt.table_name
WHERE ${allSchemas ? "t.table_schema NOT IN ('pg_catalog', 'information_schema')" : "t.table_schema = 'public'"}
AND t.table_type = 'BASE TABLE'
GROUP BY t.table_schema, t.table_name
),
constraint_info AS (
SELECT
tc.table_schema,
tc.table_name,
json_agg(
json_build_object(
'constraint_name', tc.constraint_name,
'constraint_type', tc.constraint_type,
'column_name', kcu.column_name
)
)::text as constraints
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
JOIN requested_tables rt ON tc.table_name = rt.table_name
WHERE ${allSchemas ? "tc.table_schema NOT IN ('pg_catalog', 'information_schema')" : "tc.table_schema = 'public'"}
GROUP BY tc.table_schema, tc.table_name
),
missing_tables AS (
SELECT rt.table_name as missing_table,
string_agg(et.table_name, ', ') as suggestions
FROM requested_tables rt
LEFT JOIN existing_tables et ON rt.table_name = et.table_name
WHERE et.table_name IS NULL
GROUP BY rt.table_name
)
SELECT
'found' as type,
ti.table_schema,
ti.table_name,
ti.columns,
COALESCE(ci.constraints, '[]') as constraints,
NULL as missing_table,
NULL as suggestions
FROM table_info ti
LEFT JOIN constraint_info ci
ON ti.table_schema = ci.table_schema
AND ti.table_name = ci.table_name
UNION ALL
SELECT
'missing' as type,
NULL as table_schema,
NULL as table_name,
NULL as columns,
NULL as constraints,
mt.missing_table,
(SELECT string_agg(tn, ', ') FROM (
SELECT table_name as tn
FROM existing_tables
WHERE LOWER(table_name) LIKE LOWER(LEFT(mt.missing_table, 3) || '%')
OR LOWER(table_name) LIKE '%' || LOWER(LEFT(mt.missing_table, 3)) || '%'
ORDER BY
CASE WHEN LOWER(table_name) LIKE LOWER(LEFT(mt.missing_table, 3) || '%') THEN 0 ELSE 1 END,
LENGTH(table_name)
LIMIT 3
) s) as suggestions
FROM missing_tables mt
ORDER BY type, table_schema, table_name;
`;
}
else {
// Show all tables
sql = `
WITH table_info AS (
SELECT
t.table_schema,
t.table_name,
json_agg(
json_build_object(
'column_name', c.column_name,
'data_type', c.data_type,
'is_nullable', c.is_nullable,
'column_default', c.column_default,
'character_maximum_length', c.character_maximum_length
) ORDER BY c.ordinal_position
)::text as columns
FROM information_schema.tables t
JOIN information_schema.columns c
ON t.table_schema = c.table_schema
AND t.table_name = c.table_name
WHERE ${allSchemas ? "t.table_schema NOT IN ('pg_catalog', 'information_schema')" : "t.table_schema = 'public'"}
AND t.table_type = 'BASE TABLE'
GROUP BY t.table_schema, t.table_name
),
constraint_info AS (
SELECT
tc.table_schema,
tc.table_name,
json_agg(
json_build_object(
'constraint_name', tc.constraint_name,
'constraint_type', tc.constraint_type,
'column_name', kcu.column_name
)
)::text as constraints
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
WHERE ${allSchemas ? "tc.table_schema NOT IN ('pg_catalog', 'information_schema')" : "tc.table_schema = 'public'"}
GROUP BY tc.table_schema, tc.table_name
)
SELECT
'found' as type,
ti.table_schema,
ti.table_name,
ti.columns,
COALESCE(ci.constraints, '[]') as constraints,
NULL as missing_table,
NULL as suggestions
FROM table_info ti
LEFT JOIN constraint_info ci
ON ti.table_schema = ci.table_schema
AND ti.table_name = ci.table_name
ORDER BY ti.table_schema, ti.table_name;
`;
}
}
else if (filteredArgs[0] === 'exec' || filteredArgs[0] === 'file') {
// Command recognized but missing argument
if (jsonMode) {
cliOutput.json({ error: `Missing argument for ${filteredArgs[0]} command` });
}
else {
cliOutput.error(`Error: Missing argument for ${filteredArgs[0]} command`);
}
await cleanupPool(pool);
process.exit(1);
}
else {
// Check if it looks like a SQL command
const sqlKeywords = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'ALTER'];
const firstWord = filteredArgs[0].toUpperCase();
if (sqlKeywords.includes(firstWord)) {
// Direct SQL command
sql = filteredArgs.join(' ');
}
else if (filteredArgs[0] === 'backup') {
// Handle backup command
const executor = new sql_executor_1.SqlExecutor(databaseUrl);
try {
// Parse backup options
const options = {};
for (let i = 1; i < filteredArgs.length; i++) {
const arg = filteredArgs[i];
if (arg === '--format' && i + 1 < filteredArgs.length) {
options.format = filteredArgs[++i];
}
else if (arg === '--tables' && i + 1 < filteredArgs.length) {
options.tables = filteredArgs[++i].split(',').map(t => t.trim());
}
else if (arg === '--schemas' && i + 1 < filteredArgs.length) {
options.schemas = filteredArgs[++i].split(',').map(s => s.trim());
}
else if (arg === '--output' && i + 1 < filteredArgs.length) {
options.outputPath = filteredArgs[++i];
}
else if (arg === '--data-only') {
options.dataOnly = true;
}
else if (arg === '--schema-only') {
options.schemaOnly = true;
}
else if (arg === '--compress') {
options.compress = true;
}
}
// Show progress
if (!jsonMode) {
cliOutput.log('Creating backup...');
}
const result = await executor.backup(options);
if (result.success) {
if (jsonMode) {
cliOutput.json({
success: true,
outputPath: result.outputPath,
size: result.size,
duration: result.duration,
});
}
else {
cliOutput.log(`\nā
Backup completed successfully!`);
cliOutput.log(`š Output: ${result.outputPath}`);
if (result.size) {
cliOutput.log(`š Size: ${(result.size / 1024 / 1024).toFixed(2)} MB`);
}
cliOutput.log(`ā±ļø Duration: ${(result.duration / 1000).toFixed(2)}s`);
}
await cleanupPool(pool);
process.exit(0);
}
else {
if (jsonMode) {
cliOutput.json({
success: false,
error: result.error,
});
}
else {
cliOutput.error(`\nā Backup failed: ${result.error}`);
}
await cleanupPool(pool);
process.exit(1);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (jsonMode) {
cliOutput.json({ error: errorMessage });
}
else {
cliOutput.error(`Error: ${errorMessage}`);
}
await cleanupPool(pool);
process.exit(1);
}
finally {
await executor.close();
}
}
else {
// Unknown command
if (jsonMode) {
cliOutput.json({ error: `Unknown command: ${filteredArgs[0]}` });
}
else {
cliOutput.error(`Error: Unknown command: ${filteredArgs[0]}`);
cliOutput.error('Run sequelae --help for usage information');
}
await cleanupPool(pool);
process.exit(1);
}
}
// Execute the query using SqlExecutor
const executor = new sql_executor_1.SqlExecutor(databaseUrl);
let result;
try {
if (filteredArgs[0] === 'file') {
result = await executor.executeFile(filteredArgs[1], !noTransaction, timeout);
}
else {
result = await executor.executeQuery(sql, !noTransaction, timeout);
}
}
finally {
await executor.close();
}
// Display results
if (jsonMode) {
const output = {
success: true,
command: result.command || 'Query executed',
rowCount: result.rowCount || 0,
rows: result.rows || [],
duration: result.duration || 0,
};
cliOutput.json(output);
}
else {
// Special handling for schema command
if (filteredArgs[0] === 'schema' && result.rows && result.rows.length > 0) {
cliOutput.log('DATABASE SCHEMA:\n');
// Separate found and missing tables
const foundTables = result.rows.filter(r => r.type === 'found');
const missingTables = result.rows.filter(r => r.type === 'missing');
// Display found tables
for (const table of foundTables) {
cliOutput.log(`š ${table.table_schema}.${table.table_name}`);
// Display columns
const columns = JSON.parse(table.columns);
cliOutput.log(' Columns:');
// Collect JSONB columns for analysis
const jsonbColumns = [];
for (const col of columns) {
const nullable = col.is_nullable === 'YES' ? ' (nullable)' : '';
const dataType = col.character_maximum_length
? `${col.data_type}(${col.character_maximum_length})`
: col.data_type;
const defaultVal = col.column_default ? ` DEFAULT ${col.column_default}` : '';
cliOutput.log(` - ${col.column_name}: ${dataType}${nullable}${defaultVal}`);
// Check if this is a JSONB column
if (col.data_type === 'jsonb') {
jsonbColumns.push({
column: col,
tableName: table.table_name,
schemaName: table.table_schema,
});
}
}
// Analyze JSONB columns if any
if (jsonbColumns.length > 0) {
const client = await pool.connect();
try {
for (const { column, tableName, schemaName } of jsonbColumns) {
const samples = await (0, jsonb_analyzer_1.sampleJsonbColumn)(client, `${schemaName}.${tableName}`, column.column_name, 10);
const structure = (0, jsonb_analyzer_1.analyzeJsonStructure)(samples);
const formatted = (0, jsonb_analyzer_1.formatJsonStructure)(structure);
if (formatted.trim()) {
cliOutput.log(` Structure of ${column.column_name}:`);
cliOutput.log(formatted);
}
}
}
finally {
client.release();
}
}
// Display constraints
const constraints = JSON.parse(table.constraints);
if (constraints.length > 0) {
cliOutput.log(' Constraints:');
const constraintsByType = constraints.reduce((acc, c) => {
if (!acc[c.constraint_type])
acc[c.constraint_type] = [];
acc[c.constraint_type].push(c);
return acc;
}, {});
for (const [type, consts] of Object.entries(constraintsByType)) {
const columns = consts.map(c => c.column_name).join(', ');
cliOutput.log(` - ${type}: ${columns}`);
}
}
cliOutput.log('');
}
// Display missing tables with suggestions
if (missingTables.length > 0) {
cliOutput.log('ā TABLES NOT FOUND:\n');
for (const missing of missingTables) {
cliOutput.log(` - "${missing.missing_table}"`);
if (missing.suggestions) {
cliOutput.log(` Did you mean: ${missing.suggestions}?`);
}
}
cliOutput.log('');
}
}
else if (result.rows && result.rows.length > 0) {
cliOutput.table(result.rows);
}
// Show execution info
const command = result.command || 'Query executed';
cliOutput.log(`\nā ${command} ${result.rowCount ? `(${result.rowCount} rows)` : ''} - ${result.duration}ms`);
}
}
catch (error) {
const err = error;
if (jsonMode) {
const errorOutput = {
success: false,
error: err.message,
position: err.position,
};
cliOutput.json(errorOutput);
}
else {
cliOutput.error(`\nError: ${err.message}`);
if (err.position) {
cliOutput.error(`Position: ${err.position}`);
}
}
await pool.end();
process.exit(1);
}
// Success - close pool and exit cleanly
try {
await pool.end();
}
catch (error) {
cliOutput.error(`Error closing database pool: ${error}`);
}
process.exit(0);
}
// Only run if this module is executed directly
if (require.main === module) {
// Run main and handle unhandled errors
main().catch(async (error) => {
cliOutput.error(String(error));
process.exit(1);
});
// Handle process errors properly
process.on('unhandledRejection', (reason, promise) => {
cliOutput.error(`Unhandled Rejection at: ${promise}, reason: ${reason}`);
process.exit(1);
});
process.on('uncaughtException', error => {
cliOutput.error(`Uncaught Exception: ${error}`);
process.exit(1);
});
}
//# sourceMappingURL=cli.js.map