12.8.2 Reference SQL과 Generated SQL의 병렬 실행 클래스 구현

12.8.2 Reference SQL과 Generated SQL의 병렬 실행 클래스 구현

SQLAlchemy와 Pandas로 무장한 초고속 인메모리 샌드박스 엔진의 프로비저닝이 완료되었다면, 이제 이 거대하고 위험한 체스판 위에서 인간 전문가가 확정해 둔 무결한 정답 쿼리(Reference SQL)와 통제되지 않은 미지의 AI 모델이 뱉어낸 예측 쿼리(Generated SQL)를 각각의 철저히 격리된 세션에서 병렬(Parallel)로 안전하게 실행시킬 **‘오라클 통제 실행기(Oracle Executor Class)’**의 코어 논리를 설계해야만 한다.

이 클래스의 절대적인 핵심 철학은, AI가 예측한 쿼리가 심각한 파서 문법 오류(Syntax Error)를 일으키거나, 샌드박스 스키마의 붕괴를 유도하려 시도하더라도, 클래스의 try-except-finally 방어막 메커니즘을 통해 메인 채점 파이썬 스레드(Thread)는 결코 패닉에 빠져 죽지 않고 가장 우아하게 에러 로그 메타데이터를 조립하여 반환하는 데 그 궤를 같이한다.

1. 실행기 클래스(DeterministicOracleExecutor) 뼈대 및 방어벽 구축

고도화된 객체 지향적(Object-Oriented) 엔지니어링 관점에서, 데이터베이스 커넥션 포인터와 물리적 쿼리 실행 로직을 외부로부터 감추어 캡슐화(Encapsulate)한다. 특히 12.6절에서 증명한 DDL/DML 사전 요격망과 강제 트랜잭션 롤백(Rollback) 이론이 이 단일 메서드 내부에 완벽한 코드 블록으로 녹아들어야 한다.

import pandas as pd
from sqlalchemy import Engine, text
from sqlalchemy.exc import OperationalError, ProgrammingError

class DeterministicOracleExecutor:
    """ AI 예측 쿼리와 정답 쿼리를 고립된 샌드박스에서 실행하는 메인 객체 """
    
    def __init__(self, db_engine: Engine):
        self.engine = db_engine

    def execute_query_safely(self, sql_query: str) -> dict:
        """
        단일 쿼리를 안전한 트랜잭션 통제 내부에서 실행하고,
        성공 시 Pandas DataFrame을, 실패 시 포착된 런타임 에러 딕셔너리를 반환.
        """
        # [방어선 1] 애플리케이션 계층(L7)의 파괴적 구문 차단 (12.6.2절 정적 분석 단순화 모델)
        dangerous_keywords = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "TRUNCATE"]
        if any(keyword in sql_query.upper().split() for keyword in dangerous_keywords):
            return {"status": "fail", "error_type": "CRITICAL_SECURITY_VIOLATION", "data": None}

        # [방어선 2] 물리 엔진 트랜잭션 캡슐화 및 롤백 제어 (12.6.1절)
        try:
            # engine.begin()은 파이썬 구문 블록 진입 시 BEGIN을, 블록 종료 시 무조건 ROLLBACK을 강제함
            with self.engine.begin() as connection:
                
                # Pandas를 이용해 DB의 Low-level 튜플을 고차원 Tensor(DataFrame)로 직접 인출한다.
                df = pd.read_sql_query(text(sql_query), con=connection)
                
                # 어떠한 에러도 없었다면 무결한 데이터프레임 구조체를 반환한다.
                return {"status": "success", "error_type": None, "data": df}
                
        except (OperationalError, ProgrammingError) as e:
            # Syntax Error, 테이블 명칭의 부재 등 구문 분석 엔진이 뱉어낸 런타임 오류 포착 및 격리
            return {"status": "fail", "error_type": "RUNTIME_ERROR", "data": str(e)}
        except Exception as e:
            # 기타 파이썬 내부 OOM 등의 알 수 없는 파이프라인 폭발 포착
            return {"status": "fail", "error_type": "UNKNOWN_SYSTEM_ERROR", "data": str(e)}

2. 듀얼 파이프라인(Dual Pipeline) 추출 메서드

이제 위에서 완벽하게 장갑을 두른 코어 메서드를 이용하여, 벤치마크 루프에서 전달받은 정답 쿼리와 AI 쿼리를 독립적으로 동시에 실행하고 결과를 취합하는 메인 컨트롤 파이프라인 함수를 작성한다.
주의할 점은, 골든 데이터셋의 정답 쿼리(ref_sql)마저 실행에 실패할 경우 이는 벤치마크 테스트 케이스 자체가 파괴된 심각한 인프라 오류이므로 즉시 쓰레드를 중단(Halt) 시켜야 하며, 반대로 AI 쿼리(gen_sql)의 실패는 단지 AI 지능의 오답일 뿐이므로 침착하게 상태를 로깅(Logging)하고 다음 단계로 넘어가야 한다는 점이다.

    def run_dual_pipeline(self, ref_sql: str, gen_sql: str) -> dict:
        """
        정답 쿼리와 예측 쿼리를 샌드박스에서 병렬 전개하여 두 텐서 결과를 수집한다.
        """
        # 1. 정답 쿼리 추출 전개 (이것이 붕괴하면 평가 데이터셋 자체의 무결성 오류)
        ref_result = self.execute_query_safely(ref_sql)
        if ref_result["status"] == "fail":
            raise ValueError(f"Golden Query 무결성 붕괴. 즉시 수동 점검 요망: {ref_result['data']}")

        # 2. AI 예측 쿼리 추출 전개 (자유로운 실패 허용 구역)
        gen_result = self.execute_query_safely(gen_sql)

        # 양쪽 우주선(쿼리)에서 수확한 데이터를 메타 딕셔너리 페이로드(Payload)로 조립 후 반환
        return {
            "ref_df": ref_result["data"],
            "gen_df": gen_result["data"],
            "is_gen_executable": gen_result["status"] == "success",
            "gen_error_type": gen_result["error_type"],
            "gen_error_msg": gen_result["data"] if gen_result["status"] == "fail" else None
        }

이 2개의 핵심 메서드 조립을 통해, 관계형 데이터베이스 엔진이라는 거칠고 위험무쌍한 차원에서부터 우리가 쉽게 제어할 수 있는 파이썬의 로컬 메모리 텐서(Pandas DataFrame) 공간으로 데이터를 안전하게 단 1바이트의 오염도 없이 옮겨 담아내는 **‘선제적 영지식(Zero-Knowledge) 추출 인터페이스’**의 구축이 성공적으로 완료되었다.

데이터가 텐서 형태로 무사히 준비된 이상, 다음 절에서는 드디어 이 두 데이터프레임을 거칠게 맞부딪혀 집합 수학적 동등성을 판별하는 O(N) 최적화 비교 로직의 심장부로 진입할 것이다.