5.1 파서·재작성기
PostgreSQL이 SQL 문자열 한 줄을 받아서 실제 데이터를 돌려주기까지는 파싱 → 재작성 → 계획 → 실행 네 단계를 거칩니다. 첫 두 단계를 봅니다.
전체 흐름
flowchart LR
RAW["SQL text<br/>(질의문자열)"]
PARSE["Parser<br/>(flex + bison)"]
RT["Parse Tree<br/>(raw)"]
ANALYZE["Analyzer<br/>(이름·타입 해석)"]
QT["Query Tree"]
REWRITE["Rewriter<br/>(rule, view 풀기)"]
QT2["Query Tree<br/>(재작성됨)"]
PLAN["Planner / Optimizer"]
EXEC["Executor"]
RAW --> PARSE --> RT --> ANALYZE --> QT --> REWRITE --> QT2 --> PLAN --> EXEC
classDef text fill:#fef3c7,stroke:#b45309,color:#78350f
classDef stage fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef tree fill:#ede9fe,stroke:#6d28d9,color:#3b0764
classDef exec fill:#d1fae5,stroke:#047857,color:#064e3b
class RAW text
class PARSE,ANALYZE,REWRITE,PLAN stage
class RT,QT,QT2 tree
class EXEC exec
PARSE → ANALYZE → REWRITE 까지. 다음 절(5.2)부터는 PLAN과 그 비용 모델.
1. 파서 — 문법만 본다
PostgreSQL 파서는 flex(렉서) + bison(파서 생성기) 조합입니다. 입력 SQL을 토큰으로 쪼개고, 문법 규칙에 맞게 Raw Parse Tree를 만듭니다.
이 단계에서 일어나는 검사:
- 문법 오류 (
syntax error at or near "...") - 키워드 위치
- 괄호 매칭
아직 일어나지 않는 것:
- 테이블·컬럼이 실제로 존재하는가
- 타입이 맞는가
- 권한이 있는가
SELECT * FROM no_such_table;
-- 문법은 OK — 파서 통과
-- ERROR는 다음 단계(Analyzer)에서 발생pg_query나 PG 14+의 EXPLAIN은 이 단계를 노출하지 않지만, 문법만 확인하고 싶을 때 PREPARE를 쓰면 파싱+분석까지 진행되어 검사됩니다.
2. Analyzer — 이름·타입 해석
Raw Parse Tree를 받아 카탈로그(pg_class, pg_attribute, pg_type 등)와 대조해 Query Tree를 만듭니다.
해결되는 것:
- 테이블·뷰 이름 → OID
- 컬럼 이름 →
attnum - 타입 추론과 캐스팅
- 함수 시그니처 매칭
- 권한 검사 (읽기/쓰기·EXECUTE)
오류 예:
ERROR: column "foo" does not exist
ERROR: operator does not exist: integer = text
ERROR: permission denied for table accounts이 시점에서 Query Tree의 노드는 RTE(Range Table Entry)·TargetEntry·FuncExpr·OpExpr 등으로 구성됩니다. 옵티마이저가 이해할 수 있는 internal representation입니다.
3. Rewriter — view와 rule을 풀어 펴기
재작성기는 Query Tree를 받아 view 정의와 rule을 끼워 넣어 확장된 Query Tree를 만듭니다.
view 풀기
CREATE VIEW recent_orders AS
SELECT id, user_id, total
FROM orders
WHERE created_at > now() - interval '7 days';
SELECT count(*) FROM recent_orders;재작성기 후:
SELECT count(*) FROM (
SELECT id, user_id, total
FROM orders
WHERE created_at > now() - interval '7 days'
) recent_orders;view는 항상 underlying 쿼리로 펼쳐진다. 그래서 view의 인덱스·통계는 원본 테이블 것을 사용합니다. PostgreSQL의 view 자체에 인덱스를 만들 수 없는 이유다 (필요하면 materialized view).
rule (CREATE RULE)
거의 안 쓰는 옛 기능. 한 쿼리를 다른 쿼리로 변환하는 규칙을 등록할 수 있습니다.
CREATE RULE no_delete AS ON DELETE TO logs DO INSTEAD NOTHING;복잡한 rule은 디버그가 어려워 운영에서는 trigger를 권장합니다. updatable view 구현 내부에서만 자주 보임.
row-level security 변환
RLS(CREATE POLICY)도 이 단계에서 적용됩니다. 정책이 정의한 USING/WITH CHECK 조건이 WHERE 절에 끼어 들어갑니다. 자세한 내용은 Part IX 9.3.
EXPLAIN과 단계
EXPLAIN은 PLAN 단계 결과를 보여 줍니다. 파서·분석기·재작성기는 EXPLAIN으로 안 보이지만, 이 단계의 비용이 누적되는 곳이 있다:
| 비용 | 빈도 |
|---|---|
| 파싱·분석 | 매 쿼리. prepared statement로 한 번만 가능 |
| 재작성 | 매 쿼리 |
| 카탈로그 lookup | 매 쿼리 (system catalog cache로 완화) |
매우 짧은 단순 SELECT에서는 이 앞 단계의 오버헤드가 실제 실행보다 큰 경우도 있습니다.
prepared statement로 단계 절약
PREPARE q1 (int) AS
SELECT * FROM orders WHERE user_id = $1;
EXECUTE q1(42);
EXECUTE q1(43);
EXECUTE q1(44);PREPARE는 파서·분석기·재작성기를 한 번만 거치고 결과 Query Tree를 캐시합니다. EXECUTE는 그 트리를 가져와 PLAN(나중에는 그것도 캐시 가능)부터 시작합니다.
ORM·드라이버 대부분이 내부적으로 prepared statement를 사용한다 (pgJDBC, psycopg, node-postgres 등). pgBouncer transaction 모드에서는 명시적인 PREPARE가 동작하지 않는다는 점만 주의.
카탈로그 조회 비용
분석기·재작성기는 매번 카탈로그를 봅니다. 운영에서 카탈로그 자체가 비대해지면 모든 쿼리가 느려집니다.
| 사례 | 원인 | 대응 |
|---|---|---|
pg_class·pg_attribute가 매우 큼 | 수만 개 테이블/파티션 | 파티션 수 조절, autovacuum on catalog |
pg_proc 비대 | 자동 생성된 함수 누적 | 정기 정리 |
pg_statistic 비대 | 매우 큰 테이블 + 많은 컬럼 | 자동 vacuum이 잘 돌도록 |
relcache·syscache라는 backend 로컬 캐시가 있어, 같은 카탈로그를 반복 조회할 때는 빠릅니다. 단, 새 연결이 만들어질 때마다 캐시를 다시 채우므로 연결 풀링(13.4)이 중요.
view·rule·materialized view 비교
| 객체 | 동작 |
|---|---|
| view | 재작성기에서 항상 풀려 들어감. 자체 스토리지 없음 |
| materialized view | 실제 테이블처럼 디스크에 저장, REFRESH MATERIALIZED VIEW로 갱신 |
rule (CREATE RULE) | 재작성기에서 쿼리 변환. 운영에서는 trigger 권장 |
| trigger | 실행 시점에 함수 실행. 재작성 단계 무관 |
정리
- SQL 처리는 파싱 → 분석 → 재작성 → 계획 → 실행 다섯 단계
- 파서는 문법만, 분석기가 카탈로그 대조와 타입·권한 검사
- 재작성기는 view·rule·RLS 정책을 underlying 쿼리에 펼침
- view는 인덱스를 못 갖는 이유 — 항상 원본 쿼리로 풀려 들어가기 때문
- prepared statement로 파서·분석기 비용 절약 가능
- 매우 많은 객체(파티션·함수)는 카탈로그 자체를 비대하게 만들어 모든 쿼리 비용 증가
다음 절(5.2)에서는 분석된 Query Tree를 실제 실행 계획으로 만드는 옵티마이저와 비용 모델을 봅니다.