from __future__ import annotations import time from typing import Any from app.core.database import ConnectionConfig, pool_manager from app.core.database.base import StatementResult from app.core.logger import get_logger from app.models.schemas import ( DatabaseQueryError, DatabaseQueryRequest, DatabaseQueryResponse, StatementResultSchema, ) _logger = get_logger(__name__) def _root_cause(exc: Exception) -> str: cause = exc.__cause__ or exc.__context__ if cause: return f"{exc} [{cause}]" return str(exc) class DatabaseService: async def execute_query(self, request: DatabaseQueryRequest) -> DatabaseQueryResponse: start_time = time.monotonic() config = ConnectionConfig(**request.to_connection_config()) try: executor = await pool_manager.get_executor(config) except Exception as exc: elapsed = (time.monotonic() - start_time) * 1000 _logger.error( "Failed to acquire executor for %s: %s", config.safe_repr, exc, ) return DatabaseQueryResponse( success=False, execution_time_ms=round(elapsed, 2), error=DatabaseQueryError( message=f"Connection failed: {_root_cause(exc)}", code=type(exc).__name__, ), ) try: results = await executor.execute( request.query, use_transaction=request.use_transaction, ) except Exception as exc: elapsed = (time.monotonic() - start_time) * 1000 _logger.error( "Query execution failed for %s: %s", config.safe_repr, exc, ) return DatabaseQueryResponse( success=False, execution_time_ms=round(elapsed, 2), error=DatabaseQueryError( message=f"Execution failed: {_root_cause(exc)}", code=type(exc).__name__, ), ) elapsed = (time.monotonic() - start_time) * 1000 statement_results = [ StatementResultSchema( success=r.success, rows=r.rows, data=r.data, error=r.error, error_code=r.error_code, ) for r in results ] overall_success = all(r.success for r in results) if overall_success: _logger.info( "Query success for %s (%d stmts, %.2fms)", config.safe_repr, len(results), elapsed, ) return DatabaseQueryResponse( success=True, execution_time_ms=round(elapsed, 2), results=statement_results, ) _logger.warning( "Query partial/full failure for %s (%d stmts, %.2fms)", config.safe_repr, len(results), elapsed, ) return DatabaseQueryResponse( success=False, execution_time_ms=round(elapsed, 2), results=statement_results, )