코드 분석
오픈소스 DBMS 내부를 소스 레벨에서 읽는 분석서 — 현재 CUBRID와 PostgreSQL 코드베이스를 다루며, 두 프로젝트 모두 같은 영역 구분으로 묶여 있습니다. 프로젝트를 골라 전체 분석 트리로 이동하세요:
CUBRID 107개 문서
CUBRID 코드베이스를 소스 레벨에서 읽는다 — 스토리지, MVCC, 락 매니저, 복구, 질의 파이프라인, 복제/HA, 그리고 PL 계열.
PostgreSQL 140개 문서
PostgreSQL 백엔드(REL_18)를 모듈 단위로 읽는다 — 공유 메모리 프로세스 모델, WAL 내구성 축, 스토리지와 플러거블 액세스 메서드, 플래너/익스큐터 파이프라인, 카탈로그/캐시 계층, 복제, 확장성 표면, 그리고 버전 진화 문서까지.
CUBRID
섹션 제목: “CUBRID”CUBRID 코드베이스를 소스 레벨에서 읽는다 — 스토리지, MVCC, 락 매니저, 복구, 질의 파이프라인, 복제/HA, 그리고 PL 계열.
먼저 읽기: 아키텍처 개요
바로가기: Overview & Reading Paths · Base / Infrastructure · Storage Engine · Transaction & Recovery · Query Processing · DDL & Schema · Replication & HA · Server Architecture · System Catalog · Procedural Language · Internationalization & Specialty · Utilities
Overview & Reading Paths (17)
- (KO) CUBRID 아키텍처 개요 — 프로세스 모델, 계층 스택, 세부 문서로 가는 지도 — CUBRID 코드 분석 트리의 정문 역할을 하는 문서. 네 가지 상주 프로세스(`cub_master`, `cub_server`, `cub_pl`, `cub_broker`+`cub_cas`)와 그들의 IPC, 계층 스토리지 스택(디스크 → 페이지 버퍼 + DWB → heap/B+Tree/extendible-hash → catalog → class-object → workspace), 질의 파이프라인(파서 → 의미 검사 → 재작성 → 옵티마이저 → XASL → 실행기 → 스캔 매니저 → 접근 방법 → list-file), 동시성·로깅·복구 축(MVCC + lock + transaction + log + prior-list + checkpoint + recovery + DWB), 분산 계층(heartbeat + HA 복제 + CDC + 2PC + flashback + backup), PL 패밀리(`pl_server` JVM, JavaSP, PL/CSQL), 그리고 모든 계층을 가로지르는 인프라(boot, 세션, 스레드 풀, 네트워크 프로토콜, broker, 에러, 파라미터, 모니터링, DBI/CCI, SA/CS)를 축마다 다이어그램 한 장씩으로 풀어내고, 여덟 개 서브카테고리에 흩어진 약 70개의 세부 문서로 곧장 길을 내준다.
- (KO) CUBRID 코드 분석 커버리지 — 어디까지 정리되었고 어디가 비어 있는가 — cubrid 코드 분석 트리의 개관용 커버리지 맵. 기존 문서를 서브시스템별로 묶고, 같은 단위에서 아직 비어 있는 자리를 짚으며, 다루지 않기로 한 항목을 따로 적어 둔다. 문서별 카탈로그(README.md)도 아키텍처 맵(cubrid-architecture-overview.md)도 아니다. 오직 '다음에 무엇을 써야 하는가'에만 답한다.
- (KO) CUBRID 설계 철학 — 코드베이스가 지금의 모습이 된 이유 — CUBRID 코드베이스의 모양을 만들어 낸 13가지 아키텍처 결정. UniSQL에서 물려받은 OODB 혈통, 논문 그대로의 ARIES 복구, MVCC와 락 매니저의 공존, Volcano 실행기, Selinger 식 옵티마이저, lock-free prior list, 더블 라이트 버퍼, PL을 위한 별도 JVM, broker 프로세스 풀, 로컬에서만 결정짓는 HA 생존 판정, 페이지 기반 스토리지, SA/CS 두 갈래 빌드, 그리고 일부러 *하지 않은* 일들 — 각각의 역사적·학술적 뿌리까지 짚어 둔다.
- (KO) CUBRID 베이스 / 인프라 — 섹션 개요 — 베이스/인프라 서브카테고리 라우터 — 모든 상위 레이어가 깔고 앉는 `src/base/` 기반 코드. 두 갈래로 나뉜다. 하나는 커스텀 메모리 할당기 군 (고정 크기 객체용 AREA 슬랩 풀, 스레드 단위 Lea-힙 기반 private 할당기와 그 C++ STL 래퍼) 이고, 다른 하나는 락프리 자료구조 군 (legacy C와 modern C++ 두 세대가 한 개의 트랜잭션 회수 척추를 공유) 이다. 스토리지·쿼리·트랜잭션·PL이 모두 이 위에 올라간다.
- (KO) CUBRID DDL과 스키마 — 섹션 개요 — DDL과 스키마 레이어 라우터. DDL 파이프라인은 SM_TEMPLATE를 거쳐 디스크 카탈로그(catcls 시스템 클래스)에 기록되고, 인메모리 SM_CLASS 그래프를 다시 만든다. 그렇게 굳어진 스키마가 이후의 모든 DML과 플랜 컴파일을 통제하는 권한·트리거·통계와 맞물린다.
- (KO) CUBRID 국제화 — 섹션 개요 — i18n 묶음의 라우터 문서. 모든 문자열 연산자, 모든 비교, 모든 날짜 산술이 거치는 두 가지 횡단 기본 요소(charset+collation, timezone)만 다룬다. 두 모듈 모두 외부 표준 데이터(LDML / IANA tzdata)를 빌드 시 공유 라이브러리로 컴파일해 두고 서버 부팅 시 dlopen, 실행 중에는 작은 고정 폭 ID로만 노출하는 동일한 아키텍처 패턴을 따른다.
- (KO) CUBRID 절차적 언어 패밀리 — 섹션 개요 — 외부 JVM 프로세스 cub_pl 하나가 JavaSP와 PL/CSQL 두 언어 프론트엔드를 모두 호스팅한다. 두 언어는 같은 전송 채널을 타고, 같은 카탈로그를 공유하며, 같은 실행기로 디스패치된다.
- (KO) CUBRID 쿼리 처리 — 섹션 개요 — CUBRID code-analysis 트리에서 가장 큰 묶음의 라우터. SQL 텍스트가 행으로 풀려 나오는 길을 프론트엔드(파서, 의미 검사, 재작성), 미들엔드(비용 기반 옵티마이저, XASL 생성기, XASL 캐시), 백엔드(실행기, 스캔 매니저, list file)로 묶어 두고, 런타임 도구(술어 평가기, 스칼라 함수, external sort, 후처리, hash join, 메모이제이션)와 특수 기능(파티션, 커서, serial, 병렬 쿼리)까지 19개 세부 문서를 짚어 준다.
- (KO) CUBRID 복제와 HA — 섹션 개요 — replication-ha 묶음의 라우터. WAL 위에 얹은 논리 로그 스트리밍으로 동작하는 primary/standby 복제, 전역 합의 없이 노드별로 따로 매기는 점수로 master를 가르는 리더 선출, 그리고 같은 보조 로그 스트림에 함께 올라타는 CDC를 한자리에서 본다.
- (KO) CUBRID 서버 아키텍처 — 섹션 개요 — CUBRID 서버 계층의 프로세스 차원 그림. JDBC/CCI 클라이언트가 `cub_broker` 리스너에 닿고, 포크된 `cub_cas` 워커가 CSS 프레임 NRP 트래픽을 `cub_server`로 프록시한다. 서버 안에서는 레거시 워커 풀이나 CBRD-26177 NG 재설계가 굴리는 워커 스레드가 클라이언트별 `SESSION_STATE`에 도달해 로케이터 OID 워크스페이스를 거쳐 스토리지로 들어가고, 그 모두가 sysparam·에러·모니터링이라는 횡단 인프라 위에서 돈다.
- (KO) CUBRID 스토리지 엔진 — 섹션 개요 — 스토리지 엔진 묶음의 라우터. OS 파일(볼륨 / 섹터 / 페이지)에서 페이지 버퍼와 더블 라이트 버퍼까지 이어지는 계층 스택, 페이지 위에 자리 잡는 레코드 구조(heap, B+Tree, extendible hash)와 그 오버플로 체인, 본 데이터 볼륨 바깥으로 빠지는 길(호스트 파일시스템의 LOB), 그리고 페이지 단위 암호화 계층까지 — `subcategory: storage-engine`에 속한 9개 세부 문서가 어떻게 맞물리는지와 어떤 순서로 읽는 것이 가장 자연스러운지를 정리한다. 한때 이 섹션에 있었던 AREA 슬랩 풀은 `cubrid-overview-base-infra.md`로 옮겨 갔다. 어떤 계층이든 다 가져다 쓸 수 있는 메모리 할당자 부품이지 디스크에 자취를 남기는 의미의 스토리지 계층이 아니기 때문이다.
- (KO) CUBRID 시스템 카탈로그 — 섹션 개요 — system-catalog 묶음의 라우터 문서. 엔진의 SQL에서 보이는 자기-기술 표면을 다루는 두 문서: cubrid-system-catalog-classes는 *정적* 표면(_db_class, _db_attribute, _db_index, ...과 그 위에 얹힌 22개 시스템 뷰를 정의하고 설치하는 데이터 주도 프레임워크), cubrid-show-commands는 *동적* 표면(SHOW 명령이 서버 런타임 상태 — 스레드, 페이지 버퍼, 로그 헤더, 트랜잭션 테이블, 락 — 위의 가상 스캔으로 다시 쓰여 실행되는 흐름).
- (KO) CUBRID 트랜잭션과 복구 — 섹션 개요 — txn-recovery 묶음의 라우터. CUBRID가 ACID를 어떻게 풀어내는지 — 격리는 MVCC + 락 매니저로, 원자성과 영속성은 로그 매니저 + prior list + 체크포인트 + 복구 매니저(+ 스토리지 쪽 DWB)로 — 그리고 같은 도구를 distributed commit (2PC), 시간 여행(flashback), 시점 복구(backup-restore)로 이어 가는 흐름을 정리하며, 그 11개 세부 문서가 어디서 무엇을 다루는지를 가리킨다.
- (KO) CUBRID 읽기 경로 — 저장 프로시저 호출이 끝에서 끝까지 어떻게 실행되는가 (JavaSP/PL/CSQL과 임베디드 SQL 콜백) — 한 건의 `CALL my_sp(...)` — 본문이 JDBC로 임베디드 `SELECT`을 발행하는 JavaSP — 가 JDBC `CallableStatement.execute`에서 출발해, cub_broker → cub_cas → cub_server의 CALL 문 컴파일을 거치고, `cubpl::executor::request_invoke_command`가 `SP_CODE_INVOKE`을 cub_pl로 실어 보내고, ListenerThread/ExecuteThread 디스패치를 지나, 리플렉티브 TargetMethod.invoke로 사용자 코드의 CUBRIDServerSidePreparedStatement 임베디드 SELECT가 실행되고, JVM이 METHOD_CALLBACK_QUERY_PREPARE을 SP_CODE_INTERNAL_JDBC 봉투에 담고, 서버의 response_callback_command가 callback_prepare/_execute/_fetch을 통해 일반 컴파일·실행 파이프라인을 재귀적으로 다시 들어가는 — METHOD_MAX_RECURSION_DEPTH = 15 한도 안에서 — 그리고 다시 돌아오는 흐름. 약 15개의 상세 문서(broker, network-protocol, server-session, transaction, parser, semantic-check, optimizer, xasl-generator, query-executor, list-file, mvcc, pl-javasp, pl-plcsql, pl-server-bridge, scan-manager)를 한 여정으로 엮어, cubrid-rpath-select.md / cubrid-rpath-write.md / cubrid-rpath-recovery.md과 함께 rpath 시리즈의 네 번째 자리를 차지한다.
- (KO) CUBRID 읽기 경로 — 서버 재시작이 어떻게 복구되는가 — cub_server 콜드 스타트를 따라가는 읽기 경로. 프로세스가 부팅되어 볼륨을 열고 복구로 디스패치하면, DWB가 찢어진 페이지를 손본 뒤, 로그 헤더가 가리키는 가장 최근 체크포인트를 단서로 ARIES analysis/redo/undo가 커밋된 일은 되살리고 loser는 지운다. 그 뒤 vacuum과 HA가 따라잡고, 마지막으로 네트워크 리스너가 클라이언트 트래픽을 받기 시작한다.
- [KO] CUBRID 읽기 경로 — SELECT 한 문장의 끝에서 끝까지 — SELECT 한 문장의 전체 여정 — JDBC에서 시작해 브로커, cub_cas, cub_server를 거쳐 파서, 의미 검사, 재작성, 옵티마이저, XASL 생성기, XASL 캐시, 실행기, 스캔 매니저, heap/B+Tree, 술어 평가기, MVCC 가시성, list-file, 커서, 다시 브로커, JDBC까지. 스무 편 가까운 세부 문서를 한 줄기로 꿰어 본 종합 읽기 경로.
- (KO) CUBRID 쓰기 경로 — INSERT와 COMMIT의 끝까지 — INSERT INTO t VALUES (...)에 이어 COMMIT까지 — 파싱, locator force fan-in, MVCC 스탬프가 찍히는 heap slotted-page 쓰기, B+Tree 키||OID 삽입, locator의 제약·FK 검사, BEFORE/AFTER 트리거, prior-list로 들어가는 WAL, 옵션인 복제 레코드, locator를 거친 X-lock, log_commit_local의 강제 flush, log-flush 데몬 fsync, 그 뒤 DWB로 흘러가는 더티 페이지 flush, 마지막으로 죽은 버전을 거두는 vacuum까지.
Base / Infrastructure (8)
- (KO) CUBRID AREA 할당기 — 동일 크기 객체에 자리를 떼어 주는 슬랩(slab) 풀 — CUBRID의 AREA 모듈이 같은 크기의 객체(DB_VALUE, TP_DOMAIN, OBJ_TEMPLATE, DB_OBJLIST, set 객체 …)를 어떻게 슬랩으로 잘라 내주는지 본다. 256개짜리 BLOCKSET 배열을 사슬로 잇고 각 블록 앞에 lock-free 비트맵을 두며, 흔한 경우는 hint 포인터 한 번으로 끝낸다.
- (KO) CUBRID 락프리 비트맵 — 스레드 인덱스와 슬롯 풀을 위한 청크 어토믹 할당기 — CUBRID가 작은 정수 슬롯을 동시에 할당하고 회수하는 방식 — `std::atomic<unsigned int>` 워드의 청크 배열, 두 청킹 스타일(one-chunk full-usage, list-of-chunks usage-bounded), CAS 비트 플립, 그리고 SERVER_MODE에서 `get_entry`마다 어토믹으로 bump되는 라운드로빈 시작 힌트.
- (KO) CUBRID 락프리 원형 큐 — 슬롯별 블록 플래그를 가진 고정 용량 MPMC 링 — CUBRID가 핫 패스에서 스레드 사이로 작업을 넘기는 방식 — 두 커서 어토믹과 슬롯별 블록 플래그 워드를 가진 고정 용량 multi-producer multi-consumer 링 버퍼. vacuum 로그 블록 디스패치, 페이지 버퍼 victim 핸드오프, CDC log-info 전달에 쓰인다.
- (KO) CUBRID 락프리 free list — 백버퍼 블록 할당기를 가진 타입드 노드 풀 — CUBRID가 락프리 노드를 연산 사이에 재활용하는 방식 — 단일 available 스택을 가진 타입드 `freelist<T>`. available이 비면 동시 claimer가 할당 경쟁을 하지 않도록 한 블록 백버퍼가 늦지 않게 swap-in되며, 페이로드 타입이 구현하는 `on_reclaim` 훅과 명시적으로 문서화된 ABA 윈도우(백버퍼 시간으로 좁아짐)를 가진 pop 경로를 제공한다.
- (KO) CUBRID 락프리 해시 맵 — Legacy C, Modern C++, 다리, 그리고 소비자들 — CUBRID의 주력 동시 연관 테이블 구현 방식 — Harris–Michael 체이닝 해시(엔트리별 뮤텍스 선택 가능). 두 구현(legacy C `lf_hash_*`와 modern C++ `lockfree::hashmap<K,T>`)이 `cubthread::lockfree_hashmap<K,T>`로 다리가 놓여 있고, init 시점에 `PRM_ID_ENABLE_NEW_LFHASH` 값으로 `m_type ∈ {OLD, NEW}`이 정해지며, `lf_entry_descriptor`가 두 코드 경로를 같은 엔트리 레이아웃으로 운전하는 공유 타입이다.
- (KO) CUBRID 락프리 기본 자료구조 — 개요, 두 세대, 회수 척추 — CUBRID의 락프리 자료구조 지도 — legacy C `lock_free.{h,c}` 계열과 신형 C++ `lockfree::*` 네임스페이스가 어떻게 공존하는지, 그리고 모든 자료구조가 한 개의 트랜잭션 기반 회수 척추 위에 어떻게 올라와 있는지를 정리한다.
- (KO) CUBRID 락프리 트랜잭션 회수 — system, table, descriptor, address marker — CUBRID가 락프리 자료구조에서 은퇴된 노드를 안전하게 회수하는 방식 — 자료구조마다 트랜잭션 ID 한 개, 스레드별 디스크립터가 읽기를 브래킷, 주기적 최소 활성 ID 스캔이 더 이상 어떤 리더도 닿을 수 없는 시점을 free list에 알려준다.
- (KO) CUBRID Private 할당기 — 스레드별 Lea 힙, C++ STL 할당기 래퍼, 빌드 모드별 라우팅 — 스레드별 Lea-힙 아레나(Doug Lea의 `dlmalloc`을 `customheaps` 아래 자체 사본으로 가져와 `THREAD_ENTRY`마다 한 개씩 등록). `db_private_alloc / _free / _realloc` 매크로가 SERVER_MODE에서는 스레드의 힙으로, CS_MODE에서는 클라이언트 워크스페이스로, SA_MODE에서는 `PRIVATE_MALLOC_HEADER` 태깅 디스패치로 갈래를 나눈다(Lea 힙에서 왔는지 워크스페이스에서 왔는지 free 시점에 알아낼 수 있게). C++ STL 래퍼 `cubmem::private_allocator<T>`로 STL 컨테이너도 같은 힙을 쓰게 만들고, `private_unique_ptr<T>`와 `PRIVATE_BLOCK_ALLOCATOR`가 편의 계층을 채우며, `switch_to_global_allocator_and_call`이 스레드를 넘는 또는 프로세스 전역 할당이 필요할 때의 비상구다.
Storage Engine (10)
- (KO) CUBRID B+Tree — 노드 배치, latch-coupling, unique 키 접미부 — CUBRID가 B+Tree 인덱스를 어떻게 깔아 두는지 본다. 슬롯 페이지로 짠 노드, key||OID로 묶인 키, non-leaf와 leaf의 갈라진 레코드 모양, OID 목록을 받아 주는 overflow 페이지가 한 짝이고, 그 위에서 insert와 delete와 scan이 latch-coupling 규율을 따라 어떻게 트리를 걷는지, unique 제약은 OID 접미부 단계에서 어떻게 검사되는지가 다른 한 짝이다.
- CUBRID B+Tree — 코드 수준 심층 분석 — CUBRID B+Tree 구현의 코드 수준 심층 분석 — 노드·레코드 레이아웃, 래치 커플링 하강, 새 키 삽입과 유니크 키 OID 처리, 노드 분할·병합, 객체 삭제와 VACUUM 협조, LSA 기반 범위 스캔 재개, 유니크/PK-FK 제약 강제, 복구와 온라인 인덱스 적재를 소스 코드 발췌와 함께 추적한다.
- (KO) CUBRID Disk Manager와 File Manager — 볼륨, 섹터, 파일, 페이지 할당, 그리고 볼륨 확장 — CUBRID는 OS 파일 = 볼륨, 64페이지 묶음 = 섹터(디스크 매니저의 할당 단위), 섹터들의 논리적 묶음 = 파일, 16KB 블록 = 페이지(I/O 단위)라는 네 단의 계층을 받침으로 깔아 둔다. 디스크 캐시는 영구와 임시 두 목적을 갈라 두고, 두 단계 섹터 예약과 적응형 볼륨 확장을 굴린다. 파일 매니저는 받아 둔 섹터를 세 가지 확장형 데이터 표(Partial / Full / User)로 페이지마다 풀어 준다.
- (KO) CUBRID Double Write Buffer — 페이지 버퍼와 데이터 파일 사이의 torn-page 방어선 — CUBRID는 모든 더티 데이터 페이지를 home 자리로 보내기 전에 먼저 정해진 크기의 순차 DWB 볼륨에 staging해 두고 fsync를 한 번 매단다. 그 뒤에야 home 쓰기를 한다. 그 덕에 home 쓰기 도중 크래시가 나도 디스크 위에는 늘 멀쩡한 home 페이지나 멀쩡한 DWB 사본 가운데 하나가 남고, 복구가 그것을 받아 쓴다.
- (KO) CUBRID Extendible Hash — 디렉토리 더블링과 local depth 로 동적으로 자라는 디스크 상주 해시 파일 — CUBRID이 Fagin 외 (1979) 의 extendible hashing 을 페이지 버퍼 위에 어떻게 얹어 두는지 본다. EHID 가 가리키는 디렉토리 파일, 버킷이 넘치면 두 배로 자라는 포인터 배열, 슬롯 페이지에 binary search 를 얹은 버킷, 버킷별 local depth, system-op 으로 묶인 split / merge, redo 와 logical undo 를 모두 가진 RVEH_* WAL 레코드, 그리고 엔진이 실제로 부르는 네 자리 — 클래스 이름 → OID, 카탈로그 → repr-id, UPDATE/DELETE OID 중복 제거, 해시 스캔 — 까지 정리한다.
- (KO) CUBRID Heap Manager — 슬롯 페이지, 레코드 레이아웃, 연산, MVCC, 캐시 — CUBRID이 가변 길이 레코드를 슬롯 페이지에 어떻게 저장하는지, INSERT / UPDATE / DELETE / READ 가 9 개의 레코드 타입을 거치며 어떻게 흘러가는지, MVCC 버전 체인이 레코드 헤더에 어떻게 박혀 있는지, 그리고 핫 패스를 짧게 유지하는 5 개의 캐시까지 본다.
- (KO) CUBRID LOB — 외부 파일 저장, locator 라이프사이클, 그리고 트랜잭션 정리 — CUBRID 이 BLOB / CLOB 데이터를 데이터 볼륨 바깥의 파일로 저장하고, locator URI 로 이름을 짓고, TDES 위의 red-black 트리로 트랜잭션 단위 상태를 추적하며, commit / rollback 한 곳에서 파일시스템을 정리하는 골격을 본다.
- (KO) CUBRID Overflow File — Heap big-record 와 B+Tree overflow-OID 페이지 사슬 — CUBRID 이 슬롯 힙 페이지에 다 들어가지 않는 거대 레코드와 B+Tree 리프에 다 들어가지 않는 OID 리스트를 어떻게 별도 파일과 페이지 사슬로 흘려 보내는지 본다. 두 갈래의 페이지 형식이 한 substrate (`FILE_MULTIPAGE_OBJECT_HEAP` / `FILE_BTREE_OVERFLOW_KEY` / 트리별 OID overflow) 위에서 어떻게 동거하는지, 그리고 사슬을 crash-safe 하게 유지하는 WAL 규율을 함께 본다.
- (KO) CUBRID Page Buffer Manager — BCB, 3-zone LRU, private quota, direct victim 인계, custom latch — CUBRID 가 디스크 페이지를 BCB (Buffer Control Block) 로 메모리에 매핑하고, 3-zone LRU 와 worker 별 private / shared 리스트로 victim 을 결정하고, lock-free 큐를 통해 victim 을 대기 중인 스레드에게 직접 넘기고, 각 BCB 를 사용자 정의 read / write / flush latch 로 보호하는 방식을 본다.
- (KO) CUBRID TDE — 마스터 키로 래핑된 DEK 위에서 동작하는 투명 페이지 단위 암호화 — CUBRID 가 투명 데이터 암호화 (TDE) 를 어떻게 구현하는지 본다. 두 단계 키 계층 (마스터 키가 데이터베이스별 세 개의 데이터 키를 래핑), AES-256-CTR 또는 ARIA-256-CTR 을 페이지별 nonce (영구 페이지는 LSA, 임시 페이지는 원자 카운터, 로그 페이지는 논리 페이지 ID) 와 함께 사용, 페이지 버퍼와 로그 페이지 버퍼에서의 flush 시 암호화 후크와 read 시 복호화 후크, 데이터베이스 외부에 보관되는 별도의 ``<db>_keys`` 마스터 키 파일, 그리고 파일 단위 (테이블스페이스 형식의) TDE 플래그가 각 페이지의 ``pflag`` 비트로 전파되는 구조.
Transaction & Recovery (14)
- (KO) CUBRID 2PC — 2단계 commit과 in-doubt 복구 — CUBRID이 2단계 commit을 어떻게 구현하는지 — coordinator/participant FSM이 `LOG_2PC_EXECUTE` enum으로 디스패치되고, prepared 상태가 충돌을 견디는 로그 레코드로 보존되며, ARIES analysis 패스 도중에 in-doubt 복구가 처리된다. 내부 nested coordinator와 XA 구동 분산 트랜잭션 모두를 다룬다.
- (KO) CUBRID 백업과 복원 — 온라인 볼륨 백업, LSA 마커, 그리고 시점 복구 — 데이터 볼륨을 스냅샷으로 잡고 `start_lsa` 와 다음 체크포인트 사이의 로그 레코드를 함께 묶어 보관한 뒤, 복원 시점에 사용자가 지정한 정지 시각까지 로그를 forward로 재생해 임의 시점으로 복구하는 온라인 물리 백업 엔진.
- (KO) CUBRID Checkpoint — 퍼지 ARIES 체크포인트 프로토콜, 액티브 트랜잭션 스냅샷, 그리고 복구 anchor LSA — CUBRID의 퍼지 ARIES 체크포인트 프로토콜이 재시작 복구 작업량을 어떻게 묶어 두는가 — 주기적 데몬이 액티브 트랜잭션 스냅샷과 페이지 버퍼 dirty list에서 끌어낸 redo-LSA 힌트를 들고 `LOG_START_CHKPT` / `LOG_END_CHKPT` 쌍을 발행하고 `log_Gl.hdr.chkpt_lsa`을 전진시키면, 다음 analysis 패스가 그 아래의 모든 것을 건너뛸 수 있게 된다.
- (KO) CUBRID Flashback — 트랜잭션 요약과 그 트랜잭션의 로그 단위 재생 — CUBRID이 사용자에게 "T1..Tn 트랜잭션이 시간 A와 B 사이에 무엇을 했는가" 라는 질문에 답하기 위해 어떻게 로그를 두 단계 — 트랜잭션별 요약, 그리고 선택된 트랜잭션의 상세 loginfo — 로 따라 가는지에 대한 분석. CDC와 같은 entry 형식을 공유하지만, archive 로그를 대상으로 동작한다.
- (KO) CUBRID Lock Manager — 다중 단위 잠금, 모드 변환, 그리고 데드락 탐지 — CUBRID가 OID 단위로 계층적 잠금을 부여·변환·해제하고, 트랜잭션 waits-for graph로 데드락을 탐지하는 방식.
- CUBRID Lock Manager — 코드 수준 심층 분석 — CUBRID lock manager 내부 구현의 코드 수준 심층 분석 — 자료구조, 획득, 변환, 해제, 에스컬레이션, 데드락 감지, 특수 경로, RR/SERIALIZABLE first-updater-wins 충돌 검사를 소스 코드 발췌와 함께 추적한다.
- (KO) CUBRID 로그 매니저 — WAL, LSA, 그리고 append 규율 — CUBRID이 로그 레코드를 어떻게 배치하고 LSA로 이름을 붙이는지, 그리고 메모리에 머무는 prior 리스트와 페이지 버퍼가 어떤 단계로 디스크 플러시까지 이어지는지 — 결국 ACID-D와 복구가 의지하는 받침대를 어떻게 만드는지에 대한 분석.
- (KO) CUBRID Log Manager — 코드 수준 심층 분석 — CUBRID log manager 내부 구현의 코드 수준 심층 분석 — LSA 주소지정과 로그 페이지 레이아웃, 로그 레코드 타입 계층, prior-list append 파이프라인, flush와 group commit, 페이지 버퍼에 대한 WAL 규칙 강제, 로그 페이지 버퍼, archiving을 소스 코드 발췌와 함께 추적한다.
- (KO) CUBRID MVCC — 스냅샷 생성, 활성 MVCCID 추적, 그리고 vacuum 협조 — CUBRID가 어떻게 MVCCID를 부여하고, 트랜잭션별 스냅샷을 만들며, vacuum과 협조해 죽은 버전을 회수하는지.
- CUBRID MVCC — 코드 수준 심층 분석 — CUBRID MVCC 내부 구현의 코드 수준 심층 분석 — 스냅샷과 레코드 헤더 자료구조, MVCCID 할당, 스냅샷 생성, satisfies-* 가시성 술어, 활성 MVCCID 추적, VACUUM 협조를 소스 코드 발췌와 함께 추적한다.
- (KO) CUBRID Prior List — 트랜잭션별 commit과 로그 writer을 분리하는 락프리 producer 측 WAL 큐 — CUBRID이 트랜잭션별 로그 레코드 producer 경로를 글로벌 로그 플러시 핫스폿에서 어떻게 떼어 내는가 — 짧게 잡혔다 풀리는 단일 mutex로 보호되는 단방향 연결 LOG_PRIOR_NODE 큐, 그리고 로그 플러시 데몬이 로그 critical section 안에서 그 큐를 끌어내는 구조. 그 결과 group commit이 큐의 batching에서 자연스럽게 떨어진다.
- (KO) CUBRID 복구 매니저 — ARIES 세 패스 재시작 — CUBRID이 ARIES 세 패스 재시작(analysis, redo, undo)을 어떻게 구현하는지, 어떻게 가장 최근 체크포인트 LSA를 닻으로 삼고 record-type별 RV_fun[]으로 복구를 디스패치하며, VPID 기반 worker 풀로 redo를 어떻게 병렬화하는지에 대한 분석.
- (KO) CUBRID 트랜잭션 — TDES, 격리 수준, 그리고 savepoint — CUBRID이 트랜잭션을 어떻게 모델링하고 — TDES descriptor가 서버 전체 트랜잭션 테이블 위에서 살아 가는 방식 — 그리고 격리 수준 결정을 SI / lock 디스패치로 어떻게 갈라 보내며, 부분 작업의 경계를 시스템 op과 savepoint로 어떻게 중첩시키는지에 대한 분석.
- (KO) CUBRID Vacuum — 로그 재생을 통한 죽은 MVCC 버전 회수 — CUBRID이 죽은 MVCC 버전을 어떻게 회수하는지에 대한 분석. WAL을 고정 크기 블록으로 잘라 두고, 가장 오래된 가시 MVCCID 워터마크 아래에 있는 블록을 master/worker 풀이 하나씩 소비한다. 별도로 dropped-file 표를 둬 사라진 파일을 추적한다.
Query Processing (20)
- (KO) CUBRID Cursor — 서버 측 list-file을 한 페이지씩 끌어오는 클라이언트 fetch 핸들과 holdable / scroll 상태 관리 — CUBRID는 ANSI 스타일 fetch 커서를 클라이언트 측 `CURSOR_ID`로 구현한다. 이 핸들은 서버에 살아 있는 `QFILE_LIST_ID`에 매달려 한 번에 하나의 네트워크 페이지씩 `qfile_get_list_file_page` 라운드트립을 통해 끌어오고, 길이 prefix가 박힌 packed row를 `DB_VALUE`로 풀어 내며, 역참조(dereference) 대상이 되는 OID를 페이지 단위 벡터로 prefetch한다. 브로커가 `RESULT_HOLDABLE`을 요청하면 COMMIT을 넘어 살아남는데, 이는 세션 범위에 매달린 holdable cursor 리스트를 통해 실현된다.
- (KO) CUBRID External Sort — 런 생성, 다중 병합, 그리고 정렬 기반 substrate — CUBRID이 메모리에 들어가지 않는 입력을 어떻게 디스크 기반으로 정렬하는지에 대한 분석. 두 단계 — replacement-selection 풍의 런 생성기(`sort_inphase_sort`)와 `FILE_TEMP` 위에서 도는 균형 k-way 병합기(`sort_exphase_merge`) — 가 콜백 기반 단일 진입점(`sort_listfile`)을 통해 ORDER BY / sort GROUP BY / DISTINCT, B+Tree 벌크 로드, 병렬 인덱스 빌드에 동일하게 노출된다.
- (KO) CUBRID Hash Join — 빌드/프로브 패턴, hash-scan 프리미티브, 그리고 spill 동작 — CUBRID가 hash join을 어떻게 구현하는지 — `query_hash_join.c`의 빌드/프로브 드라이버가 `query_hash_scan.c`의 `HASH_LIST_SCAN` 프리미티브를 재사용하고, `max_hash_list_scan_size` 예산에 따라 세 가지 테이블 레이아웃(인메모리 `mht_hls`, 하이브리드 인메모리-인덱스+파일-튜플, 확장형 `FHS` 해시 파일) 중 하나를 고르며, 빌드 측이 spill되면 grace 스타일 equi-hash partitioning으로 에스컬레이션하고, optimizer는 의도적으로 보수적인 `qo_examine_hash_join` 게이트(키는 `USE_HASH` 힌트)를 통해 인입을 허용한다.
- (KO) CUBRID JSON_TABLE — JSON 문서를 가상 행으로 풀어내는 테이블 함수 — 하나의 C++ 스캐너가 파서가 만든 `cubxasl::json_table::node` 트리를 커서 스택으로 깊이 우선 탐색하면서, NESTED PATH마다 `db_json_iterator_*` 로 입력 JSON을 펼치고 잎(leaf)에서 행을 방출한다. JSONPath와 컬럼 스펙을 합쳐서 executor의 `scan_next_scan` 루프가 직접 끌어다 쓸 수 있는 행 소스(row source)로 승격시키는 SCAN_TYPE이다.
- (KO) CUBRID List-File — 연산자 사이를 흐르는 spillable 튜플 스트림과 머터리얼라이즈 기반 — CUBRID는 머터리얼라이즈가 필요한 모든 튜플 스트림 — 서브쿼리 결과, 정렬 출력, 해시 빌드 측, GROUP BY 누적, 최종 질의 결과 — 을 단일 `QFILE_LIST_ID`라는 페이지 연결 추상으로 통합한다. 그 아래에는 쿼리당 하나의 `QMGR_TEMP_FILE`이 깔려 있고, 작은 결과는 in-memory `membuf[]` 위에 머무르며 압력이 가해질 때 비로소 `FILE_TEMP`으로 spill된다. 실행기와 스캔 계층은 모두 동일한 open / add / scan / close 인터페이스 위에서 데이터를 흘려보낸다.
- [KO] CUBRID 병렬 질의 — Heap Scan, Hash Join, Query Execute에 걸친 Intra-Query 병렬성 — 단 하나의 전역 병렬-질의 워커 풀, 페이지 수를 키로 하는 `compute_parallel_degree()` 정책, 예약 핸들 `worker_manager`, 그리고 세 종류의 연산자별 오케스트레이터 — heap-sector 블록 범위 분할에 mergeable-list / xasl-snapshot / buildvalue 결과 핸들러를 얹은 `parallel_heap_scan::manager`, 공유 파티션 fan-out 후 파티션별 build+probe를 수행하는 `parallel_query::hash_join::{build_partitions,execute_partitions}`, `BUILDLIST_PROC` / `BUILDVALUE_PROC` / `UNION_PROC` / `HASHJOIN_PROC` / `MERGELIST_PROC` 의 비상관 aptr fan-out을 담당하는 `parallel_query_execute::query_executor` — 이 모두가 \parallel-query\ 라는 이름의 `cubthread::worker_pool` 위에 올라타고, XASL 안으로는 `px_executor`, `m_px_orig_thread_entry`, 그리고 `S_PARALLEL_HEAP_SCAN` 스위치 가지를 통해 스며든다.
- (KO) CUBRID Parser — Flex/Bison 파이프라인, PT_NODE 트리, 그리고 파서 메모리 모델 — CUBRID가 SQL 텍스트를 어떻게 `PT_NODE` 파스 트리로 바꾸는가. 단일 버퍼 `YY_INPUT`이 구동하는 Flex 렉서, GLR Bison 문법이 reduce 액션을 통해 `parser_new_node`를 호출하며 트리를 쌓아 올리는 방식, 노드 타입별 자식 레이아웃을 세 개의 함수 포인터 배열(`pt_apply_f`, `pt_init_f`, `pt_print_f`)로 인코딩하는 다형 태그 유니언 `PT_NODE`, 그리고 트리 전체를 한 번의 호출로 해제할 수 있게 해 주는 `PARSER_CONTEXT` 단위 블록 할당자.
- (KO) CUBRID 파티셔닝 — Range/Hash/List 전략, 파티션 가지치기, 파티션별 실행 — 하나의 논리 테이블을 마스터 클래스 + N 개의 자식 클래스로 쪼개고, 파티션 규칙(range 경계, hash modulus, list 값) 을 마스터 클래스의 SM_PARTITION 에 새겨 두고, 서버 측 PRUNING_CONTEXT 로 옵티마이즈 시점에 파티션을 잘라 내고, INSERT/UPDATE 레코드를 목적 파티션 heap 으로 라우팅하고, 실행 시점에는 파티션별 scan list 를 한 줄로 풀어 흘려 보내는 CUBRID 의 구현.
- (KO) CUBRID 후처리 — 집계, 윈도우/분석 함수, 그리고 Sort vs Hash GROUP BY — CUBRID 질의 실행기가 정렬된(또는 해시로 누적된) 리스트 파일을 `qexec_groupby`와 `qexec_execute_analytic`을 통해 그룹·집계·윈도우 프레임 출력으로 변환하는 방법, 런타임에 sort 기반과 hash 기반 GROUP BY 사이를 어떻게 선택하는지, 그리고 해시 테이블이 `max_agg_hash_size`를 넘어설 때 외부 정렬로 폴백하는 메커니즘.
- (KO) CUBRID 질의 평가기 — PRED_EXPR 순회, regu_variable fetch, 그리고 행 단위 필터 엔진 — CUBRID가 끌어올린 한 튜플을 어떻게 통과/탈락 판정으로 바꾸는지 — `eval_pred`가 `T_PRED` 불리언 노드와 `T_EVAL_TERM` 리프로 구성된 `PRED_EXPR` 트리를 3치 논리(`V_TRUE` / `V_FALSE` / `V_UNKNOWN` / `V_ERROR`) 아래서 순회하고, 모든 리프가 호출하는 `fetch_peek_dbval`은 `REGU_VARIABLE::type` 태그(상수, 속성 fetch, 리스트 파일 위치, 산술식, 함수 호출, 호스트 변수, OID, list-id)로 디스패치되며, `eval_fnc`는 자주 등장하는 단일 형태 술어를 미리 골라 재귀 자체를 우회한다.
- (KO) CUBRID Query Executor — XASL 해석, 이터레이터 모델, 그리고 힙/인덱스 스캔 연산자 — CUBRID가 직렬화된 XASL 계획 트리를 Volcano 스타일 연산자 트리로 어떻게 해석하는가. `qexec_execute_mainblock_internal`이 `xasl->type`으로 분기하고, 균일한 open/next/close 루프로 `SCAN_ID` 연산자(heap, index, list, set, value, JSON, dblink, parallel-heap)를 구동하며, 결과를 XASL 단위 list file에 적재해 상위 연산자가 list scan으로 다시 읽어 들이는 방식.
- (KO) CUBRID Query Optimizer — 쿼리 그래프, 비용 모델, 조인 열거, 그리고 컴파일된 플랜 — 의미 검사를 마친 PT_NODE 가 어떻게 `QO_NODE`/`QO_SEGMENT`/`QO_TERM` 으로 구성된 `QO_ENV` 쿼리 그래프로 내려가는지, 2^N 크기 `join_info` 벡터 위에서 부분-탐색 후 전체-탐색 형태의 동적 계획법 조인 열거가 어떻게 동작하는지, System R 식 fixed-cpu/io + variable-cpu/io 4-슬롯 비용 모델이 각 후보를 어떻게 비교하는지, 마지막으로 살아남은 `QO_PLAN` 트리가 어떻게 서버로 전달되는 XASL 액세스-스펙 트리로 변환되는지를 따라간다.
- (KO) CUBRID 질의 재작성 — 최적화 이전의 트리 변환과 LIMIT 절 사례 연구 — CUBRID이 의미 검사 단계에서 LIMIT 절을 INST_NUM/ORDERBY_NUM/GROUPBY_NUM 술어로 어떻게 내리고, mq_rewrite에서 잔존 LIMIT를 다시 재작성하며, 그 내림이 CNF 변환·술어 축약·뷰 인라이닝·서브쿼리 평탄화·자동 파라미터화·계획 생성 시점의 multi-range LIMIT 최적화와 어떻게 맞물리는지.
- (KO) CUBRID 런타임 메모이제이션 — Subquery Cache, Filter-Predicate Cache, 그리고 XASL별 Memoize 헬퍼 — CUBRID는 행 단위 반복 작업의 군더더기를 제거하기 위해 세 개의 독립적 캐시를 둔다. 셋 다 같은 플레이북 — `DB_VALUE` 배열 해시 키, 메모리 예산 초과 시 fail-on-full, hit-ratio 가드 — 을 따르지만 수명 범위가 다르다. XASL별 `sq_cache` 는 비상관 스칼라 서브쿼리 결과, 서버 전역 BTID 단위 `fpcache` 는 역직렬화된 함수 인덱스 술어, 그리고 XASL별 `memoize::storage` 는 nested-loop join 안쪽 튜플 묶음을 각각 메모이즈한다.
- (KO) CUBRID 스칼라 함수 — 산술·문자열·수치·JSON·정규식·암호 연산자 프리미티브 — CUBRID의 스칼라 함수 라이브러리 — `arithmetic.c`, `numeric_opfunc.c`, `string_opfunc.c`, `query_opfunc.c`, `crypt_opfunc.c`, `string_regex_*` 군 — 가 regu-variable 평가기 아래에서 연산자 프리미티브 계층을 어떻게 구현하는지. `fetch_peek_arith`가 `OPERATOR_TYPE`별 switch로 `qdata_*_dbval` 산술 디스패처를 호출하고, 그 디스패처는 다시 `DB_TYPE`별로 `qdata_add_int_to_dbval` 같은 단형(monomorphic) 변형으로 갈라진다. `qdata_evaluate_function`은 `FUNC_CODE`별 switch로 JSON / 정규식 / list / generic 핸들러로 분기한다. `DB_NUMERIC`에 대한 BCD 산술은 `numeric_db_value_{add,sub,mul,div}`가 바이트 단위 binary digit를 `numeric_{add,sub,mul,long_div}`에서 훑는 방식이다. 콜레이션을 의식하는 문자열 연산(`db_string_substring`, `db_string_lower`, `db_string_like`, `db_string_concatenate`)은 `INTL_CODESET`을 존중하며, 정규식 façade(`cubregex::compile / search / count / instr / replace / substr`)는 `regexp_engine` 시스템 파라미터에 따라 RE2 또는 `std::regex`로 라우팅된다.
- (KO) CUBRID Scan Manager — SCAN_ID 디스패치, open/next/close 프로토콜, 그리고 액세스 메서드 카탈로그 — 하나의 다형(polymorphic) `SCAN_ID` 핸들, switch 기반의 open/start/next/end/close 프로토콜, 그리고 `SCAN_TYPE`별로 heap·B+Tree·list-file·set·value·JSON-table·dblink·show·parallel-heap· method 스캔으로 분기하는 디스패치 — 즉 executor의 `scan_next_scan` 루프가 그 위에 곧바로 얹혀 있는 액세스 메서드 카탈로그.
- (KO) CUBRID 의미 검사 — 이름 해석, 타입 검사, 상수 접기, 그리고 문 종별 검증 — CUBRID 의 `pt_check_with_info` 드라이버가 갓 파싱된 `PT_NODE` 트리를 어떻게 분석된, 형이 굳은, 상수가 접힌, CNF 정규화된 중간 표현으로 다듬는지 — 이름 해석, WHERE-절 집계 검사, 호스트 변수 치환, 문 종별 `pt_semantic_check_local`(내부에서 `pt_semantic_type` 을 부르며 형 평가와 상수 접기를 수행), 마지막으로 옵티마이저 진입 전의 `pt_cnf` 술어 정규화까지 — 네 패스의 사슬을 따라간다.
- (KO) CUBRID SERIAL — 카탈로그 상태와 캐시 값을 가진 시퀀스 / AUTO_INCREMENT 서브시스템 — CUBRID이 모든 시퀀스를 `_db_serial` 시스템 클래스의 한 행으로 저장하고, 객체 단위 X-lock 아래에서 클라이언트 측 캐싱을 선택적으로 끼워 값을 전진시키며, 같은 메커니즘을 합성된 `<class>_ai_<attr>` serial을 통해 AUTO_INCREMENT 컬럼 구동에도 그대로 재사용하는 방식.
- (KO) CUBRID XASL Cache — SQL 해시 텍스트의 SHA-1 키, RT 재컴파일, 클래스별 무효화로 동작하는 plan cache — CUBRID는 두 번째 실행부터 parse → semantic-check → optimize → XASL-generate 단계를 건너뛰기 위해, 재작성된 SQL의 SHA-1 해시와 엔트리별 `time_stored` 를 키로 갖는 서버 전역 latch-free 해시맵을 유지한다. 단일 32비트 `cache_flag` 하나로 fix count와 상태 플래그를 함께 관리하며, RT(recompile threshold) 검사로 통계 drift를 감시하고, DDL이 발생하면 `xcache_remove_by_oid` 가 클래스별 OID 의존 엔트리를 무효화한다.
- (KO) CUBRID XASL Generator — 최적화된 plan 트리를 서버 측 실행 트리로 컴파일하기 — 이름 해소(name-resolved)와 타입 검사를 마친 PT_NODE 와 옵티마이저의 QO_PLAN 트리가 어떻게 서버가 실제로 실행하는 절차적 XASL_NODE 트리로 컴파일되는가. `gen_outer`/`gen_inner` 재귀 워크, 한 노드 안에 서브쿼리와 조인을 숨기는 `aptr/dptr/scan_ptr` 슬롯, REGU_VARIABLE / ACCESS_SPEC / OUTPTR_LIST 하위 IR, 그리고 트리 전체를 서버로 실어 보내는 `xts_*` offset-table 직렬화까지 다룬다.
DDL & Schema (6)
- [KO] CUBRID 인증과 권한 — 사용자, 비밀번호, 권한 그리고 시스템 카탈로그 — CUBRID이 사용자, 비밀번호, 그리고 객체별 권한을 `db_user`, `db_password`, `_db_auth`, `db_authorization` 의 MOP 키 행으로 어떻게 모델링하는지, 그리고 `au_login`, `au_fetch_class`, 클래스별 `AU_CLASS_CACHE` 가 SELECT 시점의 권한 lookup 을 어떻게 하나의 비트마스크 검사로 압축하는지에 대한 분석.
- (KO) CUBRID 카탈로그 매니저 — disk representation, 시스템 클래스, 통계 — CUBRID이 클래스별 disk representation 과 통계를 전용 카탈로그 (CTID) 에 어떻게 저장하는지, 그리고 그 옆에 사용자에게 보이는 시스템 클래스(`_db_class`, `_db_attribute`, `_db_index` 등)를 고정 OID의 root class로부터 어떻게 부트스트랩하는지에 대한 분석.
- (KO) CUBRID Class Object — 인메모리 스키마, attribute, method, partition, 그리고 OODB 유산 — CUBRID이 데이터베이스 클래스를 클라이언트 측 workspace 메모리 위의 `SM_CLASS` 그래프로 어떻게 구체화하는지 — attribute, method, partition, 제약, 트리거 — 그리고 `catcls_*` 가 그 그래프와 cubrid-catalog-manager.md 에 기술된 디스크 카탈로그 레코드 사이를 어떻게 매개하는지에 대한 분석.
- (KO) CUBRID DDL 실행 — 파스 트리에서 카탈로그와 클래스 객체 캐시 무효화까지의 스키마 변경 파이프라인 — CUBRID이 트랜잭셔널 DDL을 어떻게 구현하는가. `do_statement`이 `PT_CREATE_ENTITY`/`PT_ALTER`/`PT_DROP`을 `execute_schema.c`의 `do_create_entity` / `do_alter` / `do_drop`으로 디스패치하고, `dbt_create_class` / `smt_def_class`로 `SM_TEMPLATE`을 빌드한 뒤 `sm_finish_class → update_class → install_new_representation`을 거쳐 마무리하고, `locator_add_class` + `catcls_insert_catalog_classes`로 영구화하고, `sm_bump_local_schema_version`을 bump해 워크스페이스의 클래스 객체 캐시와 XASL 캐시가 새 모양을 보게 만든다.
- (KO) CUBRID 통계 — 카디널리티, NDV, Min/Max, 그리고 비용 옵티마이저용 히스토그램 — 옵티마이저가 필요로 하는 카디널리티, NDV, leaf/page 수, partial-key fanout 을 CUBRID이 어떻게 만들어 내고 (서버 측 `xstats_update_statistics` 의 heap+B+Tree 워크), 시스템 카탈로그의 최신 disk representation 위에 어떻게 보존하며, packed 버퍼 형태로 와이어를 건너 어떻게 보내고, 클라이언트에서 `qo_get_attr_info` 로 어떻게 다시 읽어 비용 함수 `qo_iscan_cost` / `qo_sscan_cost` 와 selectivity 추정기 `qo_equal_selectivity` / `qo_range_selectivity` 가 플랜 점수를 매길 수 있게 하는지에 대한 분석.
- (KO) CUBRID 트리거 — ECA 능동 규칙, 문장 vs 행 단위, 그리고 locator 인접 firing 경로 — CUBRID이 SQL-99 ECA 규칙 모델을 어떻게 구현하는지 — `_db_trigger` 인스턴스, `SM_CLASS` 에 매달린 인메모리 `TR_TRIGGER` 캐시, 그리고 locator 인접 `tr_prepare_class` / `tr_before_object` / `tr_after_object` 삼각편대 — 그리고 OODB 혈통이 왜 문장 단위 재귀 제어를 문장당 가드가 아닌 OID 스택에 두게 했는지에 대한 분석.
Replication & HA (3)
- (KO) CUBRID CDC — WAL 위로 흐르는 DML·DDL 스트리밍 — CUBRID이 다운스트림 컨슈머에게 DML과 DDL 변경을 어떻게 흘려보내는지에 대한 분석. 현대 `cdc_*` API가 `LOG_SUPPLEMENTAL_INFO` 레코드를 `log_reader` 로 앞에서부터 따라가고, 그 옆에 legacy HA `la_*` log applier가 archive 로그를 slave에 재생하는 구조를 함께 본다.
- (KO) CUBRID HA 복제 — copylogdb와 applylogdb로 구현되는 논리 로그 기반 master/slave 복제 — master 엔진이 DML 도중 자기 physiological WAL 옆에 보조 `LOG_REPLICATION_DATA` / `LOG_REPLICATION_STATEMENT` 레코드를 함께 발행하고, 별도의 `copylogdb` daemon이 그 로그 볼륨을 slave 호스트로 운반하면, slave 측 `applylogdb` (`la_apply_log_file`) 가 그것을 forward로 따라 걸으며 레코드 종류별로 storage 계층에 디스패치해 직렬화된 트랜잭션 일관성으로 재생하는 구조에 대한 분석이다.
- (KO) CUBRID Heartbeat — 클러스터 생존 신호, failover, failback — CUBRID의 `cub_master` 들이 어떻게 UDP로 서로의 생존을 주기적으로 알리고, 각 노드가 자기 시점의 점수 계산만으로 단일 master를 선출하며, 그 결과로 발생하는 상태 전이(slave→to-be-master→master, master→slave)를 네 개의 워커 쓰레드와 잡 큐 FSM을 통해 failover와 failback으로 풀어내는지를 정리한 문서다.
Server Architecture (14)
- (KO) CUBRID 부팅 — 서버 기동, 최초 생성, 재시작-복구 디스패치, 클라이언트 접속 — `cub_server` 가 모든 서브시스템을 어떻게 차례로 깨우는지 — 최초 `createdb` 는 볼륨을 포맷하고 root-class 카탈로그를 부트스트랩하며, 재시작은 `log_recovery` 의 세 패스 재생으로 넘어가고, 클라이언트 측은 `boot_restart_client` 가 네트워크를 통해 `xboot_register_client` 로 이어진다.
- (KO) CUBRID Broker — CAS 프로세스 풀, 연결 라우팅, 클라이언트 측 프론트엔드 — CUBRID의 `cub_broker` 부모 프로세스가 어떻게 고정 크기의 `cub_cas` 워커 풀을 fork로 띄우고, 단일 TCP 리스너를 외부로 노출하며, accept 한 클라이언트 소켓을 Unix 도메인 랑데부 채널 위의 SCM_RIGHTS 파일 디스크립터 전달로 idle CAS에게 넘기고, 각 CAS가 클라이언트의 CSS 프레임 트래픽을 `cub_server` 로 프록시하는지를 — 잡 큐, ACL 상태, 모니터링 카운터, 브로커 관리 인터페이스까지 한 SysV 공유 메모리 세그먼트 위에서 조율하는 방식과 함께 — 정리한 문서다.
- (KO) CUBRID DBI와 CCI — 클라이언트 API 표면, 구문 수명 주기, 와이어 드라이버 파사드 — CUBRID가 `boot_cl`과 `network_cl` 위에 하나의 클라이언트 C API(`db_*`)를 어떻게 쌓는지를 설명한다. `db_open_buffer` / `db_compile_statement_local` / `db_execute_statement` / `db_query_first_tuple` / `db_query_get_tuple_value` / `db_close_session`은 `DB_SESSION` 안에서 구문을 네 단계 상태 기계(초기 → 컴파일됨 → 준비됨 → 실행됨)로 이끌며, 브로커의 CAS 프로세스는 이 표면을 `T_SRV_HANDLE` 키 기반 와이어 드라이버(`ux_database_connect`, `ux_prepare`, `ux_execute`, `ux_fetch`, `ux_end_tran`)로 감싸고, 평탄한 `server_fn_table`을 통해 JDBC, CCI, ODBC, Python, PHP 모두 동일한 `db_*` 코어에 닿게 한다.
- (KO) CUBRID 오류 관리 — 스레드별 오류 컨텍스트, 스택, 메시지 카탈로그, 네트워크 전파 — CUBRID가 어느 서브시스템에서 발생한 실패든 어떻게 보고하는지 분석한 문서. 전역 `ER_*` 오류 코드 열거형(`error_code.h`), 기본 `er_message`와 중첩 오류용 `std::stack<er_message>`를 갖는 스레드별 `cuberr::context`, `er_study_fmt`가 미리 컴파일한 형식 명세를 통해 인자를 포매팅하는 `er_set` 계열 함수, NetBSD/FreeBSD `nl_catd` 형식으로 `msgcat_init`이 로드하는 로케일별 카탈로그(`cubrid.msg` / `csql.msg` / `utils.msg`), 크기 기반 회전과 `_latest` 심볼릭 링크를 갖는 `cubrid_*.err` 로그 파일, 세 개의 `OR_INT` 필드와 메시지 문자열로 오류를 평탄화해 클라이언트-서버 간 전달하는 와이어 형식(`er_get_area_error` / `er_set_area_error`)을 다룬다.
- [KO] CUBRID loaddb — 벌크 로더, 직접 경로 heap+B+Tree 삽입, 그리고 로드 후 통계 재구축 — CUBRID의 loaddb 유틸리티가 CUBRID 포맷의 객체 파일을 토큰화해 배치로 쪼개고, 각 배치를 Bulk-Update 락을 잡고 있는 서버 측 워커 풀로 실어 보내 `locator_multi_insert_force`로 heap 페이지에 직접 써 넣은 뒤, 클래스별 통계 재구축으로 로드를 마무리하는 과정에 대한 분석.
- (KO) CUBRID Locator — OID 워크스페이스, 벌크 fetch/flush, 그리고 서버 측 INSERT/UPDATE/DELETE 게이트웨이 — CUBRID가 메모리 안의 객체와 디스크의 OID 사이를 어떻게 연결하는지 — 클라이언트는 dirty 객체를 `LC_COPYAREA` 버퍼에 묶어 한 번에 보내고, 서버는 `locator_*_force` 패밀리 단일 진입점에서 heap, btree, lock, log, FK, 복제 경로로 부채꼴처럼 펴낸다.
- (KO) CUBRID cub_master 프로세스 — 데몬 lifecycle, 연결 레지스트리, 요청 디스패치, 자동 재시작 server monitor — `cub_master` 의 end-to-end 분석 — host 별 CUBRID 서비스 레지스트리를 소유하는 long-lived 데몬. 부팅 시퀀스 (`master_util_config_startup` → `css_does_master_exist` 중복 검사 → `css_daemon_start` fork → `css_master_init` 소켓 바인딩 + 시그널 핸들러 → HA 면 옵션 `hb_master_init` → `auto_restart_server = on` 일 때 옵션 `server_monitor` 인스턴스화); listening 소켓을 `css_Master_socket_anchor` 의 등록된 모든 자식 연결과 multiplex 하는 `select()` 루프; `commdb` 와 `cubrid commdb` 가 보내는 것을 구현하는 `process_master_request` 오피코드 분류 체계 (~30 오피코드, status / shutdown / HA-process 등록 / HA-info-query 패밀리에 split); 전용 `std::thread` 위에서 producer-consumer job queue (REGISTER / UNREGISTER / REVIVE / CONFIRM_REVIVE / SHUTDOWN)를 돌리고 등록된 모든 cub_server 의 PID + argv 를 추적하는 `m_server_entry_map` 으로 비정상 exit 시 re-fork 할 수 있는 `server_monitor` C++ 서브시스템. 그 위에 얹힌 HA-replication 서브시스템을 다루는 `cubrid-heartbeat.md` 와 구별된다는 점이다.
- (KO) CUBRID 모니터링 — Perfmon 카운터, 통계 집계, 서브시스템별 모니터 — CUBRID이 두 겹의 카운터 시스템으로 핫 패스를 계측하는 방식. C++ 템플릿 기반의 cubmonitor 라이브러리가 통계 그룹을 등록하고 트랜잭션별 시트를 지원하며, 오래된 C 언어의 perf_monitor/pstat_Metadata 배열이 SHOW STATS와 statdump를 담당한다. 그 위에 per-vacuum-worker 오버플로우 페이지 임계치 추적기처럼 자체 비카운터 상태를 유지하는 서브시스템별 모니터가 덧붙는다.
- (KO) CUBRID 네트워크 프로토콜 — 연결 수락, NRP 디스패치, 서버 측 요청 핸들러 — 모든 서버 진입점을 하나의 `NET_SERVER_*` 오피코드로 추상화하여 `(action_attribute, handler)` 레코드의 정적 테이블을 통해 디스패치하는 구조를 정리한다. `cub_master`가 연결을 수락한 뒤 Unix 도메인 소켓으로 `master::connector`를 거쳐 `cub_server` 워커로 넘기고, epoll 기반 `cubconn::connection::worker`가 CSS로 프레이밍된 패킷을 읽어 서버·클라이언트 양쪽이 대칭으로 사용하는 `or_pack_*` / `or_unpack_*` 마샬링 루틴으로 위임하는 흐름까지 다룬다.
- (KO) CUBRID SA vs CS 런타임 — 단독(엔진 내장) 모드와 클라이언트-서버(소켓 통신) 모드 — CUBRID 이 같은 소스 트리를 세 번 컴파일하는 방식 — `cub_server` (SERVER_MODE), `libcubridsa` (SA_MODE), `libcubridcs` (CS_MODE) — 을 통해 관리 유틸리티가 엔진 전체를 자기 프로세스 안에 내장해 디스크 데이터베이스를 직접 다루거나, 별도로 구동 중인 데몬에 CSS 로 연결해 원격으로 조작할 수 있게 하며, 이 선택은 유틸리티별 분류(SA_ONLY / CS_ONLY / SA_CS)와 런타임의 `dlopen`(`libcubridsa.so` 또는 `libcubridcs.so`)으로 결정된다.
- (KO) CUBRID 서버 세션 — 클라이언트별 상태, prepared statement 레지스트리, 그리고 TDES 바인딩 — CUBRID이 클라이언트별 서버 측 상태 컨테이너 — `SESSION_STATE` — 를 어떻게 유지하는지에 대한 분석. 정수 세션 id를 키로 하는 lock-free 해시에 보관되며, O(1) 요청 lookup을 위해 connection entry에 캐시되고, 스레드별 TDES에 바인딩되어 모든 서버 요청이 자기 트랜잭션 descriptor, prepared statement 캐시, 파라미터 집합 위에 정확히 떨어지도록 한다.
- (KO) CUBRID 시스템 파라미터 — 튜닝 레지스트리, conf/env/URL 파싱, 세션별 범위 지정 — CUBRID의 `prm_Def[]` 레지스트리, 섹션 선택을 지원하는 `cubrid.conf` INI 파서, 환경 변수 오버라이드, `db_set_system_parameters` SQL 경로, 그리고 세션별 `SESSION_PARAM` 배열이 어떻게 결합되어 다른 모든 서브시스템이 `prm_get_*_value`를 통해 읽는 단일 우선순위 결정 흐름을 구성하는지에 대한 분석.
- [KO] CUBRID 스레드 매니저 NG — 고동시성을 위한 Connection/Worker 풀 재설계 (CBRD-26177) — CUBRID의 connection/worker 풀을 guava 버전에서 다시 설계한 결과 — 경계가 정해진 epoll 구동 connection worker, 재배치와 자동 스케일링을 중재하는 coordinator, send/recv 예산, worker별 컨텍스트 freelist, 그리고 atomic 없는 통계 수집이 한 묶음으로 들어간다. cubrid-thread-worker-pool.md가 다루던 connection-당-스레드 + max_clients 크기의 task worker 구조를 대체한다.
- (KO) CUBRID 스레드와 워커 풀 — Worker, Daemon, lock-free 자료구조, critical section — CUBRID 서버가 한 번의 실행 흐름을 어떻게 빚어내는지에 대한 분석. thread별 `cubthread::entry` 컨텍스트, 쿼리 / vacuum / loaddb / parallel-redo를 돌리는 `worker_pool` 템플릿 (core → worker → task queue), 모든 주기적 백그라운드 flush와 detect를 구동하는 `daemon` + `looper` 패턴, lock manager와 page buffer가 함께 쓰는 lock-free hashmap, 그리고 thread별 tracker가 붙은 무거운 `csect` RW 원시 동기화 — 이 다섯 조각이 본 문서의 골격이다.
System Catalog (2)
- (KO) CUBRID SHOW 명령 — 서버 런타임 상태를 가상 스캔으로 들여다보는 시스템 인트로스펙션 — CUBRID가 서버 내부 상태 — 볼륨 헤더, 로그 헤더, 힙·B+Tree 용량, 크리티컬 섹션, 스레드, 페이지 버퍼, 트랜잭션 테이블, 타임존 — 를 어떻게 SQL로 질의 가능한 가상 테이블로 노출하는가. `SHOW <name>` 구문을 `SELECT * FROM (PT_SHOWSTMT)` 로 다시 써서 access spec을 뽑고, 그것을 `S_SHOWSTMT_SCAN` 으로 디스패치한 뒤, `SHOWSTMT_TYPE` 별로 등록된 start/next/end 함수 포인터에서 튜플을 주문 즉시 합성한다.
- (KO) CUBRID 시스템 카탈로그 클래스 — 데이터 주도 정의, 부트스트랩 설치, 시스템 뷰 query spec — CUBRID이 SQL에서 보이는 시스템 클래스 가족(`_db_class`, `_db_attribute`, `_db_index`, `_db_auth`, ...)을 데이터 주도 프레임워크로 어떻게 정의·부트스트랩하는지에 대한 분석. 클래스마다 `cubschema::system_catalog_definition`(attributes + constraints + grants + 선택적 row initializer)을 `catcls_init` 시점에 전역 vector에 등록하고, `catcls_install`에서 단일 `system_catalog_builder`가 그 vector를 walk하면서 `db_create_class` -> `smt_add_attribute` -> `sm_update_class` -> constraints/grants를 차례로 발급한다. 디스크 측 `catalog_manager`와 인메모리 `schema_manager`로부터 분리되어, 이 모듈은 *SQL에서 보이는 시스템 카탈로그 자체의 메타-스키마*를 소유한다.
Procedural Language (3)
- [KO] CUBRID PL/JavaSP — Java 저장 프로시저, JDBC 백채널, 그리고 PL/CSQL과 형제인 외부 PL 엔진 — CUBRID이 Java 와 PL/CSQL 저장 프로시저를 별도 JVM 프로세스 (`cub_pl`) 위에서 어떻게 굴리는가. 카탈로그 행과 전송 인프라는 PL/CSQL 과 공유하면서, 사용자 JAR 위의 reflective 디스패치, 클래스로더 위계, 보안 샌드박스만은 JavaSP 가 단독으로 소유하는 모양에 관한 분석.
- [KO] CUBRID PL/CSQL — Oracle 호환 절차적 SQL을 PL 패밀리 런타임에서 Java로 컴파일하기 — PL 패밀리의 Oracle 방언 절반 — JavaSP 와 짝을 이루는 인 CUBRID PL/CSQL 이 어떻게 공유 `pl_server` JVM 안에서 ANTLR 4 문법으로 파싱되고, CUBRID 고유의 Java AST (DeclProgram / StmtBlock / ExprBinaryOp / loopOpt) 로 lower 되며, 방출 visitor 가 Java 소스로 번역하고, 인-프로세스 `javax.tools.JavaCompiler` 가 이를 컴파일한 뒤, JAR (Base64) 로 패키징되어 C 측 `compile_handler` 로 돌려보내져 `sp_add_stored_procedure_code` 가 JavaSP 가 사용하는 같은 카탈로그 행 옆에 영속화하는지에 대한 분석.
- (KO) CUBRID PL 서버 브리지 — 두 PL 런타임이 함께 타고 있는 실행 중 콜백 채널 — PL 패밀리의 세 번째 형제 문서 — JavaSP 와 PL/CSQL 이 둘 다 위에 얹혀 있는 공유 실행-중 콜백 채널을 다룬다. 물리적으로 서로 다른 두 경로가 같은 오피코드 분류 체계와 packed 와이어 구조체를 공유한다는 점이다. Path A 는 SCAN_TYPE_METHOD 를 위한 레거시 cub_server→CAS 채널(cubscan::method::scanner 가 구동하고, CAS 측에서 cubmethod::callback_handler::callback_dispatch 가 처리)이며, Path B 는 JavaSP 와 PL/CSQL 호출을 위한 모던 cub_pl→cub_server 채널(SP_CODE_INTERNAL_JDBC 가 같은 METHOD_CALLBACK_* 오피코드들을 운반하고, 서버 측에서 cubpl::executor::response_callback_command 가 처리). 요청 분류 체계(METHOD_REQUEST_INVOKE / _CALLBACK / _END / _ARG_PREPARE / _COMPILE / _SQL_SEMANTICS / _GLOBAL_SEMANTICS), 응답 분류 체계(약 18 개의 METHOD_CALLBACK_* 오피코드), PL/CSQL 이 임베디드 SQL 검증에 쓰는 컴파일타임 시맨틱 round-trip, 재귀 가드(METHOD_MAX_RECURSION_DEPTH = 15), 그리고 콜백 nest 를 부모 트랜잭션 아래로 묶어주는 tran_begin/end_libcas_function bracketing 까지 다룬다.
Internationalization & Specialty (2)
- (KO) CUBRID 문자셋과 콜레이션 — 코드셋 변환, 로케일 인지 비교, 다중 인코딩 — CUBRID이 텍스트를 네 가지 코드셋(binary, ISO-8859-1, EUC-KR, UTF-8)으로 어떻게 다루는지, LDML 로케일 규칙을 UCA 가중치를 담은 공유 라이브러리로 어떻게 컴파일하는지, 그리고 함수 포인터 vtable인 LANG_COLLATION이 B+Tree·정렬·문자열 연산자에 어떻게 콜레이션-인지 비교를 dispatch 하는지를 정리한다.
- (KO) CUBRID Timezone — IANA 타임존 데이터 컴파일, tz_id 해석, 그리고 DATETIMETZ/TIMESTAMPTZ 변환 — CUBRID가 IANA tzdata 원본을 어떻게 `timezones.c`로 생성하고 공유 라이브러리 `libcubrid_timezones.so`로 빌드하는지, 런타임에 `dlsym`으로 TZ_DATA 블롭을 적재하는지, (zone, gmt-offset-rule, ds-rule) 트리플을 32비트 `TZ_ID`로 압축하면서 상위 두 비트로 지역 ID와 원시 오프셋을 구분하는지, `tz_datetime_utc_conv`가 zone의 오프셋 규칙 리스트와 일광절약 규칙 집합을 순회하며 `LOCAL_STD`/`LOCAL_WALL`/`UTC` \AT\ 한정자와 중복 구간을 어떻게 처리하는지, 그리고 SQL 계층에 `DATETIMETZ`, `TIMESTAMPTZ`, `DATETIMELTZ`, `TIMESTAMPLTZ`를 `tz_create_datetimetz`, `tz_conv_tz_datetime_w_region`, `tz_explain_tz_id`로 노출하는지를 정리한다.
Utilities (8)
- (KO) CUBRID checksumdb — chunked, replication-replayed 비교를 통한 HA replica vs master 행 checksum 검증기 — `checksumdb` 의 end-to-end 분석 — master 와 slave 사이의 조용한 divergence 를 감지하는 HA-cluster 무결성 검증기. 각 표를 primary key 따라 chunk 로 자르고, master 에서 chunk 별 checksum 을 계산하고, 데이터를 운반하는 같은 WAL stream 을 통해 그 checksum 들을 복제하고, slave 가 다시 계산하고 비교하게 한다. on-disk artefact (`db_ha_apply_info_chksum_*` 결과 표 + `_schema` 표) 와 chunked walk (PK 순서를 통한 lower-bound iteration, chunk 당 `--chunk-size` 행, cursor 를 구동하는 `chksum_get_next_lower_bound`); 직렬화된 클래스 정의를 비교하는 schema-checksum 측; include/exclude list 필터링; resume-from-prior-run 모드; report formatter (`chksum_report_summary` / `_diff` / `_schema_diff`); chunk 계산 동안 획득되는 SHARED lock — 읽기는 허용하지만 chunk 를 일관되게 만들 만큼 쓰기를 막는다는 점이다.
- (KO) CUBRID compactdb — 오프라인 데이터베이스 압축과 페이지 조각 모음 유틸리티 — CUBRID의 compactdb 유틸리티가 온라인 MVCC vacuum을 어떻게 보완하는지에 대한 분석. 클래스별 heap을 따라 걸으며 끊어진 OID 참조를 NULL로 만들고, 비어 버린 heap 페이지를 회수하고, 더 이상 쓰이지 않는 카탈로그 representation을 떨어뜨리고, heap 파일을 조각 모음한다. 클라이언트 측에서 구동되며 클래스 단위로 스코프가 잡히고, 마운트는 풀려 있지만 재시작 가능한 데이터베이스에 대해 번호가 매겨진 세 패스로 동작한다.
- (KO) CUBRID csql — 인터랙티브 SQL 클라이언트, 두 바이너리 launcher 분리, 세션 명령 prefix, 단일행 실행 모드 — `csql` 인터랙티브 SQL 클라이언트의 end-to-end 분석 — 두 바이너리 launcher 패턴 (`csql_launcher.c` 가 argv 를 파싱한 다음 `--SA-mode` / `--CS-mode` 에 따라 `cubridsa` 또는 `cubridcs` 를 dlopen 하고 `csql` 진입점을 dlsym), CSQL_ARGUMENT 옵션 표와 잘못된 조합을 거절하는 검증 매트릭스 (`-p` / `-q` / `--loaddb-output` 상호배타, `--write-on-standby` 는 `--sysadm` 필요, `--skip-vacuum` 은 SA 전용), `start_csql` 의 read-execute-print 루프와 in-block (string / comment / identifier) 추적을 위한 `csql_walk_statement` / `csql_is_statement_complete` 단일행 실행 감지, 파일 / 편집 / 명령 / 환경 / 도움 / 히스토리 6 패밀리에 걸친 47 항목의 세션 명령 (`;`-prefix) 디스패치 표와 세션이 오프라인일 때 DDL-class 명령을 게이트하는 `CMD_CHECK_CONNECT` 플래그, readline / `stifle_history` 통합과 `.hist` histogram 명령들, DDL audit 로깅 hook (`logddl_*`), 그리고 네 가지 출력 스타일 (column, line-output, plain, query, loaddb).
- (KO) CUBRID cubrid Admin CLI — Verb 디스패처, SA/CS 라우팅 라이브러리 로딩, Service · Server · Broker · Heartbeat 패밀리 — 통합 `cubrid` admin CLI 의 end-to-end 분석 — 두 축 verb 분류 체계 (서비스 패밀리 verb `service|server|broker|manager|heartbeat|pl|gateway` × 명령 패밀리 verb `start|stop|restart|status|reload|on|off|acl|reset|info|deregister|list|getid|test|copylogdb|applylogdb|replication`, 거기에 데이터베이스 admin verb 들 `createdb|backupdb|loaddb|unloaddb|...|memmon`)을 `us_Service_map` + `us_Command_map` + `ua_Utility_Map` 표가 뒷받침. 무의미한 (verb, command) 쌍을 파싱 시점에 거절하는 비트마스크 cross-check (`MASK_SERVICE`/`MASK_SERVER`/`MASK_BROKER`/`MASK_HEARTBEAT`/`MASK_PL`/`MASK_GATEWAY`). verb 마다 `libcubridsa` vs `libcubridcs` 를 고르는 SA_ONLY/CS_ONLY/SA_CS 라우팅. 표준 `UTIL_FUNCTION_ARG` 시그니처를 가진 verb-별 진입 함수의 `dlsym`. 레거시 짧은-arg 호출 (`createdb -p 1000 ...`, `loaddb -u dba ...`)을 모던 `cubrid <verb> --long-arg ...` 로 번역하고 통합 진입점으로 `execvp` 하는 `util_front.c` 의 호환 shim.
- (KO) CUBRID loadjava — JavaSP 클래스로더 트리를 위한 JAR · .class 설치기 — `loadjava` 의 end-to-end 분석 — JavaSP 가 찾을 수 있도록 데이터베이스의 per-database Java 클래스로더 root 에 `.class` 또는 `.jar` 를 설치하는 작은 standalone utility. 전체 utility 가 `<filesystem>` 을 사용하는 한 C++17 파일이다. 경로 해석을 다룬다는 점이다 — 기본 (dynamic) 트리 (cub_pl JVM 의 ContextClassLoader 가 watch) 에는 `$CUBRID_DATABASES/<db>/java/<package>/<file>`, 또는 (`--jni` 와 함께) JVM 시작 시 한 번 로드되는 static 트리에는 `$CUBRID_DATABASES/<db>/java_static/<package>/<file>`; regex 검증된 dot 표기 디렉토리 hierarchy 가 되는 `--package` 플래그; 비-인터랙티브 overwrite 를 위한 `--overwrite` / `-y` 플래그; JVM 의 classloader-manager 가 재시작 없이 변경을 pickup 하도록 디렉토리의 mtime 을 update 하는 의도적인 copy 전 `fs::remove` (CBRD-24695); 데이터베이스 서버 연결 없음 — 설치는 순수 파일시스템 측이고, JVM 측 classloader manager 가 발견을 한다.
- (KO) CUBRID migrate — 볼륨 헤더, active log codeset, collation sync 를 위한 one-shot 9.1→9.2 in-place 형식 업그레이더 — `migrate` 의 end-to-end 분석 — CUBRID 9.1 에서 9.2 disk 형식으로의 버전 잠긴 one-shot in-place 형식 업그레이더. 네 단계 시퀀스: (1) v9.1 disk-var-header 레이아웃을 v9.2 로 변환하는 볼륨별 헤더 재작성 (mid-migration 크래시가 `undo_fix_volume_header` 로 rollback 될 수 있도록 undo journal 과 함께); (2) 카탈로그와 일치하게 log 헤더의 codeset 필드를 retag 하는 active-log codeset patch (`fix_codeset_in_active_log`); (3) `db_restart` 부팅, 새 locale 라이브러리에서 카탈로그의 collation 행을 refresh 하는 `synccoll_force` — 엄격한 collation 호환 검사 (codeset match, contraction 이 없으면 checksum match); (4) 사용된-페이지 통계를 refresh 하는 `file_update_used_pages_of_vol_header`. lockstep undo 규율과 명시적 `rel_disk_compatible() != V9_2_LEVEL` 가드가 migrate 를 CUBRID 가 disk-format 업그레이드를 어떻게 처리하는지에 대한 정규 reference 로 만든다는 점이다.
- (KO) CUBRID unloaddb — 스키마와 데이터 export, 4-파일 출력 레이아웃, per-class 멀티스레드 · 멀티프로세스 driver — `unloaddb` 의 end-to-end 분석 — `loaddb` 의 자연스러운 역방향인 스키마 + 데이터 exporter. 4-파일 출력 레이아웃 (`<prefix>_schema`, `<prefix>_trigger`, `<prefix>_indexes`, `<prefix>_objects`)이 고정 순서로 방출되어 downstream `loaddb` 가 schema → trigger → data → index 로 replay 할 수 있게 한다. 모든 emitter 에 user / auth / storage-order / split-files / dba-bypass 설정을 thread 하는 `extract_context` carrier 구조체. 표 전 → 뷰 의존성을 푸는 `extract_classes_to_file` → `emit_schema(EXTRACT_CLASS)` → `emit_schema(EXTRACT_VCLASS)` 순서. `--thread-count` (per-class 동시 fetch, ≤127) 와 `--mt-process N/M` (≤36 프로세스에 걸쳐 클래스로 partition, 상호 배타)를 가진 `extract_objects` driver. 한 큰 bag 대신 클래스마다 한 데이터 파일을 쓰는 `--datafile-per-class` 모드. `--latest-image` MVCC 스냅샷 토글. `--keep-storage-order` vs 기본 `FOLLOW_ATTRIBUTE_ORDER` 축. cross-owner extract 를 위한 `--as-dba` 그룹 우회. `--split-schema-files` 와 `--input-class-file` 선택적 export 경로.
- (KO) CUBRID 유틸리티 모음 — commdb, gencat, generate_timezone, daemon, cubrid_version, pl bootstrap helper — 각각 전용 문서가 필요하지 않은 작은 `src/executables/` 유틸리티들의 옴니버스 커버리지. 6 개 도구가 세 카테고리에 걸쳐 있다 — (1) **런타임 probe / helper** — `commdb` (`cubrid commdb` 보다 앞선 standalone `cub_master` RPC client; MASTER_REQ_* 오피코드 전송), `pl` (`$CUBRID/var/pl_<db>.info` 를 읽는 `cubrid pl ping/start/stop/restart/status` 가 사용하는 cub_pl JVM bootstrap helper), `daemon` (스스로 detach 하지 않는 다른 바이너리가 사용하는 작은 `fork`/`setsid` double-fork wrapper); (2) **빌드타임 generator** — `gencat` (`MSGCAT_CATALOG_*` lookup 을 위한 binary `.cat` 파일을 생산하는 NetBSD 유래 POSIX 메시지-카탈로그 컴파일러), `generate_timezone` (IANA `tzdata` 를 `cubrid_timezones` 라이브러리가 빌드되는 C 소스로 바꾸는 `timezone_compile_data` 의 62-줄 wrapper); (3) **trivia** — `cubrid_version` (`PRODUCT_STRING` 을 출력하고 exit 하는 37-줄 프로그램).
PostgreSQL
섹션 제목: “PostgreSQL”PostgreSQL 백엔드(REL_18)를 모듈 단위로 읽는다 — 공유 메모리 프로세스 모델, WAL 내구성 축, 스토리지와 플러거블 액세스 메서드, 플래너/익스큐터 파이프라인, 카탈로그/캐시 계층, 복제, 확장성 표면, 그리고 버전 진화 문서까지.
먼저 읽기: 아키텍처 개요
바로가기: Overview & Reading Paths · Base / Infrastructure · Storage Engine · Transaction & Recovery · Query Processing · DDL & Schema · Replication & HA · Server Architecture · System Catalog · Monitoring & Statistics · Client Protocol · Extensibility · Internationalization & Text · Utilities · Version Evolution
Overview & Reading Paths (16)
- (KO) PostgreSQL 아키텍처 개요 — 단일 바이너리, 공유 메모리 머신, WAL 척추 — PostgreSQL 코드 분석 트리의 정문 역할을 하는 문서. PostgreSQL은 단일 바이너리다. 하나의 postmaster(마스터 프로세스)가 역할 유형이 다른 자식 프로세스들을 fork하며, 이 자식들은 모두 부팅 시점에 크기가 고정된 하나의 공유 메모리 세그먼트에 연결된다. 공유 메모리 안의 구조체들 — PGPROC/ProcGlobal, procarray, 버퍼 풀, 락 테이블, sinval — 이 곧 아키텍처이고, 프로세스는 그 머신의 주민이다. WAL은 복구, 물리/논리 복제, 아카이빙, 백업이 모두 소비하는 단일 내구성 이벤트 로그다. 이 문서는 일곱 가지 아키텍처 축 — 프로세스 모델, 공유 메모리/IPC 기반, WAL 내구성 척추, 스토리지와 플러그인 접근 방법, 질의 파이프라인, 카탈로그·캐시 계층, 확장성 표면 — 을 축마다 다이어그램 한 장씩으로 풀어내고, 각 메커니즘을 소유한 세부 문서로 길을 안내한다.
- (KO) PostgreSQL 코드 분석 커버리지 맵 — knowledge/code-analysis/postgres/ 트리 전체의 커버리지 추적 문서. REL_18_STABLE(커밋 273fe94, PostgreSQL 18.x) 기준으로 작성되었으며, 각 서브카테고리별 모듈 문서를 소스 앵커·상호 참조 우선순위·작업 상태와 함께 열거한다. 캠페인이 완료되었다: 117개 모듈 문서 + 15개 개요/커버리지/상태 문서 + 7개 진화 문서, 모두 EN+KO+메타 완성; PG 관련 연구 논문 13편이 knowledge/research/dbms-papers/에 수록됨. 문서 타입 범례(module / overview / detail / evolution), in-tree README + dbms-papers 앵커 전략, 명시적 비목표(contrib 전체, PG19 전용 기능, 비libpq 클라이언트 드라이버, 테스트 프레임워크)를 기록한다.
- (KO) PostgreSQL Base Infrastructure — 섹션 개요 — base-infra 서브카테고리의 라우터 문서. PostgreSQL의 다른 모든 계층이 그 위에 세워지는 기반 시설 일곱 가지를 아래에서 위로 훑는다. 계층적 메모리 컨텍스트 할당기(AllocSet / Slab / Generation / Bump, 하나의 MemoryContextMethods 가상 테이블 아래), setjmp/longjmp로 풀리는 elog/ereport 오류 기계, dynahash(syscache와 공유 락 테이블의 뒷받침인 인프로세스 해시 테이블), 오류 시에도 버퍼 핀과 락이 반드시 해제되도록 보장하는 리소스 오너 트리, pg_proc 행을 호출 가능한 형태로 바꾸는 함수 매니저(fmgr), 모든 타입의 I/O와 연산자를 제공하는 데이터타입 라이브러리(utils/adt), GUC 파라미터 시스템. 메모리 컨텍스트와 elog가 진정한 바닥이고, 그 위의 모든 것 — 실행기, 플래너, 스토리지, 카탈로그 — 은 컨텍스트 안에서 할당하고, elog를 통해 오류를 던지며, 리소스 오너에 정리 작업을 등록한다. 이 문서는 경계를 명시하고 일곱 개 모듈 문서로 독서 순서를 안내하는 라우터다.
- (KO) PostgreSQL 클라이언트 프로토콜 — 섹션 개요 — 클라이언트 프로토콜 서브카테고리의 라우터 문서. PostgreSQL 프론트엔드/백엔드(FE/BE) 와이어 프로토콜의 백엔드 쪽 절반을 다룬다. 모든 연결이 한 번씩 올라가는 직선형 핸드셰이크 스택이 이 섹션의 뼈대다. 원시 소켓에서 출발해, 스타트업 패킷보다 먼저 협상되는 선택적 암호화 계층(TLS 또는 GSSAPI), 스타트업 패킷과 HBA 룩업, SASL/SCRAM 챌린지-응답 인증 순으로 올라간 뒤, pqcomm이 바이트를 읽고 pqformat이 메시지를 해석하는 PostgresMain의 정상 상태 메시지 루프에 안착한다. 세 편의 모듈 문서가 이 스택의 세 구간을 각각 담당한다. postgres-wire-protocol은 바이트 프레이밍과 메시지 루프를, postgres-authentication은 ClientAuthentication 디스패치와 SCRAM 교환을, postgres-tls-gssapi는 전송 보안 계층을 소유한다.
- (KO) PostgreSQL DDL & 스키마 — 섹션 개요 — DDL & 스키마 서브카테고리의 라우터 문서. 이 섹션의 통합 사실은 하나다. 여기서 다루는 거의 모든 것은 ProcessUtility를 거친다. ProcessUtility는 플래너가 최적화하지 않는 구문들의 디스패치 지점이다. standard_ProcessUtility가 단순한 경우를 인라인으로 처리하고, 스키마를 변경하는 구문들(CREATE/ALTER TABLE, 인덱스, 제약, 트리거, 파티션, 정책)은 ProcessUtilitySlow로 넘긴다. ProcessUtilitySlow는 commands/ 아래 명령별 모듈로, catalog/(heap.c, index.c, partition.c)의 카탈로그 라이터로, partitioning/ 아래 파티션 바운드 기계로 다시 이어진다. 두 가지는 ProcessUtility 흐름 밖에 있으며, 이 섹션은 그 점을 명시적으로 다룬다. 행 수준 보안(RLS)은 쿼리 재작성 시점 메커니즘(get_row_security_policies, rewriteHandler.c에서 호출)이고, 파티션 튜플 라우팅(ExecFindPartition)은 DML 시점에 실행기가 수행한다. COPY는 유틸리티로 디스패치되는 대량 I/O다. 구체화 뷰는 DDL(릴레이션 정의)과 쿼리 파이프라인(채우는 SELECT)에 걸쳐 있다. 이 문서는 그 경계들을 이름 붙이고, 열 개의 모듈 문서로 메커니즘을 넘긴다.
- (KO) PostgreSQL 확장성 — 섹션 개요 — 확장성 서브카테고리의 라우터 문서. PostgreSQL의 정의적 설계 형태는 서드파티 코드가 엔진을 패치하지 않고 고정된 간접 지점에 꽂히는 구조에 있다. 모든 플러그 포인트는 콜백 구조체이거나 코어가 정해진 자리에서 확인하는 함수 포인터 전역 변수다. 이 문서는 사용자 대면 플러그 포인트들 — 외부 데이터 래퍼(FdwRoutine), CREATE EXTENSION 패키징 시스템, 훅 전역 변수(planner/executor/ProcessUtility/shmem), pg_language를 통한 절차형 언어와 SPI 재진입 경로, 커스텀 스캔 제공자(CustomScanMethods) — 로 안내한다. 하위 수준 플러그 포인트인 테이블/인덱스 AM, 커스텀 WAL rmgr, 백그라운드 워커는 같은 패턴이지만 각각 storage-engine, txn-recovery, server-architecture 서브카테고리가 소유한다. 메커니즘은 코어에 있고, 대부분의 구체적 구현은 contrib 또는 서드파티로 배포된다(이 트리의 범위 밖).
- (KO) PostgreSQL 국제화·텍스트 검색 — 섹션 개요 — i18n-text 서브카테고리의 라우터 문서. 문자열의 바이트 표현 위에 자리잡은 세 가지 텍스트 처리 계층을 소개한다. (1) 콜레이션 제공자 추상화 — 로케일 의존 비교, 대소문자 변환, 패턴 매칭은 모두 단일 pg_locale_t 핸들로 수렴하고, 이 핸들의 collate vtable이 libc·ICU·PG17에서 추가된 builtin 제공자 중 하나로 디스패치한다. 덕분에 str_tolower / varstr_cmp는 제공자 분기를 직접 처리하지 않아도 된다. (2) 서버 인코딩 — utils/mb가 데이터베이스별 서버 인코딩, 인코딩 간 변환 함수 행렬, 멀티바이트 문자열 연산을 담당하며, 인코딩 이름과 wchar 테이블은 src/common에서 프론트엔드 도구와 공유된다. (3) 전문 검색 — tsearch가 파서와 사전 체인으로 구성된 텍스트 검색 설정을 거쳐 문서를 정규화된 tsvector로 만들고, tsquery와 매칭하며, GIN extractValue/extractQuery를 통해 GIN 접근 방법의 대표 소비자가 된다. 이 문서는 세 모듈 문서의 이름, 기반 인프라(데이터 타입, fmgr) 및 스토리지 엔진(GIN)과의 경계를 명시하고 메커니즘 설명은 각 모듈 문서에 맡긴다.
- (KO) PostgreSQL 모니터링과 통계 — 섹션 개요 — monitoring-stats 서브카테고리 라우터. PostgreSQL의 인트로스펙션(introspection, 내부 상태 조회) 평면을 다룬다. 세 기둥이 utils/activity/ 하나를 공유한다. (1) PG15 누적 통계 시스템 — 기존의 단일 stats-collector 프로세스와 UDP 패킷 방식을 대체한 공유 메모리 서브시스템. 각 백엔드는 카운터를 로컬에 쌓다가 고정 수량 종류(checkpointer/bgwriter/WAL/IO)는 고정 shmem 블록으로, 가변 수량 종류(릴레이션/함수 단위)는 PgStat_HashKey 키를 가진 DSA 기반 dshash로 플러시한다. checkpointer가 종료 시 디스크에 직렬화하고, startup 프로세스가 재로드하거나 크래시 후 버린다. (2) 대기 이벤트(wait event)와 백엔드 상태 — 각 백엔드가 자신의 PGPROC 슬롯에 직접 게시하는 시점별 보고. 대기 이벤트 분류 체계는 wait_event_names.txt에서 코드 생성된다. (3) 진행 보고(progress reporting) — 장기 실행 명령이 갱신하고 pg_stat_progress_* 뷰가 읽는 명령별 진행 배열. 라이브 뷰(상태/대기/진행)와 누적 뷰(카운터)의 구분이 이 섹션의 핵심이며, 서버 프로세스 배관은 server-architecture로, I/O 기반은 storage-engine과 txn-recovery로 위임한다.
- (KO) PostgreSQL 질의 처리 — 섹션 개요 — 질의 처리 서브카테고리의 안내 문서. 백엔드가 SQL 텍스트를 받아 행을 반환하기까지 다섯 단계를 거친다 — 파싱(gram.y/scan.l → 원시 파스 트리), 분석(parse_analyze → Query 트리), 재작성(규칙 시스템이 뷰 확장 및 RLS 처리), 플래닝(비용 기반 옵티마이저가 Path 후보를 최소 비용 우선으로 쌓고, 동적 프로그래밍 조인 순서 결정과 GEQO 유전 폴백으로 승자를 가려 Plan 트리로 구체화), 실행(ExecProcNode가 구동하는 demand-pull 노드 트리, switch-threaded 인터프리터 또는 선택적 LLVM JIT로 표현식 평가). 이 척추 주변에 병렬 질의(Gather 노드가 shm_mq 위에서 워커를 fork), prepared statement·포털(실행 간 플랜 캐시), 플래너 통계(단일 컬럼 및 확장 다중 컬럼 통계), 그리고 모든 단계의 트리를 구성하는 Node 인프라가 자리한다. 아래로는 storage-engine에 접근 방법 스캔을 위임하고, 옵티마이저의 선택도 입력을 system-catalog와 공유한다.
- (KO) PostgreSQL 복제·고가용성 — 섹션 개요 — replication-ha 서브카테고리의 라우터 문서. 이 섹션의 모든 문서는 txn-recovery 섹션이 생산하는 WAL 스트림을 소비한다. walsender가 WAL을 walreceiver로 전송하는 물리 스트리밍, 복제 슬롯이 WAL(및 카탈로그 스냅샷)을 핀하는 북마크 메커니즘, 논리 디코딩이 reorderbuffer와 snapbuild로 물리 WAL을 행 변경 스트림으로 변환하고 pgoutput 플러그인을 거쳐 apply 워커에 전달하는 경로, 동기 복제가 COMMIT을 스탠바이 ack까지 대기시키는 메커니즘, 아카이버·WAL 요약기가 아카이브와 블록 수정 요약을 생성하는 경로, basebackup·증분 백업이 WAL 위치에 고정된 물리 스냅샷을 생성하는 방식을 다룬다. 이 섹션은 WAL 소비자를 소유한다. WAL 생산자와 복구/redo는 txn-recovery로, FE/BE 복제 명령 전송은 client-protocol로 돌려보낸다.
- (KO) PostgreSQL 서버 아키텍처 — 섹션 개요 — server-architecture 서브카테고리의 섹션 개요 라우터: fork-and-attach 머신 그 자체를 다룬다. postmaster 하나가 부팅 시 공유 메모리 세그먼트 하나의 크기를 정하고, 역할 유형이 다른 자식 프로세스(BackendType 패밀리)를 fork한다. 각 백엔드는 InitProcess → InitPostgres → 메시지 루프 순으로 실행된다. 그 기반 위에 IPC 프리미티브(고정 공유 세그먼트 + 동적 dsm/dsa/shm_mq)와 세 가지 락 계층이 있다. lmgr/README가 직접 열거하는 그 세 계층은 — 짧은 인메모리 임계 구역을 보호하는 LWLock·스핀락, waits-for 교착 감지기를 갖춘 헤비웨이트 정규 락 테이블, SERIALIZABLE을 구현하는 SSI 서술 락이다. 래치와 시그널(procsignal, SetLatch)이 깨움 신호를 전달하며, 보조 프로세스들과 백그라운드 워커 프레임워크가 나머지 주민을 이룬다. 이 라우터는 경계를 명시하고 각 메커니즘을 해당 모듈 문서로 넘긴다.
- (KO) PostgreSQL 스토리지 엔진 — 섹션 개요 — 스토리지 엔진 서브카테고리의 라우터 문서. OS 파일에서 시작해 위로 올라가는 계층 구조를 다룬다. smgr/md는 릴레이션을 1 GB 파일 세그먼트로 매핑하고, 공유 버퍼 풀은 메모리와 디스크 사이의 유일한 경유지이며(PG18 비동기 I/O가 그 밑에 깔린다), 모든 블록은 슬롯 단위 페이지(ItemId 라인 포인터 배열)이고, 그 위에 플러그인 가능한 접근 방법이 얹힌다. 구조적 핵심은 간접 계층이다. 테이블은 TableAmRoutine을 통해, 인덱스는 IndexAmRoutine을 통해 도달하므로, 힙의 온-페이지 레코드, HOT 가지치기, TOAST, 가시성 맵, 빈 공간 맵은 모두 공유 페이지/버퍼 바닥 위에 쌓인 힙 AM 고유의 관심사다.
- (KO) PostgreSQL 시스템 카탈로그와 캐시 — 섹션 개요 — 시스템 카탈로그 서브카테고리의 라우터 문서. PostgreSQL은 카탈로그 주도 엔진이다. 타입·연산자·함수·접근 방법·릴레이션이 모두 pg_* 시스템 테이블의 행이므로, 핵심 실행 경로의 과제는 그 테이블을 쿼리하는 것이 아니라 캐시하는 것이다. 카탈로그 앞에는 세 단계의 캐시가 쌓인다. 개별 튜플 조회를 담당하는 catcache(syscache는 그 위의 타입별 디스패치 계층), syscache/lsyscache 편의 접근자, 그리고 여러 카탈로그를 조합해 만든 릴레이션 전체 디스크립터를 보관하는 relcache가 그것이다. 각 백엔드가 사본을 사적으로 유지하기 때문에 네 번째 장치인 sinval 공유 무효화 루프가 정합성을 맡는다. 한 백엔드의 카탈로그 변경은 공유 메모리의 메시지 큐에 항목을 쌓고, 다른 모든 백엔드는 AcceptInvalidationMessages 지점에서 그것을 소비해 낡은 캐시를 버린다. 이 코어 주변에는 의존성 추적(pg_depend / pg_shdepend, DROP ... CASCADE 그래프)과 비정규화된 이름을 카탈로그 OID로 바꾸는 네임스페이스 / search_path 해석기가 자리한다. 이 문서는 계층 구조를 설명하고 여섯 개의 모듈 문서로 안내한다.
- (KO) PostgreSQL 트랜잭션과 복구 — 섹션 개요 — 트랜잭션·복구 서브카테고리의 라우터 문서. PostgreSQL의 트랜잭션 이야기는 공유 메모리 기반 위에서 맞물리는 두 기계로 이루어진다. 첫 번째는 동시성 기계다. procarray(공유 메모리 안의 실행 중 XID 목록)에서 스냅샷을 계산하고, SLRU 페이지 캐시 위의 clog에 커밋 상태를 기록하며, heapam_visibility에서 가시성을 판정하는 no-overwrite MVCC 힙이다. 두 번째는 내구성 기계다. WAL이 단일 redo 로그이며, xact가 커밋 프로토콜을 주도하고, rmgr 태그가 붙은 레코드를 startup 프로세스가 크래시·아카이브 복구 시 재생하며, checkpointer가 재생 범위를 제한하고, 2PC가 prepared 상태를 영속화한다. 이 순환을 닫는 것이 vacuum과 autovacuum이다. MVCC가 남긴 죽은 튜플 버전을 회수하고 XID 순환(wraparound)을 방어한다. 이 문서는 그 계층들의 이름, 열네 개 모듈 문서의 읽기 순서, 그리고 storage-engine·server-architecture·replication-ha와의 경계 접합부를 다룬다.
- (KO) PostgreSQL 유틸리티 — 섹션 개요 — 유틸리티 서브카테고리의 길잡이 문서. src/bin/ 아래 독립 실행형 프로그램들과 그 기반이 되는 빌드 타임 카탈로그 코드 생성기를 다룬다. 이 도구들의 공통점은 실행 중인 postmaster의 공유 메모리 머신 바깥에서 동작한다는 것이다. 클러스터에 접근하는 방식에 따라 네 계열로 묶인다. (1) 클러스터 생성: genbki.pl이 빌드 타임에 pg_*.h / pg_*.dat 카탈로그 소스를 postgres.bki로 변환하고, initdb가 부트스트랩 모드(bootstrap.c)로 독립 백엔드를 구동해 .bki를 재실행하고 컨트롤 파일을 기록한다. (2) FE/BE 프로토콜을 통한 논리 내보내기/마이그레이션: pg_dump/pg_restore/pg_dumpall이 스키마와 데이터를 SQL 또는 아카이브로 재구성하고, pg_upgrade는 스키마 전용 덤프와 relfilenode 물리 교체를 조율한다. (3) WAL과 데이터 파일을 직접 소비하는 물리 백업/복구 도구: pg_basebackup, pg_combinebackup(증분 복원), pg_rewind. (4) 검사·제어 도구: pg_waldump는 백엔드의 rmgr desc 콜백을 재사용해 WAL을 디코딩하고, psql은 대화형 클라이언트이며, pg_ctl/pg_controldata는 postmaster 생명주기와 pg_control 읽기를 감싼다.
- (KO) PostgreSQL KO 번역 현황 — 문서별 단계 보드 — `knowledge/ko/code-analysis/postgres/` 아래 KO 미러들의 단계 보드다. 5단계 모델(P0 직역 미러 → P1 S1 기계 클린업 → P2 구조 재작성 → P3 자연스러운 한국어 prose → P4 검수 통과)의 정의와 마커 규약은 `knowledge/code-analysis/cubrid/cubrid-translation-status.md`(모델의 단일 출처)와 `knowledge/methodology/korean-translation.md`에 있다. 이 문서는 PostgreSQL에 적용한 사례다. PostgreSQL KO 미러는 sonnet 단일 패스로 처음부터 한국어 병렬 구성으로 작성했고 `kb-ai-tell-check`로 S1을 0으로 정리했다. 그래서 cubrid처럼 P0→P1(직역 후 클린업) 경로를 거치지 않고 곧바로 P2에서 출발한다. P3(전용 prose 다듬기)와 P4(원어민 검수)는 아직 남아 있고, 문서별 frontmatter 단계 태그는 아직 찍지 않았다(추적 중인 후속 작업).
Base / Infrastructure (7)
- (KO) PostgreSQL 데이터타입 라이브러리 — varlena, numeric, datetime, jsonb, 배열 — PostgreSQL의 모든 SQL 타입은 pg_type 행 하나와 pg_proc에 등록된 C 함수 묶음으로 정의된다. utils/adt 아래의 ADT 라이브러리는 그 함수들의 구현체다. 타입의 계약은 네 개의 I/O 함수 쌍(in/out/recv/send)과 비교·해시·SortSupport 함수이며, 모두 fmgr의 V1 PG_FUNCTION_ARGS 규약을 따른다. 가변 길이 타입은 varlena로 표현되고, 첫 바이트 하위 비트가 4-byte 정렬 헤더, 1-byte 단축 헤더, 인라인 압축, TOAST 포인터의 네 가지 물리 레이아웃을 구분한다. numeric은 base-10000 NBASE 자리 배열에 packed short/long/special 헤더를 얹은 임의 정밀도 십진수 타입이다. date와 timestamp는 고정 길이 정수 타입으로 Julian-day 커널(date2j/j2date)을 통해 달력 변환을 격리한다. jsonb는 JEntry 배열과 가변 길이 페이로드로 이루어진 이진 문서 트리이며, 32번째마다 누적 오프셋을 저장해 O(1) 접근과 압축 효율을 동시에 확보한다. 배열은 ArrayType 헤더에 차원 정보, 선택적 null 비트맵, 엘리먼트 타입 OID, 팩된 데이터를 담는 varlena 구조다. 이 문서는 I/O 쿼텟 계약, varlena 헤더 형태, numeric 자릿수 산술, datetime Julian 커널, jsonb 이진 레이아웃, 배열 내부 구조를 다루며, fmgr 디스패치는 postgres-fmgr.md에, 아웃오브라인 저장·압축은 postgres-toast.md에 위임한다.
- (KO) PostgreSQL 동적 해시 테이블(dynahash) — Larson 선형 해싱, 파티션 공유 해시, simplehash 템플릿 — dynahash는 PostgreSQL의 범용 체인 해시 테이블로, 백엔드 로컬 테이블(palloc, 자동 확장)과 명명된 공유 메모리 테이블(고정 디렉터리, 프로세스 간 탐색 가능) 모두를 같은 코드로 처리한다. Larson 1988 동적 해싱을 구현하며, 한 번에 정확히 하나의 버킷을 분할해 stop-the-world 리해시 없이 점진적으로 확장한다. HASH_PARTITION 모드는 버킷 분할을 포기하는 대신 NUM_FREELISTS 스핀락 슬라이스로 동시성을 확보하며, 이 방식으로 버퍼 매니저의 SharedBufHash와 락 매니저의 LOCK/PROCLOCK 테이블이 병렬 접근을 지원한다. 로컬 핫패스용 simplehash.h는 매크로 템플릿 기반 오픈 어드레싱(로빈후드)으로, 안정 포인터와 파티셔닝을 포기하는 대신 인라인 비교와 포인터 추적 제거로 속도를 얻는다.
- (KO) PostgreSQL 에러 처리 — ereport/elog, PG_TRY/sigsetjmp, ErrorContext 탈출구 — PostgreSQL은 모든 진단 메시지를 2단계 ereport/elog 파이프라인으로 처리한다. errstart()는 깊이 5짜리 ErrorData 스택에 슬롯을 할당하고 메시지 처리 여부를 결정한다. errfinish()는 포매팅과 발송을 수행하며, ERROR 수준에서는 PG_exception_stack을 통해 siglongjmp로 가장 가까운 PG_TRY 핸들러로 점프한다. FATAL은 proc_exit()로, PANIC은 abort()로 종료한다. 전용 ErrorContext 메모리 컨텍스트를 미리 예약해 두어 메모리 부족 에러도 항상 보고할 수 있다. ErrorContextCallback 훅은 호출 스택 설명을 덧붙이고, errsave/ereturn는 입력 검증 코드를 위한 비(非)longjmp 소프트 에러 경로를 제공한다. Assert()는 컴파일 타임 옵션으로, 에러 처리 재진입 문제를 피하기 위해 elog를 완전히 우회하여 ExceptionalCondition()을 호출한다.
- (KO) PostgreSQL 함수 매니저(fmgr) — V1 호출 규약, 조회 파이프라인, 확장 ABI — PostgreSQL이 모든 SQL 함수 호출을 FmgrInfo 조회와 FunctionCallInfo 호출의 두 단계 파이프라인으로 처리하는 방식. 내장 함수는 코드 생성 OID 인덱스로, C 확장은 dlopen 캐시로, 그 외 언어는 call handler로 분기하며, PG_FUNCTION_INFO_V1 선언과 Pg_magic_struct ABI 검사가 모듈 호환성을 강제하는 구조.
- (KO) PostgreSQL GUC 파라미터 — config_generic, PGC 컨텍스트, SET/SHOW, Check/Assign 훅, SIGHUP 리로드 — PostgreSQL의 런타임 설정 시스템(GUC — Grand Unified Configuration)은 테이블 구동 방식의 단일 메커니즘으로 플래너 토글부터 WAL 크기까지 모든 조정 가능한 파라미터를 관리한다. 각 파라미터는 정적으로 초기화된 config_bool/int/real/string/enum 레코드로 표현되며, 첫 번째 멤버인 config_generic 헤더가 파라미터 이름, GucContext(PGC_INTERNAL / PGC_POSTMASTER / PGC_SIGHUP / PGC_SU_BACKEND / PGC_BACKEND / PGC_SUSET / PGC_USERSET — 변경 권한과 타이밍을 결정하는 계층 구조), 플래그 워드, 런타임 상태(현재 소스, RESET 값, 저장된 값 스택)를 담는다. build_guc_variables()가 다섯 개의 타입별 배열을 하나의 대소문자 무관 dynahash로 통합하고, find_option()이 모든 조회를 이 해시로 처리하면서 커스텀(점 포함) 이름에 대한 플레이스홀더 레코드를 자동 생성한다. 쓰기 경로는 set_config_with_handle()이라는 단일 진입점으로 통합되며, SQL SET(ExecSetVariableStmt)와 설정 파일(ProcessConfigFile) 모두 이 경로를 거친다.
- (KO) PostgreSQL 메모리 컨텍스트 — 계층형 영역 할당과 longjmp 안전 정리 — PostgreSQL은 백엔드 전용 메모리를 거의 모두 TopMemoryContext를 루트로 하는 MemoryContext 포리스트에서 할당한다. palloc()은 CurrentMemoryContext에서 가져오고, 컨텍스트를 리셋하거나 삭제하면 그 안의 모든 청크가 한 번의 호출로 해제된다. 에러 처리가 이 메커니즘 위에서 동작한다. elog/longjmp 경로는 트랜잭션 컨텍스트들을 삭제하는 것만으로 메모리를 회수한다. 컨텍스트는 추상 헤더 MemoryContextData와 메서드 vtable로 구성된다. 구체적 할당기는 네 가지다 — AllocSet(범용, 2의 거듭제곱 크기 클래스 프리리스트), Slab(고정 크기 청크), Generation(FIFO/수명 그룹, 청크 재사용 없음), Bump(헤더 없음, pfree 불가). 네 할당기는 모두 청크 헤더에 1바이트 MemoryContextMethodID를 인코딩해 pfree()가 포인터만으로 올바른 free 루틴을 찾을 수 있게 한다.
- (KO) PostgreSQL ResourceOwner — 계층적 자원 추적과 에러 안전 해제 — ResourceOwner는 PostgreSQL이 C 언어에서 RAII를 직접 구현한 객체다. 트랜잭션·서브트랜잭션·포털 단위로 생성되며, 해당 범위에서 획득한 버퍼 핀·락·릴레이션캐시 참조·열린 파일·스냅샷·JIT 컨텍스트·DSM 세그먼트·AIO 핸들을 기억해 두었다가 트랜잭션 종료 시(커밋 또는 어보트) 모두 해제한다. 소유자들은 트랜잭션·포털 중첩을 반영하는 포레스트(forest)를 이루며, 부모를 해제할 때 자식을 먼저 재귀 처리한다. 각 자원 종류는 릴리스 단계(BEFORE_LOCKS, LOCKS, AFTER_LOCKS)와 우선순위를 선언하는 ResourceOwnerDesc를 등록하고, xact.c가 단계별로 세 번 ResourceOwnerRelease를 호출해 정해진 순서를 보장한다. 스토리지 레이어는 32슬롯 고정 배열과 넘칠 때 쓰는 개방 주소 해시, 그리고 서브트랜잭션 커밋 시 락을 부모에게 일괄 이전하는 15엔트리 손실형 락 캐시로 구성된다. 이 문서는 ResourceOwnerData 구조체, Remember/Forget, 배열→해시 스필, 3단계 정렬 해제, 락 캐시, xact.c/portalmem.c 연결을 다룬다. MemoryContext는 postgres-memory-contexts.md, elog/PG_TRY 기계는 postgres-error-handling.md, 버퍼 핀 내부는 postgres-buffer-manager.md로 위임한다.
Storage Engine (20)
- (KO) PostgreSQL 비동기 I/O — PG18 AIO 서브시스템, io_uring, 읽기 스트림 — PG18이 도입한 비동기 I/O 서브시스템 분석. AIO 핸들(PgAioHandle)의 8단계 상태 머신과 수명 주기, 세 가지 I/O 메서드(sync / worker / io_uring)의 vtable 설계, 배치 제출과 교착 회피 규칙, 콜백-정수-ID 체계와 오류 전파 모델, 그리고 대부분의 호출자가 실제로 쓰는 ReadStream의 적응형 선독(look-ahead) 로직을 다룬다. 버퍼 풀 통합은 postgres-buffer-manager.md에, 세그먼트·fd 변환은 postgres-smgr-md.md에 위임한다.
- (KO) PostgreSQL BRIN — 블록 범위 인덱스와 범위 맵 — BRIN(Block Range INdex)은 자연 정렬된 초대형 테이블을 위한 손실(lossy) 블록 단위 인덱스다. 힙 튜플마다 항목을 만드는 대신, 기본값 128 페이지 단위인 페이지 범위(page range)마다 요약 튜플 하나를 저장한다. minmax 연산 클래스에서는 칼럼별 min/max와 null 플래그를 보관한다. 메타페이지 바로 뒤 인덱스 블록에 위치하는 범위 맵(revmap)은 HEAPBLK_TO_REVMAP_BLK / HEAPBLK_TO_REVMAP_INDEX 산술로 힙 블록에서 요약 튜플 TID를 O(1)에 찾아준다. BRIN은 amgettuple을 지원하지 않으며, amgetbitmap(bringetbitmap)만 구현해 revmap을 순서대로 걷고 일치 범위의 모든 페이지를 손실 TIDBitmap에 추가한다. 삽입은 기존 요약을 brin_doupdate로 제자리 확장하고, 새 범위는 VACUUM·brin_summarize_new_values·autosummarize가 summarize_range를 호출할 때까지 미요약 상태로 둔다.
- (KO) PostgreSQL 버퍼 매니저 — 공유 풀, Clock-Sweep 교체, WAL-Before-Flush 규칙 — PostgreSQL이 디스크 페이지를 고정 크기 공유 풀에 캐싱하는 방식을 분석한다. refcount+usagecount+flags를 하나의 atomic 워드에 압축한 BufferDesc 배열, (spc,db,rel,fork,block) 다섯 필드로 구성된 파티션 분할 버퍼 매핑 해시, 콘텐츠 락과 분리된 핀/언핀 참조 카운팅, usagecount를 감소시키며 손을 돌리는 clock-sweep 피해자 선택(StrategyGetBuffer/GetVictimBuffer), 대량 스캔을 소수의 프레임에 가두는 BAS_* 링 버퍼, 그리고 페이지를 쓰기 전 반드시 XLogFlush를 호출하는 WAL 규칙(FlushBuffer)이 steal/no-force 복구를 성립시키는 원리를 다룬다.
- (KO) PostgreSQL 데이터 체크섬 — 페이지 무결성 검증 — PostgreSQL 데이터 체크섬은 클러스터 전체에 적용되는 선택적 무결성 방어선으로, initdb -k 또는 pg_checksums로 활성화하면 모든 힙·인덱스·FSM·VM 페이지의 헤더 필드 pd_checksum에 16비트 체크섬을 기록한다. 알고리즘(FNV-1a 기반 32-병렬 SIMD 친화 구현), 쓰기 경로(PageSetChecksumCopy / PageSetChecksumInplace), 읽기 검증(PageIsVerified), 클러스터 활성화 플래그(pg_control의 data_checksum_version), 실패 보고(pg_stat_database.checksum_failures) 다섯 부분의 계약을 다룬다.
- (KO) PostgreSQL Free Space Map — 페이지별 재사용 가능 공간 추적 — Free Space Map(FSM, 여유 공간 맵)은 힙 릴레이션 전체를 스캔하지 않고도 O(log n) 페이지 읽기로 여유 공간이 있는 페이지를 찾게 해주는 별도 관계 fork다. 페이지당 여유 공간을 1바이트(256 단계)로 양자화하고, 그 바이트를 2단계 트리로 배열한다. FSM 페이지 안에서는 이진 최대 힙으로, FSM 페이지 사이에서는 팬아웃 트리로 조직된다. 검색은 라운드로빈 시작 슬롯(fp_next_slot)을 기점으로 '탐색 삼각형을 오른쪽으로 확장'하여 동시 백엔드들이 삽입 지점을 분산시킨다. WAL 로깅을 의도적으로 하지 않고, 여러 자기 치유 수단으로 lossy 근사값을 교정한다.
- (KO) PostgreSQL GIN — 일반화 역색인, 포스팅 트리, Fast-Update 목록 — GIN은 PostgreSQL의 역색인 AM이다. 배열 원소, tsvector 어휘소, jsonb 경로처럼 복합 항목에서 추출한 키를 색인한다. 온디스크 구조는 키 항목의 Lehman-Yao B-트리(엔트리 트리)이며, 리프 튜플은 힙 TID의 인라인 압축 포스팅 목록 또는 포스팅 트리 다운링크를 담는다. 소매 삽입 비용을 줄이기 위해 GIN은 opt-in fast-update 경로를 제공한다. gininsert는 새 항목을 메타페이지에 앵커된 미정렬 펜딩 목록에 추가하고 반환하며, 나중에 ginInsertCleanup이 이를 일괄 병합한다. 스캔은 펜딩 목록을 먼저 선형 읽고 메인 인덱스로 이어진다. 쿼리는 extractQuery와 consistent 함수로 구동되고, keyGetItem이 TID 스트림을 TID 순서로 병합하며 consistent 함수로 행 일치 여부를 판정한다. VACUUM은 포스팅 목록·트리에서 죽은 TID를 제거하고 빈 포스팅 트리 페이지를 회수하지만 엔트리 트리 페이지는 삭제하지 않는다.
- (KO) PostgreSQL GiST — 일반화 탐색 트리와 Opclass 지원 함수 — GiST(Generalized Search Tree, 일반화 탐색 트리)는 PostgreSQL의 프레임워크형 인덱스 AM이다. 균형 잡힌 우-연결(right-linked) 트리 골격과 충돌 안전 동시성은 AM이 제공하고, 키의 의미는 opclass가 소수의 지원 함수로 공급한다. 지원 함수는 consistent(키/술어가 쿼리와 겹치는가), union(자식 집합의 경계 술어), penalty(키를 삽입할 때의 확장 비용), picksplit(꽉 찬 페이지의 분할 방법), compress/decompress, equal, distance(k-NN ORDER BY), fetch(인덱스 전용 스캔)로 구성된다. gisthandler가 이 함수들을 IndexAmRoutine에 연결하고, initGISTstate가 각 FmgrInfo를 캐싱한다. 삽입은 gistdoinsert가 최소 penalty 경로(gistchoose/gistpenalty)를 따라 하강하며 내려오는 길에 다운링크를 조정해 트리 일관성을 유지한다. 꽉 찬 페이지는 gistplacetopage가 opclass PickSplit(gistSplitByKey)으로 분할하고, WAL 원자 단계로 분해한다. 새 오른쪽 페이지에 F_FOLLOW_RIGHT 플래그를 세운 뒤 부모에 다운링크를 올리고 NSN을 설정하는 두 단계 구조다. 검색(gistScanPage)은 한 번에 페이지 하나만 잠그며 NSN 대 부모 LSN 비교로 동시 분할을 감지하고 right-link를 추적한다. 빌드는 SortSupport 기반 바텀업 정렬 빌드, 캐시 인식 버퍼링 빌드(Arge et al.), 단순 삽입 빌드 중 하나를 선택한다.
- (KO) PostgreSQL 해시 인덱스 — 선형 해싱, 버킷, 오버플로 페이지 — PostgreSQL 해시 AM은 Seltzer-Yigit(1991) 선형 해시 테이블을 디스크에 구현한다. 키의 32비트 해시 코드를 마스크로 버킷 번호로 변환하며, 테이블은 한 번에 버킷 하나씩 증분 성장한다. 각 버킷은 영구 primary 페이지와 재활용 가능한 오버플로 페이지의 체인이다. 메타페이지(block 0)는 live 버킷 수(hashm_maxbucket), highmask/lowmask 쌍, hashm_spares[] 분할점 배열을 저장해 BUCKET_TO_BLKNO가 디렉터리 없이 물리 블록을 계산하게 한다. 분할 시 old 버킷의 튜플을 복사·INDEX_MOVED_BY_SPLIT 마킹하고, 동시 스캔은 primary 버킷 페이지 핀을 유지한 채 페이지 전체 매칭 결과를 로컬 스토리지에 복사해 인덱스 락 없이 힙을 접근한다.
- (KO) PostgreSQL 힙 접근 방법 — 덮어쓰지 않는 MVCC 튜플, 슬롯 페이지, HOT — PostgreSQL의 기본 테이블 접근 방법은 슬롯 구조의 힙 페이지에 모든 행 버전을 그 자리에 남겨 두는 방식이다. INSERT는 xmin이 찍힌 튜플을 쓰고, UPDATE/DELETE는 그 자리를 덮어쓰지 않고 xmax를 찍은 뒤 UPDATE의 경우 새 버전을 따로 기록하며 이전 튜플의 t_ctid가 앞으로 이어 준다. 가시성 판단은 튜플마다 xmin/xmax와 스냅샷을 대조해 결정하므로 죽은 버전이 쌓이고, HOT 정리(단일 페이지)나 vacuum이 그것을 회수한다. HOT은 인덱스 컬럼을 건드리지 않는 UPDATE에 한해 새 버전을 같은 페이지에 두고 인덱스 항목을 추가하지 않으며, 루트 라인 포인터에서 체인을 걸어 인덱스가 여전히 찾아올 수 있게 한다.
- (KO) PostgreSQL 인덱스 접근 방법 — IndexAmRoutine과 제네릭 인덱스 계층 — 인덱스 접근 방법(Index AM) API는 PostgreSQL의 제네릭 인덱스 계층과 개별 인덱스 구현체(btree, hash, GiST, GIN, SP-GiST, BRIN) 사이의 계약을 정의한다. IndexAmRoutine은 각 인덱스 릴레이션의 rd_indam 필드에 저장되는 함수 포인터 테이블이고, indexam.c는 디스패처 코드가 호출하는 안정적인 index_ 래퍼를 제공하며, genam.c는 스캔 디스크립터 할당과 시스템 카탈로그 스캔 헬퍼를 담당한다. index_beginscan은 AM 스캔과 힙 측의 table_index_fetch_begin 세션을 함께 열고, index_getnext_tid가 amgettuple을 구동하며, index_fetch_heap이 table_index_fetch_tuple을 호출해 각 TID를 가시적 힙 튜플로 변환한다.
- (KO) PostgreSQL Large Objects — pg_largeobject 청크 저장과 lo API — PostgreSQL의 대형 객체(large object, LO) 기능은 최대 4 TB의 바이트 스트림을 LOBLKSIZE = BLCKSZ/4 = 2 KB 단위로 잘라 pg_largeobject 시스템 카탈로그의 (loid, pageno) 키 튜플로 저장한다. 별도의 pg_largeobject_metadata 행이 소유자와 ACL을 기록하므로 객체는 0바이트 상태에서도 존재하고 권한 검사를 받는다. inv_api.c의 inversion API(inv_create/open/read/write/lseek/tell/truncate/drop)는 이 카탈로그 위에 POSIX 파일 추상화를 제공한다. 읽기는 (loid, pageno >= start) btree 인덱스 스캔으로 청크를 순서대로 가져오며 빈 구간(hole)을 0으로 채운다. 쓰기는 2 KB 청크마다 읽기-수정-쓰기(heap_modify_tuple + CatalogTupleUpdate)를 수행한다. 모든 청크가 일반 MVCC 힙 튜플이므로 대형 객체는 트랜잭션 보장과 충돌 복구를 추가 코드 없이 얻는 대신 MVCC 블로트와 쓰기 증폭을 감수한다. be-fsstubs.c의 be_lo_* fmgr 래퍼는 per-transaction 파일 디스크립터 테이블(fscxt 내 cookies 배열), 스냅샷 등록, lo_import/lo_export, lo_get/lo_put 전체 객체 헬퍼를 추가한다.
- (KO) PostgreSQL B-Tree (nbtree) — Lehman-Yao 동시성, 페이지 분할, 인덱스 튜플 삭제 — PostgreSQL의 기본 인덱스 AM은 Lehman-Yao B-트리다. 각 페이지는 오른쪽 형제 페이지를 가리키는 right-link와 자신의 키 공간 상한선인 high key를 갖는다. 덕분에 한 번에 페이지 하나만 잠근 채로도 동시 분할을 감지하고 right-link를 따라 복구할 수 있다. 삽입은 래치 연결(latch-coupling)로 하강하고, 꽉 찬 페이지는 단일 WAL 액션으로 분할된다. 인덱스 튜플은 LP_DEAD 힌트 마킹, simple/bottom-up 삭제, VACUUM의 btbulkdelete로 지연 회수된다. 빈 페이지는 two-stage half-dead 프로토콜로 삭제하고 스냅샷 참조가 없어질 때까지 FSM 반환을 미룬다. 중복 non-pivot 튜플은 posting-list 튜플로 접어 split 직전 최후 수단으로 활용된다.
- (KO) PostgreSQL 페이지 레이아웃 — 슬롯 페이지 구조, ItemId 간접 참조, 체크섬 메커니즘 — PostgreSQL이 고정 크기 디스크 페이지를 구성하는 방식을 분석한다. pd_lsn/pd_lower/pd_upper/pd_special 오프셋을 담은 PageHeaderData, 증가하는 ItemId(라인 포인터) 배열과 감소하는 튜플 영역이 만나는 자유 공간 갭, 네 가지 LP_* 상태를 비트 필드로 인코딩하는 ItemIdData, 힙을 가로지르는 6바이트 TID(ItemPointerData), 그리고 블록 번호를 섞어 스토리지 오염을 감지하는 16비트 병렬 FNV-1a 체크섬 메커니즘을 다룬다.
- (KO) PostgreSQL 시퀀스 — 릴레이션 기반 생성기, 캐싱, 그리고 WAL — PostgreSQL 시퀀스는 특별한 in-memory 카운터가 아니라 block 0에 FormData_pg_sequence_data {last_value, log_cnt, is_called} 튜플 하나를 보유한 RELKIND_SEQUENCE 릴레이션이다. 불변 파라미터(start, increment, min, max, cache, cycle, typid)는 SEQRELID syscache로 접근하는 pg_sequence 카탈로그 행에 따로 존재한다. nextval_internal 핫 경로는 relid를 키로 하는 백엔드별 SeqTable 해시 항목을 참조해 [last, cached] 윈도우가 남아 있으면 버퍼·잠금·WAL 없이 즉시 반환하고, 소진 시에만 block 0을 ex-lock 후 산술 연산과 WAL 기록을 수행한다. WAL 비용 절감을 위해 SEQ_LOG_VALS(32)개 만큼 미리 로그를 기록해 다음 32회 정도의 캐시 재충전은 추가 WAL 없이 log_cnt를 감소시킨다. 이 설계로 충돌 시 미발급 값은 영구 갭이 되지만 재발급은 절대 발생하지 않는다. ALTER SEQUENCE와 ResetSequence는 RelationSetNewRelfilenumber로 새 스토리지 파일을 만들어 트랜잭션성을 확보하고, seq_redo는 block 0을 palloc 워크스페이스에서 재초기화한 후 memcpy로 삽입해 핫-스탠바이 동시 읽기에도 찢긴 페이지가 노출되지 않는다.
- (KO) PostgreSQL smgr & md — 스토리지 매니저 스위치와 자기 디스크 드라이버 — PostgreSQL가 백엔드별 SMgrRelation 핸들 테이블(smgr.c)과 그 아래 유일한 구현체(md.c)를 통해 모든 페이지 수준 I/O를 라우팅하는 방식. md.c는 논리 주소(릴레이션+포크+블록)를 POSIX 파일 세그먼트에 매핑하고, 포크별 세그먼트 벡터를 관리하며, fsync 의무를 sync 서브시스템을 통해 체크포인터에 위임한다.
- (KO) PostgreSQL SP-GiST — 공간 분할 트리의 디스크 매핑 — SP-GiST(공간 분할 GiST)가 포인터 연결 불균형 트리(쿼드트리·k-d 트리·래딕스 트라이)를 고정 크기 디스크 페이지에 매핑하는 방식을 분석한다. inner tuple(선택적 prefix + node 배열)과 leaf tuple(leaf datum + heap TID + nextOffset 체인 링크)의 물리적 구조, 체인 단일 페이지 불변식, 다섯 opclass 메서드(config/choose/picksplit/inner_consistent/leaf_consistent) 책임 분할, 조건부 래치 커플링과 triple-parity 배치로 데드락을 억제하는 동시성 프로토콜, redirect/placeholder 4-state 래티스, pending list 기반 VACUUM 순차 스캔을 다룬다.
- (KO) PostgreSQL 테이블 접근 방법 — TableAmRoutine을 통한 플러그형 스토리지 디스패치 — PostgreSQL 12에서 도입된 테이블 접근 방법(Table Access Method) API는 각 릴레이션의 relcache 항목에 함수 포인터 테이블(TableAmRoutine)을 두어, 익스큐터가 table_tuple_insert·table_beginscan·table_scan_getnextslot를 호출할 때 해당 릴레이션이 생성된 스토리지 구현으로 디스패치되도록 한다. REL_18 기준 인-트리 AM은 heap(heapam_methods)뿐이다. 계약은 여섯 영역으로 구성된다: 슬롯 콜백, 순차 스캔 생명주기, 인덱스 페치, DML(insert/update/delete/lock), DDL·vacuum·analyze, 기타 크기 관련 함수. TM_Result는 공유 DML 결과 코드 체계이고, ScanOptions 플래그는 스캔 유형과 동작 힌트를 scan_begin에 전달한다.
- (KO) PostgreSQL 테이블 샘플링 — TSM API, SYSTEM과 BERNOULLI — PostgreSQL은 SQL:2003 TABLESAMPLE 절을 플러그인 가능한 인터페이스인 TSM(Tablesample Method) API로 구현한다. 메서드는 TsmRoutine 콜백 구조체를 반환하는 핸들러 함수로 정의된다. SampleScanGetSampleSize는 플래너에 페이지·튜플 추정치를 제공하고, BeginSampleScan은 퍼센트 인수를 정수 cutoff로 변환하며, NextSampleBlock은 읽을 힙 블록을 고르고, NextSampleTuple은 블록 내 오프셋을 고른다. 실행기 노드 nodeSamplescan.c가 테이블 AM의 scan_sample_next_block / scan_sample_next_tuple을 거쳐 이 콜백들을 구동하므로 동일한 TSM이 모든 테이블 AM에서 동작한다. 코어에 두 메서드가 내장된다. SYSTEM은 블록 단위로 샘플링하여 수락된 페이지의 모든 행을 반환하므로 일부 페이지만 읽어 저렴하지만 클러스터링된 표본을 만든다. BERNOULLI는 행 단위로 샘플링하여 모든 페이지를 읽어야 하지만 각 튜플을 독립적으로 판정하므로 진정한 단순 임의 표본을 생성한다. 두 메서드 모두 후보 식별자와 시드를 해시한 뒤 32비트 해시 값을 cutoff와 비교하여 이력 독립적이고 재현 가능한 샘플링을 달성한다.
- (KO) PostgreSQL TOAST — 대형 속성 저장, 압축, 역직렬화 — TOAST(The Oversized-Attribute Storage Technique)란 페이지 경계를 넘는 가변 길이 속성을 저장하는 PostgreSQL의 메커니즘이다. 약 2 KB를 넘는 속성이 들어오면 4단계 토스터가 동작한다. 먼저 인라인 PGLZ 또는 LZ4 압축을 시도하고, 그래도 크기가 크면 TOAST_MAX_CHUNK_SIZE 단위로 조각을 잘라 per-relation pg_toast_<OID> 힙에 저장한다. 인라인 Datum은 18바이트 varatt_external 포인터로 교체된다. 역직렬화(detoasting)는 필요 시점에 투명하게 일어나며, detoast_attr가 (valueid, chunkidx) 인덱스 스캔으로 조각을 가져와 재조합한다. 컬럼별 저장 전략(PLAIN/MAIN/EXTENDED/EXTERNAL)이 압축 및 외부 저장 시도 여부를 결정한다.
- (KO) PostgreSQL Visibility Map — 진공 건너뜀과 인덱스 전용 스캔을 가능하게 하는 페이지당 2비트 비트맵 — PostgreSQL의 visibility map(가시성 맵)이 힙 페이지당 2비트(all-visible, all-frozen)를 전용 fork에 저장해 VACUUM이 깨끗한 페이지를 건너뛰고 인덱스 전용 스캔이 힙 패치를 생략할 수 있게 하며, 힙 페이지의 PD_ALL_VISIBLE 플래그와 협조적 WAL 로깅으로 두 비트를 크래시 안전하게 유지하는 방식.
Transaction & Recovery (14)
- (KO) PostgreSQL Autovacuum — 런처, 워커, 그리고 안티-랩어라운드 스케줄링 — PostgreSQL이 전역 타임테이블 없이 유지보수를 스케줄링하는 방식: 단일 런처 프로세스가 공유 메모리 안의 데이터베이스별 라운드-로빈 목록을 관리하고 postmaster에 신호를 보내 단명(短命) 워커를 포크하며, 각 워커는 pg_class를 스캔해 테드 튜플·삽입 건수가 임계식(base + scale × reltuples)을 넘었는지, 혹은 relfrozenxid/relminmxid가 freeze age를 넘어 안티-랩어라운드 강제 vacuum이 필요한지를 판정하고, 스케줄 락으로 테이블을 점유하며, 공유 I/O 비용 예산을 실행 중인 워커 수로 나누어 각자의 cost limit을 공유 메모리에 기록한다.
- (KO) PostgreSQL 체크포인트 — 내구성 앵커: redo 포인터 고정, 버퍼 플러시, WAL 세그먼트 재활용 — PostgreSQL 체크포인트는 checkpointer 보조 프로세스(B_CHECKPOINTER)가 전담한다. 정상 운영에서는 CreateCheckPoint가 XLOG_CHECKPOINT_REDO로 redo 포인터를 먼저 고정한 뒤 CheckPointGuts로 더티 버퍼와 SLRU를 플러시하고 XLOG_CHECKPOINT_ONLINE 레코드를 기록한 다음 pg_control(ControlFileData)을 갱신하고 오래된 WAL 세그먼트를 재활용한다. 복구 중에는 CreateRestartPoint가 동일한 CheckPointGuts 경로를 따라 실행된다. CheckpointerShmemStruct 요청 큐는 백엔드가 fsync 요청을 전달하고 ckpt_started/ckpt_done 조건 변수로 체크포인트 완료를 기다릴 수 있게 한다. 체크포인트 페이싱은 checkpoint_completion_target, CheckpointWriteDelay, IsCheckpointOnSchedule이 함께 제어한다.
- (KO) PostgreSQL CLOG & 커밋 타임스탬프 — 트랜잭션 상태 비트맵, 서브트랜잭션 부모 추적, commit_ts SLRU — pg_xact(CLOG)는 XID마다 2비트 커밋/중단/서브커밋 상태를 SLRU 페이지에 저장하며, 동시 커밋이 많을 때 그룹 업데이트 최적화로 단일 뱅크 락 획득 아래 여러 백엔드의 상태를 일괄 기록한다. pg_subtrans는 각 서브트랜잭션의 즉시 부모 XID를 저장하며, 크래시에 살아남지 않아도 되는 휘발성 구조다. pg_commit_ts는 track_commit_timestamp가 활성화된 경우에만 XID마다 10바이트(TimestampTz + RepOriginId)를 세 번째 SLRU에 기록한다. 세 모듈 모두 postgres-slru 기반 위에 얹힌 단순한 클라이언트다.
- (KO) PostgreSQL MultiXact — 하나의 튜플에 여러 잠금 보유자와 갱신자 — 힙 튜플의 xmax 필드는 하나의 XID만 담을 수 있지만, 행 수준 잠금은 여러 트랜잭션이 동시에 호환 가능한 잠금을 보유하도록 허용한다. PostgreSQL은 이 다대일 관계를 MultiXactId(MXID)라는 간접 참조로 해결한다. MXID는 offsets SLRU와 members SLRU 두 개의 병렬 SLRU로 뒷받침되며, 여섯 가지 멤버 상태(ForKeyShare~Update)로 잠금 강도와 갱신 여부를 구분한다. MultiXactIdCreate은 두 XID를 두 멤버짜리 멀티로 묶고, MultiXactIdExpand는 기존 멀티를 읽어 죽은 멤버를 제거한 뒤 새 MXID를 발급한다(불변 규칙). MXID 공간과 멤버 오프셋 공간은 각각 독립된 32비트 순환 도메인이며, VACUUM은 FreezeMultiXactId로 두 공간을 동시에 관리한다.
- (KO) PostgreSQL MVCC 스냅샷 — 가시성, procarray 조사, 격리 수준 — PostgreSQL의 MVCC 스냅샷은 SnapshotData 구조체다. xmin, xmax, 그리고 진행 중인 XID 목록인 xip[] 배열로 이루어지며, GetSnapshotData가 ProcArrayLock을 잡고 procarray를 한 번 순회해 구성한다. HeapTupleSatisfiesMVCC는 튜플의 xmin/xmax를 스냅샷과 비교해 가시성을 결정하고, 그 결과를 infomask 힌트 비트에 캐시한다. READ COMMITTED는 문장마다 새 스냅샷을 취하고, REPEATABLE READ는 트랜잭션 전체에서 하나의 스냅샷을 재사용한다. 모든 백엔드의 xmin 중 가장 오래된 값이 죽은 버전 회수를 제한하는 vacuum 수평선이 된다.
- (KO) PostgreSQL procarray — PGPROC 슬롯 배치, XID 게시, 스냅샷 인구조사 — procarray는 모든 백엔드의 PGPROC 슬롯을 담는 공유 메모리 배열이다. ProcGlobal->xids[] 같은 밀집 배열(dense array)로 XID를 게시하고, GetSnapshotData가 ProcArrayLock 공유 잠금 아래 한 번의 순회로 스냅샷을 구성한다. 커밋 시 ProcArrayEndTransaction이 xid 슬롯을 소거하고 xactCompletionCount를 올려 재사용 최적화를 활성화한다. 핫 스탠바이에서는 KnownAssignedXids 배열이 기본 서버의 진행 중 XID를 대역한다.
- (KO) PostgreSQL 복구와 Redo — 크래시 복구, PITR, 그리고 Hot Standby — PostgreSQL 복구는 세 가지 모드(크래시, 아카이브/PITR, 스탠바이)를 하나의 스타트업 프로세스 루프로 통합한다. InitWalRecovery가 pg_control과 시그널 파일을 읽어 모드를 결정하고, PerformWalRecovery가 ReadRecord/ApplyWalRecord를 반복해 복구 목표 또는 WAL 끝에 도달할 때까지 재실행하며, FinishWalRecovery가 WAL 끝 지점을 확정해 정상 운영을 시작한다. ApplyWalRecord는 디코딩된 레코드를 각 rmgr의 rm_redo 콜백으로 보내고, 멱등성 불변성(PageLSN >= 레코드 LSN이면 건너뜀)을 강제하며, XLogRecoveryCtlData의 lastReplayedEndRecPtr 수위 마커를 갱신하고, 타임라인 전환을 감지한다. WAL 프리페처(XLogPrefetcher / LsnReadQueue)는 디코딩된 레코드를 선행 읽기해 참조되는 버퍼 페이지에 비동기 커널 읽기 힌트를 발행함으로써 I/O 지연을 redo CPU 작업 뒤에 숨긴다. 타임라인 관리(readTimeLineHistory / writeTimeLineHistory / tliSwitchPoint)는 각 승격에 정수 ID를 부여하고 .history 파일에 분기점을 기록한다. 일관성 도달 시 CheckRecoveryConsistency가 PMSIGNAL_RECOVERY_CONSISTENT를 보내고, 이후 KnownAssignedTransactionIds가 충분히 채워지면 hot standby 쿼리 연결이 허용된다.
- (KO) PostgreSQL SLRU — 순환 가능한 메타데이터를 위한 단순 LRU 버퍼링 — PostgreSQL SLRU가 고정 크기 뱅크로 조직된 공유 메모리 페이지 풀을 할당하고, 뱅크 내 LRU 교체를 수행하며, 뱅크 수준과 버퍼 수준의 두 계층 LWLock으로 동시 접근을 제어하고, WAL-before-data 쓰기 경로·잘라내기·디렉터리 스캔 가능한 세그먼트 레이아웃을 제공하여 clog·subtrans·commit_ts·multixact·notify·직렬화 격리 등 클라이언트를 플러그인 방식으로 지원하는 구조.
- (KO) PostgreSQL 2단계 커밋 — 전역 트랜잭션, 더미 PGPROC, WAL 우선 상태 영속화 — PREPARE TRANSACTION은 GID와 더미 PGPROC을 공유 메모리 TwoPhaseState 배열에 예약하고, 서브트랜잭션 XID·릴레이션 삭제 목록·캐시 무효화 메시지·2PC rmgr 레코드를 직렬화한 상태 블롭을 StartPrepare/EndPrepare로 WAL 레코드에 기록한다. 더미 PGPROC은 별도 백엔드가 COMMIT PREPARED 또는 ROLLBACK PREPARED를 FinishPreparedTransaction으로 호출할 때까지 ProcArray 안에 남아 XID를 '실행 중'으로 유지한다. 상태 데이터는 체크포인트 시점에 WAL에서 pg_twophase 파일로 지연 승격되며, RecoverPreparedTransactions가 복구 종료 시점에 그 통합 저장소에서 모든 준비 트랜잭션을 복원한다.
- (KO) PostgreSQL Vacuum — 죽은 튜플 회수, 동결, XID 순환 방지 — PostgreSQL이 죽은 힙 튜플 공간을 회수하고 트랜잭션 ID 순환을 막는 방식을 분석한다. lazy_scan_heap이 구동하는 3단계 루프(힙 스캔 → 인덱스 일괄 삭제 → 힙 LP_DEAD 수거), vacuum 시작 시 한 번만 계산되는 VacuumCutoffs(OldestXmin·FreezeLimit·MultiXactCutoff), LVRelState가 관계별 작업 상태를 추적하는 방법, vacuum_delay_point를 이용한 비용 기반 pace 조절, VM 비트를 활용한 페이지 건너뜀, 일반 모드와 aggressive 모드의 구분, relfrozenxid가 위험 수준으로 오래됐을 때 인덱스 vacuum을 포기하고 힙 동결 진행을 보장하는 wraparound failsafe, 그리고 DSM 내 ParallelVacuumState를 활용한 병렬 인덱스 vacuum을 다룬다.
- (KO) PostgreSQL WAL 레코드와 리소스 매니저 — rmgr 테이블과 레코드 해부 — PostgreSQL의 WAL은 모든 서브시스템의 redo 로그를 하나의 물리 바이트 스트림에 다중화하며, 리소스 매니저(rmgr) 테이블이 그것을 역다중화하는 디스패치 계층이다. XLogRecord의 8비트 xl_rmid가 RmgrTable[] 인덱스로 쓰이고, 이 배열은 rmgrlist.h를 PG_RMGR X-매크로로 #include해 컴파일 타임에 생성된다. REL_18 기준 22개 항목(XLOG부터 LogicalMessage까지)이 각각 최대 여덟 개의 콜백(redo, desc, identify, startup, cleanup, mask, decode)을 가진다. 레코드는 24바이트 고정 헤더 뒤에 자기 기술적 청크가 이어지는 구조로, 블록 참조 헤더가 (relfilelocator, fork, block)을 명시하고 선택적으로 '빈 구멍'이 제거된 전체 페이지 이미지(FPI)를 포함한다. generic_xlog는 커스텀 rmgr 없이 표준 페이지의 바이트 단위 델타를 WAL에 기록할 수 있게 해준다. WAL 삽입·플러시 내부는 postgres-xlog-wal.md에, 복구 드라이버 루프는 postgres-recovery-redo.md에서 다룬다.
- (KO) PostgreSQL 트랜잭션 관리 — 커밋 상태 머신, 서브트랜잭션, 2PC 연결 지점 — PostgreSQL 백엔드는 모든 구문을 두 겹의 상태 머신으로 처리한다. 하위 레벨 TransState(DEFAULT/START/INPROGRESS/COMMIT/ABORT/PREPARE)는 엔진이 현재 무엇을 하고 있는지를 추적하고, 상위 레벨 TBlockState(TBLOCK_*)는 클라이언트의 BEGIN/COMMIT/SAVEPOINT 블록이 무엇을 원하는지를 추적한다. XID는 지연 할당 방식이다. GetCurrentTransactionId는 쓰기가 처음 필요한 시점에야 AssignTransactionId를 호출하므로, 읽기 전용 트랜잭션은 XID를 소비하지 않는다. GetNewTransactionId는 XidGenLock을 잡고 다음 FullTransactionId를 내어 주며, 락을 놓기 전에 ProcArray에 XID를 기록한다. CommitTransaction은 고정된 순서로 실행되는 파이프라인이며, 내구성이 확정되는 순간은 RecordTransactionCommit이다. 이 함수는 XLOG_XACT_COMMIT 레코드 하나를 쓰고, 플러시하고(synchronous_commit=off가 아닌 경우), TransactionIdCommitTree로 pg_xact를 표시한 뒤, ProcArray에서 빠져나온다. 중단(abort)은 이 과정의 거울 이미지이지만 플러시가 없다는 점이 다르다. 레코드가 없으면 중단으로 간주되기 때문이다. 서브트랜잭션(세이브포인트)은 TransactionStateData 노드의 스택으로 구현된다. 커밋된 자식의 XID는 부모의 childXids 배열로 병합되고, 최상위 커밋 레코드 하나가 전체 트리를 원자적으로 커밋한다. 2단계 준비(2PC)는 같은 레코드 작성 헬퍼를 재사용하지만, 별도의 문서에서 다룬다.
- (KO) PostgreSQL XID 랩어라운드와 프리즈 — 트랜잭션 ID 할당, 한계 강제, 튜플 동결 — PostgreSQL가 XID 랩어라운드 데이터 손실을 방지하는 방식: 32비트 XID 모듈러 산술과 2^31 윈도우 비교자, SetTransactionIdLimit이 갱신하는 TransamVariablesData 4단계 한계(xidVacLimit/xidWarnLimit/xidStopLimit/xidWrapLimit), XidGenLock 하에서 CLOG 확장 후 카운터를 증가시키는 GetNewTransactionId, vacuum_get_cutoffs가 vacuum_freeze_min_age로부터 FreezeLimit를 유도하는 방식, 그리고 heap_prepare_freeze_tuple이 구성한 HeapTupleFreeze 계획을 heap_freeze_execute_prepared가 적용해 t_infomask에 HEAP_XMIN_FROZEN을 기록함으로써 튜플을 영구 가시 상태로 만들고 relfrozenxid를 전진시켜 CLOG를 잘라낼 수 있게 하는 전체 흐름.
- (KO) PostgreSQL Write-Ahead Log — 레코드 삽입, LSN, 그리고 내구성 척추 — PostgreSQL의 WAL은 단 하나의 추가-전용 바이트 스트림이며, LSN은 그 스트림 안의 바이트 오프셋이다. 백엔드는 XLogBeginInsert / XLogRegister* / XLogInsert 호출 시퀀스로 레코드를 조립한다. 어셈블러 XLogRecordAssemble은 rmgr 태그가 달린 헤더, 등록된 데이터 청크, 그리고 선택적으로 전체 페이지 이미지를 XLogRecData 체인으로 묶는다. XLogInsertRecord는 두 단계 삽입을 수행한다. 스핀락으로 보호되는 바이트 예약(ReserveXLogInsertLocation이 CurrBytePos를 증가시켜 LSN을 할당)이 첫 번째 단계이고, 8개의 WAL 삽입 LWLock 가운데 하나 아래에서 WAL 버퍼로 병렬 복사가 두 번째 단계다. 내구성은 삽입과 분리되어 있다. XLogFlush/XLogWrite가 바이트를 커널로 내보내고 issue_xlog_fsync가 플러시를 강제하며, Insert <= Write <= Flush 세 수위 마커가 이 과정을 추적한다. 두 가지 규칙이 이 서브시스템을 엔진의 척추로 만든다. 첫째, 페이지 LSN이 버퍼 매니저의 관문이다(더티 페이지는 WAL이 해당 LSN까지 플러시되기 전에는 기록될 수 없다). 둘째, COMMIT 내구성은 정확히 XLogFlush(commit-LSN)다. 전체 페이지 쓰기는 체크포인트 이후 첫 번째 변경 시 페이지 전체 이미지를 기록해 torn-page 문제를 막는다. 복구 redo는 postgres-recovery-redo.md에, rmgr 카탈로그는 postgres-wal-records-rmgr.md에서 다룬다.
Query Processing (20)
- (KO) PostgreSQL 집계, 정렬, 윈도우 실행기 노드 — 블로킹·스트리밍 파이프라인 연산자는 모두 같은 demand-pull PlanState 트리 안에 놓이지만, 첫 튜플을 반환하기 전에 얼마나 많은 입력을 버퍼링해야 하는지는 서로 다르다. Agg(nodeAgg.c)는 플래너가 고른 네 가지 전략(AGG_PLAIN, AGG_SORTED, AGG_HASHED, AGG_MIXED)을 실행한다. 정렬 집계는 미리 정렬된 입력에서 그룹 경계 비교를 통해 한 번에 하나의 그룹을 스트리밍하고, 해시 집계는 그룹 집합별 TupleHashTable을 구축하다가 테이블이 hash_mem을 초과하면 스필 모드(hash_agg_enter_spill_mode)로 전환해 오버플로 튜플을 분할 논리 테이프에 기록한 뒤 재처리한다. 그룹 집합은 하나의 정렬 패스를 공유하는 단계(phase) 체인으로 처리되며, AGG_MIXED는 해시 단계와 정렬 단계를 교차 실행한다. Sort(nodeSort.c)는 순수 블로킹 연산자로 첫 ExecSort 호출 시 자식 노드를 tuplesort.c로 완전히 드레인한 뒤 정렬된 튜플을 스트리밍한다. IncrementalSort(nodeIncrementalSort.c)는 이미 정렬된 접두사를 활용해 네 가지 상태 기계(INCSORT_LOADFULLSORT/READFULLSORT/LOADPREFIXSORT/READPREFIXSORT)로 접두사 그룹을 하나씩 정렬해 행을 일찍 반환하고 메모리를 제한한다. WindowAgg(nodeWindowAgg.c)는 각 파티션을 튜플스토어에 버퍼링하고 WindowObject API를 통해 행 단위 프레임에 대해 윈도우 함수를 평가하며, 이동 프레임 집계에는 역전이 함수를 사용해 슬라이딩 상태를 증분 유지한다.
- (KO) PostgreSQL 파스 분석 — 원시 트리를 쿼리로 변환하기 — 파스 분석은 파서의 두 번째 절반이다. 문법(gram.y)이 카탈로그 이름을 전혀 참조하지 않는 순수 구문적 RawStmt를 만들면, transformStmt가 그 트리를 걸어 내려가며 의미적으로 완결된 Query를 만든다. 모든 테이블은 카탈로그에서 조회되어 range-table 인덱스를 배정받고, 모든 컬럼 참조는 (varno, varattno, 타입, 콜레이션)을 담은 Var로 귀결된다. 이 패스 전체는 단일 가변 ParseState(pstate)가 꿰고 있다. p_rtable에는 RangeTblEntry가 쌓이고, p_namespace는 현재 시점에 이름 조회 가능한 RTE 목록을 추적하며, p_next_resno는 타겟 리스트 컬럼 번호를 배정한다. transformSelectStmt는 SELECT의 골격 함수다. FROM 절(transformFromClause — RTE와 joinlist 구성)을 가장 먼저 처리하고, 이어 타겟 리스트(transformTargetList — 별표 확장과 FigureColname 자동 이름 지정), WHERE/HAVING(불리언 강제 변환), ORDER BY(GROUP BY와 DISTINCT보다 먼저), LIMIT, 윈도우 정의, 마지막으로 콜레이션 배정과 집계 합법성 검사를 순서대로 수행한다. 출력된 Query는 리라이터와 플래너의 읽기 전용 입력이 되며, pstate는 즉시 해제된다.
- (KO) PostgreSQL 비용 모델 — 페이지/CPU 비용과 경로 비교 — PostgreSQL 비용 모델은 플래너(planner)가 물리적 접근 경로를 비교할 때 사용하는 스칼라 채점 함수다. 모든 Path는 다섯 개의 조정 가능한 GUC에서 파생된 추상 단위로 (startup_cost, total_cost) 쌍을 보유한다. GUC 기본값은 seq_page_cost(1.0), random_page_cost(4.0), cpu_tuple_cost(0.01), cpu_index_tuple_cost(0.005), cpu_operator_cost(0.0025)다. costsize.c의 cost_* 함수군이 이 쌍을 계산한다. cost_seqscan은 페이지당 spc_seq_page_cost와 튜플당 (cpu_tuple_cost + 조건 평가 비용)을 부과한다. cost_index는 인덱스 내부 비용을 AM의 amcostestimate에 위임한 뒤, Mackert-Lohman의 index_pages_fetched 공식으로 힙 페이지 접근 횟수를 추정하고 correlation² 값으로 완전 순차(상관관계 높음)와 완전 랜덤(상관관계 없음) 두 경계를 보간한다. cost_tuplesort는 메모리 내 퀵소트를 comparison_cost * N * log2(N)으로, 외부 병합 정렬은 추가 페이지 I/O로 모델링한다. 조인 함수는 initial_cost_* / final_cost_* 2단계 분할로 설계되어 값비싼 조건 평가 패스 전에 하한값으로 먼저 가지치기한다. startup_cost는 첫 번째 튜플을 반환하기 위한 비용(정렬은 전체 입력을 읽어야 하지만 순차 스캔은 즉시 출력 가능)이고, total_cost는 전체를 소진하는 비용이다. add_path는 이 쌍을 부분 순서로 사용해 어느 축에서든 이기는 경로를 유지한다. 행 수는 clauselist_selectivity를 통해 set_baserel_size_estimates에서 공급된다. cost_qual_eval은 pg_proc.procost를 튜플당 CPU 요금으로 환산한다. 이 모델은 Selinger의 System R 접근 경로 선택 방식을 직계 계승한다.
- (KO) PostgreSQL 익스큐터 — 요구 풀 방식 플랜 노드 트리와 튜플 흐름 — PostgreSQL 익스큐터는 플래너가 만든 읽기 전용 Plan 트리를 노드 하나당 하나씩 대응시키는 PlanState 트리 위에서 동작하는 요구 풀(Volcano 스타일) 이터레이터다. ExecutorStart가 상태 트리와 질의별 EState를 구축하고, ExecutorRun은 루트 노드에 ExecProcNode를 호출해 튜플을 끌어올린다. 이 호출은 리프 스캔이 다음 TupleTableSlot을 반환하거나 빈 슬롯으로 데이터 끝을 알릴 때까지 자식 노드로 재귀한다. ExecutorFinish는 ModifyTable 노드를 소진하고 AFTER 트리거를 발화하며, ExecutorEnd는 상태 트리와 질의별 메모리 컨텍스트를 한 번에 해제한다. 데이터 흐름의 단위는 TupleTableSlot이다. TupleTableSlot은 virtual / heaptuple / minimal / buffer-heap 네 가지 실제 저장 방식을 TupleTableSlotOps vtable로 선택하는 균일한 튜플 핸들이므로, 어느 노드든 자식 노드의 출력이 물리적으로 어떻게 저장됐는지 알 필요 없이 소비할 수 있다. 메모리 관리는 두 컨텍스트에 기댄다. 상태 트리 전체를 담는 질의별 컨텍스트와, 인트라-질의 할당량을 제한하기 위해 튜플마다 초기화되는 튜플별 ExprContext다.
- (KO) PostgreSQL 표현식 평가 — 평탄화 스텝 배열, 디스패치 루프, 그리고 빠른 경로 — PostgreSQL는 실행기 시작 시 모든 표현식 트리를 평탄화된 ExprEvalStep 배열로 컴파일한 뒤, 런타임에 ExecInterpExpr 내부의 switch 루프 또는 GCC computed-goto 테이블로 스텝을 디스패치한다. ExecInitExpr는 Expr 트리를 한 번 순회해 타입별 옵코드(EEOP_*)를 steps 배열에 내보내면서 인자 슬롯을 FunctionCallInfo 구조체에 직접 연결해 런타임 복사를 없앤다. ExecReadyExpr는 먼저 JIT를 시도하고, 비활성화됐거나 비용 임계치 이하면 ExecReadyInterpretedExpr로 넘어가 가장 흔한 단일 스텝 패턴에 전용 fast-path evalfunc를 설치한다. ExprContext의 ecxt_per_tuple_memory가 튜플 단위 메모리 수명을 관리한다.
- (KO) PostgreSQL 통계 — ANALYZE, pg_statistic, 확장 통계 — PostgreSQL 카디널리티 추정 파이프라인 전체를 다룬다. ANALYZE는 두 단계 블록 샘플 + Vitter 알고리즘으로 고정 크기 표본(300 × statistics_target 행)을 수집하고, 열마다 타입별 compute_stats 루틴이 표본을 pg_statistic 행으로 압축한다(stanullfrac, stawidth, Haas-Stokes Duj1 추정치 stadistinct, MCV 목록, 비-MCV 잔여분의 등깊이 히스토그램, 물리/논리 상관계수). 플래너는 selfuncs.c에서 이 값을 읽는다. 다중 컬럼 상관 오차를 교정하기 위해 확장 통계(CREATE STATISTICS → pg_statistic_ext_data)는 함수 종속성, 다변량 n-distinct, 다변량 MCV 세 종류를 제공한다.
- (KO) PostgreSQL JIT — LLVM 기반 표현식 및 튜플 변환 컴파일 — PostgreSQL가 인터프리터 기반 표현식 평가와 튜플 변환(tuple deforming) 핫 패스를 질의 실행 시점에 네이티브 코드로 변환하는 방식을 다룬다. LLVM 의존을 격리하는 jit.c 래퍼의 지연 로드 구조, llvmjit_expr.c의 옵코드별 LLVM IR 생성이 execExprInterp.c 스텝 배열을 그대로 반영하는 방식, llvmjit_deform.c의 TupleDesc 특화 deform 함수, 빌드 시 bitcode로 준비된 연산자 본문 인라이닝, 플래너가 per-query JIT 플래그를 결정하는 비용 임계치(jit_above_cost / jit_inline_above_cost / jit_optimize_above_cost), 그리고 전체 질의 단위로 코드 방출을 묶는 지연 컴파일 방식을 설명한다.
- (KO) PostgreSQL 조인 실행기 — 중첩 루프, 머지 조인, 해시 조인 — PostgreSQL은 세 가지 고전적 이진 조인 알고리즘을 demand-pull PlanState 노드로 구현한다. 각 노드는 ExecProcNode 호출마다 조인된 TupleTableSlot 하나를 반환한다. 중첩 루프(ExecNestLoop)는 외부 튜플마다 내부 서브트리를 한 번씩 스캔하며, 내부가 매개변수화된 인덱스 스캔일 때는 외부 컬럼 값을 PARAM_EXEC 슬롯에 밀어 넣은 뒤 ExecReScan으로 인덱스 조회를 수행한다. 머지 조인(ExecMergeJoin)은 정렬된 두 입력에 대해 11-상태 기계로 동작하며, ExecMarkPos로 동등 그룹의 첫 내부 튜플을 마킹하고 ExecRestrPos로 복원해 외부 중복 키를 재조인한다. 해시 조인(ExecHashJoinImpl)은 6-상태 하이브리드 해시 기계로, MultiExecHash / ExecHashTableInsert가 내부 HashJoinTable을 빌드하고 ExecScanHashBucket으로 버킷을 탐색하며, hash_mem 예산 초과 시 ExecHashIncreaseNumBatches가 배치 수를 배로 늘려 비현재 배치 튜플을 BufFile로 스필한다. 세 노드 모두 JoinState 기반(joinqual, otherqual, single_match, 외부 조인용 NULL 채움 슬롯)을 공유하며 ExecProject로 가상 결과 슬롯에 투영한다.
- (KO) PostgreSQL 조인 순서 결정 — 동적 프로그래밍 탐색과 GEQO 폴백 — PostgreSQL은 바텀업 동적 프로그래밍(dynamic programming)으로 조인 순서를 결정한다. 이는 1979년 Selinger가 System R 논문에서 제시한 방식과 동일하다. make_rel_from_joinlist가 조인트리 항목 수(levels_needed)를 세고, geqo_threshold(기본값 12) 미만이면 standard_join_search에 위임한다. standard_join_search는 root->join_rel_level[k]를 레벨 단위로 채운다. 레벨 1에는 기본 릴레이션이 들어가고, join_search_one_level(level)은 하위 레벨 조합으로 가능한 k-항목 조인 릴레이션을 구축한다. 좌/우 편향 조인은 make_rels_by_clause_joins로, 부시(bushy) 조인은 k-릴레이션과 (level-k)-릴레이션의 조합으로, 최후 수단으로 절 없는 카테시안 패스를 수행한다. make_join_rel은 join_is_legal을 확인하고, relid 집합을 키로 RelOptInfo를 탐색·생성(DP 메모)한 뒤 populate_joinrel_with_paths -> add_paths_to_joinrel을 호출한다. add_paths_to_joinrel은 정렬 병합·중첩 루프·해시 물리 경로를 열거하고 add_path로 비지배 경로만 유지한다. geqo_threshold 이상이면 탐색 공간이 초지수적으로 커지므로 geqo()가 유전 알고리즘으로 조인 순서 투어(tour) 풀을 진화시키고, geqo_eval -> gimme_tree(클럼프 병합 휴리스틱, 내부적으로 make_join_rel 재사용)로 비용을 평가해 가장 저렴한 투어를 반환한다.
- (KO) PostgreSQL 노드 시스템 — NodeTag, copyObject, gen_node_support 코드 생성 — PostgreSQL의 파스 트리, 플랜 트리, 실행기 상태 트리는 모두 NodeTag 태그를 첫 필드로 갖는 C 구조체(Node)로 구성된다. makeNode(T)는 palloc0으로 메모리를 확보하고 T_T 태그를 찍는다. List는 v13부터 연결 리스트가 아닌 확장 가능 배열이다. copyObject·equal·outNode·nodeRead 등 노드 공통 연산은 손으로 작성하지 않고, gen_node_support.pl이 빌드 시점에 헤더 파일을 파싱해 구조체 필드 타입으로부터 자동 생성한다. 손으로 작성한 copyfuncs.c와 equalfuncs.c에는 매크로, 디스패치 진입점, List 특수 처리, Const처럼 custom_copy_equal이 필요한 소수의 노드만 남는다.
- (KO) PostgreSQL 병렬 질의 — Gather, 워커, DSM 플랜 — PostgreSQL은 부분 플랜 위에 Gather(또는 순서 보존형 GatherMerge) 노드를 삽입해 질의를 병렬화한다. 부분 플랜 아래의 서브트리는 parallel-aware로 표시되어 N개의 복사본이 동시에 실행되더라도 중복 없이 전체 결과를 생성한다. Gather 노드는 첫 실행 시 ExecInitParallelPlan을 호출해 nodeToString으로 직렬화한 플랜을 shm_toc 목차로 색인된 동적 공유 메모리(DSM) 세그먼트에 저장한다. 세그먼트 크기는 PlanState 트리를 두 번 순회해 결정되며(추정 후 초기화), 워커별 튜플 큐, DSA 영역, 파라미터 슬롯, 계측 슬롯이 배치된다. LaunchParallelWorkers가 포스트마스터에 백그라운드 워커 포크를 요청하면, 각 워커는 DSM을 연결한 뒤 리더 상태(GUC, 스냅샷, 트랜잭션 XID 집합, 콤보 CID, 라이브러리, 사용자 ID)를 복원해 가시성 검사 결과가 리더와 일치하도록 맞춘 후 ParallelQueryMain으로 익스큐터 복사본을 실행하며 MinimalTuple을 shm_mq 튜플 큐에 쓴다. 리더의 gather_readnext는 이 큐들을 라운드로빈으로 소진하고, parallel_leader_participation 설정에 따라 리더 자신도 생산자로 참여할 수 있다. GatherMerge는 정렬 순서를 보존하기 위해 워커 스트림을 이진 힙으로 병합한다. 병렬 모드는 EnterParallelMode로 강제되는 엄격한 읽기 전용이며, 오류는 전용 오류 shm_mq와 PROCSIG_PARALLEL_MESSAGE 시그널을 거쳐 리더의 다음 CHECK_FOR_INTERRUPTS 지점에서 재발생된다.
- (KO) PostgreSQL 파서 — 렉싱, LALR(1) 문법, 의미 분석 — PostgreSQL이 SQL 문자열을 Query 트리로 변환하는 과정을 분석한다. 백트랙 없는 Flex 렉서(scan.l)가 한 토큰 선독 필터(base_yylex)를 거쳐 약 19,700줄짜리 Bison LALR(1) 문법(gram.y)으로 토큰을 공급하고, 그 결과로 카탈로그 접근이 없는 원시 파스 트리(RawStmt/List)가 만들어진다. 이어 두 번째 단계(analyze.c + parse_*.c)가 ParseState 네임스페이스 워커를 구문별로 구동해 이름을 해석하고 OID를 부여하며 타입을 강제 변환해 플래너가 소비하는 Query 노드를 생성한다.
- (KO) PostgreSQL 경로 생성 — 스캔·조인 경로 빌더, add_path 지배 가지치기, Pathkeys — 경로 생성(path generation)은 PostgreSQL 옵티마이저에서 각 RelOptInfo의 pathlist를 채우는 단계다. make_one_rel이 set_base_rel_pathlists를 호출해 RTE 유형별 빌더(set_plain_rel_pathlist, set_append_rel_pathlist, set_foreign_pathlist 등)로 분기하고, set_plain_rel_pathlist는 순차 스캔·TID 스캔·인덱스 경로를 추가한다. create_index_paths는 rel->indexlist를 순회하며 제한 조건·조인 조건·EC 파생 조건을 인덱스 컬럼에 매칭해 IndexPath, BitmapHeapPath, 매개변수화 경로를 생성한다. add_path는 비용·pathkeys·매개변수화·행 수·병렬 안전성 기준으로 지배 가지치기를 수행하고, set_cheapest가 cheapest_total_path 등을 RelOptInfo에 고정한다. pathkeys.c는 EquivalenceClass 기반 PathKey 리스트로 인덱스 순서·병합 조인 입력·ORDER BY 요건을 통합된 어휘로 표현한다.
- (KO) PostgreSQL 플랜 생성 — 승자 경로에서 실행 가능한 플랜 트리로 — 플랜 생성은 옵티마이저의 마지막 인계 단계다. 조인 순서 탐색이 선택한 단 하나의 최저 비용 Path를 완전히 실행 가능한 Plan 트리로 낮추고(lower), 이후 모든 변수 참조를 카탈로그 식별자 대신 슬롯 오프셋으로 재작성해 실행기가 컬럼을 위치로 역참조할 수 있게 한다. create_plan_recurse는 Path 트리를 위에서 아래로 순회하며 pathtype에 따라 create_<node>_plan 루틴으로 분기해 Plan 노드를 할당하고 타깃 리스트(build_path_tlist, 또는 상위가 투영하지 않을 때의 넓은 물리 tlist)를 구성하며 한정 조건을 정렬·제거(order_qual_clauses, extract_actual_clauses)하고 비용·폭 추정치를 복사한다(copy_generic_path_info). 조인 루틴은 양쪽 자식에 재귀하고 lateral/outer Var를 replace_nestloop_params로 NestLoopParam으로 변환한다. 이 단계의 tlist와 qual은 아직 파서 번호 체계를 따르며 Var는 범위 테이블 기반의 (varno, varattno) 쌍을 갖는다. set_plan_references는 두 번째 하향 패스를 수행해 모든 서브쿼리 범위 테이블을 하나의 전역 finalrtable로 병합하고 각 스캔 노드의 Var를 rtoffset만큼 이동시키며, 조인·상위 노드에서 자식 출력 Var를 (INNER_VAR | OUTER_VAR, resno)로 재작성해 실행기가 indexed_tlist 해시를 통해 inner/outer TupleTableSlot에서 위치 기반으로 컬럼을 읽게 한다. 상관·비상관 서브 SELECT는 subselect.c에서 별도로 처리된다. build_subplan은 initPlan(한 번 실행 후 PARAM_EXEC 슬롯에 결과를 저장)과 튜플별 SubPlan 중 하나를 결정하고, SS_attach_initplans은 해당 쿼리 레벨의 initPlan들을 최상위 노드에 건다. 출력은 모든 표현식이 슬롯 오프셋으로 해결된 Plan 트리로, ExecInitNode에 넘길 준비가 된 상태다.
- (KO) PostgreSQL 플래너 — 쿼리 트리에서 실행 계획까지: Path, 비용, System-R 계보 — PostgreSQL 플래너는 비용 기반 System-R 계보의 옵티마이저다. 재작성된 Query 트리를 실행 가능한 Plan 트리로 변환하는 과정은 세 단계로 중첩된다. subquery_planner가 하나의 Query 레벨을 전처리하고(서브링크/서브쿼리 끌어올리기, 조건식 정규화, 상수 폴딩), grouping_planner가 상위 연산을 처리하며(집합 연산, 그룹핑/집계, 윈도우 함수, DISTINCT, ORDER BY, LIMIT), query_planner/make_one_rel이 스캔·조인 핵심부를 담당한다. 핵심 자료구조는 RelOptInfo(릴레이션마다 하나, 베이스 또는 조인)와 Path 노드(해당 릴레이션을 생성하는 모든 후보 방법)의 분리다. Path는 아래에서 위로 생성된다. set_base_rel_pathlists가 각 베이스 릴레이션의 스캔 Path를 만들고, 동적 프로그래밍 조인 탐색이 레벨별로 joinrel Path를 쌓는다. set_cheapest는 비용/정렬 순서/파라미터화 조합마다 가장 저렴한 Path만 남긴다. create_plan은 최종 승자 Path 트리를 위에서 아래로 순회하며 일치하는 Plan을 조립한다.
- (KO) PostgreSQL 포털, 준비된 문장, 플랜 캐시 — 포털(Portal)은 PostgreSQL의 실행 컨테이너다. 실행 가능한 SQL 명령 하나의 라이브 상태를 담으며, PlannedStmt 목록, CachedPlan 참조, 결과 tupdesc, 커서 위치를 포함한다. PortalStart는 ChoosePortalStrategy로 포털을 다섯 가지 PortalStrategy(ONE_SELECT, ONE_RETURNING, ONE_MOD_WITH, UTIL_SELECT, MULTI_QUERY) 중 하나로 분류하고, 스냅샷을 설정하며, ONE_SELECT의 경우 ExecutorStart를 호출한다. PortalRun은 PortalRunSelect를 통해 익스큐터를 증분 방식으로 구동하거나(FETCH n이 질의 중간에 멈출 수 있는 이유) 명령을 완료까지 실행해 튜플스토어에 적재한다. PortalDrop은 컨테이너를 해제하고 익스큐터를 종료하며 캐시드 플랜의 참조 카운트를 감소시킨다. 준비된 문장(PREPARE / 프로토콜 Parse 메시지)은 백엔드별 해시 테이블에 저장된 CachedPlanSource에 파스 분석 결과를 한 번만 기록한다. EXECUTE는 파라미터를 바인딩하고 GetCachedPlan을 호출한다. GetCachedPlan에서 플랜 캐시는 파라미터 독립적 범용 플랜(한 번 계획해 재사용)과 파라미터별 커스텀 플랜(매 실행마다 재계획) 중 하나를 선택한다. choose_custom_plan의 판단 기준은 처음 다섯 번은 항상 커스텀, 이후에는 캐시된 generic_cost와 커스텀 비용의 누적 평균(계획 비용 포함)을 비교해 범용 플랜이 더 저렴할 때만 유지한다. sinval 메시지를 통한 캐시 무효화는 is_valid를 false로 설정하고, 다음 사용 시 RevalidateCachedQuery가 재분석/재계획을 수행하는 지연 처리 방식이다.
- (KO) PostgreSQL 플래너 Prep — 서브쿼리 끌어올리기, 조건식 정규화, 집합 연산 — PostgreSQL 플래너의 prep 단계는 파스 트리를 경로 생성(path generation) 전에 납작하고 최적화하기 좋은 형태로 정규화한다. prepjointree.c는 ANY/EXISTS SubLink를 세미조인/안티조인으로 끌어올리고 단순 서브쿼리와 UNION ALL 멤버를 부모 조인트리에 병합한다. prepqual.c는 NOT을 리프까지 밀어 내리는 드모르간 변환과 역 OR 분배 법칙을 적용해 WHERE/CHECK 조건식을 정규화하되 전체 CNF 변환은 하지 않는다. preptlist.c는 INSERT/UPDATE/DELETE/MERGE의 타깃 리스트를 확장하고 행 마크 전용 junk 컬럼을 추가한다. prepunion.c는 UNION/INTERSECT/EXCEPT를 독립적으로 계획한 리프 서브쿼리 위에 Append/SetOp 트리로 계획한다.
- (KO) PostgreSQL 룰 시스템과 쿼리 리라이터 — 뷰, DO INSTEAD, RLS — PostgreSQL 쿼리 리라이터는 파스 분석과 플래닝 사이에 위치하여 룰 시스템을 적용하는 단계다. 단일 진입점 QueryRewrite는 세 단계로 동작한다. 1단계 RewriteQuery가 비-SELECT(INSERT/UPDATE/DELETE) 룰과 자동 갱신 가능 뷰 재작성을 처리해 product query 목록을 만든다. 2단계 fireRIRrules가 각 결과 쿼리의 range-table 항목을 순회하며 ApplyRetrieveRule로 뷰를 인라인 서브쿼리로 치환한다. 3단계가 command tag를 붙일 쿼리를 결정한다. 뷰는 저장 객체가 아니라 ON SELECT DO INSTEAD 룰 하나를 가진 릴레이션이며, DefineQueryRewrite로 정의한다. INSERT/UPDATE/DELETE 룰은 matchLocks로 매칭하고 fireRules로 실행하며, INSTEAD/ALSO 구분과 조건부 qual 처리를 위해 반전 qual이 붙은 '기본 복사본'을 생성한다. 행 수준 보안(RLS)은 fireRIRrules의 마지막 구간에서 get_row_security_policies가 반환하는 securityQuals와 withCheckOptions를 각 릴레이션 RTE에 앞붙임한다. SEARCH/CYCLE 절 확장도 fireRIRrules 안에서 처리한다. 리라이터의 출력은 플래너가 받을 수 있는 Query 트리 목록이며, 재귀는 rewrite_events 스택과 activeRIRs OID 목록으로 제어한다.
- (KO) PostgreSQL 스캔 노드 — 순차 스캔, 인덱스 스캔, 비트맵 스캔, TID 스캔 — PostgreSQL 실행기는 SeqScan, IndexScan, IndexOnlyScan, BitmapHeapScan, TidScan, TidRangeScan 여섯 종류의 리프 스캔 노드를 제공한다. 모든 노드는 ExecScan/ExecScanExtended 공유 루프를 통해 Volcano 이터레이터 인터페이스를 구현한다. SeqScan은 테이블 AM에 전적으로 위임하고, IndexScan은 인덱스 항목마다 힙 튜플을 한 건씩 가져오며, IndexOnlyScan은 가시성 맵을 게이트로 삼아 힙 접근을 생략하고, BitmapHeapScan은 TIDBitmap을 매개로 인덱스 패스와 힙 패스를 분리한다. TidScan과 TidRangeScan은 명시적 ctid 조건을 처리한다. 모든 노드 유형은 Estimate/InitializeDSM/ReInitializeDSM/InitializeWorker 프로토콜로 병렬 DSM을 지원한다.
- (KO) PostgreSQL tuplesort — 인메모리 퀵정렬, 외부 병합 정렬, 논리 테이프, Tuplestore — tuplesort.c는 데이터가 work_mem에 맞는지 여부에 따라 정반대로 동작하는 단일 모듈이다. 모두 맞으면 SortTuple 배열에 쌓고 특화 퀵정렬로 마무리하며(TSS_SORTEDINMEM), 넘치면 외부 모드(TSS_BUILDRUNS)로 한 번만 전환해 work_mem 단위 배치를 논리 테이프에 정렬 런으로 기록한 뒤 이진 최솟값 힙으로 런들을 병합한다. 런은 별도 OS 파일에 저장되지 않는다. logtape.c가 모든 논리 테이프를 하나의 임시 파일에 BLCKSZ 블록 체인으로 묶고, 병합 중 블록을 읽는 즉시 최솟값 힙 프리리스트로 반환해 피크 디스크 사용량을 나이브한 방식의 2x 대신 약 1x로 유지한다. 병합 차수 M은 tuplesort_merge_order가 work_mem으로부터 계산하며, 런 수가 M을 초과하면 다중 패스 균형 병합을 수행한다. 비교 비용을 낮추는 세 가지 최적화가 있다. 단축 키(abbreviated key)는 선두 컬럼의 고정 크기 정렬 가능 프록시를, 바운드 정렬(bounded sort)은 LIMIT n을 n개 요소 고정 최댓값 힙으로 변환해 스필을 원천 차단하며, 타입 특화 qsort 변형은 비교자 함수 포인터 호출 자체를 제거한다. 병렬 정렬은 워커가 각자 하나의 정렬된 런을 만들고 리더가 워커 테이프를 가져와 단일 최종 병합을 수행한다. tuplestore.c는 정렬 없는 형제 컴포넌트로, 커서·Materialize·재귀 CTE에 필요한 다중 독립 읽기 포인터를 지원하며 work_mem 초과 시 BufFile로 스필하되 순서를 바꾸지 않는다.
DDL & Schema (10)
- (KO) PostgreSQL ALTER TABLE — 다중 패스 기계와 힙 재작성 — PostgreSQL ALTER TABLE은 tablecmds.c에 거의 전부 구현된 3단계 다중 패스 기계다. AlterTable은 락 수준을 미리 계산해(AlterTableGetLockLevel) 테이블을 열기도 전에 가장 강한 락을 획득하고, ATController가 3단계를 순서대로 실행한다. 1단계 ATPrepCmd는 권한·relkind 검사와 상속 재귀를 수행하고 AlteredTableInfo 작업 큐를 구성한다. 2단계 ATRewriteCatalogs는 AT_PASS_DROP부터 AT_PASS_MISC까지 13개 패스를 순서대로 실행해 카탈로그를 변경한다. 3단계 ATRewriteTables는 tab->rewrite가 0이 아닌 테이블에 한해 make_new_heap + ATRewriteTable + finish_heap_swap으로 새 relfilenode를 만든다. 핵심 설계 긴장은 메타데이터 전용 변경과 전체 힙 재작성 사이에 있다. ADD COLUMN은 pg_attribute.attmissingval에 기본값을 저장하는 빠른 기본값(fast default)으로 재작성을 피하고, ALTER COLUMN TYPE은 ATColumnChangeRequiresRewrite로 이진 강제 변환 여부를 확인하며, DROP COLUMN은 attisdropped 플래그만 세우는 순수 논리 삭제다.
- (KO) PostgreSQL 제약 조건 — CHECK, NOT NULL, Unique, Primary Key, Foreign Key 내부 구조 — PostgreSQL의 모든 무결성 제약 조건은 pg_constraint 카탈로그의 행 하나로 기록된다. 작성 함수는 CreateConstraintEntry 하나이며, contype 컬럼('c' CHECK, 'n' NOT NULL, 'p' PRIMARY KEY, 'u' UNIQUE, 'f' FOREIGN KEY, 'x' EXCLUSION)이 강제 메커니즘을 결정한다. CHECK와 NOT NULL은 실행기가 수정된 튜플마다 평가하는 표현식 서술어다(StoreRelCheck / StoreRelNotNull이 conbin 트리를 저장하고 ExecConstraints가 실행한다). UNIQUE와 PRIMARY KEY는 표현식도 트리거도 없고, 뒷받침하는 유일 B-트리 인덱스(conindid)에 강제를 위임한다. FOREIGN KEY는 예외적으로 숨겨진 AFTER ROW 트리거 집합으로 구현된다. 참조 측(child)에는 RI_FKey_check_ins / RI_FKey_check_upd, 피참조 측(parent)에는 ON DELETE/UPDATE 액션 트리거가 붙어 SPI 쿼리를 실행한다. ri_triggers.c는 FK별 RI_ConstraintInfo와 쿼리 형태별 준비된 SPI 계획을 캐시한다. NOT VALID는 생성 시 스캔을 건너뛰고(convalidated=false), 이후 VALIDATE CONSTRAINT가 RI_Initial_Check(anti-join LEFT JOIN ... IS NULL 쿼리 하나) 또는 힙 순차 스캔으로 소급 검증한다. 지연 가능 제약 조건(condeferrable/condeferred)은 AFTER 트리거 지연 이벤트 큐를 그대로 탄다. 도메인도 동일한 pg_constraint 경로를 공유하며 typecmds.c의 domainAddCheckConstraint가 처리한다.
- (KO) PostgreSQL COPY — 대량 적재/추출, 포맷, 멀티-인서트 — COPY는 PostgreSQL의 대량 적재/추출 명령이다. copy.c의 DoCopy()가 옵션 목록을 파싱(ProcessCopyOptions)하고 권한을 검사한 뒤 BeginCopyFrom/CopyFrom(적재) 또는 BeginCopyTo/DoCopyTo(추출)로 분기한다. 내장 포맷 세 가지(text, CSV, binary)는 각각 CopyFromRoutine / CopyToRoutine vtable로 표현되며 CopyFromGetRoutine이 선택한다. 적재 경로에서 copyfromparse.c는 단계 파이프라인(raw_buf → input_buf → line_buf → attribute_buf)을 실행하고, CopyFrom은 CopyMultiInsertInfo를 통해 튜플을 버퍼링(MAX_BUFFERED_TUPLES=1000, MAX_BUFFERED_BYTES=65535)한 뒤 table_multi_insert로 플러시한다. COPY FREEZE는 동일 서브트랜잭션에서 생성·트런케이트된 릴레이션에 TABLE_INSERT_FROZEN을 기록한다. ON_ERROR IGNORE는 ErrorSaveContext로 소프트 오류를 포착하고 num_errors를 누적한다.
- (KO) PostgreSQL DDL 실행 — ProcessUtility 디스패치, 단순/느린 분리, 그리고 CREATE TABLE 경로 — PostgreSQL이 유틸리티(비최적화) 구문을 처리하는 방식을 분석한다. ProcessUtility는 훅을 확인하고, standard_ProcessUtility는 단순 구문을 인라인으로 처리하며 카탈로그 변경이 필요한 구문은 ProcessUtilitySlow로 넘긴다. ProcessUtilitySlow는 모든 명령을 이벤트 트리거 펜스로 감싼다. CREATE TABLE이 정식 경로다. transformCreateStmt가 LIKE/상속을 확장하고, DefineRelation이 네임스페이스와 접근 메서드를 해석한 뒤 heap_create_with_catalog를 호출해 pg_class/pg_attribute 행을 쓰고 물리 파일을 할당한다. 이어서 CommandCounterIncrement가 새 튜플을 같은 트랜잭션 안에서 가시화함으로써 AddRelationNewConstraints가 raw DEFAULT 표현식을 변환할 수 있게 한다.
- (KO) PostgreSQL 이벤트 트리거 — DDL 훅과 명령 수집 — 이벤트 트리거는 테이블 DML이 아닌 DDL과 세션 이벤트에 반응하는 사용자 정의 함수다. PostgreSQL은 ddl_command_start, ddl_command_end, sql_drop, table_rewrite, login 다섯 가지 이벤트를 정의하고, 각각을 범용 디스패처가 아닌 코드 내 특정 위치에서 직접 호출한다. DDL 이벤트는 ProcessUtilitySlow 안에서 발화한다. EventTriggerBeginCompleteQuery가 명령별 EventTriggerQueryState를 스택에 올리고, EventTriggerDDLCommandStart가 실행 전에 발화하며, EventTriggerSQLDrop과 EventTriggerDDLCommandEnd가 실행 후 순서대로 발화한다. table_rewrite는 tablecmds.c의 ATRewriteTables에서, login은 인증 완료 후 PostgresMain에서 발화한다. 각 발화 지점은 EventTriggerCommonSetup을 거쳐 syscache 기반 이벤트 트리거 캐시를 조회하고, 세션 복제 역할과 명령 태그로 후보를 걸러낸 뒤 EventTriggerData 노드를 EventTriggerInvoke에 넘긴다. 두 개의 사이드 채널이 트리거 함수가 읽을 데이터를 쌓는다. SQLDropList는 드롭된 객체를 담아 pg_event_trigger_dropped_objects로 노출되고, commandList는 CollectedCommand 레코드를 담아 pg_event_trigger_ddl_commands로 노출된다.
- (KO) PostgreSQL 인덱스 생성 — CREATE INDEX, 빌드 과정, CONCURRENTLY — CREATE INDEX는 두 계층이 협력하는 구조다. indexcmds.c::DefineIndex가 SQL 수준 작업(테이블 잠금, 접근 방법·연산자 클래스 결정, IndexInfo 계산, 이름 확정)을 담당하고, catalog/index.c::index_create가 카탈로그 행(pg_class, pg_attribute, pg_index, 의존성)을 생성한 뒤 별도 지시가 없으면 index_build를 호출한다. index_build는 AM에 무관한 빌드 드라이버로 접근 방법의 ambuild 콜백(btbuild 등)을 실행한다. ambuild는 table_index_build_scan으로 힙을 스캔해 인덱스를 벌크 적재하고, index_update_stats가 reltuples를 pg_class에 기록한다. 일반 빌드는 ShareLock을 유지해 읽기는 허용하고 쓰기는 차단한다. CREATE INDEX CONCURRENTLY는 단일 블로킹 빌드 대신 여러 트랜잭션에 걸친 ShareUpdateExclusiveLock 프로토콜을 사용한다. not-ready/not-valid 카탈로그 항목을 커밋해 쓰기 트랜잭션이 HOT 안전성을 유지하도록 한 뒤, WaitForLockers → 빌드 → indisready 설정 → 커밋 → WaitForLockers → 참조 스냅샷 취득 → validate_index(힙과 인덱스 TID를 머지 조인해 누락 항목 삽입) → 스냅샷 해제 → WaitForOlderSnapshots → indisvalid 설정 순으로 진행한다. REINDEX는 isreindex=true로 index_build를 재사용하며 AccessExclusiveLock을 잡는다. REINDEX CONCURRENTLY는 섀도 인덱스를 만들어 스왑한다. 이 모든 메커니즘의 근거는 pg_index 생존 비트 세 개 — indislive, indisready, indisvalid — 이며, index_set_state_flags가 순서대로 비트를 전환한다.
- (KO) PostgreSQL 구체화 뷰 — 저장 구조와 REFRESH (CONCURRENTLY 포함) — PostgreSQL 구체화 뷰는 실제 heap(힙 파일)과 정의 쿼리를 담은 SELECT-INSTEAD 재작성 규칙을 동시에 소유하는 RELKIND_MATVIEW 릴레이션이다. CREATE MATERIALIZED VIEW는 항상 WITH NO DATA로 릴레이션을 먼저 만든 뒤(createas.c), REFRESH 코드 경로를 재사용해 데이터를 채운다. REFRESH는 DR_transientrel DestReceiver를 통해 저장된 쿼리를 실행하고 결과를 frozen 벌크 삽입으로 임시 힙에 적재한 뒤, AccessExclusiveLock 하에 relfilenode를 통째로 교체하는 refresh_by_heap_swap(기본) 또는 ExclusiveLock만 잡고 FULL JOIN 기반 행 단위 diff를 수행하는 refresh_by_match_merge(CONCURRENTLY) 중 하나를 선택한다. pg_class.relispopulated 플래그(RelationIsPopulated / RelationIsScannable 매크로로 노출)가 스캔 허용 여부를 결정하며, 비어 있는 구체화 뷰를 스캔하면 'has not been populated' 오류가 발생한다. CONCURRENTLY는 이미 채워진 구체화 뷰와 사용 가능한 UNIQUE 인덱스가 최소 하나 있어야 한다.
- (KO) PostgreSQL 선언적 파티셔닝 — 경계, 튜플 라우팅, 프루닝 — PostgreSQL 선언적 파티셔닝은 논리 테이블 하나를 물리 리프 테이블 계층으로 나누고, 작업을 세 계층에 분산한다. 카탈로그·릴캐시는 파티션된 릴레이션마다 PartitionDesc를 구축한다. PartitionDesc는 정렬된 oids[] 배열과, LIST/RANGE/HASH 경계를 정렬된 datums[] 배열과 indexes[] 맵으로 정규화한 PartitionBoundInfo로 구성된다. 값 조회는 이진 탐색(partition_list_bsearch / partition_range_datum_bsearch)이나 모듈로(compute_partition_hash_value)로 처리된다. INSERT 시 execPartition.c가 각 튜플을 탑-다운으로 라우팅한다. ExecFindPartition은 FormPartitionKeyDatum으로 파티션 키를 추출하고 get_partition_for_tuple을 호출하며, 동일 파티션이 PARTITION_CACHED_FIND_THRESHOLD(16)회 연속 적중하면 마지막-발견 캐시로 이진 탐색을 생략한다. SELECT 시 partprune.c가 두 단계로 파티션을 제거한다. 플랜 타임 프루닝(prune_append_rel_partitions)은 WHERE 절을 PartitionPruneStepOp / PartitionPruneStepCombine 스텝 프로그램으로 컴파일해 플랜 확정 전에 get_matching_partitions를 실행하고, 런타임 프루닝(ExecDoInitialPruning / ExecFindMatchingSubPlans)은 중첩 루프나 제너릭 플랜의 PARAM_EXEC 값이 확정된 뒤 같은 스텝을 재실행해 조건에 맞지 않는 Append/MergeAppend 하위 플랜을 제거한다. 파티션와이즈 조인과 집계(enable_partitionwise_join / _aggregate)는 partition_bounds_merge로 두 입력의 경계 호환성을 확인한 뒤 조인이나 GROUP BY를 Append 아래로 밀어 넣어 하나의 큰 연산을 N개의 작은 연산으로 대체한다.
- (KO) PostgreSQL 행 수준 보안 — 정책과 리라이트 시점 자격 조건 주입 — 행 수준 보안(Row-Level Security, RLS)은 PostgreSQL의 행 단위 접근 제어 계층이다. 정책(policy)은 pg_policy 한 튜플로 저장되며, 릴레이션·명령 클래스(polcmd)·역할 목록·허용/제한 플래그·두 표현식(USING qual과 WITH CHECK qual)을 담는다. pg_class.relrowsecurity가 RLS를 활성화하고, relforcerowsecurity는 테이블 소유자에게도 RLS를 적용한다. 쿼리 리라이트 시점에 fireRIRrules()가 각 릴레이션 RTE마다 get_row_security_policies()를 호출하며, check_enable_rls()가 릴레이션 플래그·BYPASSRLS·소유권·row_security GUC·RI 검사 컨텍스트를 종합해 RLS_NONE / RLS_NONE_ENV / RLS_ENABLED 세 값 중 하나를 반환한다. add_security_quals()는 허용적 USING qual을 OR 결합하고 제한적 qual을 AND 결합하여 rte->securityQuals(보안 배리어 자격 조건)에 앞붙임한다. add_with_check_options()는 쓰기 행 검사용 WithCheckOption 노드를 생성한다. 허용 정책이 없으면 항상 거짓인 Const 하나가 묵시적 기본 거부를 구현한다. 역할·GUC 의존성 때문에 get_row_security_policies는 hasRowSecurity를 설정하여 환경 변화 시 플랜 캐시가 재수립되도록 한다.
- (KO) PostgreSQL 트리거 — 정의, 발화 지점, After-Trigger 큐 — PostgreSQL 트리거는 pg_trigger 카탈로그 행 하나가 함수를 (타이밍, 레벨, 이벤트) 조합에 묶은 객체다. RelationBuildTriggers가 해당 행들을 릴캐시(relcache)에 캐시되는 관계별 TriggerDesc로 구체화하고, SetTriggerFlags가 불리언 요약 플래그를 미리 계산해 두어 익스큐터가 해당 이벤트 클래스의 트리거가 없을 때 배열 순회를 건너뛸 수 있게 한다. 익스큐터는 nodeModifyTable에서 튜플 연산이 일어나는 정확한 시점에 ExecBR/ExecAR/ExecIR(행 레벨)과 ExecBS/ExecAS(구문 레벨) 같은 발화 지점 훅 패밀리를 호출한다. BEFORE 행 트리거는 동기적으로 실행되어 튜플을 재작성하거나 거부할 수 있다. INSTEAD OF 트리거(뷰 전용)는 연산 자체를 대체한다. AFTER 트리거는 발화 시점에 거의 아무 작업도 하지 않는다. ExecAR*는 AfterTriggerSaveEvent를 호출해 플래그 워드와 한두 개의 CTID만 담은 AfterTriggerEventData 레코드를 전역 afterTriggers 상태의 쿼리 레벨 AfterTriggerEventList에 추가한다. AfterTriggerEndQuery에서 즉시 모드 이벤트에 발화 표시를 찍고 afterTriggerInvokeEvents를 통해 큐 순서대로 발화하며(각 튜플을 SnapshotAny로 CTID 재조회), 지연 가능 이벤트는 트랜잭션 전역 목록으로 이동했다가 커밋 직전 AfterTriggerFireDeferred에서 발화한다. 전환 테이블(REFERENCING OLD/NEW TABLE)은 행이 흐르는 과정에서 MakeTransitionCaptureState를 통해 튜플스토어로 포착되며, CTID 기반 이벤트 레코드와 별개 구조로 운용되고 지연 불가능하다.
Replication & HA (10)
- (KO) PostgreSQL WAL 아카이빙 & 요약 — PITR과 증분 백업의 기반 구조 — WAL 아카이빙과 WAL 요약은 PostgreSQL의 추가-전용 WAL 스트림을 PITR(특정-시점 복구)와 증분 백업의 내구성 기반으로 변환하는 두 배경 프로세스다. 아카이버(PgArchiverMain)는 래치 대기 후 archive_status 디렉터리를 스캔하고 WAL 기계가 남긴 .ready 파일을 max-heap으로 우선순위 정렬해 archive_file_cb를 호출한다. 내장 shell_archive(archive_command)나 로드된 archive_library 중 하나를 사용하며, 성공하면 NNN.ready를 NNN.done으로 이름 변경한다. 복구 측에서는 xlogarchive.c의 RestoreArchivedFile이 restore_command로 세그먼트를 아카이브에서 꺼내 오고, .ready/.done 상태 프로토콜이 세그먼트 재활용 시점을 결정한다. PG17에서 도입된 WAL 요약기(WalSummarizerMain)는 summarize_wal이 켜져 있을 때 xlogreader로 WAL을 읽고 수정된 릴레이션-포크 블록을 BlockRefTable에 기록한 뒤 TLI-LSN-LSN 형식의 .summary 파일을 pg_wal/summaries/에 배출한다. 증분 백업(pg_basebackup --incremental)은 이 요약 파일을 소비해 기준 백업 이후 변경된 블록만 복사한다.
- (KO) PostgreSQL 베이스 백업 — BASE_BACKUP, 백업 레이블, 스트리밍 프로토콜 — PostgreSQL 베이스 백업은 클러스터가 계속 실행되는 동안 데이터 디렉터리를 물리적으로 그대로 찍는 스냅샷이다. 복제 프로토콜 명령 BASE_BACKUP은 SendBaseBackup으로 디스패치되며, do_pg_backup_start와 do_pg_backup_stop 사이에 파일 복사를 끼워 넣는다. start는 WAL 삽입 락 아래 runningBackups를 증가시켜 전체 페이지 쓰기를 강제하고 체크포인트로 리두 포인트를 고정한다. stop은 종료 LSN을 기록하고 해당 구간의 WAL이 아카이브될 때까지 기다린다. 그 사이에 perform_base_backup이 sendDir/sendFile로 $PGDATA를 순회하며 각 파일을 bbsink — copystream, progress, throttle, compression, target으로 쌓인 데코레이터 체인 — 를 통해 CopyData 메시지로 내보낸다. base.tar의 첫 번째 멤버는 backup_label(START WAL LOCATION과 CHECKPOINT LOCATION을 담은 복구 안내서)이고, pg_control은 일관된 워크 후 상태를 반영하도록 가장 마지막에 전송된다. 복원된 사본은 그 자체로 바로 사용할 수 없다. 체크포인트 리두 포인트부터 백업 종료 LSN까지 WAL을 크래시 방식으로 재생해야 비로소 일관된 상태가 된다. 증분 백업(PG17+)도 같은 구조를 재사용한다. 클라이언트가 UPLOAD_MANIFEST로 이전 백업 매니페스트를 올리면 GetFileBackupMethod가 WAL 서머리의 블록 참조 테이블을 참조해 파일별로 전체 전송 또는 변경 블록만 담은 INCREMENTAL.* 파일 전송을 결정한다.
- (KO) PostgreSQL 증분 백업 — WAL 서머리, 매니페스트, pg_combinebackup — PostgreSQL 17에서 도입된 증분 물리 백업 구현을 추적한다. 클라이언트가 UPLOAD_MANIFEST로 이전 백업 매니페스트를 서버에 올리면 CreateIncrementalBackupInfo가 스트리밍 JSON 파서로 이를 파싱한다. 이어지는 BASE_BACKUP ... INCREMENTAL에서 PrepareForIncrementalBackup은 매니페스트의 WAL 범위를 타임라인 이력과 대조하고, WAL 서머라이저가 백업 시작 LSN까지 따라잡기를 기다린 뒤, 해당 WAL 서머리 파일들을 하나의 BlockRefTable로 병합한다. GetFileBackupMethod는 이 테이블을 참조해 릴레이션 파일별로 전체 전송, 증분 파일(변경 블록 + 헤더), 또는 헤더만 있는 스텁 중 하나를 결정한다. sendFile은 INCREMENTAL_MAGIC·블록 수·절단 블록 길이·상대 블록 번호 배열로 구성된 헤더를 기록하고 해당 블록만 스트리밍한다. pg_combinebackup은 프론트엔드 툴로, 백업 체인을 최신순으로 내려가며 블록별 sourcemap을 구성해 전체 데이터 디렉터리를 재구성한다. FSM 포크는 WAL에 기록되지 않으므로 항상 전체 전송된다.
- (KO) PostgreSQL 논리적 디코딩 — Reorder Buffer, Snapshot Builder, 출력 플러그인 — 논리적 디코딩은 블록 단위 물리 WAL 스트림을 트랜잭션별·커밋 순서별 행 변경 스트림으로 되돌린다. decode.c는 rmgr 디스패처로서 XLogReadRecord()가 읽어온 각 레코드를 per-rmgr rm_decode 핸들러(xact_decode, heap_decode 등)로 라우팅하고, 핸들러는 물리 레코드를 ReorderBufferChange로 변환해 xid 키로 리오더 버퍼에 넘긴다. reorderbuffer.c는 동시 트랜잭션들의 뒤섞인 변경 스트림을 최상위 트랜잭션 단위로 재조합하며, logical_decoding_work_mem 초과 시 가장 큰 트랜잭션을 디스크에 스필하거나 스트리밍하고, 커밋 레코드 도착 후 k-way 이진 힙 병합으로 변경을 순서대로 재생한다. snapbuild.c는 WAL만으로 SNAPSHOT_HISTORIC_MVCC 카탈로그 스냅샷을 구축한다. [xmin, xmax) 구간의 카탈로그 수정 커밋 xid를 추적하고 XLOG_HEAP2_NEW_CID 레코드로 cmin/cmax를 복원하며, START → BUILDING_SNAPSHOT → FULL_SNAPSHOT → CONSISTENT 4단계 상태 기계를 거쳐 디코딩 가능 상태에 도달한다. logical.c는 출력 플러그인의 _PG_output_plugin_init을 로드하고 begin/change/commit 콜백을 검증하며, 리오더 버퍼가 재생 중 호출하는 에러 컨텍스트 래퍼(begin_cb_wrapper, change_cb_wrapper, commit_cb_wrapper)를 통해 전체 파이프라인을 연결한다.
- (KO) PostgreSQL 논리 복제 — 구독자 Apply, Launcher, Table Sync — 구독자 쪽 논리 복제는 세 종류의 프로세스가 협력해 구동된다. 런처(ApplyLauncherMain)는 postmaster에 등록된 백그라운드 워커로 pg_subscription을 주기적으로 폴링하며, 활성화된 구독마다 리더 apply 워커를 logicalrep_worker_launch로 기동한다. 리더 apply 워커(ApplyWorkerMain → run_apply_worker)는 퍼블리셔에 walrcv_connect로 접속하고 복제 오리진을 설정한 뒤 LogicalRepApplyLoop에서 디코딩된 논리 복제 메시지를 apply_dispatch로 라우팅해 apply_handle_* 핸들러를 실행한다. 행 변경은 LogicalRepRelMapEntry 관계 캐시를 통해 로컬 TupleTableSlot으로 변환되고 executor를 통해 적용된다. INSERT/UPDATE/DELETE 충돌은 ConflictType 열거형(CT_*)으로 분류되어 ReportApplyConflict로 기록된다. 피드백(write/flush/apply LSN)은 get_flush_position이 로컬 내구성을 확인한 뒤 send_feedback으로 업스트림에 전송된다. 초기 데이터 복사는 테이블별 tablesync 워커(TablesyncWorkerMain → LogicalRepSyncTableStart → copy_table)가 담당하며, pg_subscription_rel의 상태 기계 INIT → DATASYNC → FINISHEDCOPY → SYNCWAIT → CATCHUP → SYNCDONE → READY를 거쳐 테이블을 리더 apply 워커에 인계한다.
- (KO) PostgreSQL pg_rewind — 타임라인 분기 감지와 데이터 디렉터리 재동기화 — pg_rewind는 분기된 PostgreSQL 데이터 디렉터리를 재동기화한다. 스테일 타깃과 권위 있는 소스 사이의 마지막 공통 WAL 체크포인트를 찾고, 타깃의 분기 후 WAL을 읽어 수정된 데이터 페이지를 파악한 뒤, 해당 페이지와 내용이 달라진 비관계 파일만 가져와 나머지는 그대로 둔다. 소스 백엔드는 rewind_source vtable로 추상화되며, 라이브 서버용 init_libpq_source와 로컬 디렉터리용 init_local_source 두 가지가 있다. 파일 수준 결정은 경로별 해시 테이블(filehash, file_entry_t)에 누적되고, decide_file_actions()가 해시를 정렬된 filemap_t로 압축할 때 CREATE → COPY → REMOVE 순서를 보장한다. 마지막 공통 체크포인트를 가리키는 backup_label을 기록해 두면, 소스의 WAL 리플레이가 다음 시작 시 남은 간격을 메운다.
- (KO) PostgreSQL pgoutput — 내장 논리 복제 출력 플러그인 — pgoutput은 PostgreSQL 내장 논리 디코딩 출력 플러그인이다. 논리 디코딩이 생성한 트랜잭션 순서 변경 스트림을 구독자 apply 워커가 소비하는 와이어 수준 논리 복제 프로토콜로 직렬화한다. _PG_output_plugin_init에서 OutputPluginCallbacks 테이블을 채우고, proto.c의 logicalrep_write_* 함수로 각 콜백을 'B' BEGIN·'R' RELATION·'Y' TYPE·'I'/'U'/'D' INSERT/UPDATE/DELETE·'T' TRUNCATE·'C' COMMIT·'S'/'E'/'A'/'c' STREAM 프레임으로 변환한다. RelationSyncCache는 테이블별로 발행 여부·pubactions·partition 루트·컬럼 목록·행 필터 ExprState를 메모이즈하고, BEGIN은 첫 번째 살아남은 변경까지 지연하며, 스키마는 세션당 한 번만 전송하고, 스트리밍 모드에서는 각 프레임에 xid를 붙여 구독자가 미확정 서브트랜잭션을 버릴 수 있도록 한다.
- (KO) PostgreSQL 복제 슬롯 — WAL 보존과 catalog_xmin — 복제 슬롯(replication slot)은 공유 메모리에 상주하는 내구성 있는 이름표다. 다운스트림 소비자가 아직 필요로 하는 두 자원을 고정한다. restart_lsn으로 가장 오래된 WAL 세그먼트를, 논리적 슬롯의 경우 catalog_xmin으로 가장 오래된 시스템 카탈로그 행 버전을 보존한다. 고정 크기 배열 ReplicationSlotCtl->replication_slots[]에 max_replication_slots 개의 ReplicationSlot 구조체가 들어간다. 각 슬롯은 필드 갱신을 위한 슬롯별 스핀락과 in_use 비트를 위한 클러스터 전역 ReplicationSlotControlLock으로 보호된다. 영속 슬롯은 크래시에 안전하다. pg_replslot/<name>/state 파일에 원자적으로 기록(tmp + fsync + rename)되며 redo 전에 StartupReplicationSlots가 복원한다. 보존 결정은 간접적으로 강제된다. ReplicationSlotsComputeRequiredLSN이 최솟값 restart_lsn을 집계해 xlog 모듈에 전달하면 체크포인트가 필요한 세그먼트를 재활용하지 않는다. ReplicationSlotsComputeRequiredXmin은 최솟값 catalog_xmin을 ProcArray에 게시해 vacuum이 논리 디코더가 아직 필요한 카탈로그 튜플을 제거하지 않도록 한다.
- (KO) PostgreSQL 동기 복제 — synchronous_commit과 대기 큐 — 동기 복제(synchronous replication)는 커밋 중인 백엔드가 WAL을 로컬 플러시한 뒤 설정된 스탠바이 집합이 동일 LSN을 확인할 때까지 블로킹하는 메커니즘이다. 전체 로직은 주 서버 측에서만 동작한다. 커밋 백엔드는 RecordTransactionCommit에서 SyncRepWaitForLSN(XactLastRecEnd, true)을 호출하고, 공유 메모리의 LSN 순서 대기 큐(WalSndCtl->SyncRepQueue[mode])에 MyProc를 등록한 뒤 자신의 래치(latch)에서 잠든다. synchronous_commit GUC는 내구성 수준(off/local/remote_write/on=remote_flush/remote_apply)을 선택하고, synchronous_standby_names는 FIRST(우선순위) 또는 ANY(정족수) 방식과 필요 스탠바이 수를 SyncRepConfigData로 파싱한다. walsender가 스탠바이 응답을 수신하면 SyncRepReleaseWaiters를 호출해 동기화된 write/flush/apply 위치를 계산(우선순위: 최솟값, 정족수: k번째 최댓값)하고, 조건이 충족된 백엔드를 SyncRepWakeQueue로 깨운다. 스탠바이는 자신이 동기 복제 대상임을 알지 못한다.
- (KO) PostgreSQL WAL 송신자/수신자 — 스트리밍 복제 전송 계층 — walsender(주 서버)와 walreceiver(대기 서버)는 PostgreSQL 스트리밍 복제 전송의 양 끝점이다. walsender는 소형 복제 명령 언어(IDENTIFY_SYSTEM, TIMELINE_HISTORY, START_REPLICATION)를 처리하는 백엔드 변형으로, CopyBoth 모드에서 sentPtr부터 현재 플러시 포인터까지 WAL을 스트림한다. 송신 루프(WalSndLoop)는 XLogSendPhysical 또는 XLogSendLogical로 WAL을 소비하고, 킵얼라이브를 끼워 넣으며, 대기 서버의 응답 메시지(write/flush/apply LSN, 핫스탠바이 피드백 xmin)를 처리한다. 대기 서버의 walreceiver는 WalReceiverMain을 실행하고, libpqwalreceiver를 동적으로 로드한 뒤 IDENTIFY_SYSTEM 핸드셰이크를 수행하고, WAL을 pg_wal에 XLogWalRcvWrite/XLogWalRcvFlush로 기록하며 WalRcvData->flushedUpto를 갱신하고 스타트업 프로세스를 깨운다.
Server Architecture (9)
- (KO) PostgreSQL 보조 프로세스 — bgwriter, walwriter, checkpointer, startup, syslogger — PostgreSQL의 다섯 인프라 프로세스 — bgwriter, walwriter, checkpointer, startup, syslogger — 는 AuxiliaryProcessMainCommon이라는 공통 초기화 경로를 거쳐 각자의 이벤트 루프를 돌린다. bgwriter는 더티 버퍼를 선제적으로 플러시하고, walwriter는 WAL fsync를 묶어 비동기 커밋 지연을 보장하며, checkpointer는 체크포인트 전체를 소유하고 CheckpointerShmem으로 백엔드와 협력한다. startup은 WAL 복구를 주도한 뒤 종료하고, syslogger는 파이프로 stderr를 수집해 로그 파일에 기록한다. syslogger를 제외한 네 프로세스는 ServerLoop 매 반복마다 LaunchMissingBackgroundProcesses가 자동으로 재기동한다.
- (KO) PostgreSQL 백엔드 생애주기 — 포크, 초기화, 명령 루프, 종료 — PostgreSQL 백엔드 프로세스가 생성되어 공유 메모리에 등록되고 카탈로그 접근을 초기화한 뒤 세션별 명령 루프(ReadCommand → exec_simple_query / 확장 쿼리 메시지 → start_xact_command / finish_xact_command)를 실행하고 종료하기까지의 네 단계를 분석한다. postmaster 포크에서 PostgresMain의 sigsetjmp 오류 복구 루프를 거쳐 proc_exit 콜백에 이르는 전체 흐름을 다룬다.
- (KO) PostgreSQL 백그라운드 워커 — 동적 워커 프레임워크 — PostgreSQL가 멀티프로세스 포스트마스터를 확장 가능한 태스크 프레임워크로 만드는 방법: BackgroundWorker 등록 구조체, 정적(RegisterBackgroundWorker, shared_preload_libraries 전용)과 동적(RegisterDynamicBackgroundWorker, 임의 백엔드에서 호출) 등록 경로의 분리, 포스트마스터가 락 없이 워커를 fork할 수 있도록 설계된 공유 메모리의 BackgroundWorkerSlot 배열, 제너레이션 번호가 붙은 핸들 프로토콜, 종료 코드로 결정되는 재시작 생명주기, 그리고 병렬 쿼리·논리 복제 apply·서드파티 확장이 동일한 다섯 개 심볼 API와 DSM/DSA 위에서 작동하는 방식.
- (KO) PostgreSQL 래치, 대기 이벤트 셋, 프로세스 간 시그널 — PostgreSQL 프로세스가 잠드는 방법과 깨어나는 방법: sleep/poll-the-flag 루프를 대체하는 Latch(불리언 + 웨이크업), epoll/kqueue/poll 위에서 래치·소켓·포스트마스터 사망·타임아웃을 하나의 경쟁 없는 대기로 다중화하는 WaitEventSet, 블로킹 poll을 시그널 핸들러가 중단시키도록 하는 self-pipe/signalfd/SIGURG 기법, 백엔드 간 메시지를 위한 SIGUSR1 다중화 ProcSignal 메커니즘, 그리고 비동기 시그널을 동기적·안전한 취소나 종료로 전환하는 InterruptPending/CHECK_FOR_INTERRUPTS/ProcessInterrupts 구조.
- (KO) PostgreSQL Lock Manager — 헤비웨이트 잠금 테이블, 잠금 모드, 그리고 데드락 탐지 — PostgreSQL가 파티셔닝된 공유 메모리 해시(LOCK/PROCLOCK 구조)와 백엔드별 LOCALLOCK 캐시, 약한 관계 잠금을 위한 fast path를 조합해 SQL 수준 잠금을 기록하고, 정적 충돌 테이블로 8가지 잠금 모드를 중재하며, 소프트 에지 큐 재배치를 포함한 waits-for graph 탐색으로 데드락을 해결하는 방식.
- (KO) PostgreSQL LWLock과 스핀락 — 원자 상태 워드, 대기 큐 프로토콜, 플랫폼 TAS — PostgreSQL가 공유 메모리 자료 구조를 두 계층의 잠금으로 보호하는 방식: 마이크로초 단위 배타 동기화를 담당하는 스핀락(slock_t / TAS)과 읽기·쓰기 공유 메모리 접근을 담당하는 LWLock의 구조. LWLock 상태는 배타 센티넬, 공유 보유자 수, 플래그 3비트를 단일 원자 uint32 워드에 인코딩하며, 획득은 큐 진입 후 재시도 경쟁 조건을 닫는 두 번 시도 프로토콜을 사용하고, 해제는 워드를 원자적으로 감소시킨 뒤 조건에 따라 proclist 대기 큐를 PGSemaphoreUnlock으로 깨운다.
- (KO) PostgreSQL Postmaster — 클러스터 감독자, 프로세스 생명주기, 장애 복구 — Postmaster는 PostgreSQL의 클러스터 감독자다. 시작 시 공유 메모리를 한 번 만들고, 이후에는 역할별 BackendType을 가진 자식 프로세스들을 fork하여 클라이언트 연결과 백그라운드 작업을 처리한다. ServerLoop은 WaitEventSet으로 블록하며, 새 연결은 BackendStartup을, SIGCHLD는 process_pm_child_exit와 PostmasterStateMachine을 깨운다. 자식 충돌 시 FatalError를 세우고 형제 프로세스에 SIGTERM/SIGQUIT를 보낸 뒤, 11개 PMState를 차례로 통과해 공유 메모리를 재생성하고 재기동한다. pmchild.c의 PMChild 풀이 모든 활성 자식을 추적하며, CleanupBackend가 슬롯을 반환하고 상태 머신을 구동한다.
- (KO) PostgreSQL 공유 메모리 & IPC — 정적 세그먼트, 동적 공유 메모리, shm_mq 메시지 계층 — PostgreSQL 프로세스가 상태를 공유하는 방식을 분석한다. postmaster 기동 시 한 번 크기가 결정되는 정적 공유 메모리 세그먼트(CalculateShmemSize / CreateSharedMemoryAndSemaphores), ShmemAlloc의 범프 할당자와 이름 레지스트리(ShmemIndex), 런타임에 생성·소멸되는 동적 공유 메모리 세그먼트(dsm_create / dsm_attach / dsm_detach)와 그 플랫폼 디스패치(dsm_impl.c), 병렬 쿼리 튜플 전송을 위해 DSM 위에 얹힌 무락(lock-free) 링 버퍼 메시지 큐(shm_mq), 그리고 가변 크기 공유 데이터 구조를 위한 동적 슬랩 할당자(dsa)를 다룬다.
- (KO) PostgreSQL SSI와 술어 잠금 — 직렬화 스냅샷 격리, SIREAD 잠금, rw-충돌 그래프 — PostgreSQL은 SSI(Serializable Snapshot Isolation)로 진정한 SERIALIZABLE 격리를 구현한다. 스냅샷 격리를 그대로 실행하되 SIREAD 술어 잠금으로 모든 읽기를 추적하고, rw-충돌 에지를 SERIALIZABLEXACT 구조체에 기록한다. OnConflict_CheckForSerializationFailure가 위험 구조(Tin→Tpivot→Tout, 두 에지 모두 rw-충돌이며 Tout이 먼저 커밋)를 감지하면 트랜잭션 하나를 SQLSTATE 40001로 중단한다. 잠금 단위는 튜플→페이지→관계 순으로 승격되어 공유 메모리 사용량을 제한하고, 오래된 커밋 트랜잭션 상태는 SLRU 기반 직렬 로그로 넘긴다.
System Catalog (6)
- (KO) PostgreSQL 캐시 무효화 — sinval 메시지 큐, inval.c 디스패처, 트랜잭션 지연 전송 — PostgreSQL이 동시 DDL 상황에서 백엔드별 catcache·relcache 항목을 일관되게 유지하는 방식을 설명한다. heap_update와 heap_delete는 CacheInvalidateHeapTuple을 호출해 메시지를 등록하고, 이 메시지는 CommandEndInvalidationMessages로 명령 경계까지 미뤄지다가 AtEOXact_Inval에서 공유 sinval 링 버퍼에 브로드캐스트된다. SIInsertDataEntries가 공유 메모리의 4096슬롯 원형 버퍼에 기록하면, 수신 백엔드는 AcceptInvalidationMessages에서 메시지를 드레인해 LocalExecuteInvalidationMessage로 디스패치한다. 버퍼가 넘쳤을 때는 전체 캐시 리셋으로 대응하며, CacheRegisterSyscacheCallback·CacheRegisterRelcacheCallback으로 서브시스템이 무효화 훅을 등록한다.
- (KO) PostgreSQL CatCache & SysCache — 백엔드별 카탈로그 튜플 캐시, 네거티브 항목, sinval 무효화 루프 — PostgreSQL이 시스템 카탈로그 튜플을 각 백엔드 프로세스에 캐싱하는 방식을 분석한다. 키 해시로 버킷화된 CatCTup 항목(양성·음성 모두 포함)으로 이뤄진 프로세스 전용 해시 테이블, 부분 키 다중 행 검색을 위한 CatCList 레이어, pg_*.h 헤더의 MAKE_SYSCACHE 선언에서 파생된 정수 ID로 catcache.c를 감싸는 syscache.c 파사드, 플래너·실행기 전반에서 쓰이는 편의 래퍼 lsyscache.c, 그리고 어떤 백엔드가 카탈로그 변경을 커밋할 때 항목을 dead로 표시하고 등록된 콜백을 발화하는 CatCacheInvalidate/sinval 루프를 다룬다.
- (KO) PostgreSQL 의존성 추적 — pg_depend, CASCADE, 삭제 순서 결정 — pg_depend(로컬)와 pg_shdepend(클러스터 공유) 카탈로그의 구조, 의존성 종류(normal/auto/internal/extension 및 partition·auto-extension 변형), recordDependencyOn과 표현식 트리 워커, DROP ... CASCADE 엔진인 findDependentObjects의 역방향·순방향 2단계 순회가 의존성 그래프를 안전하고 결정적인 삭제 순서로 변환하는 방법, reportDependentObjects의 RESTRICT 게이트, deleteOneObject/doDeletion 디스패치를 다룬다.
- (KO) PostgreSQL 스키마와 search_path — 이름 해석과 네임스페이스 캐시 — PostgreSQL이 비한정 SQL 이름을 객체 OID로 바꾸는 방법: pg_namespace 카탈로그, search_path GUC와 activeSearchPath 파생, pg_temp·pg_catalog 묵시적 선두 삽입, RangeVarGetRelidExtended의 조회-잠금 재시도 루프, 객체 유형별 가시성 스캔, 세션 임시 네임스페이스 생명주기, simplehash 검색 경로 캐시와 activePathGeneration 카운터, syscache 무효화 콜백을 통한 일관성 유지.
- (KO) PostgreSQL Relation Cache — RelationData, 부트스트랩 네일링, Sinval 기반 재빌드 — PostgreSQL이 백엔드 전용 OID 해시 테이블에 릴레이션 메타데이터를 캐시하는 방식을 설명한다. RelationData는 pg_class 행, 튜플 디스크립터, 규칙·트리거·RLS 하위 구조, 인덱스 액세스 정보, 물리 주소를 통합 보관한다. RelationBuildDesc는 시스템 카탈로그 스캔을 통해 항목을 조립하고, 핵심 카탈로그 7개는 부트스트랩 순환 참조를 끊기 위해 formrdesc 또는 pg_internal.init 파일로 시동 시 네일된다. sinval 메시지는 RelationFlushRelation을 구동하며, 참조 카운트가 0인 항목은 삭제하고 열린 항목은 포인터 안정성을 보존하는 스왑-인-플레이스 기법으로 재빌드한다.
- (KO) PostgreSQL 시스템 카탈로그 — 스키마 정의, 부트스트랩 메커니즘, 카탈로그 쓰기 경로 — PostgreSQL이 시스템 카탈로그를 어떻게 정의하고 부트스트랩하는지를 다룬다. pg_*.h 헤더의 CATALOG()/BKI_* 매크로가 genbki.pl을 통해 postgres.bki 부트스트랩 스크립트로 변환되는 과정, OID 범위로 핀 고정된 부트스트랩 객체(OID < 12000)와 일반 객체를 구분하는 방식, 퍼-데이터베이스 카탈로그(pg_class, pg_attribute 등)와 클러스터 전체 공유 카탈로그(pg_authid, pg_database 등)의 구분, 부트스트랩 카탈로그가 pg_class에 쓰지 않고도 안정적인 relfilenode를 가질 수 있게 하는 매핑 관계(relmapper.c / pg_filenode.map) 메커니즘, 그리고 모든 DDL 문이 통과하는 카탈로그 쓰기 경로(heap_create_with_catalog, AddNewRelationTuple, AddNewAttributeTuples, CatalogTupleInsert)를 설명한다.
Monitoring & Statistics (2)
- (KO) PostgreSQL 누적 통계 — PG15 공유 메모리 통계 서브시스템 — PostgreSQL 누적 통계 시스템은 객체별 카운터(릴레이션, 함수, IO, WAL, SLRU, 데이터베이스, 복제 슬롯, 구독, 백엔드)를 축적해 pg_stat_* 뷰와 오토베큠의 테이블 선정에 제공한다. PG15에서 레거시 단일 stats-collector 프로세스를 공유 메모리 서브시스템으로 대체했다. 가변 수 통계(릴레이션·함수 별)는 PgStat_HashKey {kind, dboid, objid}로 인덱싱된 DSA 기반 dshash에 보관되고, dshash 항목(PgStatShared_HashEntry)은 참조 카운트·세대 카운터·dropped 플래그·stats 본문을 가리키는 dsa_pointer만 담는다. 고정 수 통계(체크포인터, bgwriter, archiver, IO, WAL, SLRU)는 평범한 공유 메모리 제어 블록에 인라인으로 자리한다. 각 백엔드는 로컬 해시 테이블(pgStatEntryRefHash)에 캐시된 PgStat_EntryRef 핸들을 거쳐 프로세스 로컬 pending 항목에 업데이트를 버퍼링하므로, 핫 경로는 공유 메모리에 접근하지 않는다. pgstat_report_stat()은 초당 최대 한 번(커밋 시 강제) 각 항목의 전용 LWLock 아래 pending 목록을 플러시한다. 수명은 참조 카운트로 관리된다. DROP은 항목을 dropped로 표시하고 sentinel 참조 카운트를 감소시키지만, 본문은 마지막 백엔드가 참조를 해제할 때 비로소 해제된다. 세대 카운터는 해제 중 재사용 경쟁을 감지하고, gc_request_count 에포크는 백엔드가 stale 로컬 참조를 정리하도록 유도한다. DROP은 트랜잭션적이다. pgstat_drop_transactional()은 실제 드롭을 커밋까지 연기하고 커밋/중단 WAL 레코드에 기록해 복제본과 크래시 복구가 일관성을 유지한다. 체크포인터는 종료 시 테이블을 pg_stat/pgstat.stat에 직렬화하고, 기동 프로세스가 재로딩하거나 크래시 후에는 모든 데이터를 폐기한다.
- (KO) PostgreSQL 대기 이벤트와 진행 상황 보고 — PostgreSQL 백엔드가 현재 상태를 외부에 알리는 구조: 4바이트 wait_event_info 워드(클래스 바이트 + 이벤트 ID)를 잠금 없이 기록하는 pgstat_report_wait_start, wait_event_names.txt 코드 생성으로 만들어지는 클래스별 열거형과 이름 조회, st_changecount 시퀀스 락으로 보호되는 PgBackendStatus 슬롯(pg_stat_activity의 st_state·st_activity_raw·쿼리/플랜 ID 포함), 그리고 20슬롯 st_progress_param 배열로 구현되는 명령 진행 채널(병렬 워커에서는 PqMsg_Progress 메시지로 중계).
Client Protocol (3)
- (KO) PostgreSQL 인증 — pg_hba, SCRAM-SHA-256, SASL — 시작 패킷 이후 PostgreSQL 백엔드가 클라이언트를 인증하는 방식을 분석한다. pg_hba.conf 기반 메서드 선택, AUTH_REQ_* 요청/응답 코드, 채널 바인딩을 포함한 SCRAM-SHA-256 SASL 교환, crypt.c의 MD5·평문 검증기, 모의 인증(mock authentication) 열거 방지 기법, ClientAuthentication_hook 확장점을 다룬다.
- (KO) PostgreSQL 전송 보안 — TLS(OpenSSL)와 GSSAPI 암호화 — PostgreSQL 백엔드가 FE/BE 프로토콜 아래 바이트 스트림을 보호하는 방식. secure_read/secure_write 디스패치 구조가 원시 소켓, OpenSSL TLS, GSSAPI 경로를 어떻게 선택하는지, SSLRequest·GSSENCRequest 단일 바이트 협상이 인증 전에 어떻게 진행되는지, secure_raw_read/secure_raw_write를 감싸는 커스텀 BIO 위에서 SSL_accept가 어떻게 핸드셰이크를 수행하는지, 클라이언트 인증서 CN/DN 추출과 내장 NUL 거부, 채널 바인딩 해시, 그리고 GSSAPI 전송 암호화의 길이-접두사 패킷 프레이밍을 다룬다.
- (KO) PostgreSQL 와이어 프로토콜 — FE/BE 프레이밍, 시작 핸드셰이크, 단순/확장 쿼리 루프 — PostgreSQL 백엔드가 클라이언트와 통신하는 방식을 분석한다. 프로토콜 버전 3의 길이 접두사 이진 프레이밍, 인증에 앞서 수행되는 SSL/GSSAPI 협상과 시작 패킷 핸드셰이크, PostgresMain이 처리하는 단순 쿼리와 확장 쿼리(Parse/Bind/Execute) 메시지 루프, 메시지 조립과 소켓 쓰기를 분리하는 pqcomm/pqformat 송신 버퍼 쌍의 구조를 다룬다.
Extensibility (6)
- (KO) PostgreSQL CustomScan — 플러그인 플랜 노드용 프로바이더 API — PostgreSQL은 익스텐션이 코어를 수정하지 않고 플랜 트리에 완전히 새로운 물리 연산자를 삽입할 수 있도록 CustomScan 프로바이더 API를 제공한다. 세 개의 메서드 vtable 구조체가 질의 처리의 세 단계를 관통한다. CustomPathMethods는 커스텀 패스를 플랜 노드로 낮추는 PlanCustomPath 콜백 하나를 담고, CustomScanMethods는 CustomScan 플랜 노드에서 익스큐터 상태를 생성하는 CreateCustomScanState 콜백 하나를 담으며, CustomExecMethods는 런타임 vtable(BeginCustomScan / ExecCustomScan / EndCustomScan / ReScanCustomScan 필수 + 선택적 mark-restore·병렬 DSM·shutdown·EXPLAIN 훅)을 담는다. 프로바이더는 set_rel_pathlist_hook 또는 set_join_pathlist_hook 전역 훅을 설치하고 add_path()로 CustomPath를 제안하는 방식으로 제어권을 얻는다. 플랜 트리는 복사·직렬화되므로 메서드 필드는 절대 복사되지 않는 정적 vtable의 포인터다. 프로바이더는 RegisterCustomScanMethods로 vtable을 문자열 이름으로 프로세스 로컬 해시에 등록하고, copyfuncs/outfuncs/readfuncs는 GetCustomScanMethods로 이름을 재분해한다. 같은 이름 키 레지스트리가 익스텐서블 노드 프레임워크(RegisterExtensibleNodeMethods / T_ExtensibleNode)까지 확장돼, 프로바이더는 custom_private 안에 임의의 사설 노드 타입을 보관하고 공급자 제공 nodeCopy/nodeEqual/nodeOut/nodeRead 콜백으로 copyObject/nodeToString/stringToNode 왕복을 처리한다.
- (KO) PostgreSQL 익스텐션 — CREATE EXTENSION, 컨트롤 파일, 버전 스크립트 — 익스텐션(extension)은 PostgreSQL이 하나의 설치·삭제 단위로 추적하는 이름 붙은 버전별 SQL 오브젝트 묶음이다. 디스크에는 컨트롤 파일(extname.control, postgresql.conf 파서로 읽힘)과 extname--version.sql / extname--from--to.sql 형태의 버전 스크립트 디렉터리로 존재한다. CREATE EXTENSION(CreateExtension → CreateExtensionInternal)은 컨트롤 파일을 읽고, 버전 그래프 위에서 다익스트라(Dijkstra) 최단 경로(find_install_path / find_update_path)로 설치/업그레이드 스크립트 순서를 결정하며, pg_extension 카탈로그 행(InsertExtensionTuple)을 삽입한 뒤 creating_extension = true 상태에서 각 스크립트를 실행한다. 스크립트 안에서 생성된 오브젝트는 자동으로 pg_depend에 DEPENDENCY_EXTENSION 엣지를 기록한다. DROP EXTENSION은 거의 익스텐션 전용 코드 없이 의존성 기계에 위임된다.
- (KO) PostgreSQL Foreign Data Wrapper — 인-코어 FDW 메커니즘 — FDW(Foreign Data Wrapper, 외부 데이터 래퍼)는 외부 데이터 소스를 일반 테이블처럼 질의할 수 있게 해 주는 확장 기구다. PostgreSQL은 SQL/MED를 엔진 코어에 전부 구현한다. 카탈로그 계층(pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, pg_foreign_table)이 래퍼·연결 끝점·자격증명을 기록하고, DDL 계층(foreigncmds.c)이 그 카탈로그를 관리하며, 디스패치 계층(foreign.c)이 각 래퍼의 핸들러 함수를 호출해 FdwRoutine 콜백 구조체를 얻는다. 플래너는 세 개의 필수 콜백(GetForeignRelSize, GetForeignPaths, GetForeignPlan)으로 외부 관계의 크기 추정·경로 생성·플랜 노드 생성을 위임하고, 익스큐터는 BeginForeignScan / IterateForeignScan / EndForeignScan 커서 인터페이스로 스캔을 구동한다. 선택적 콜백은 원격 조인/집계 푸시다운, 직접 수정, ANALYZE, IMPORT FOREIGN SCHEMA, TRUNCATE, 병렬 실행, 비동기 실행을 담당한다. postgres_fdw는 contrib 모듈이므로 이 문서의 범위 밖이다.
- (KO) PostgreSQL 훅 — 함수 포인터 확장 지점 — PostgreSQL 훅(hook)은 기본값이 NULL인 전역 함수 포인터 변수다. 코어는 안정적인 진입점마다 이 포인터를 검사해, 로드 가능한 모듈이 서버를 포크하지 않고도 동작을 가로챌 수 있게 한다. 패턴은 균일하다. 훅 가능한 연산 Foo()는 얇은 디스패처 Foo()와 실제 로직을 담은 standard_Foo()로 분리된다. 모듈의 _PG_init()은 저장-후-체인(save-and-chain) 관례를 따라 훅을 설치한다. 질의 경로 훅(planner_hook, ExecutorStart/Run/Finish/End_hook, ExecutorCheckPerms_hook, ProcessUtility_hook)은 어느 프리로드 타이밍에서든 설치할 수 있다. 공유 메모리 훅 두 개는 페이즈-게이트(phase-gated)다. shmem_request_hook은 process_shmem_requests() 안에서만, shmem_startup_hook은 CreateSharedMemoryAndSemaphores() 꼬리에서만 유효하다. ClientAuthentication_hook은 인증 판정 후 ClientAuthentication() 끝에서 호출된다.
- (KO) PostgreSQL PL/pgSQL — 핸들러, PLpgSQL_function AST 컴파일, 트리 순회 실행기 — PL/pgSQL은 함수 매니저 핸들러 계약을 구현한 로더블 모듈로, 첫 호출 시 prosrc 본문을 Bison 파서로 파싱해 PLpgSQL_function AST를 만들고 funccache에 캐싱한다. 실행기는 PLpgSQL_stmt 노드를 재귀적으로 순회하며 SQL 구문은 SPI를 통해 처리하고, 스칼라 표현식은 ExprState 직접 평가로 SPI를 우회한다. EXCEPTION 블록은 내부 서브트랜잭션으로 구현해 실패한 본문의 부분 효과를 롤백한 뒤 핸들러를 실행한다.
- (KO) PostgreSQL SPI — 서버 프로그래밍 인터페이스: PL과 확장이 백엔드 내부에서 SQL을 실행하는 방법 — SPI(Server Programming Interface)는 백엔드 내부에서 이미 실행 중인 코드가 SQL을 발행하기 위한 C API다. PL/pgSQL·PL/Python·PL/Perl 등 모든 절차적 언어 핸들러와 대부분의 데이터 수정 확장은 와이어 프로토콜이 아닌 SPI를 통해 파서/플래너/익스큐터 파이프라인에 접근한다. 핵심 추상은 연결 스택(connection stack)이다. SPI_connect는 _SPI_connection 프레임을 하나 쌓는데, 이 프레임은 두 메모리 컨텍스트(결과를 보존하는 procCxt, 매 호출 후 리셋되는 execCxt)를 소유하고 세 API 전역 변수(SPI_processed, SPI_tuptable, SPI_result)의 스냅샷을 저장해 중첩 호출이 상위 호출자의 결과를 덮어쓰지 못하게 한다. SPI_execute는 단발성 플랜(parse→rewrite→plan→execute를 매번 수행)을 실행하고, SPI_prepare는 재사용 가능한 CachedPlanSource를 만들며, SPI_keepplan은 그것을 CacheMemoryContext로 승격시킨다. 결과 행은 DestSPI 수신기(spi_dest_startup과 spi_printtup)가 채우는 SPITupleTable에 누적된다. 커서(SPI_cursor_open)는 플랜을 포털(Portal) 기반 구조에 넘겨 PL이 점진적으로 가져올 수 있게 한다. 미묘한 핵심은 스냅샷·트랜잭션 관리다. _SPI_execute_plan은 네 가지 스냅샷 체제를 구현하며, 비원자(non-atomic) 컨텍스트에서는 SPI_commit/SPI_rollback으로 트랜잭션 경계를 넘을 수 있다.
Internationalization & Text (3)
- (KO) PostgreSQL 콜레이션 프로바이더 — libc, ICU, 그리고 내장 프로바이더 — pg_locale_t 핸들 하나로 libc·ICU·내장(builtin) 세 프로바이더를 추상화하는 구조, 결정론적(deterministic)과 비결정론적(nondeterministic) 동등 비교의 차이, collversion 불일치 경고 메커니즘을 REL_18 소스 기준으로 분석한다.
- (KO) PostgreSQL 문자 집합 인코딩 — 서버/클라이언트 인코딩과 변환 — PostgreSQL이 문자 집합 인코딩 간에 텍스트를 표현하고 변환하는 방식: 데이터베이스 고정 서버 인코딩과 세션별 클라이언트 인코딩, pg_do_encoding_conversion과 pg_conversion 등록 프로시저를 통해 클라이언트↔서버 바이트를 연결하는 변환 프레임워크(UTF-8을 범용 피벗으로 UtfToLocal/LocalToUtf 기수 트리 사용), src/common에서 프런트엔드 도구와 공유하는 인코딩별 pg_wchar_table 함수 포인터 테이블, UTF-8 길이 디코딩과 시프트 기반 DFA 검증기, 그리고 식별자와 문자열 리터럴이 서버 인코딩으로 진입하는 방식을 제어하는 ingress 검증 / egress 신뢰 원칙.
- (KO) PostgreSQL 전문 검색 — tsvector, tsquery, 사전 체인, GIN — PostgreSQL이 문서를 검색 가능한 형태로 변환하는 전 과정을 소스 수준에서 추적한다. 텍스트 파서(wparser_def.c)가 입력을 23종 토큰으로 분류하고, 토큰 타입별 사전 체인(단순/불용어, 동의어, 어간 추출)이 렉셈으로 정규화하며, to_tsvector / to_tsquery가 tsvector(정렬된 렉셈+위치 블롭)와 tsquery(연산자 트리)를 구성한다. @@ 연산자 내부의 TS_execute 3진 매처는 구문 거리 로직을 포함하며, ts_rank / ts_rank_cd 근접 밀도 랭킹이 결과를 정렬한다. GIN 브리지(gin_extract_tsvector / gin_extract_tsquery / gin_tsquery_consistent)는 @@ 를 인덱스 가속 경로로 연결하되 손실적 재검사(lossy recheck) 의미론을 통해 위치·가중치 조건을 정확히 처리한다.
Utilities (6)
- (KO) PostgreSQL 클러스터 부트스트랩 — initdb, 부트스트랩 백엔드, genbki — PostgreSQL 데이터 디렉터리가 탄생하는 과정. genbki.pl은 빌드 타임에 pg_*.h 카탈로그 헤더와 pg_*.dat 데이터 파일을 읽어 postgres.bki 스크립트를 생성한다. initdb는 디스크 위에 디렉터리 트리를 만들고 PG_VERSION과 설정 파일을 쓴 뒤 백엔드를 두 번 구동한다. 첫 번째는 --boot 모드(단독 프로세스 BKI 인터프리터)로 postgres.bki를 소비해 부트스트랩 카탈로그 힙을 만들고, 두 번째는 일반 standalone 백엔드로 system_constraints.sql·system_views.sql·pg_*.sql과 CREATE DATABASE를 실행해 template1을 template0과 postgres로 복제한다.
- (KO) pg_basebackup — BASE_BACKUP 구동, 병렬 WAL 스트리밍, 출력 형식, pg_receivewal / pg_recvlogical 패밀리 — pg_basebackup가 libpq 복제 연결로 BASE_BACKUP 명령을 조립·발행하고, 두 번째 연결에서 ReceiveXlogStream으로 WAL을 병렬 스트리밍하며, COPY 스트림 아카이브를 astreamer 파이프라인으로 plain/tar·압축 출력으로 변환하는 클라이언트 구조; 형제 도구 pg_receivewal(FindStreamingStart 재개 로직을 갖춘 물리 WAL 아카이빙)과 pg_recvlogical(논리 디코딩 소비자)이 공유 streamutil 연결·슬롯 헬퍼를 어떻게 재활용하는지 포함.
- (KO) PostgreSQL pg_ctl / pg_controldata — 서버 생명주기 제어와 클러스터 상태 검사 — pg_ctl은 postgres 바이너리를 fork하고 postmaster.pid를 통해 신호를 전달하는 방식으로 서버를 시작·정지·재시작·재로드·승격·감시하는 커맨드라인 도구다. pg_controldata는 바이너리 제어 파일($PGDATA/global/pg_control)을 읽기 전용으로 출력하며, 512바이트 원자적 쓰기 구조인 ControlFileData 안에서 클러스터의 DBState, 마지막 체크포인트 LSN, 타임라인 ID, WAL 레벨 설정, 컴파일 타임 상수를 확인할 수 있다. 두 도구를 함께 쓰면 SQL 연결 없이 서버 생명주기 전체와 클러스터 건강 상태를 파악할 수 있다.
- (KO) PostgreSQL pg_dump / pg_restore — 카탈로그 기반 논리 백업 — pg_dump은 카탈로그 질의로 스키마를 재구성한다. 직렬화 가능 스냅샷 아래에서 DumpableObject 그래프를 만든 뒤 위상 정렬(TopoSort)로 순서를 결정하고, 아카이브 형식(plain / custom / directory / tar)이 다른 back end로 결과를 직렬화한다. 각 항목은 TOC(목차)에 DDL·COPY 훅·의존 목록으로 기록되며, pg_restore가 이를 직렬 또는 병렬로 재생한다. pg_dumpall은 pg_dump를 데이터베이스별로 위임 호출하면서 역할·테이블스페이스 같은 클러스터 전역 객체를 추가로 내보낸다.
- (KO) PostgreSQL pg_upgrade — 메이저 버전 현위치 업그레이드 — pg_upgrade가 덤프-복구 없이 PostgreSQL 클러스터를 메이저 버전 간 이행하는 방법: 구/신 포스트마스터를 binary-upgrade 모드로 기동하고, pg_dump로 스키마만 덤프한 뒤 신 클러스터에 복원하며, 힙·인덱스 파일을 복사·하드링크·클론·copy_file_range·디렉터리 교체(swap) 중 하나로 옮긴다. OID와 relfilenumber를 보존하여 toast 포인터와 enum 값이 유효하게 유지되도록, 사전 호환성 검사를 거쳐 두 클러스터가 온디스크 데이터를 공유할 수 있음을 확인한다.
- (KO) PostgreSQL pg_waldump — WAL 디코딩 및 검사 유틸리티 — pg_waldump은 디스크의 WAL 세그먼트를 사람이 읽을 수 있는 텍스트로 디코딩하는 읽기 전용 프런트엔드 바이너리다. WALDumpReadPage, WALDumpOpenSegment, WALDumpCloseSegment 세 콜백을 XLogReaderState에 연결한 뒤 XLogReadRecord를 루프로 구동하며, rmgr·xid·릴레이션·블록·포크·전체 페이지 쓰기 존재 여부로 레코드를 필터링한다. 매칭된 레코드마다 rmgr의 rm_desc·rm_identify 콜백을 호출해 표시 줄을 생성한다. --stats 모드에서는 XLogStats를 누적한 뒤 rmgr별(옵션으로 레코드 타입별) 카운트·크기 표를 출력한다. --follow는 WAL 끝 이후에도 루프를 유지하며, --save-fullpage는 전체 페이지 이미지를 디스크에 압축 해제해 저장한다. 서버와 동일한 XLogReader 인프라를 사용하지만 공유 메모리와 버퍼 매니저 없이 완전히 외부 프로세스로 동작한다. WAL 레코드 구조와 rmgr 디스패치는 postgres-wal-records-rmgr.md에서, WAL 삽입과 LSN 메커니즘은 postgres-xlog-wal.md에서 다룬다.
Version Evolution (7)
- (KO) PostgreSQL I/O — 동기 버퍼 읽기에서 비동기 I/O로 — PostgreSQL 읽기 경로는 블록 단위 동기 pread(백엔드가 shared_buffers를 놓치면 커널이 페이지를 돌려줄 때까지 블로킹)에서 출발해, 스토리지 지연을 유용한 작업과 겹치는 방향으로 진화했다. 8.3 시대 ReadBuffer/pread(동기) → 9.0 posix_fadvise(WILLNEED)를 이용한 PrefetchBuffer(비트맵 힙 스캔에 처음 연결, effective_io_concurrency로 조절) → 15 WAL 프리패치(xlogprefetcher.c가 redo 레코드가 곧 건드릴 블록에 fadvise 힌트를 보냄, recovery_prefetch + maintenance_io_concurrency로 제어) → 17 read_stream 추상화(read_stream.c, 블록 번호 콜백을 벡터 읽기로 변환하고 적응형 룩어헤드 적용, 순차 스캔·ANALYZE·VACUUM에 소급 적용) → 18 최초의 독자적 AIO 서브시스템(aio.c, 플러그인형 IoMethodOps vtable, 공유 메모리 내 PgAioHandle 상태 머신, B_IO_WORKER 프로세스, read_stream이 진짜 비동기 완료 위에서 재구성). 핵심 흐름: PG18 이전까지 PostgreSQL은 I/O 동시성 기본 요소를 직접 소유한 적이 없었다. 커널 readahead를 advisory hint로 빌려 쓰다가, PG18에서 비로소 비동기 읽기를 직접 발행하고 추적하며 완료한다.
- (KO) PostgreSQL 병렬 쿼리 — 단일 프로세스에서 병렬 조인·집계·유지보수까지 — PostgreSQL은 9.6 이전까지 쿼리 내 병렬 처리가 전혀 없었다. 9.6에서 동적 공유 메모리(DSM) 위의 Gather 노드와 워커별 튜플 큐가 처음 도입됐고, PG10에서 Gather 아래 서브트리가 풍성해졌다 — 병렬 해시·머지·중첩 루프 조인, partial+finalize 집계, 정렬 순서를 보존하는 GatherMerge. PG11은 병렬 CREATE INDEX(btree 정렬 병렬화)와 파티션을 워커에게 분배하는 parallel-aware Append로 병렬 처리 영역을 DDL과 파티션 레이어까지 넓혔다. PG13은 병렬 VACUUM으로 유지보수 영역에 진입했다. PG14–18은 리더 참여 조정, 병렬 안전 경로 확대, 노드 수준 확장을 통한 점진적 강화로, 현재 Gather/GatherMerge + ParallelContext 설계(postgres-parallel-query.md)에 수렴한다.
- (KO) PostgreSQL 파티셔닝 — 테이블 상속에서 선언적 파티셔닝까지 — PostgreSQL 파티셔닝의 역사: 테이블 상속과 CHECK 제약 조건·INSERT 트리거·constraint_exclusion 프루닝으로 짜 맞추던 수작업 구조가, PG10에서 PARTITION BY 문법·카탈로그 기반 PartitionBoundInfo·C 수준 튜플 라우팅으로 교체되고, PG11에서 해시 파티셔닝·DEFAULT 파티션·파티션와이즈 조인/집계·런타임 프루닝이 추가됐으며, PG12에서 플랜 시점 프루닝 속도와 온라인 ATTACH/DETACH가 개선된 과정을 시대별로 추적한다.
- (KO) PostgreSQL 스토리지 — 고정 배선 힙에서 플러그형 테이블 접근 메서드 API까지 — PostgreSQL은 오랫동안 단 하나의 테이블 스토리지 모델 — 비덮어쓰기 MVCC 힙 — 을 실행기·플래너·DDL 계층에 직접 연결해 사용했다. PostgreSQL 12가 이 결합을 끊고 테이블 접근 메서드(Table Access Method) API를 도입했다. TableAmRoutine은 각 릴레이션의 relcache 항목에 저장되는 약 40개 함수 포인터 vtable이며, 힙은 pg_am을 통해 등록된 하나의 접근 메서드(heapam_methods)로 재포지셔닝됐다. 같은 릴리스에서 TupleTableSlotOps 추상화가 투플 슬롯을 일반화해 실행기가 어떤 AM 고유의 인메모리 형태로도 투플을 운반할 수 있게 됐다. PG15에서 ALTER TABLE ... SET ACCESS METHOD가 추가돼 테이블을 다른 AM으로 재작성할 수 있게 됐고, PG17에서는 SET ACCESS METHOD가 구체화 뷰까지 확장되고 default_table_access_method GUC가 이들을 지배하게 됐다. REL_18 현재 힙은 코어 트리의 유일한 AM으로 남아 있으나, API는 컬럼형·분석형 AM 생태계(Citus columnar, Hydra, ParadeDB)와 중단된 zheap 인플레이스 엔진의 확장점으로 기능하고 있다.
- (KO) PostgreSQL 복제 — WAL 파일 배송에서 논리적 Pub/Sub까지 — PostgreSQL 복제는 8.x의 거친 파일 기반 로그 배송(웜 스탠바이)에서 시작해, 9.0의 바이트 수준 스트리밍과 읽기 전용 핫 스탠바이, 9.1~9.2의 동기·캐스케이드 토폴로지, 9.4의 논리적 디코딩과 복제 슬롯, 10의 내장 Pub/Sub, 그리고 15의 행 필터·열 목록, 14~15의 2PC 디코딩, 17의 페일오버 슬롯과 슬롯 동기화까지 진화했다.
- (KO) PostgreSQL 통계 — 수집기 프로세스에서 확장 통계와 공유 메모리 누적 통계까지 — PostgreSQL의 두 통계 서브시스템 — 플래너 통계(기수성 추정)와 누적 활동 통계(pg_stat_* 뷰, 오토베큠) — 은 서로 다른 경로로 발전하면서 같은 목표를 향했다. 플래너 통계는 열 독립성을 가정하는 pg_statistic 단일 열 요약에서 출발해, PG10의 CREATE STATISTICS(함수적 종속성·다변량 n-distinct), PG12의 다변량 MCV 목록, PG14의 표현식 통계로 확장됐다. 누적 통계는 단일 UDP 수집기 프로세스에서 출발해, PG15에서 DSA/dshash 공유 메모리 방식으로 완전히 교체됐다. PG16은 I/O 전용 고정 stat kind인 pg_stat_io를, PG18은 백엔드별 I/O 통계와 바이트 단위 열을 추가했다.
- (KO) PostgreSQL Vacuum & Visibility — 단순 Lazy Vacuum에서 HOT, Freeze Map, 병렬 처리까지 — PostgreSQL 죽은 튜플 회수 기술의 변천사: 단일 스레드 전체 테이블 lazy vacuum이 HOT(8.3)으로 가비지 생성 자체를 줄이고, visibility map(8.4)으로 깨끗한 페이지를 건너뛰고, all-frozen 비트(9.6)로 이미 동결된 페이지를 anti-wraparound 스캔에서도 건너뛰고, 병렬 인덱스 vacuum(13)으로 인덱스 정리를 워커에 분산하고, wraparound failsafe(14)로 비상 시 인덱스 vacuum을 우회해 동결을 강제하고, TidStore(17)로 flat 배열 대신 압축 기수 저장소로 전환한 과정을 추적한다.
Other (1)
- (KO) PostgreSQL psql — 인터랙티브 터미널: MainLoop, 백슬래시 명령, 쿼리 디스패치, describe.c 카탈로그 계층 — psql이 내부적으로 작동하는 방식: 읽기-스캔-디스패치 사이클을 구동하는 재진입 가능한 MainLoop, 백슬래시 명령 디스패치 테이블인 HandleSlashCmds/exec_command, SQL을 서버에 전송하고 결과를 렌더링하는 SendQuery/ExecQueryAndProcessResults 파이프라인, 그리고 \d 명령을 뒷받침하는 describe.c 카탈로그 쿼리 계층.