(KO) PostgreSQL DDL & 스키마 — 섹션 개요
목차
이 섹션이 다루는 것
섹션 제목: “이 섹션이 다루는 것”이 서브카테고리는 스키마 변경 기계다. 행을 읽거나 쓰는 것이 아니라 데이터베이스 객체를 정의하거나 변경하는 구문이 실행될 때 동작하는 코드를 다룬다. 핵심 구분은 디스패치 경계에 있다. 플래너가 최적화할 수 있는 구문(SELECT, INSERT, UPDATE, DELETE, MERGE)은 쿼리 파이프라인으로 내려가고, 나머지 — CREATE TABLE, ALTER TABLE, CREATE INDEX, CREATE TRIGGER, CREATE POLICY, COPY, REFRESH MATERIALIZED VIEW 등 유틸리티 구문 전체 — 는 ProcessUtility(tcop/utility.c)로 넘어간다. 이 섹션은 바로 그 지점에서 시작한다.
구체적으로 여기서 다루는 문서는 다음과 같다.
- DDL 실행과 디스패치 —
ProcessUtility→standard_ProcessUtility→ProcessUtilitySlow, 테이블 생성 경로(DefineRelation→heap_create_with_catalog),define.c헬퍼. 섹션 전체의 라우터 문서다. ALTER TABLE—tablecmds.c의 다중 패스 재작성 엔진(AlterTable→AT*페이즈 워크). 소스 트리에서 가장 큰 명령 파일이다.- 인덱스 생성 —
DefineIndex(indexcmds.c)와 카탈로그 라이터index_create(catalog/index.c).CONCURRENTLY다중 트랜잭션 빌드를 포함한다. - 제약 —
CHECK,NOT NULL, 기본 키, 유니크, 외래 키, 배타 제약이 어떻게 저장되고(pg_constraint,heap.c의StoreConstraints/AddRelationNewConstraints) 어떻게 강제되는지. - 트리거와 이벤트 트리거 — 행/구문 레벨 트리거(
commands/trigger.c, 실행기에서 발화)와 그것과 별개인 명령 레벨 이벤트 트리거 메커니즘(commands/event_trigger.c). - 선언적 파티셔닝 — 파티션 바운드와 디스크립터(
partitioning/,catalog/partition.c), 실행기 쪽 튜플 라우팅과 프루닝 경계. COPY— 유틸리티로 디스패치되는 대량 로드/언로드(copyfrom.c/copyto.c).- 구체화 뷰 —
CREATE/REFRESH MATERIALIZED VIEW(matview.c,createas.c). - 행 수준 보안(RLS) —
CREATE POLICY저장(commands/policy.c)과 쿼리 재작성 시점 강제(rewrite/rowsecurity.c).
경계 — 여기에 없는 것과 그것을 소유하는 곳:
- 카탈로그의 데이터 구조는 system-catalog 소속이다.
pg_class/pg_attribute/pg_index레이아웃, relcache/catcache, 커밋된 DDL이 발생시키는sinval무효화는 모두postgres-overview-system-catalog.md에 있다. 이 섹션은 카탈로그 행을 쓰고, 저 섹션은 그것들이 무엇이며 모든 백엔드가 어떻게 정합성을 유지하는지를 설명한다. - 재작성기와 플래너는 query-processing 소속이다. RLS는 양쪽에 걸쳐 있다. 이 섹션은
CREATE POLICY와 정책 의미론을 소유하지만, 재작성 단계의 주입 지점(fireRIRrules→get_row_security_policies)은postgres-overview-query-processing.md에 문서화된 재작성기 경계에 있다. - 트리거와 파티션 라우팅 실행은 query-processing과 공유된다. 이 섹션은 트리거와 파티션을 정의하고, 실행기는 DML 시점에 트리거를 발화(
AfterTriggerBeginQuery)하고 튜플을 라우팅(ExecFindPartition)한다. VACUUM/CLUSTER/ autovacuum은 스키마 변경이 아니라 유지 관리 작업이다.commands/디렉터리를 공유하더라도 txn-recovery 소속이다.CREATE EXTENSION, 외부 테이블, PL은 extensibility 소속이고, 퍼블리케이션/구독은 replication-ha 소속이다. 동일한ProcessUtility로 디스패치되는 유틸리티 구문이지만, 그 메커니즘은 해당 섹션에 있다. 이 섹션은 공유 프론트 도어만 언급한다.
파이프라인: ProcessUtility와 두 탈출자
섹션 제목: “파이프라인: ProcessUtility와 두 탈출자”구조적 흐름의 핵심은 하나의 디스패치 함수와, 의도적으로 그것을 거치지 않는 두 멤버다. standard_ProcessUtility는 자급자족 구문들을 인라인으로 처리(큰 switch문)하고, 카탈로그에 무거운 구문들은 ProcessUtilitySlow로 넘긴다. ProcessUtilitySlow는 각 명령의 commands/*.c 모듈을 호출하고, 그 모듈들은 catalog/ 아래 카탈로그 라이터와 partitioning/ 아래 바운드 기계를 다시 호출한다. 행 수준 보안 강제와 파티션 튜플 라우팅이 두 탈출자다. 이들은 각각 재작성기와 실행기에서 발화하며, 유틸리티 디스패치 시점에는 실행되지 않는다.
flowchart TB
PORTAL["portal / PortalRunUtility<br/>(query-processing)"] --> PU["ProcessUtility / standard_ProcessUtility<br/>postgres-ddl-execution.md"]
PU -->|단순, 인라인| SIMPLE["DROP, RENAME, COPY 디스패치,<br/>REFRESH 디스패치, ..."]
PU -->|카탈로그 중량| SLOW["ProcessUtilitySlow"]
SLOW --> CRT["DefineRelation (CREATE TABLE)<br/>postgres-ddl-execution.md"]
SLOW --> ALT["AlterTable / AT 페이즈 워크<br/>postgres-alter-table.md"]
SLOW --> IDX["DefineIndex<br/>postgres-index-creation.md"]
SLOW --> CON["제약 정의<br/>postgres-constraints.md"]
SLOW --> TRG["CreateTrigger<br/>postgres-triggers.md"]
SLOW --> EVT["이벤트 트리거<br/>postgres-event-triggers.md"]
SLOW --> POL["CREATE POLICY 저장<br/>postgres-row-level-security.md"]
SLOW --> MV["CREATE MATERIALIZED VIEW<br/>postgres-matview.md"]
SIMPLE --> CP["DoCopy<br/>postgres-copy.md"]
SIMPLE --> MVR["REFRESH MATERIALIZED VIEW<br/>postgres-matview.md"]
CRT --> CAT["카탈로그 라이터<br/>heap.c / index.c / partition.c"]
ALT --> CAT
IDX --> CAT
CON --> CAT
POL --> CAT
CAT --> PART["파티션 바운드 / 디스크립터<br/>postgres-partitioning.md"]
subgraph ESCAPE["두 탈출자 — ProcessUtility를 거치지 않음"]
direction TB
RLS["RLS 강제<br/>get_row_security_policies @ 재작성 시점<br/>postgres-row-level-security.md"]
ROUTE["파티션 튜플 라우팅 + 프루닝<br/>ExecFindPartition @ 실행 시점<br/>postgres-partitioning.md"]
end
REWRITE["재작성기 (query-processing)"] --> RLS
EXECDML["INSERT/UPDATE 실행기 (query-processing)"] --> ROUTE
EXECDML --> TRGFIRE["트리거 발화<br/>postgres-triggers.md"]
CAT -. "커밋 시 sinval 발생" .-> SYSCAT["system-catalog 섹션"]
다이어그램에서 읽어야 할 세 가지가 있다.
ProcessUtilitySlow가 DDL의 실질적인 작업마인이다. 분리가 존재하는 이유는, 카탈로그를 변경하는 명령들이ParseState, 테이블 오픈 순서, 이벤트 트리거 펜스를 필요로 하기 때문이다. 그것들을 필요로 하지 않는 단순 구문에는 그런 비용을 들이지 않는다.postgres-ddl-execution.md가 이 분리를 소유한다.- 카탈로그 라이터는 공유 인프라다.
heap.c(heap_create_with_catalog,StoreConstraints),index.c(index_create),partition.c는 명령 모듈 여럿이 호출한다. 각 모듈 문서는 자신이 호출하는 진입점을 명시하고, 그 라이터들이 만들어 내는 행 레이아웃은 system-catalog 섹션이 설명한다. - 두 탈출자가 이 섹션의 핵심 미묘함이다. “모든 스키마 기능은 DDL 시점에 실행된다”고 가정하면 두 번 틀린다. RLS 정책은 DDL로 정의되지만 매 쿼리마다 재작성기가 적용하고, 파티션 멤버십은 DDL로 선언되지만 실행기가 행 단위로 결정한다. 두 모듈 문서 모두 이 점을 서두에서 다룬다.
읽는 순서
섹션 제목: “읽는 순서”상호 참조 우선 — 잎 문서보다 디스패치 척추를 먼저 읽는다.
postgres-ddl-execution.md—ProcessUtility디스패치 모델과CREATE TABLE. 이 섹션의 다른 모든 문서는 이 진입점을 전제한다.postgres-alter-table.md— AT 페이즈 재작성 엔진. 가장 복잡한 명령이며 가장 넓은 카탈로그 표면을 건드린다.postgres-index-creation.md— **postgres-constraints.md**와 쌍으로 읽는다. 제약은 인덱스로 뒷받침되는 경우가 많고, 둘 다 같은 카탈로그 경로에 쓴다.postgres-triggers.md→postgres-event-triggers.md— 행/구문 모델을 먼저 읽고, 이름은 같지만 메커니즘이 다른 명령 레벨 모델을 나중에 읽는다.postgres-partitioning.md— 카탈로그(바운드), 플래너(프루닝), 실행기(라우팅)를 하나로 엮는다. 트리거와 제약을 이해한 뒤가 더 쉽다. 파티션은 둘 다와 상호작용하기 때문이다.postgres-row-level-security.md— query-processing 섹션의 재작성기 개요를 읽은 뒤에 읽는다. RLS는 재작성 시점 메커니즘이기 때문이다.postgres-copy.md—postgres-matview.md— 독립적인 잎 문서다. 마지막에 임의 순서로 읽으면 된다.
세부 문서 요약
섹션 제목: “세부 문서 요약”전방 참조. 아래 모듈 문서들은 아직 없을 수 있다.
| 문서 | 한 줄 범위 |
|---|---|
postgres-ddl-execution.md | ProcessUtility → standard_ProcessUtility → ProcessUtilitySlow 디스패치, 단순/중량 분리, CREATE TABLE 경로(DefineRelation → heap_create_with_catalog). 섹션 전체의 라우터. |
postgres-alter-table.md | tablecmds.c의 다중 페이즈 ALTER TABLE 엔진 — AlterTable 준비/실행/재작성 패스(AT* 서브커맨드), 테이블 재작성, 자식 테이블 재귀. |
postgres-index-creation.md | DefineIndex와 index_create, 연산자 클래스/콜레이션 해석, CONCURRENTLY 다중 트랜잭션 빌드, REINDEX. |
postgres-constraints.md | CHECK, NOT NULL, 기본 키, 유니크, 외래 키, 배타 제약이 pg_constraint에 저장되고 강제되는 방식(FK는 트리거, PK/유니크는 인덱스). |
postgres-triggers.md | 행/구문 레벨 트리거 정의(CreateTrigger)와 실행기 시점 발화 기계(AfterTrigger* 큐, 전환 테이블). |
postgres-event-triggers.md | 명령 레벨 이벤트 트리거(ddl_command_start/end, sql_drop, table_rewrite) — 행 트리거와는 구별되며 ProcessUtility 주위를 펜스로 감싸는 메커니즘. |
postgres-partitioning.md | 선언적 파티셔닝 전 과정: 바운드(partbounds.c), 디스크립터(partdesc.c), 플래너 프루닝(partprune.c), 실행기 튜플 라우팅(ExecFindPartition). |
postgres-row-level-security.md | CREATE POLICY 저장(commands/policy.c)과 쿼리 재작성 시점 강제(get_row_security_policies, rewriteHandler.c에서 호출) — 재작성기 메커니즘이며 DDL 시점 메커니즘이 아니다. |
postgres-copy.md | COPY 대량 로드/언로드: DoCopy 디스패치, copyfrom.c/copyto.c 경로, 멀티 인서트 배칭, COPY ... FROM 중 파티션 라우팅. |
postgres-matview.md | 구체화 뷰: CREATE MATERIALIZED VIEW(createas.c와 공유)와 REFRESH MATERIALIZED VIEW(ExecRefreshMatView), 동시 갱신 diff 포함. |
인접 섹션
섹션 제목: “인접 섹션”postgres-overview-system-catalog.md— 가장 가까운 이웃. 이 섹션은 카탈로그 행을 쓰고, 저 섹션은 라이터들이 대상으로 하는pg_*레이아웃, 그것을 읽는 relcache/catcache, 커밋된 DDL이 발생시키는sinval무효화를 설명한다. 쓰기 측면 / 읽기 측면 쌍으로 함께 읽는다.postgres-overview-query-processing.md— RLS가 주입되는 재작성기 경계(get_row_security_policies), 파티셔닝이 의존하는 플래너 프루닝, 트리거를 발화하고 파티션 튜플을 라우팅하는 실행기를 소유한다. 이 섹션의 세 문서가 이 경계를 넘어 메커니즘을 넘긴다.postgres-overview-txn-recovery.md—VACUUM/CLUSTER/autovacuum은commands/디렉터리를 공유하지만 스키마 변경이 아닌 유지 관리 작업이므로 여기 산다. 모든 DDL 구문이 실행되는 트랜잭션 보장도 여기 있다. PostgreSQL에서 DDL은 트랜잭션적이며 WAL 기록된다.postgres-overview-extensibility.md—postgres-overview-replication-ha.md— 이들 역시ProcessUtility로 디스패치된다(CREATE EXTENSION, 외부 테이블, PL; 퍼블리케이션/구독). 그러나 메커니즘은 해당 섹션에 있다. 이 섹션은 공유 디스패치 프론트 도어만 소유한다.