@bhagat-surya-dev/dashchat-database-manager
Version:
AI-powered database schema analysis and management library
185 lines • 7.68 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqliteHandler = void 0;
// SQLite specific handler
const typeorm_1 = require("typeorm");
// import { SqlDatabase } from "langchain/sql_db";
const perf_hooks_1 = require("perf_hooks");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const base_handler_1 = require("./base-handler");
class SqliteHandler extends base_handler_1.BaseDatabaseHandler {
getDatabaseType() {
return 'sqlite';
}
// Parse the SQLite connection string to get the file path
getSqliteFilePath(databaseUrl) {
// Handle different URL formats
if (databaseUrl.startsWith('sqlite:///')) {
// Absolute path: sqlite:///path/to/database.db
return databaseUrl.substring(10);
}
else if (databaseUrl.startsWith('sqlite://')) {
// Relative path: sqlite://./database.db or sqlite://database.db
const relativePath = databaseUrl.substring(9);
return path_1.default.resolve(process.cwd(), relativePath);
}
else if (databaseUrl.endsWith('.db') || databaseUrl.endsWith('.sqlite')) {
// Direct file path (not a URL)
return path_1.default.resolve(databaseUrl);
}
throw new Error('Invalid SQLite connection string format');
}
// Create TypeORM DataSource options for SQLite
createDataSourceOptions(databaseUrl) {
const filePath = this.getSqliteFilePath(databaseUrl);
return {
type: 'sqlite',
database: filePath,
entities: [], // Not used for schema inference
synchronize: false,
logging: false
};
}
// Validates if SQLite file exists and is accessible
validateSqliteFile(databaseUrl) {
try {
const filePath = this.getSqliteFilePath(databaseUrl);
return fs_1.default.existsSync(filePath);
}
catch {
// Ignore the error and return false
return false;
}
}
// Quotes identifiers safely for SQLite
getQuotedIdentifier(identifier) {
return `"${identifier.replace(/"/g, '""')}"`;
}
// Get table column metadata and sample data
async getTableInfo(datasource, tableName) {
const method = 'getTableInfo';
// SQLite's pragma table_info is better for getting column info than information_schema
const columnsQuery = `PRAGMA table_info(${this.getQuotedIdentifier(tableName)})`;
const sampleDataQuery = `SELECT * FROM ${this.getQuotedIdentifier(tableName)} LIMIT ${this.maxSampleSize}`;
try {
const [columnsResult, sampleData] = await Promise.all([
datasource.query(columnsQuery),
datasource.query(sampleDataQuery)
]);
// Convert PRAGMA table_info results to our common format
const columns = columnsResult.map((col) => ({
column_name: col.name,
data_type: col.type,
is_nullable: col.notnull === 0 ? 'YES' : 'NO' // SQLite uses 0 for nullable, 1 for NOT NULL
}));
return {
name: tableName,
columns, sampleData
};
}
catch (error) {
this.logError(method, error instanceof Error ? error : new Error(String(error)), { tableName });
throw error;
}
}
// Test if connection is valid
async testConnection(databaseUrl) {
const method = 'testConnection';
const start = perf_hooks_1.performance.now();
if (!databaseUrl) {
this.logError(method, new Error('Database URL is required'));
return false;
}
// First check if the file exists
if (!this.validateSqliteFile(databaseUrl)) {
this.logError(method, new Error('SQLite database file does not exist or is not accessible'));
return false;
}
const datasource = new typeorm_1.DataSource(this.createDataSourceOptions(databaseUrl));
try {
await this.withTimeout(datasource.initialize(), this.timeout, 'Connection timed out');
this.logInfo(method, 'Successfully connected to SQLite database');
return true;
}
catch (error) {
this.logError(method, error, { databaseUrl });
return false;
}
finally {
if (datasource.isInitialized) {
await datasource.destroy();
}
const duration = perf_hooks_1.performance.now() - start;
this.log(base_handler_1.LogLevel.DEBUG, {
method,
action: 'performance_measurement',
duration,
metadata: { durationMs: duration.toFixed(2) }
}, 'Connection test completed');
}
}
// Main method to get schema info
async getSchemaInfo(databaseUrl) {
const method = 'getSchemaInfo';
const start = perf_hooks_1.performance.now();
if (!databaseUrl) {
this.log(base_handler_1.LogLevel.ERROR, { method, action: 'validation' }, 'Database URL is required.');
throw new Error('Please provide a valid database URL');
}
// Validate file existence
if (!this.validateSqliteFile(databaseUrl)) {
throw new Error('SQLite database file does not exist or is not accessible');
}
this.logInfo(method, 'Starting schema extraction for SQLite', { databaseUrl });
const datasource = new typeorm_1.DataSource(this.createDataSourceOptions(databaseUrl));
try {
await datasource.initialize();
try {
// Get all table names from SQLite
const tableQuery = `
SELECT name FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
ORDER BY name;
`;
const tables = await datasource.query(tableQuery);
const tableInfoPromises = tables.map((table) => this.getTableInfo(datasource, table.name));
const rawTablesInfo = await Promise.all(tableInfoPromises);
return {
databaseType: this.getDatabaseType(),
tables: rawTablesInfo.map((table) => ({
name: table.name,
columns: table.columns.map((col) => ({
name: col.column_name,
type: col.data_type,
nullable: col.is_nullable === 'YES'
})),
sampleData: table.sampleData
}))
};
}
finally {
if (datasource.isInitialized)
await datasource.destroy();
}
}
catch (error) {
this.logError(method, error, { databaseUrl });
throw error; // Re-throw after logging
}
finally {
const duration = perf_hooks_1.performance.now() - start;
this.log(base_handler_1.LogLevel.DEBUG, {
method,
action: 'performance_measurement',
duration,
metadata: { durationMs: duration.toFixed(2) }
}, 'Schema extraction completed');
}
}
}
exports.SqliteHandler = SqliteHandler;
//# sourceMappingURL=sqlite-handler.js.map