# app/utils/database_utils.py (execute_insert_with_return 메소드 추가) """ PostgreSQL 데이터베이스 유틸리티 (databases + asyncpg) - RETURNING 지원 추가 """ import databases import logging from typing import Dict, Any, List, Optional from app.config.settings import settings from app.repositories.queries import BaseQueries logger = logging.getLogger(__name__) class SimpleDatabase: """PostgreSQL 데이터베이스 연결 클래스""" def __init__(self): self.database = databases.Database(settings.database_url) self._connected = False async def connect(self): """데이터베이스 연결""" if not self._connected: try: await self.database.connect() self._connected = True logger.info("데이터베이스 연결 성공") except Exception as e: logger.error(f"데이터베이스 연결 실패: {str(e)}") raise async def disconnect(self): """데이터베이스 연결 해제""" if self._connected: try: await self.database.disconnect() self._connected = False logger.info("데이터베이스 연결 해제") except Exception as e: logger.error(f"데이터베이스 연결 해제 실패: {str(e)}") async def test_connection(self) -> Dict[str, Any]: """데이터베이스 연결 테스트""" try: if not self._connected: await self.connect() # 기본 쿼리 실행 test_result = await self.database.fetch_val(BaseQueries.CONNECTION_TEST) db_version = await self.database.fetch_val(BaseQueries.DATABASE_VERSION) current_db = await self.database.fetch_val(BaseQueries.CURRENT_DATABASE) current_user = await self.database.fetch_val(BaseQueries.CURRENT_USER) return { "status": "connected", "test_query": test_result, "database_name": current_db, "username": current_user, "host": settings.db_host, "port": settings.db_port, "db_version": db_version[:50] + "..." if len(db_version) > 50 else db_version } except Exception as e: logger.error(f"데이터베이스 연결 실패: {str(e)}") return { "status": "failed", "error": str(e), "error_type": type(e).__name__ } async def list_tables(self) -> List[Dict[str, Any]]: """테이블 목록 조회""" try: if not self._connected: await self.connect() rows = await self.database.fetch_all(BaseQueries.LIST_TABLES) tables = [] for row in rows: tables.append({ "table_name": row["table_name"], "table_schema": row["table_schema"], "table_type": row["table_type"] }) return tables except Exception as e: logger.error(f"테이블 목록 조회 실패: {str(e)}") raise Exception(f"테이블 목록 조회 실패: {str(e)}") async def query_table(self, table_name: str, limit: int = 5) -> Dict[str, Any]: """테이블 데이터 조회""" try: if not self._connected: await self.connect() # 안전한 쿼리 실행 query = BaseQueries.get_table_data_query(table_name, limit) rows = await self.database.fetch_all(query) # 컬럼 정보 조회 columns_result = await self.database.fetch_all( BaseQueries.GET_TABLE_COLUMNS, {"table_name": table_name} ) columns = [col["column_name"] for col in columns_result] # 결과 변환 data = [] for row in rows: data.append(dict(row)) return { "table_name": table_name, "column_count": len(columns), "row_count": len(data), "columns": columns, "data": data } except Exception as e: logger.error(f"테이블 조회 실패: {str(e)}") raise Exception(f"테이블 '{table_name}' 조회 실패: {str(e)}") async def execute_query(self, query: str, values: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]: """쿼리 실행 (SELECT 전용)""" try: if not self._connected: await self.connect() logger.info(f"쿼리 실행: {query[:100]}{'...' if len(query) > 100 else ''}") # SELECT 쿼리 실행 rows = await self.database.fetch_all(query, values or {}) result = [dict(row) for row in rows] logger.info(f"쿼리 결과: {len(result)}건 조회") return result except Exception as e: logger.error(f"쿼리 실행 실패: {str(e)}") logger.error(f"실행 쿼리: {query}") logger.error(f"파라미터: {values}") raise Exception(f"쿼리 실행 실패: {str(e)}") async def execute_insert_update(self, query: str, values: Optional[Dict[str, Any]] = None) -> int: """INSERT, UPDATE, DELETE 쿼리 실행""" try: if not self._connected: await self.connect() logger.info(f"INSERT/UPDATE 실행: {query[:100]}{'...' if len(query) > 100 else ''}") result = await self.database.execute(query, values or {}) logger.info(f"INSERT/UPDATE 결과: {result}건 영향") return result except Exception as e: logger.error(f"INSERT/UPDATE 실행 실패: {str(e)}") logger.error(f"실행 쿼리: {query}") logger.error(f"파라미터: {values}") raise Exception(f"INSERT/UPDATE 실행 실패: {str(e)}") async def execute_insert_with_return(self, query: str, values: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: """INSERT ... RETURNING 쿼리 실행""" try: if not self._connected: await self.connect() logger.info(f"INSERT RETURNING 실행: {query[:100]}{'...' if len(query) > 100 else ''}") # RETURNING이 있는 INSERT는 fetch_one으로 실행 result = await self.database.fetch_one(query, values or {}) if result: result_dict = dict(result) logger.info(f"INSERT RETURNING 결과: {result_dict}") return result_dict else: raise Exception("INSERT RETURNING 실행 결과가 없음") except Exception as e: logger.error(f"INSERT RETURNING 실행 실패: {str(e)}") logger.error(f"실행 쿼리: {query}") logger.error(f"파라미터: {values}") raise Exception(f"INSERT RETURNING 실행 실패: {str(e)}") # 전역 데이터베이스 인스턴스 simple_db = SimpleDatabase()