import { DatabaseConnector, ConnectionConfig, QueryResult, } from "../interfaces/DatabaseConnector"; import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes"; // @ts-ignore import * as mysql from "mysql2/promise"; export class MariaDBConnector implements DatabaseConnector { private connection: mysql.Connection | null = null; private config: ConnectionConfig; constructor(config: ConnectionConfig) { this.config = config; } async connect(): Promise { if (!this.connection) { this.connection = await mysql.createConnection({ host: this.config.host, port: this.config.port, user: this.config.user, password: this.config.password, database: this.config.database, connectTimeout: this.config.connectionTimeoutMillis, ssl: typeof this.config.ssl === "boolean" ? undefined : this.config.ssl, }); } } async disconnect(): Promise { if (this.connection) { await this.connection.end(); this.connection = null; } } async testConnection(): Promise { const startTime = Date.now(); try { await this.connect(); const [rows] = await this.connection!.query( "SELECT VERSION() as version" ); const version = (rows as any[])[0]?.version || "Unknown"; const responseTime = Date.now() - startTime; await this.disconnect(); return { success: true, message: "MariaDB/MySQL 연결이 성공했습니다.", details: { response_time: responseTime, server_version: version, }, }; } catch (error: any) { await this.disconnect(); return { success: false, message: "MariaDB/MySQL 연결에 실패했습니다.", error: { code: "CONNECTION_FAILED", details: error.message || "알 수 없는 오류", }, }; } } async executeQuery(query: string, params: any[] = []): Promise { try { await this.connect(); const [rows, fields] = await this.connection!.query(query, params); await this.disconnect(); return { rows: rows as any[], rowCount: Array.isArray(rows) ? rows.length : 0, fields: fields as any[], }; } catch (error: any) { await this.disconnect(); throw new Error(`쿼리 실행 실패: ${error.message}`); } } async getTables(): Promise { try { await this.connect(); const [rows] = await this.connection!.query(` SELECT TABLE_NAME as table_name, TABLE_COMMENT as description FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME; `); // 테이블 목록만 반환 (컬럼 정보는 getColumns에서 개별 조회) const tables: TableInfo[] = (rows as any[]).map((row) => ({ table_name: row.table_name, description: row.description || null, columns: [], })); await this.disconnect(); return tables; } catch (error: any) { await this.disconnect(); throw new Error(`테이블 목록 조회 실패: ${error.message}`); } } async getColumns(tableName: string): Promise { try { console.log(`[MariaDBConnector] getColumns 호출: tableName=${tableName}`); await this.connect(); console.log(`[MariaDBConnector] 연결 완료, 쿼리 실행 시작`); const [rows] = await this.connection!.query( ` SELECT c.COLUMN_NAME AS column_name, c.DATA_TYPE AS data_type, c.IS_NULLABLE AS is_nullable, c.COLUMN_DEFAULT AS column_default, c.COLUMN_COMMENT AS description, CASE WHEN tc.CONSTRAINT_TYPE = 'PRIMARY KEY' THEN 'YES' ELSE 'NO' END AS is_primary_key FROM information_schema.COLUMNS c LEFT JOIN information_schema.KEY_COLUMN_USAGE k ON c.TABLE_SCHEMA = k.TABLE_SCHEMA AND c.TABLE_NAME = k.TABLE_NAME AND c.COLUMN_NAME = k.COLUMN_NAME LEFT JOIN information_schema.TABLE_CONSTRAINTS tc ON k.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA AND k.CONSTRAINT_NAME = tc.CONSTRAINT_NAME AND k.TABLE_SCHEMA = tc.TABLE_SCHEMA AND k.TABLE_NAME = tc.TABLE_NAME AND tc.CONSTRAINT_TYPE = 'PRIMARY KEY' WHERE c.TABLE_SCHEMA = DATABASE() AND c.TABLE_NAME = ? ORDER BY c.ORDINAL_POSITION; `, [tableName] ); console.log(`[MariaDBConnector] 쿼리 결과:`, rows); console.log( `[MariaDBConnector] 결과 개수:`, Array.isArray(rows) ? rows.length : "not array" ); await this.disconnect(); return rows as any[]; } catch (error: any) { console.error(`[MariaDBConnector] getColumns 오류:`, error); await this.disconnect(); throw new Error(`컬럼 정보 조회 실패: ${error.message}`); } } }