# FiveSql2 하이브리드 실행 모델 — 구현 계획 **Date:** 2026-04-14 **Decision Owner:** Charles KWON **Status:** 확정 (user: "좋아요 그게 답입니다") --- ## 1. 아키텍처 결정 ### 1.1 설계 원칙 FiveSql2는 **AST tree-walk 평가기**와 **pcode/Go 핫패스**를 공존시킨다. SQLite 방식(AST 폐기 + 전량 VDBE 컴파일)은 **채택하지 않는다**. ### 1.2 왜 하이브리드인가 | 관점 | AST 유지 (FiveSql2) | VDBE 전량 (SQLite) | |------|---------------------|-------------------| | 신규 SQL 표준 추가 | evaluator에 CASE 추가 | 코드젠 + opcode 신설 | | Window/RECURSIVE CTE | tree-walk로 자연 표현 | 서브프로그램·임시 커서 필요 | | 단순 SELECT 성능 | 느림 (트리 워크) | 빠름 (op dispatch) | | 구현 복잡도 | 낮음 | 매우 높음 | FiveSql2는 SQL:1999 → SQL:2013 → 향후 표준 확장이 최우선이므로 **AST 기반 확장성**을 희생하지 않는다. 성능은 **핫패스 선별 하강**으로 해결. ### 1.3 역할 분담 ``` ┌─────────────────────────────────────────────────────┐ │ Parser (PRG) → AST (영속) │ │ │ │ │ ▼ │ │ TSqlExecutor.RunSelect (PRG) │ │ ├─ Window / CTE / Recursive → tree-walk evaluator │ │ ├─ GROUP BY / Aggregate → tree-walk evaluator │ │ ├─ JOIN → tree-walk evaluator │ │ │ │ │ └─ ★ Hot path (simple scan) │ │ ├─ TryBuildFieldPositions(aExprs) │ │ ├─ TryCompileWhere(xWhere) → PcCompile │ │ └─ SqlScan(fields, pcWhere) ── Go RTL ──┐ │ └─────────────────────────────────────────────────┼────┘ ▼ ┌────────────────────────┐ │ hbrtl/sqlscan.go │ │ area.GoTop/EOF/Skip │ │ ExecPcode per row │ │ GetValue(idx) direct │ └────────────────────────┘ ``` --- ## 2. 현재 상태 (2026-04-14) ### 완료 - [x] `compiler/genpc/genpc.go` — `CompileExpr` 공개 API - [x] `hbrtl/pcexpr.go` — `PcCompile` / `PcEval` RTL - [x] `hbrtl/sqlscan.go` — Go 네이티브 스캔 루프 - [x] `hbrdd/dbf/dbf.go` — `FieldPosCache` O(1) - [x] `_FiveSql2/src/TSqlExecutor.prg` — `TryBuildFieldPositions` / `TryCompileWhere` 메서드 - [x] Fast path 통합 (WHERE 없음 + 단순 projection 한정) - [x] 회귀: 43/43 · 51/51 · Go tests ALL PASS ### 미완성 - [ ] `TryCompileWhere`가 항상 NIL 반환 — WHERE 있는 쿼리는 느린 경로 - [ ] `BindColumns/ResolveCache` 4-test 회귀 미해결 - [ ] 소형 PRG `TSqlIndex:FindExclusive` 패닉 (격리 벤치 차단) --- ## 3. 단계별 작업 계획 ### Phase 1 — WHERE 컴파일러 (우선순위 최상) **목표:** `TryCompileWhere`가 단순 WHERE를 pcode로 변환해 `SqlScan`에 넘김. **범위:** - ND_COL → `FieldGet(n)` (CHAR 비교 시 `AllTrim()` 자동 래핑) - ND_LIT → 숫자/문자열 literal - ND_BIN — `=`, `<>`, `!=`, `<`, `<=`, `>`, `>=`, `AND`, `OR` - 그 외 (ND_FN, ND_CASE, ND_SUB, LIKE, IN, IS NULL, BETWEEN, ND_PAR) → NIL 반환 (fallback) **의미 보존 원칙:** - `SqlCmpEq`의 CHAR trim 규칙을 PRG 변환 시점에 반영 (비교 양변이 ND_COL이고 CHAR면 `AllTrim()` 래핑) - NULL 비교는 첫 버전에서 미지원 — ND_NIL 포함 시 NIL 반환 - 타입 강제는 Five의 기본 연산자 오버로딩에 위임 **파일:** - `_FiveSql2/src/TSqlExecutor.prg` — `METHOD SqlExprToPrg(xNode)` 신설 - `TryCompileWhere`에서 호출 **검증:** 43/43 유지 + simple WHERE 벤치 (50k rows, `salary > 50000`) pcode 경로 확인. --- ### Phase 2 — Projection 확장 **목표:** `TryBuildFieldPositions`를 넘어 단순 식 projection 지원. **범위:** - `SELECT a + b, c * 2 FROM t` — ND_BIN 산술식도 pcode로 - 설계 변경: `SqlScan`이 필드 인덱스 배열 대신 **pcode 배열**(projection 표현식) 수신 - `aSelectExprs []*PcodeFunc` 형태로 RTL 확장 **파일:** - `hbrtl/sqlscan.go` — 시그니처 변경: `SqlScan(aProjs, pcWhere)` - `_FiveSql2/src/TSqlExecutor.prg` — projection 빌더 **Risk:** SQL 함수(UPPER/ALLTRIM/SUBSTR)는 PRG 런타임에 존재. pcode ExecPcode가 이들 함수를 호출 가능한지 확인 필요. --- ### Phase 3 — BindColumns 회귀 해결 **목표:** 이전 세션의 `ResolveCache` PushLocal(0) 버그 근본 원인 파악. **증상:** 4 tests panic at `class.go:278` Send. "Unresolved variable → PushLocal(0)". **조사 항목:** - CLASS 내부에서 Resolve를 캐시할 때 `self` 참조가 깨지는 경우 - `pendingParams` 순서와의 상호작용 - gengo가 캐시 변수를 local index 0으로 emit하는지 **파일:** `hbrt/class.go`, `compiler/gengo/gen_class.go` (조사 우선, 수정은 증거 기반) --- ### Phase 4 — 소형 PRG FindExclusive 패닉 **목표:** 격리 벤치가 가능하도록 class-system edge case 수정. **증상:** 소형 PRG에서 `TSqlIndex:FindExclusive` "local variable index out of range: 0" **영향:** 벤치 격리 차단. 43-test 통합 실행에서는 발생 안 함 → **Phase 1 종료 후** 진행. --- ### Phase 5 — 벤치 + 커밋 **목표:** 실측 데이터로 고속화 배수 확정 후 커밋. **벤치 케이스:** - 50k × 3 컬럼 DBF - SELECT * (fast path) - SELECT a, b WHERE ... (fast path after Phase 1) - JOIN / GROUP BY / Window (fallback, 변화 없음 확인) **커밋 규칙:** Phase 별 개별 커밋. CLAUDE.md의 3-테스트 게이트 매번 통과. --- ## 4. 비목표 (Non-goals) - SQLite식 전량 VDBE 컴파일 — 거부 - AST 구조 변경 — SQL:2013 이상 확장 경로 보존 - 옵티마이저 전면 재작성 — 기존 `oIndex:TryIndexScan`, plan cache는 유지 - 동시성/트랜잭션 모델 변경 — 이번 범위 밖 --- ## 5. 검증 게이트 (불변) 모든 Phase 종료 시 CLAUDE.md 규칙대로 3개 통과: ```bash go test ./... # Go 유닛 ./five build _FiveSql2/test/test_sql1999.prg _FiveSql2/src/*.prg -o /tmp/test_sql && \ cd ~/tmp && rm -f *.dbf __cte_*.dbf 2>/dev/null; /tmp/test_sql # 43/43 ./five build tests/compat_harbour.prg -o /tmp/test_compat && /tmp/test_compat # 51/51 ``` 하나라도 실패 → 해당 Phase 롤백.