diff --git a/app/core/database.py b/app/core/database.py index cbf4052..b7ebc88 100644 --- a/app/core/database.py +++ b/app/core/database.py @@ -57,12 +57,12 @@ def _parse_migration_files(migrations_dir: Path) -> list[tuple[int, str, Path]]: def _validate_sql_content(name: str, sql: str) -> None: """migration SQL에 BEGIN/COMMIT이 포함되어 있으면 에러 (외부 트랜잭션 깨짐 방지)""" - # 주석(-- ...) 라인 제거 후 검사 - lines = [ - line for line in sql.splitlines() - if not line.strip().startswith("--") - ] - stripped = "\n".join(lines).upper() + # 주석(전체 줄 + 인라인 `-- ...`) 제거 후 검사. ★인라인 주석을 안 지우면 설명 주석의 + # 'commit/begin' 단어(예 365_scan_jobs 의 `-- commit 시 documents.title 로 전파`)를 + # 트랜잭션 제어문으로 false-positive 로 잡아 fresh DB/DR 부트스트랩이 깨진다(verification + # 실측 2026-06). 줄별로 `--` 이후를 잘라 주석 텍스트를 검사에서 제외. + cleaned = [re.sub(r"--.*$", "", line) for line in sql.splitlines()] + stripped = "\n".join(cleaned).upper() for keyword in ("BEGIN", "COMMIT", "ROLLBACK"): # 단어 경계로 매칭 (예: BEGIN_SOMETHING은 제외) if re.search(rf"\b{keyword}\b", stripped): @@ -70,6 +70,13 @@ def _validate_sql_content(name: str, sql: str) -> None: f"migration {name}에 {keyword} 포함됨 — " f"migration SQL에는 트랜잭션 제어문을 넣지 마세요" ) + # schema_migrations 수정 금지 (runner 가 스탬프 관리) — 주석 제외(stripped) 검사. + # (구: _run_migrations 의 raw `"schema_migrations" in sql.lower()` 가 주석 미제외라 + # 365 의 '-- ... schema_migrations 를 건드리지 않음' 주석을 false-positive 로 잡았음.) + if "SCHEMA_MIGRATIONS" in stripped: + raise RuntimeError( + f"Migration {name} must not modify schema_migrations table" + ) # R1: baseline 스냅샷이 대표하는 마지막 마이그레이션 버전 (이하 버전은 baseline 에 포함). @@ -167,11 +174,7 @@ async def _run_migrations(conn) -> None: for version, name, path in pending: sql = path.read_text(encoding="utf-8") - _validate_sql_content(name, sql) - if "schema_migrations" in sql.lower(): - raise ValueError( - f"Migration {name} must not modify schema_migrations table" - ) + _validate_sql_content(name, sql) # BEGIN/COMMIT + schema_migrations 검사(주석 제외) logger.info(f"[migration] {name} 실행 중...") # raw driver SQL 사용 — text() 의 :name bind parameter 해석으로 # SQL 주석/literal 에 콜론이 들어가면 InvalidRequestError 발생.