(KO) CUBRID Class Object — 인메모리 스키마, attribute, method, partition, 그리고 OODB 유산
목차
학술적 배경
섹션 제목: “학술적 배경”관계형 데이터베이스에서 카탈로그는 각 행이 어떤 모양을 가지는지 를 엔진에 알려 준다. 그러나 엔진이 매 attribute 접근마다 디스크에서 카탈로그 행을 읽지는 않는다 — 그렇게 하면 모든 SELECT가 컬럼당 두 번의 heap fetch로 늘어진다. 대신 각 엔진은 인메모리 스키마 표현, 즉 클래스 식별자를 키로 한 런타임 자료 구조를 만들어 둔다. 파서, 플래너, 실행기, locator가 이 자료 구조에 싸게 묻는다. Database System Concepts (Silberschatz/Korth/Sudarshan, 7판) 24장 “Object-Based Databases 가 이를 메타데이터 캐시” 라고 부르며, 객체 / 객체 관계형 엔진의 경우 이 캐시가 컬럼 모양뿐 아니라 클래스 계층, method, 상속 격자(lattice)까지 들고 있어야 한다고 정리한다.
CUBRID의 출신은 이 캐시를 순수 관계형 엔진보다 더 풍부하게 만든다.
CUBRID은 UniSQL/X 라는 진성 객체 지향 데이터베이스(OODB)에서
출발했다. 클래스, 단일 상속, method (서버에 링크되는 C 함수), shared
attribute, class attribute, root class (클래스의 클래스) 와 일반
클래스를 가르는 metaclass 구분이 모두 있었다. 그 후 ORDB로, 다시
SQL-우선 관계형 엔진으로 변모했지만 OODB 골격을 지우지는 않았다 —
관계형 기능을 그 위에 얹었을 뿐이다. CUBRID의 모든 테이블은 클래스
로, 모든 행은 OID 를 가진 인스턴스 객체로, 모든 컬럼은
SM_REPRESENTATION 으로 결정된 메모리 레이아웃 안의 attribute 슬
롯으로 구현된다. class_object.c 가 220 KB, class_object.h 가
46 KB 인 이유가 여기에 있다 — 한 그래프 안에 관계형 스키마 (컬럼,
제약, 인덱스, partition) 와 OODB 스키마 (method, signature,
resolution, 클래스 계층) 의 합집합을 함께 담는다.
본 문서의 골격을 만드는 두 교과서적 재료가 있다.
- 컴포넌트의 그래프로서의 클래스. 통상적인 처리 (Database
System Concepts, 24장 §Object-Relational Databases, 그리고
Atkinson 외 “The Object-Oriented Database System Manifesto”) 는
클래스를 라벨 그래프로 모델링한다. 클래스 노드가 헤더 메타데이터를
들고 있고, attribute 노드, method 노드, signature 노드, partition
노드, 제약 노드, 그리고 (전이적으로) 슈퍼클래스 / 서브클래스로
엣지가 나간다. CUBRID의
SM_CLASS가 정확히 이 그래프 다 — 다만 서버 heap 이 아니라 클라이언트 측 workspace heap 위에 할당된다. - representation 단위의 스키마 버저닝. ALTER TABLE 이 컬럼을
추가하면 기존 행은 여전히 읽혀야 한다. 교과서적인 답은
REPR_ID를 키로 한 역사적 representation 리스트를 두고, 모든 행이 자기 헤더에REPR_ID를 함께 들고 다니게 하는 것이다. CUBRID의SM_REPRESENTATION사슬은 cubrid-catalog-manager.md 의 디스크 상DISK_REPR사슬을 인메모리에서 거울처럼 따라간다 — 클래스 객체는 최신 representation은 즉시(eagerly), 이전 representation들은 인스턴스 변환을 위해 lazy하게 들고 있다.
본 문서의 나머지는 CUBRID이 이 인메모리 클래스 그래프를 어떻게
구현하는지, catcls_* 로 디스크에서 어떻게 채워지는지,
obj_get / obj_set 가 인스턴스 attribute 접근을 위해 그 그래프를
어떻게 걷는지, 그리고 DDL이 SM_TEMPLATE 그림자 구조로 그것을
어떻게 변형하는지를 추적한다.
DBMS 공통 설계 패턴
섹션 제목: “DBMS 공통 설계 패턴”성능을 챙기는 모든 관계형 엔진은 인메모리 스키마 주변에서 비슷한 패턴을 채택한다.
클래스 식별자를 키로 한 relcache
섹션 제목: “클래스 식별자를 키로 한 relcache”엔진은 class_oid → SM_CLASS * lookup을 O(1) 로 하고, 그 결과를
프로세스 라이프타임 동안 (또는 DDL 무효화가 일어날 때까지) 메모이즈
하고 싶다. PostgreSQL은 이를 relcache 라고 부른다
(src/include/utils/rel.h 의 Relation 구조체). 백엔드별이며 처음
접근 시점에 채워지고, shared-memory 무효화 메시지
(RelationCacheInvalidateEntry) 로 무효화된다. MySQL의 InnoDB
계층은 dictionary cache (storage/innobase/dict/dict0dict.cc 의
dict_table_t) 라고 부른다. CUBRID의 대응물은 workspace
(work_space.h) 다 — 클래스 객체는 사용자 인스턴스를 다루는 동일
기계가 다루는 정상 MOP (Most persistent Object Pointer / “managed
object pointer”) 다. 그래서 라이프사이클과 무효화 모두 별도 스키마
캐시를 만들지 않고 인스턴스 수준 workspace 로직을 재사용한다.
행 디코딩 표로서의 tuple descriptor
섹션 제목: “행 디코딩 표로서의 tuple descriptor”heap 행을 디코드하려면 엔진은 컬럼별
(offset, length, type, nullable) 의 평탄한 배열이 필요하다.
PostgreSQL은 이를 TupleDesc 라 부르고 SQL Server는 row schema 라
부른다. 매 행 디코드에서 attribute 구조체의 linked list 를 걷고
싶지는 않다 — 핫 루프가 수십억 행을 디코드하기 때문이다. CUBRID은
디코더 친화적인 형태는 디스크 측 (system_catalog.h 의 DISK_REPR,
행 디코딩 루프를 곧장 굴릴 수 있는 fixed[] 와 variable[] 패킹된
배열) 에 두고, 편집 친화적인 형태 (SM_ATTRIBUTE 의 linked list)
는 인메모리 클래스 객체에 두며, catcls_* 로 둘을 동기화한다.
1995년 이전 OODB 유산의 메아리
섹션 제목: “1995년 이전 OODB 유산의 메아리”ODMG (Object Database Management Group) 는 1990년대 초 OODB 데이터
모델을 표준화했다. UniSQL (CUBRID의 전신), Itasca, ObjectStore,
GemStone, O2 가 모두 그 변종을 구현했다. 런타임 구조는 어디나
비슷했다 — instance attribute, shared attribute (클래스 인스턴스
전체에 걸친 단일 값), class attribute (metaclass 로 도달
가능한 클래스 단위 값), method (호출 가능한 행위) 를 위한 별도
리스트를 가진 클래스 노드. SQL이 지배적인 질의 언어가 되자 이 엔진
들은 OODB 골격을 지우지 않았다 — 그 위에 SQL 을 얹었다. CUBRID은
오픈 소스로 살아남은 이 계보의 대표 주자다. SM_CLASS 가 이 사실을
반영한다 — SM_VCLASS_CT (virtual class) 와 현대 관계형 테이블이
그중 둘만 쓰는데도 네 리스트를 모두 들고 있다.
양방향 DAG로서의 클래스 계층
섹션 제목: “양방향 DAG로서의 클래스 계층”단일 상속 OODB는 계층의 양방향이 모두 필요하다 — attribute 해소
(어떤 attribute를 어느 클래스가 소유하는지) 를 위해 상위로 올라가야
하고, 상위 클래스가 변할 때 무효화하기 위해 하위로도 내려가야 한다.
관습은 클래스 노드에 두 개의 OBJLIST 를 두는 것이다 — 슈퍼클래스
용 (inheritance) 과 직속 서브클래스용 (users). 다중 상속을 지원
하면 이름 해소가 복잡해진다. CUBRID의 SM_RESOLUTION 레코드가 그
disambiguation 규칙을 들고 있다. 현대 CUBRID 테이블은 다중 상속을
쓰지 않지만 기계는 남아 있다.
DDL 그림자 객체로서의 template
섹션 제목: “DDL 그림자 객체로서의 template”DDL은 스키마를 변형한다. 엔진은 새 클래스 상태를 조립해서, 검증
하고 (제약이 충돌하지 않고, 타입이 호환되며, partition 이 잘 형성
되었는지), 그런 뒤 atomic 하게 swap-in 해야 한다. 엔진은 이를 **
template 또는 builder** 로 처리한다 — SM_CLASS 와 같은 모양의
parallel 구조체이지만 mutable 한 것이다. PostgreSQL은
AlteredTableInfo 를, CUBRID은 SM_TEMPLATE 을 쓴다. 원래의 클래스
객체는 template 가 classobj_install_template 로 commit 될 때까지
read-only 로 잡혀 있다.
카탈로그 ↔ 메모리 다리
섹션 제목: “카탈로그 ↔ 메모리 다리”디스크 위의 카탈로그 행과 메모리 위의 클래스 객체는 모양이 다르다 —
디스크 측은 컴팩트함과 버저닝을 위해 (DISK_REPR.fixed[] 와
variable[] 배열, 인라인 BTREE_STATS) 패킹하고, 메모리 측은 변형
이 쉬운 그래프다. 둘을 번역하는 일은 그 자체로 하나의 서브시스템
이다. PostgreSQL은 RelationBuildDesc 와 pg_class, pg_attribute,
pg_index 읽기를 가진다. CUBRID은 catalog_class.c 의
catcls_* 패밀리를 가진다 — 4–5 K 라인의 코드가 디스크 위
_db_* 레코드를 걷고 OR_VALUE 트리를 조립하면, 클라이언트 측이
그것을 다시 SM_CLASS 로 바꾼다.
이론 ↔ CUBRID 명칭 매핑
섹션 제목: “이론 ↔ CUBRID 명칭 매핑”| 이론적 개념 | CUBRID 명칭 |
|---|---|
| 클래스 헤더 / metaclass 태그 | SM_CLASS_HEADER { ch_obj_header, ch_type, ch_name, ch_rep_dir, ch_heap } |
| 클래스 노드 | SM_CLASS (class_object.h) |
| Instance attribute | SM_ATTRIBUTE (SM_COMPONENT::next 로 연결) |
| Shared attribute (클래스 단위 단일 값) | class_->shared 리스트 |
| Class attribute (metaclass 슬롯) | class_->class_attributes 리스트 |
| Method 정의 | SM_METHOD |
| Method signature | SM_METHOD_SIGNATURE |
| Method 인자 | SM_METHOD_ARGUMENT |
| Method 파일 (링크 시 로드되는 C 소스/오브젝트) | SM_METHOD_FILE |
| 상속 (슈퍼클래스) | class_->inheritance (DB_OBJLIST) |
| 서브클래스 | class_->users (DB_OBJLIST) |
| 다중 상속 disambiguation | SM_RESOLUTION |
| 제약 캐시 | SM_CLASS_CONSTRAINT |
| 외래 키 정보 | SM_FOREIGN_KEY_INFO |
| 부분 인덱스 predicate | SM_PREDICATE_INFO |
| 함수 인덱스 정보 | SM_FUNCTION_INFO |
| Partition | SM_PARTITION |
| 트리거 캐시 | tr_schema_cache *triggers |
| View / virtual class 정의 | SM_QUERY_SPEC 리스트 |
| 옛 행 디코딩 골격 | SM_REPRESENTATION + SM_REPR_ATTRIBUTE |
| DDL builder | SM_TEMPLATE |
| 읽기 전용 browse 사본 | SM_CLASS_INFO |
| 반복 접근 단축 경로 | SM_DESCRIPTOR + SM_DESCRIPTOR_LIST |
| 디스크 ↔ 메모리 다리 패밀리 | catcls_* (catalog_class.c) |
| 최상위 디스크 OR_VALUE builder | catcls_get_or_value_from_class |
| Instance attribute getter | obj_get (object_accessor.c) |
| Instance attribute setter | obj_set |
| 일반 인스턴스 생성 | obj_create |
| 일반 인스턴스 삭제 | obj_delete |
| 클래스 pretty-printer | class_description::init (class_description.cpp) |
CUBRID의 구현
섹션 제목: “CUBRID의 구현”클래스 객체 서브시스템은 여섯 개의 이동 부품으로 이루어져 있다.
SM_CLASS 그래프, attribute / method 컴포넌트 리스트들,
제약과 partition 어노테이션, SM_TEMPLATE 빌더, catcls_*
디스크 다리, 그리고 obj_get / obj_set 인스턴스 접근자. 이
순서로 본다. 그동안 잊지 말 것은 — 이 코드가 클라이언트 측 전용
이다. class_object.h 의 첫 줄이
#if defined (SERVER_MODE) #error Does not belong to server module
이다. 클래스 객체는 클라이언트의 workspace 메모리에 산다. 서버는
OID, heap 행, disk representation 만 본다.
전체 구조
섹션 제목: “전체 구조”flowchart LR
subgraph DISK["디스크 (system_catalog + _db_class)"]
DRP["DISK_REPR 행들"]
CLI["CLS_INFO 행"]
DBC["_db_class 행"]
DBA["_db_attribute 행들"]
DBI["_db_index 행들"]
DBM["_db_method 행들"]
DBP["_db_partition 행들"]
end
subgraph BRIDGE["카탈로그 다리 — catcls_∗"]
GOV["catcls_get_or_value_from_class"]
GOVA["catcls_get_or_value_from_attribute"]
GOVM["catcls_get_or_value_from_method"]
PUT["catcls_put_or_value_into_record"]
INS["catcls_insert_catalog_classes"]
end
subgraph WS["Workspace 메모리 (클라이언트 측)"]
SMC["SM_CLASS"]
ATT["SM_ATTRIBUTE 리스트"]
MET["SM_METHOD 리스트"]
CON["SM_CLASS_CONSTRAINT 리스트"]
PART["SM_PARTITION"]
REPR["SM_REPRESENTATION 사슬"]
TMP["SM_TEMPLATE (DDL 전용)"]
end
subgraph CALLER["호출자"]
OG["obj_get / obj_set"]
DESC["class_description (CSQL 출력)"]
AUTH["au_fetch_class"]
EXEC["executor / scan_manager"]
end
DRP --> GOV
CLI --> GOV
DBC --> GOV
DBA --> GOVA
DBM --> GOVM
DBI --> GOV
DBP --> GOV
GOV --> SMC
GOVA --> ATT
GOVM --> MET
SMC --> ATT
SMC --> MET
SMC --> CON
SMC --> PART
SMC --> REPR
SMC -. commit 시점 swap .- TMP
TMP --> PUT --> INS --> DBC
AUTH --> SMC
OG --> SMC
DESC --> SMC
EXEC --> SMC
이 그림은 세 개의 경계를 압축해 보여 준다. (디스크 / 메모리)
디스크의 카탈로그 행은 catalog_class.c 의 catcls_* 패밀리 — 서버
측 코드 — 가 읽어 OR_VALUE 트리로 만든 뒤 클라이언트로 보내고,
거기에서 클래스 객체가 구체화된다. (읽기 / 쓰기) 읽기 경로는
이미 존재하는 SM_CLASS 에서 끝난다. 쓰기 경로는 SM_TEMPLATE 을
하나 새로 짠 뒤 검증해 두고, 그제야 디스크와 메모리 표현을 한꺼번에
바꾼다. (클래스 / 인스턴스) 클래스 객체는 모양을 기술한다.
obj_get / obj_set 가 그 모양을 따로 fetch된 인스턴스 메모리에
적용한다.
SM_CLASS — 최상위 노드
섹션 제목: “SM_CLASS — 최상위 노드”SM_CLASS 가 허브다. 레이아웃은 다음과 같다 (압축):
// SM_CLASS — src/object/class_object.hstruct sm_class{ SM_CLASS_HEADER header; /* metaclass tag, name, ch_rep_dir, ch_heap */
DB_OBJLIST *users; /* immediate sub classes */ SM_CLASS_TYPE class_type; /* SM_CLASS_CT | SM_VCLASS_CT | SM_ADT_CT */ int repid; /* current representation id */ SM_REPRESENTATION *representations; /* old representations for instance conversion */
DB_OBJLIST *inheritance; /* immediate super classes */ int object_size; /* memory size of an instance in bytes */ int att_count; /* number of instance attributes */ SM_ATTRIBUTE *attributes; /* instance attribute definitions */ SM_ATTRIBUTE *shared; /* shared attribute definitions */ int shared_count; int class_attribute_count; SM_ATTRIBUTE *class_attributes; /* metaclass attribute definitions */
SM_METHOD_FILE *method_files; const char *loader_commands; SM_METHOD *methods; /* instance methods */ int method_count; int class_method_count; SM_METHOD *class_methods; SM_RESOLUTION *resolutions; /* multi-inheritance disambiguation */
int fixed_count; /* number of fixed-length attributes */ int variable_count; /* number of variable-length attributes */ int fixed_size; /* byte size of fixed area */
int att_ids; /* attribute id counter */ int method_ids; /* method id counter */
SM_QUERY_SPEC *query_spec; /* virtual-class query specification */ SM_TEMPLATE *new_; /* edit-in-progress template */ CLASS_STATS *stats; /* lazy-loaded statistics */
MOP owner; /* authorization object */ int collation_id; void *auth_cache;
SM_ATTRIBUTE *ordered_attributes; /* see classobj_fixup_loaded_class () */ DB_SEQ *properties; /* property list, holds constraint metadata */ struct parser_context *virtual_query_cache; struct tr_schema_cache *triggers; /* trigger cache */ SM_CLASS_CONSTRAINT *constraints; /* constraint cache */ const char *comment; SM_CLASS_CONSTRAINT *fk_ref; /* foreign-key references *to* this class */ SM_PARTITION *partition; /* partition info */
unsigned int flags; unsigned int virtual_cache_local_schema_id; unsigned int virtual_cache_global_schema_id; unsigned int virtual_cache_snapshot_version;
int tde_algorithm;
unsigned methods_loaded:1; unsigned post_load_cleanup:1; unsigned triggers_validated:1; unsigned has_active_triggers:1; unsigned dont_decache_constraints_or_flush:1; unsigned recache_constraints:1; unsigned load_index_from_heap:1;};이 구조는 관계형 테이블에 필요한 정도보다 크다 — OODB 유산이다. 짚을 만한 필드들의 의미.
-
header—SM_CLASS_HEADER를 임베드한다. 그 첫 부분이WS_OBJECT_HEADER(cache hash number), 그 다음이 metaclass 태그SM_METATYPE(SM_META_ROOT면 root class, 아니면SM_META_CLASS) 이다.ch_rep_dir필드는 그 클래스의 모든DISK_REPR레코드를 나열하는 디스크 위 directory 레코드의 OID 다 (cubrid-catalog-manager.md §클래스별 정보).ch_heap은 그 클래스의 인스턴스 heap의 HFID 이다. 클래스 단위 메타데이터가 카탈로그와 heap 양쪽으로 닿을 수 있는 정보를 모두 들고 있는 셈이다. -
users/inheritance— 클래스 계층의 양방향 엣지.users는 직속 서브클래스만 (멀티레벨은 transitively walk),inheritance는 직속 슈퍼클래스 리스트다. 둘 다DB_OBJLIST(MOP의 linked list) 이지SM_CLASS *가 아니다 — 의존 클래스는 필요한 시점에au_fetch_class로 lazy하게 로드된다. -
class_type— 세 값.SM_CLASS_CT(정상 클래스 / 테이블),SM_VCLASS_CT(가상 클래스 / view),SM_ADT_CT(추상 데이터 타입 / 의사 클래스).class_description.cpp의 pretty-printer 가 이 값으로 출력 라벨을 결정한다 (MSGCAT_HELP_*메시지 id). -
repid+representations— 최신 representation id 와 역사적SM_REPRESENTATION사슬. 옛 representation은SM_REPR_ATTRIBUTE(attid, type, domain) 만 들고 있다 — 옛 포맷의 행을 디코딩해 최신 레이아웃으로 변환하기에 충분한 분량이다. -
attributes/shared/class_attributes— 세 attribute 리스트. 각각SM_COMPONENT::next로 연결된다. 불변식 — 클래스 안의 모든 attribute의id는 세 리스트를 통틀어 유일하다. 그래서obj_locate_attribute가 어느 리스트를 뒤져도 결과가 같다.// obj_locate_attribute — src/object/object_accessor.c (condensed)intobj_locate_attribute (MOP op, int attid, int for_write,char **memp, SM_ATTRIBUTE **attp){// ... fetch the class via au_fetch_class ...if (is_class){for (att = class_->class_attributes; att != NULL && found == NULL;att = (SM_ATTRIBUTE *) att->header.next)if (att->id == attid)found = att;}else{// search instance and shared attributes}// ...} -
fixed_count/variable_count/fixed_size— 디스크 위DISK_REPR분리를 그대로 거울처럼 따라가는 derived count들이다. hot read path 가 이 값으로 헤더 파싱을 건너 뛴다 — 단순히 개수만 필요한 경우다. -
att_ids/method_ids— 클래스별 monotonic 카운터. 새 컴포넌트가 추가될 때 id를 부여하는 데 쓰인다. 옛 representation 들은 자기 죽은 id 들을 그대로 들고 있다 — 옛 행이 그 id 를 참조해 디코드되기 때문이다. -
new_— DDL 도중에만 NULL이 아니다. 현재 편집 중인SM_TEMPLATE을 가리킨다. 이 필드로classobj_install_template가 자기 타깃을 찾고, 동시 DDL 이 탐지된다. -
stats— lazy-load되는CLASS_STATS(cubrid-catalog-manager.md §통계 — 다른 갱신 주기). 옵티마이저가 통계를 요청하면 그제야 fetch되어 여기에 캐시된다. 무효화는ci_time_stamp비교로. -
ordered_attributes— 모든 인스턴스 attribute (자체 + 상속) 를 정의 순서 로 걷는 평행 포인터 사슬.SM_ATTRIBUTE의 전용order_link필드로 연결된다. 기본attributes리스트 는 storage 순서를 반영하지 정의 순서가 아니다. pretty-printer 와 DESC 출력은 정의 순서를 원한다.classobj_fixup_loaded_class가 클래스 로드 후 이 리스트를 짠다. -
properties— 제약 메타데이터를 직렬화 형태로 들고 있는DB_SEQ. 키는*U,*P,*F,*I(unique/primary/foreign/index 계열) 다. 캐시 형태인SM_CLASS_CONSTRAINT *constraints리스트는 처음 사용 시점에classobj_cache_constraints가properties를 디코드해 만든다.recache_constraints:1비트 플래그가 존재하는 유일한 이유가 이것이다. -
virtual_cache_*필드들 — view 최적화. virtual class 가 의존 하는 underlying 테이블의 스키마가 바뀌지 않았다면 view 의 query를 재컴파일할 필요가 없다.local_schema_id,global_schema_id,snapshot_version셋이 현재 스키마 상태와 비교되는 캐시 유효성 토큰을 이룬다. -
tde_algorithm— 클래스 heap의 Transparent Data Encryption 모드. 클래스 메타데이터 옆에 저장되어 있어서 heap 행을 읽을 때 추가 카탈로그 fetch 없이 복호화할 수 있다. -
꼬리의 비트 플래그 —
methods_loaded는dlopen류의 method 로딩이 끝났는지를 표시한다 (SM_METHOD_FILE파일이 실제 링크 된 상태).triggers_validated,has_active_triggers,recache_constraints도 비슷한 유효성 토큰이다.dont_decache_constraints_or_flush는 특정 reentrant op 도중 무효화 루프를 막기 위해 set한다.load_index_from_heap은 인덱스 할당 시 참조된다 — 보통은 true (기존 heap 행으로부터 재구축), TRUNCATE 가 클리어해서 빈 인덱스가 full heap scan 없이 할당되도록 한다.
SM_CLASS — 시각화
섹션 제목: “SM_CLASS — 시각화”classDiagram
class SM_CLASS {
+SM_CLASS_HEADER header
+DB_OBJLIST* users
+DB_OBJLIST* inheritance
+SM_CLASS_TYPE class_type
+int repid
+SM_REPRESENTATION* representations
+SM_ATTRIBUTE* attributes
+SM_ATTRIBUTE* shared
+SM_ATTRIBUTE* class_attributes
+SM_ATTRIBUTE* ordered_attributes
+SM_METHOD* methods
+SM_METHOD* class_methods
+SM_METHOD_FILE* method_files
+SM_RESOLUTION* resolutions
+SM_QUERY_SPEC* query_spec
+SM_PARTITION* partition
+SM_CLASS_CONSTRAINT* constraints
+SM_CLASS_CONSTRAINT* fk_ref
+tr_schema_cache* triggers
+DB_SEQ* properties
+SM_TEMPLATE* new_
+CLASS_STATS* stats
+MOP owner
}
class SM_CLASS_HEADER {
+WS_OBJECT_HEADER ch_obj_header
+SM_METATYPE ch_type
+const char* ch_name
+OID ch_rep_dir
+HFID ch_heap
}
class SM_ATTRIBUTE {
+SM_COMPONENT header
+pr_type* type
+TP_DOMAIN* domain
+MOP class_mop
+int id
+int offset
+SM_DEFAULT_VALUE default_value
+SM_CONSTRAINT* constraints
+DB_SEQ* properties
+unsigned flags
+int order
+SM_ATTRIBUTE* order_link
+tr_schema_cache* triggers
+MOP auto_increment
+int storage_order
+const char* comment
}
class SM_METHOD {
+SM_COMPONENT header
+SM_METHOD_SIGNATURE* signatures
+METHOD_FUNCTION function
+MOP class_mop
+int id
}
class SM_CLASS_CONSTRAINT {
+const char* name
+SM_ATTRIBUTE** attributes
+int* asc_desc
+SM_PREDICATE_INFO* filter_predicate
+SM_FOREIGN_KEY_INFO* fk_info
+BTID index_btid
+SM_CONSTRAINT_TYPE type
+SM_FUNCTION_INFO* func_index_info
+SM_INDEX_STATUS index_status
}
class SM_PARTITION {
+const char* pname
+int partition_type
+DB_CLASS_PARTITION_TYPE class_partition_type
+DB_SEQ* values
+const char* expr
}
class SM_REPRESENTATION {
+SM_REPR_ATTRIBUTE* attributes
+int id
+int fixed_count
+int variable_count
}
SM_CLASS *-- SM_CLASS_HEADER
SM_CLASS *-- "many" SM_ATTRIBUTE : attributes / shared / class_attributes
SM_CLASS *-- "many" SM_METHOD : methods / class_methods
SM_CLASS *-- "many" SM_CLASS_CONSTRAINT : constraints
SM_CLASS *-- SM_PARTITION : partition
SM_CLASS *-- "many" SM_REPRESENTATION : representations
SM_ATTRIBUTE — 컬럼 / 인스턴스 변수 레코드
섹션 제목: “SM_ATTRIBUTE — 컬럼 / 인스턴스 변수 레코드”// SM_ATTRIBUTE — src/object/class_object.hstruct sm_attribute{ SM_COMPONENT header; /* next, name, name_space */
const struct pr_type *type; /* basic type */ TP_DOMAIN *domain; /* allowable types (precision/scale/setdomain) */
MOP class_mop; /* origin class — for inherited attrs */
int id; /* unique id across all lists in this class */ int offset; /* memory offset inside an instance */
SM_DEFAULT_VALUE default_value; /* original_value + value + default_expr */ DB_DEFAULT_EXPR_TYPE on_update_default_expr;
SM_CONSTRAINT *constraints; /* cached constraint list */ DB_SEQ *properties; /* property list */
unsigned int flags; /* SM_ATTFLAG_* (UNIQUE, PRIMARY_KEY, NON_NULL, ...) */ int order; /* definition order */ struct sm_attribute *order_link; /* list in definition order */
struct tr_schema_cache *triggers; /* per-attribute trigger cache */
MOP auto_increment; /* instance of _db_serial */ int storage_order; /* on-disk storage order */ const char *comment;};흥미로운 필드들.
typevsdomain—type은 원시pr_type(int, varchar, …) 이고,domain은 매개변수화된 도메인 (varchar(20) collate utf8_bin, set of int, …) 이다. 둘은 항상 일관된다 —domain->type == attribute->type— 그러나 분리되어 있어서, 타입 핸들링 코드 (object_primitive.c) 가 도메인을 dereference하지 않고도type으로 싸게 switch할 수 있다.class_mop— 이 attribute 를 원래 정의 한 클래스. 상속된 attribute의 경우attribute->class_mop != containing_class->op다. pretty-printer (class_description::init) 가prt_type == SHOW_CREATE_TABLE일 때 상속 컬럼을 건너뛰는 데 이 필드를 쓴다.id— 클래스 안에서 globally unique이며, 영구적으로 bound 된다. 한 번 할당된 attribute id 는 스키마가 살아 있는 동안 변하지 않는다 (같은 이름으로 DROP 후 ADD 해도 새 id 가 할당된다). 옛SM_REPRESENTATION들이 이 id 들을 들고 있어서 옛 행이 올바로 디코드된다.offset— 인스턴스 메모리 이미지 안의 byte offset. 클래스가 로드될 때 layout 패스 (classobj_fixup_loaded_class) 가 계산한다.default_value— 헤더의 긴 주석을 보라. 두 값을 들고 있다 —original_value(초기 default. 컬럼이 없던 옛 representation 의 행을 읽을 때 쓰임) 와value(새 insert를 위한 현재 default).flags—SM_CLASSFLAG_*류의 attribute 플래그 비트마스크.class_object.h상단의SM_IS_ATTFLAG_UNIQUE_FAMILY와SM_MAP_INDEX_ATTFLAG_TO_CONSTRAINT같은 매크로가 이를 검사하고 변환한다.auto_increment—AUTO_INCREMENT컬럼의 경우 시퀀스 상태를 들고 있는_db_serial인스턴스를 가리킨다. 이 MOP 는 insert 시점에 lazy하게 dereference된다.
SM_METHOD — OODB 유산을 노출한 부분
섹션 제목: “SM_METHOD — OODB 유산을 노출한 부분”// SM_METHOD — src/object/class_object.hstruct sm_method{ SM_COMPONENT header; SM_METHOD_SIGNATURE *signatures; /* signature list (currently only one) */ METHOD_FUNCTION function; /* cached C function pointer */ MOP class_mop; /* defining class */ int id; int order; DB_SEQ *properties; unsigned unused:1;};method 는 CUBRID에서 가장 눈에 띄는 OODB 유산이다. method 는 shared
object 로 컴파일된 C 함수다. 클래스의 SM_METHOD_FILE 엔트리에
나열되며, 처음 호출될 때 dlopen 된다 (methods_loaded:1 가 로드
여부를 기록한다). signature 사슬 SM_METHOD_SIGNATURE → SM_METHOD_ARGUMENT
이 반환 타입과 인자별 타입을 들고 있다. class_object.h 가 4개 인자
부터 33개 인자까지 받는 method 용으로 METHOD_FUNC_ARG4 부터
METHOD_FUNC_ARG33 까지 28개의 typedef 를 정의한다 — 모두 void 를
반환하고 DB_VALUE * 인자를 받는 형태다. 헤더 주석은 “currently
only one signature” 라고 적고 있다 — 오버로딩은 미래 방향이었으나
실현되지 않았다.
오늘날 CUBRID 에서 method 는 애플리케이션 개발자가 거의 쓰지 않는다.
그러나 엔진은 SQL 문법으로 method 를 검증하고 부속 구조체들을 계속
로드한다. 그래서 SM_METHOD 관련 코드가 class_object.c 의 적지
않은 분량을 차지한다.
SM_CLASS_CONSTRAINT — PK, FK, UNIQUE, INDEX 캐시
섹션 제목: “SM_CLASS_CONSTRAINT — PK, FK, UNIQUE, INDEX 캐시”// SM_CLASS_CONSTRAINT — src/object/class_object.hstruct sm_class_constraint{ struct sm_class_constraint *next; const char *name; SM_ATTRIBUTE **attributes; /* NULL-terminated array of attrs in key order */ int *asc_desc; /* one per attribute */ int *attrs_prefix_length; /* prefix-key info */ SM_PREDICATE_INFO *filter_predicate; /* CREATE INDEX ... WHERE */ SM_FOREIGN_KEY_INFO *fk_info; /* FK referent + actions */ char *shared_cons_name; /* name of shared constraint, if any */ BTID index_btid; SM_CONSTRAINT_TYPE type; /* UNIQUE / PRIMARY_KEY / FOREIGN_KEY / INDEX / ... */ SM_FUNCTION_INFO *func_index_info; /* function index */ const char *comment; SM_CONSTRAINT_EXTRA_FLAG extra_status; SM_INDEX_STATUS index_status; /* NORMAL / INVISIBLE / BUILDING-IN-PROGRESS */ SM_INDEX_TYPE index_type; /* always SM_BTREE_TYPE today */ int options; /* deduplicate level + reserved */};제약은 디스크 위에서 SM_CLASS::properties 의 직렬화된 DB_SEQ 형태
로 저장되며, 키는 *U, *P, *F, *I 등이다.
classobj_cache_constraints 가 이 DB_SEQ 를 클래스 로드당 단 한 번
linked list SM_CLASS_CONSTRAINT 로 디코딩한 뒤 캐시를 set한다.
classobj_make_class_constraints 가 그 워크호스 컨스트럭터다 — 모든
property 이름 패밀리를 걸어 가며 제약 리스트를 만든다
(class_object.c:3164).
SM_FOREIGN_KEY_INFO 가 delete_action 과 update_action (CASCADE
/ SET NULL / RESTRICT / NO ACTION) 그리고 ref_match_option 을
들고 있다. SM_PREDICATE_INFO 는 partial-index 의 WHERE 술어를
pred_string (텍스트) 와 pred_stream (컴파일된 XASL) 의 두 형태로
들고 있다. SM_FUNCTION_INFO 는 함수 인덱스 (CREATE INDEX … ON t
(lower(col))) 용 같은 모양의 구조다.
SM_INDEX_STATUS enum 도 짚어 둘 만하다. SM_NORMAL_INDEX = 1,
SM_INVISIBLE_INDEX = 2, SM_ONLINE_INDEX_BUILDING_IN_PROGRESS = 3,
그리고 SM_LAST_INDEX_STATUS = 10 까지의 예약된 슬롯 여섯 개. 온라인
인덱스 빌드는 비교적 최근 기능이다. 예약 슬롯은 미래에 상태가 더
필요할 때 디스크 포맷을 재작업하지 않기 위해 한꺼번에 확보해 두었다.
SM_PARTITION — partition 메타데이터
섹션 제목: “SM_PARTITION — partition 메타데이터”// SM_PARTITION — src/object/class_object.hstruct sm_partition{ struct sm_partition *next; /* currently always NULL */ const char *pname; /* partition name; NULL on parent */ int partition_type; /* PT_PARTITION_RANGE / LIST / HASH */ DB_CLASS_PARTITION_TYPE class_partition_type; /* parent vs child */ DB_SEQ *values; /* range bounds or list members */ const char *expr; /* partition expression text */ const char *comment;};partitioned 클래스에는 두 얼굴이 있다. parent 는 pname == NULL
이며 class_partition_type == DB_PARTITIONED_CLASS 인 partition
정보를 들고 있다. child partition 들은 각자 자기 SM_CLASS 를
가지며, 각각의 SM_PARTITION 이 pname 과
class_partition_type == DB_PARTITION_CLASS 를 가진다. parent/child
관계는 class_->users (parent → children) 와 inheritance (child →
parent) 로 인코딩된다. pretty-printer 의 describe_partition_info
/ describe_partition_parts (object_printer.cpp 안) 가 두 얼굴을
모두 걷는다.
SM_TEMPLATE — DDL 빌더
섹션 제목: “SM_TEMPLATE — DDL 빌더”// SM_TEMPLATE — src/object/class_object.hstruct sm_template{ MOP op; /* class MOP, NULL for new class */ SM_CLASS *current; /* read-only handle to current state */ SM_CLASS_TYPE class_type; int tran_index; /* DDL must run in one transaction */
const char *name; DB_OBJLIST *inheritance; SM_ATTRIBUTE *attributes; SM_METHOD *methods; SM_RESOLUTION *resolutions; SM_ATTRIBUTE *class_attributes; SM_METHOD *class_methods; SM_RESOLUTION *class_resolutions; SM_METHOD_FILE *method_files; const char *loader_commands; SM_QUERY_SPEC *query_spec; SM_ATTRIBUTE *instance_attributes; SM_ATTRIBUTE *shared_attributes; DB_OBJLIST *ext_references; DB_SEQ *properties; int *super_id_map; void *triggers; DB_ATTRIBUTE *partition_parent_atts; SM_PARTITION *partition;};template 은 만들어지고 (classobj_make_template /
classobj_make_template_like), DDL 파서가 변형하고, 그런 뒤
flatten 된다 — 상속이 완전히 해소되고, 슈퍼클래스 attribute 가
복사되어 들어오며, 충돌이 resolution 으로 disambiguate 된다.
flatten된 template 이 classobj_install_template 로 새
SM_CLASS 의 내용이 된다.
// classobj_install_template — src/object/class_object.c (excerpt)intclassobj_install_template (SM_CLASS *class_, SM_TEMPLATE *flat, int saverep){ // ... save current representation if schema actually changed ... // ... copy template fields back into class_ ... // ... bump class_->repid if attributes changed ... // ... rebuild class_->ordered_attributes ... // ... mark class_->recache_constraints if constraints touched ...}saverep 플래그가 직전 representation을 class_->representations
에 append할지를 결정한다. attribute 의 storage 레이아웃이 바뀌면
append 한다. 메타데이터만 바뀌면 (코멘트, collation, owner) 건너뛴다.
카탈로그 ↔ 메모리 다리 — catcls_* 패밀리
섹션 제목: “카탈로그 ↔ 메모리 다리 — catcls_* 패밀리”이 다리는 src/object/ 의 다른 모든 코드와 달리 서버에서 동작
한다. catalog_class.c 는 class_object.c 와 개념적 짝을 이루지만
서버 측이다 — 디스크 레코드가 서버의 heap 파일 안에 살기 때문이다.
다리는 두 방향이다.
디스크 → 메모리 (read 방향). 클라이언트가 au_fetch_class 로
클래스를 fetch할 때 트리거된다.
// catcls_get_or_value_from_class — src/storage/catalog_class.c (sketch)catcls_get_or_value_from_class (THREAD_ENTRY *thread_p, OR_BUF *buf_p, OR_VALUE *value_p){ // For each field of the _db_class row, decode it from buf_p, // and place it into value_p->sub.value[i] as an OR_VALUE. // Recurse into sub-records for attributes, methods, indexes, // partitions through helpers like // catcls_get_or_value_from_attribute, catcls_get_or_value_from_method, // catcls_get_or_value_from_method_signiture, // catcls_get_or_value_from_indexes, catcls_get_or_value_from_partition.}출력 OR_VALUE 트리는 모양이 본질적으로 SM_CLASS 와 같다 — 다만
일반적인 attribute 이름 + DB_VALUE 의 시퀀스 형태다. 결과는
클라이언트로 전송되거나 (server-mode), in-process 로 사용된다
(SA-mode — 파서와 서버가 한 주소 공간을 공유). 클라이언트 측에서
schema-manager 코드 (schema_manager.c. 이 문서에서 자세히 다루지
않음) 가 OR_VALUE 트리를 걸어가며 SM_CLASS 를 짠다.
메모리 → 디스크 (write 방향). DDL 이 반대 방향이다.
catcls_insert_catalog_classes— 새로 생성된 클래스를 새_db_class행과_db_attribute,_db_index,_db_method,_db_partition의 부속 행들을 쓴다.catcls_update_catalog_classes— ALTER 시 기존 행을 교체한다.catcls_delete_catalog_classes— DROP.
이들은 내부 카탈로그 갱신과 같은 system-op 괄호 아래서 서버 측에서
동작한다 (cubrid-catalog-manager.md §“한 번의 ALTER, 처음부터 끝까
지”). 직렬화 헬퍼 catcls_put_or_value_into_buffer 와
catcls_put_or_value_into_record 가 OR_VALUE 트리를 다시 raw
RECDES 바이트로 번역한다.
서버 상주 캐시 둘이 중요하다.
catcls_class_oid_to_oid_hash_table—_db_class의 클래스 oid (_db_class자체를 가리키는 OID) 를, 주어진 사용자 클래스를 기술하는_db_class안의 행의 인스턴스 oid 로 매핑한다. 이게 없으면 모든 카탈로그-클래스 lookup이_db_class를 scan하게 된다.catcls_initialize_class_oid_to_oid_hash_table이 초기화하고,catcls_finalize_class_oid_to_oid_hash_table이 종료한다.catcls_Btid—_db_class.class_name위 인덱스의 BTID 를 캐시한다.catcls_find_btid_of_class_name으로 한 번 찾고 그 뒤로 재사용한다.
카탈로그 ↔ 메모리 흐름
섹션 제목: “카탈로그 ↔ 메모리 흐름”sequenceDiagram
participant CL as Client (parser, executor)
participant WS as Workspace cache
participant AU as au_fetch_class
participant SVR as Server (catalog)
participant CC as catcls_*
CL->>WS: lookup MOP for class C
alt cached
WS-->>CL: SM_CLASS *
else miss
WS->>AU: fetch C with intent (R/W)
AU->>SVR: locator request (with S/X lock)
SVR->>CC: catcls_get_or_value_from_class
CC->>CC: walk _db_attribute, _db_index, _db_method,<br/>_db_partition rows
CC-->>SVR: OR_VALUE tree
SVR-->>AU: serialized OR_VALUE
AU->>WS: build SM_CLASS, run classobj_fixup_loaded_class
WS-->>CL: SM_CLASS *
end
객체 접근자 — 인스턴스 위 obj_get / obj_set
섹션 제목: “객체 접근자 — 인스턴스 위 obj_get / obj_set”클래스 객체는 인스턴스 모양을 기술한다. object_accessor.c 가 그
모양을 실제 인스턴스 메모리에 적용한다. 진입점은 둘이다.
// obj_get — src/object/object_accessor.c (excerpt)intobj_get (MOP op, const char *name, DB_VALUE *value){ int error; SM_ATTRIBUTE *att; SM_CLASS *class_;
error = find_attribute (&class_, &att, op, name, 0); if (error == NO_ERROR) error = obj_get_att (op, class_, att, value); return error;}find_attribute 가 au_fetch_class 에 이어 세 attribute 리스트
(attributes, shared, class_attributes) 위로 이름 lookup을
한다. obj_get_att 가 그 뒤에 au_fetch_instance 로 인스턴스
메모리를 가져 와 attribute 의 offset 과 type 으로 값을 복사해 온다.
setter 의 짝은 다음과 같다.
// obj_set — src/object/object_accessor.c (excerpt)intobj_set (MOP op, const char *name, DB_VALUE *value){ int error; SM_ATTRIBUTE *att; SM_CLASS *class_; // ... error = find_attribute (&class_, &att, op, name, 1); // 1 = for_write if (error == NO_ERROR) error = obj_set_att (op, class_, att, value, NULL); return error;}obj_set_att (대형 함수, 약 200줄) 는 attribute 의 name_space 에
따라 네 분기를 처리한다.
- instance attribute — byte offset 을 찾고,
tp_value_cast로 검증하고, 인스턴스 메모리에 쓰고, dirty 표시. - shared attribute — 클래스 객체의 attribute별
default_value.value에 쓴다 (shared attribute는 인스턴스마다가 아니라 클래스에 단일 저장소를 가진다). - class attribute — shared 와 같지만 metaclass 로 도달한다.
- set / multiset / sequence — collection update 로 재귀한다. 참
조된 객체 위에서
obj_set을 다시 거치는 경로를 탈 수도 있다.
descriptor 변형 (obj_desc_get / obj_desc_set) 이 fast path 다.
사전 계산된 SM_DESCRIPTOR 가 (class_mop, attribute) 쌍을 캐시
하므로, 같은 attribute 를 여러 인스턴스를 반복 접근할 때 이름
lookup 을 피할 수 있다. descriptor 는 SM_DESCRIPTOR_LIST 도 들고
있어서, 인스턴스가 우연히 derived 클래스의 인스턴스일 때를 위한
서브클래스별 override 들을 함께 다룬다.
obj_create / obj_delete 가 인스턴스 라이프사이클의 짝이다.
// obj_create — src/object/object_accessor.c (excerpt)MOPobj_create (MOP classop){ MOP new_mop; OBJ_TEMPLATE *obj_template;
obj_template = obt_def_object (classop); // build a fresh template // ... apply default values for each attribute ... obt_update (obj_template, &new_mop); // commit, get back the new MOP return new_mop;}CUBRID 의 인스턴스 생성은 object template (object_template.h 의
OBJ_TEMPLATE) 을 거친다 — SM_TEMPLATE 와 비슷하지만 클래스가
아니라 인스턴스용이다. template 이 모든 attribute 쓰기를 모은 뒤
한 패스에 제약 검증을 돌리고, 끝에 한 번의 heap insert 를 emit
한다.
한 번의 ALTER, 처음부터 끝까지
섹션 제목: “한 번의 ALTER, 처음부터 끝까지”sequenceDiagram participant P as Parser participant SM as schema_manager participant TMPL as SM_TEMPLATE participant CO as classobj_* participant AU as au_fetch_class participant SVR as Server participant CC as catcls_* P->>SM: ALTER TABLE t ADD COLUMN c SM->>AU: au_fetch_class (t, FETCH_WRITE) AU-->>SM: SM_CLASS *t_class SM->>CO: classobj_make_template (t_class) CO-->>SM: SM_TEMPLATE *tmpl SM->>TMPL: append SM_ATTRIBUTE for c SM->>SM: flatten template (inheritance + resolutions) SM->>SM: validate (no name clash, types compatible) SM->>CO: classobj_install_template (t_class, flat, saverep=1) CO->>CO: append previous SM_REPRESENTATION,<br/>bump repid, rebuild ordered_attributes SM->>SVR: send updated _db_class row SVR->>CC: catcls_update_catalog_classes (t, new record) CC->>SVR: write _db_class, _db_attribute, _db_index<br/>under one system-op SVR-->>SM: ack SM->>AU: invalidate cached t_class on all clients
짚어 둘 만한 두 불변식. 첫째, SM_TEMPLATE 는 install 전에
flatten 된다 — 슈퍼클래스의 attribute 들이 template 의
instance_attributes 리스트로 복사되어 들어오고, resolution 들이
적용되어 이름이 disambiguate 된다. template 은 그 결과로 클래스가
보게 될 완전한 스키마를 들고 있는 셈이다. 로컬 선언분만이 아니다.
둘째, classobj_install_template 는 storage layout 이 변했을
때 직전 representation을 저장한다. 저장된 SM_REPRESENTATION 은
나중에 엔진이 scan 도중 옛 포맷의 행을 만났을 때 필요해진다
(SM_REPR_ATTRIBUTE 와 SM_REPRESENTATION 위의 긴 주석을 참고).
Pretty-printer — class_description
섹션 제목: “Pretty-printer — class_description”class_description.cpp 가 사람이 읽을 수 있는 얼굴이다. type enum
으로 두 가지 출력을 만든다.
CSQL_SCHEMA_COMMAND— CSQL 의;sc <name>/;schema <name>명령의 출력. 상속된 attribute, 트리거, partition 을 포함한다.SHOW_CREATE_TABLE— SQL 호환의SHOW CREATE TABLE출력. 상속 된 attribute 는 제외 (그것들은 슈퍼클래스의 CREATE TABLE 에 속한다), 식별자는 bracket-quote, default 표현식은 SQL 형태로 포맷한다.
흐름은 — class_description::init(op, prt_type) → au_fetch_class
→ sm_clean_class → SM_CLASS 의 리스트들을 걸어가며 컴포넌트마다
object_printer::describe_attribute, describe_method,
describe_constraint, describe_partition_info 등을 호출하면서
class_description 의 string-array 필드 (attributes[],
methods[], constraints[], partition[]) 에 누적해 간다. 이
walk 는 안정적인 출력 순서를 위해 ordered_attributes 를 따르며,
class_mop != op 비교로 자체 컴포넌트와 상속 컴포넌트를 구분한다.
식별자 검증 — identifier_store
섹션 제목: “식별자 검증 — identifier_store”identifier_store.cpp 는 DDL 경로가 식별자 집합 (컬럼 이름, 인덱스
이름) 을 한꺼번에 검증할 때 쓰는 작은 유틸리티다. 클래스는
std::vector<std::string> 으로부터 생성된다. check_valid 옵션을
주면 각 엔트리를 걸어가며 “글자로 시작해야 하고, 나머지는 ASCII
alnum + 밑줄. 단 _db 시스템 클래스 prefix 는 예외” 규칙을 적용
한다.
// check_identifier_is_valid — src/object/identifier_store.cppboolidentifier_store::check_identifier_is_valid (const std::string_view str, bool is_enclosed){ if (is_enclosed) { if (std::any_of (str.begin (), str.end (), [] (char c) { return c == '.' || c == '[' || c == ']'; })) return false; } else { if (strncasecmp (str.data (), SYSTEM_CLASS_PREFIX.c_str (), SYSTEM_CLASS_PREFIX.length ()) != 0) { int size = str.size (); for (int i = 0; i < size; i++) if ((i == 0 && !char_isalpha (str[0])) || (i >= 1 && (!char_isalnum (str[i]) && str[i] != '_'))) return false; } } return true;}내부 저장소는 cubbase::string_set_ci_lower 다 — case-insensitive
set 이라서 is_exists 가 어떤 케이스 lookup 도 받는다. 클래스는
immutable 하고 thread-safe 하지 않다 (한 DDL 작업 동안 한 번 만들어
서 중복 식별자 탐지에 쓰이고 그대로 버려진다).
소스 코드 가이드
섹션 제목: “소스 코드 가이드”anchor 는 심볼명 이다. 라인은 흘러간다.
최상위 타입 (class_object.h)
섹션 제목: “최상위 타입 (class_object.h)”SM_CLASS— 인메모리 클래스 노드.SM_CLASS_HEADER— 클래스와 root class 가 공유하는 공통 헤더 (WS_OBJECT_HEADER,SM_METATYPE,ch_name,ch_rep_dir,ch_heap을 들고 있다).SM_CLASS_TYPEenum —SM_CLASS_CT/SM_VCLASS_CT/SM_ADT_CT.SM_METATYPEenum —SM_META_ROOTvsSM_META_CLASS.SM_CLASS_FLAGenum —SYSTEM,WITHCHECKOPTION,LOCALCHECKOPTION,REUSE_OID,SUPPLEMENTAL_LOG.SM_COMPONENT— attribute 와 method 가 공유하는 generic 헤더 (next, name, name_space).SM_NAME_SPACEenum — name-space 태그 (instance attribute, shared attribute, class attribute, method 등을 식별).SM_ATTRIBUTE— attribute 정의.SM_DEFAULT_VALUE— initial + current default + default expr.SM_CONSTRAINT(attribute별 캐시) vsSM_CLASS_CONSTRAINT(클래스 단위, 다 attribute).SM_CONSTRAINT_TYPEenum — UNIQUE, INDEX, NOT_NULL, REVERSE_UNIQUE, REVERSE_INDEX, PRIMARY_KEY, FOREIGN_KEY.SM_FOREIGN_KEY_INFO— 참조 클래스, 참조 attr, 액션, match 옵션, 참조 BTID.SM_FOREIGN_KEY_ACTIONenum — CASCADE / SET NULL / RESTRICT / NO ACTION.SM_PREDICATE_INFO— partial-indexWHERE술어.SM_FUNCTION_INFO— 함수 인덱스 표현식 메타데이터.SM_INDEX_STATUSenum — NORMAL, INVISIBLE, ONLINE_BUILDING_IN_PROGRESS, 그리고 예약 슬롯 여섯 개.SM_METHOD,SM_METHOD_SIGNATURE,SM_METHOD_ARGUMENT,SM_METHOD_FILE— method 기계.METHOD_FUNC_ARG4..ARG33typedef — 인자 개수별 method 함수 포인터 타입 (28개).SM_RESOLUTION— 다중 상속 disambiguation.SM_REPRESENTATION+SM_REPR_ATTRIBUTE— 역사적 행 레이아웃.SM_QUERY_SPEC— virtual class query 명세.SM_PARTITION— partition 메타데이터.SM_TEMPLATE— DDL 빌더.SM_CLASS_INFO— 읽기 전용 browse 사본.SM_DESCRIPTOR+SM_DESCRIPTOR_LIST— 반복 attribute 접근용 fast 캐시.SM_VALIDATION— descriptor 안에 임베드된 type/domain 검증 캐시.
생성 / 소멸 (class_object.c)
섹션 제목: “생성 / 소멸 (class_object.c)”classobj_area_init/classobj_area_final—db_open/db_shutdown시 area allocator 설정.classobj_make_class— 빈SM_CLASS할당.classobj_free_class— 모든 linked list 의 deep free.classobj_make_template/classobj_make_template_like/classobj_free_template.classobj_make_attribute/classobj_copy_attribute/classobj_free_attribute.classobj_make_method/classobj_copy_method/classobj_free_method.classobj_make_method_signature/classobj_make_method_arg.classobj_make_method_file.classobj_make_resolution/classobj_free_resolution.classobj_make_query_spec/classobj_free_query_spec.classobj_make_repattribute/classobj_make_representation/classobj_free_representation.classobj_make_partition_info/classobj_copy_partition_info/classobj_free_partition_info.
Lookup
섹션 제목: “Lookup”classobj_find_attribute(이름) /classobj_find_attribute_id(id).classobj_find_method.classobj_find_component/classobj_complist_search.classobj_find_class_constraint(type+name) /classobj_find_class_constraint_by_btid.classobj_find_class_index/classobj_find_constraint_by_name/classobj_find_constraint_by_attrs.classobj_find_class_primary_key/classobj_find_cons_primary_key.classobj_find_resolution.classobj_find_representation.
제약 캐시
섹션 제목: “제약 캐시”classobj_make_class_constraints— property 리스트를SM_CLASS_CONSTRAINT리스트로 디코드.classobj_cache_constraints/classobj_cache_class_constraints/classobj_decache_class_constraints.classobj_get_cached_constraint— attribute별 캐시된 BTID lookup.classobj_has_class_unique_constraint/classobj_has_unique_constraint/classobj_has_function_constraint.classobj_class_has_indexes.classobj_check_attr_in_unique_constraint.classobj_check_index_exist.classobj_change_constraint_status/classobj_change_constraint_comment.classobj_populate_class_properties.classobj_remove_class_constraint_node.classobj_is_pk_referred.classobj_describe_foreign_key_action.classobj_map_constraint_to_property.classobj_free_foreign_key_ref/classobj_free_function_index_ref.
Property 리스트 (DB_SEQ 인코딩)
섹션 제목: “Property 리스트 (DB_SEQ 인코딩)”classobj_make_prop/classobj_copy_props/classobj_free_prop.classobj_put_prop/classobj_drop_prop.classobj_put_index— index 엔트리 추가.classobj_find_prop_constraint.classobj_btid_from_property_value/classobj_oid_from_property_value.
Template 라이프사이클
섹션 제목: “Template 라이프사이클”classobj_install_template— commit 시점의 atomic swap.classobj_fixup_loaded_class— load 후 layout 패스.ordered_attributes를 짜고 attribute별offset을 계산.classobj_filter_components— 컴포넌트 리스트를 name-space 별로 분리.classobj_annotate_method_files— 상속 method 파일에class_mop설정.classobj_initialize_attributes/classobj_initialize_methods.classobj_copy_attlist/classobj_copy_reslist/classobj_copy_methfiles.classobj_copy_default_expr.classobj_class_size— SM_CLASS heap footprint 계산 (디버그).
Descriptor (fast 접근 캐시)
섹션 제목: “Descriptor (fast 접근 캐시)”classobj_make_descriptor/classobj_make_desclist.classobj_free_descriptor/classobj_free_desclist.
Pretty-print (디버그 전용)
섹션 제목: “Pretty-print (디버그 전용)”classobj_print—#if defined (CUBRID_DEBUG)안.
카탈로그 다리 (catalog_class.c)
섹션 제목: “카탈로그 다리 (catalog_class.c)”catcls_Enable— 카탈로그-클래스 유지 글로벌 토글.catcls_initialize_class_oid_to_oid_hash_table/catcls_finalize_class_oid_to_oid_hash_table.catcls_find_oid/catcls_put_entry/catcls_remove_entry/catcls_replace_entry_oid.catcls_get_or_value_from_class— 최상위 read 디코더.catcls_get_or_value_from_attribute/catcls_get_or_value_from_attrid/catcls_get_or_value_from_domain.catcls_get_or_value_from_method/catcls_get_or_value_from_method_signiture/catcls_get_or_value_from_method_argument/catcls_get_or_value_from_method_file.catcls_get_or_value_from_resolution.catcls_get_or_value_from_query_spec.catcls_get_or_value_from_indexes.catcls_get_or_value_from_partition.catcls_get_subset/catcls_get_object_set/catcls_get_property_set.catcls_reorder_attributes_by_repr/catcls_expand_or_value_by_repr/catcls_expand_or_value_by_subset/catcls_expand_or_value_by_def.catcls_get_or_value_from_buffer/catcls_put_or_value_into_buffer/catcls_put_or_value_into_record.catcls_insert_subset/catcls_delete_subset/catcls_update_subset.catcls_insert_instance/catcls_delete_instance/catcls_update_instance.catcls_insert_catalog_classes/catcls_update_catalog_classes/catcls_delete_catalog_classes.catcls_compile_catalog_classes— install 시점 스키마 빌드.catcls_get_server_compat_info/catcls_get_db_collation.catcls_update_class_stats.catcls_find_class_oid_by_class_name/catcls_find_btid_of_class_name/catcls_find_oid_by_class_name.catcls_convert_class_oid_to_oid/catcls_convert_attr_id_to_name.catcls_apply_component_type/catcls_apply_resolutions/catcls_resolution_space.catcls_set_or_value_timestamps/catcls_update_or_value_updated_time/catcls_copy_or_value_times_and_statistics/catcls_update_or_value_class_stats_fields.catcls_cache_fixed_attr_indexes.
객체 접근자 (object_accessor.c)
섹션 제목: “객체 접근자 (object_accessor.c)”obj_get/obj_set— 이름으로의 public attribute 접근.obj_get_att/obj_set_att—SM_ATTRIBUTE *로의 접근.obj_desc_get/obj_desc_set—SM_DESCRIPTOR *로의 fast 접근.obj_locate_attribute— id 로의 접근. 메모리 포인터 반환.obj_assign_value— generic setter. template 들이 사용.obj_get_value/obj_get_path/obj_get_temp/obj_set_temp— collection, path, 임시 객체용 변형들.obj_get_shared/obj_set_shared— shared-attribute 강제 접근.obj_alloc— 클래스 layout 으로 인스턴스 메모리 할당.obj_create/obj_create_by_name/obj_copy/obj_delete/obj_free_memory— 인스턴스 라이프사이클.
Pretty-printer (class_description.cpp)
섹션 제목: “Pretty-printer (class_description.cpp)”class_description::class_description()— ctor (zero 채움).class_description::~class_description()— dtor (string 배열 과 vector 모두 free).class_description::init(const char *name)— 클래스 이름으로.class_description::init(db_object *, type)— MOP 으로.class_description::init(db_object *, type, string_buffer &)— shared-buffer 변형 (임베드 사용).init_triggers(file-static) — 트리거 리스트 채움.describe_trigger_list(file-static).
Identifier store (identifier_store.cpp)
섹션 제목: “Identifier store (identifier_store.cpp)”cubbase::identifier_store::identifier_store(vector, check_valid)— ctor.identifier_store::~identifier_store()— dtor.identifier_store::is_exists— case-insensitive lookup.identifier_store::is_valid— 전체 valid 여부 (생성 시점에 설정).identifier_store::get_size.identifier_store::check_identifier_condition(private).identifier_store::check_identifier_is_valid(static) — 단일 식별자 규칙 검사.identifier_store::is_enclosed(static) — 인용 / bracket / backtick 술어.
이 개정 시점의 위치 힌트 (2026-04-30)
섹션 제목: “이 개정 시점의 위치 힌트 (2026-04-30)”| 심볼 | 파일 | 라인 |
|---|---|---|
SM_CONSTRAINT_TYPE (enum) | class_object.h | 281 |
SM_CLASS_TYPE (enum) | class_object.h | 296 |
SM_CLASS_FLAG (enum) | class_object.h | 305 |
SM_METATYPE (enum) | class_object.h | 318 |
SM_CLASS_HEADER (struct) | class_object.h | 351 |
SM_CONSTRAINT (struct) | class_object.h | 370 |
SM_COMPONENT (struct) | class_object.h | 387 |
SM_DEFAULT_VALUE (struct) | class_object.h | 397 |
SM_ATTRIBUTE (struct) | class_object.h | 445 |
SM_FOREIGN_KEY_INFO (struct) | class_object.h | 478 |
SM_PREDICATE_INFO (struct) | class_object.h | 497 |
SM_FUNCTION_INFO (struct) | class_object.h | 508 |
SM_INDEX_STATUS (enum) | class_object.h | 518 |
SM_CLASS_CONSTRAINT (struct) | class_object.h | 553 |
SM_METHOD_ARGUMENT (struct) | class_object.h | 591 |
SM_METHOD_SIGNATURE (struct) | class_object.h | 603 |
SM_METHOD (struct) | class_object.h | 626 |
SM_METHOD_FILE (struct) | class_object.h | 650 |
SM_RESOLUTION (struct) | class_object.h | 663 |
SM_REPR_ATTRIBUTE (struct) | class_object.h | 683 |
SM_REPRESENTATION (struct) | class_object.h | 702 |
SM_QUERY_SPEC (struct) | class_object.h | 718 |
SM_PARTITION (struct) | class_object.h | 728 |
SM_CLASS (struct) | class_object.h | 746 |
SM_TEMPLATE (struct) | class_object.h | 819 |
SM_CLASS_INFO (struct) | class_object.h | 867 |
SM_DESCRIPTOR_LIST (struct) | class_object.h | 903 |
SM_VALIDATION (struct) | class_object.h | 916 |
SM_DESCRIPTOR (struct) | class_object.h | 933 |
classobj_cache_constraints | class_object.c | 2528 |
classobj_make_class_constraint | class_object.c | 2631 |
classobj_free_class_constraints | class_object.c | 2690 |
classobj_make_class_constraints | class_object.c | 3164 |
classobj_make_class | class_object.c | 6832 |
classobj_free_class | class_object.c | 6928 |
classobj_fixup_loaded_class | class_object.c | 7109 |
classobj_install_template | class_object.c | 7349 |
classobj_make_partition_info | class_object.c | 8638 |
obj_locate_attribute | object_accessor.c | 321 |
obj_assign_value | object_accessor.c | 645 |
obj_set_att | object_accessor.c | 721 |
obj_set | object_accessor.c | 931 |
obj_set_shared | object_accessor.c | 1012 |
obj_get_value | object_accessor.c | 1307 |
obj_get_att | object_accessor.c | 1387 |
obj_desc_get | object_accessor.c | 1470 |
obj_get | object_accessor.c | 1508 |
obj_get_shared | object_accessor.c | 1549 |
obj_alloc | object_accessor.c | 1863 |
obj_create | object_accessor.c | 1921 |
obj_copy | object_accessor.c | 1983 |
obj_delete | object_accessor.c | 2090 |
class_description::init (3-arg) | class_description.cpp | 192 |
catcls_finalize_class_oid_to_oid_hash_table | catalog_class.c | 286 |
catcls_find_oid | catalog_class.c | 321 |
catcls_put_entry | catalog_class.c | 353 |
catcls_remove_entry | catalog_class.c | 391 |
catcls_get_or_value_from_class | catalog_class.c | 999 |
catcls_get_or_value_from_attribute | catalog_class.c | 1273 |
catcls_get_or_value_from_attrid | catalog_class.c | 1622 |
catcls_get_or_value_from_domain | catalog_class.c | 1690 |
catcls_get_or_value_from_method | catalog_class.c | 1834 |
identifier_store::check_identifier_is_valid | identifier_store.cpp | 95 |
소스 검증 노트
섹션 제목: “소스 검증 노트”- 디스크와 메모리 representation 은 의도적으로 같은 모양이 아니
다.
cubrid-catalog-manager.md가DISK_REPR을 빠른 행 디코딩 을 위한 packedfixed[]/variable[]배열로 기술한다. 인메모리 의SM_ATTRIBUTE사슬은 storage 순서로SM_COMPONENT::next로 연결된 linked list 다. 두 representation 은 서로 다른 소비자 (디코더 vs 에디터) 를 위해 존재하며,catcls_*패밀리로 만 동기화된다. 버그나 불일치는 항상 그 다리 위에서만 발생한다. 한쪽 안에서만 일어날 수 없다. SM_CLASS::stats는statistics.h에 정의된 동일한CLASS_STATS다 (cubrid-catalog-manager.md §통계). 이 필드는 옵티마이저가 통계를 요청하기 전까지NULL이다. 첫 요청이 클래스 별DISK_ATTR::bt_stats[]등의 서버 fetch 를 트리거한다. 캐시 무효화는 catalog manager 가 쓰는 것과 동일한ci_time_stamp비교 로 일어난다.class_->header.ch_heap이 heap 측 scan 의 canonical HFID 다. 이 값은CLS_INFO::ci_hfid에 저장된 HFID 와 동일하다 (cubrid-heap-manager.md §Heap file identifier). 클래스 객체가 이 HFID 를 inline 으로 들고 있어서 클래스가 로드된 뒤에는 heap scan 에 카탈로그 round-trip 이 필요 없다.class_->header.ch_rep_dir은 directory-record OID 다 —CLS_INFO::ci_rep_dir가 저장하는 OID 와 같다. 거기로부터 카탈 로그의 representation directory 를 걸어가서 그 클래스의 모든DISK_REPR을 열거할 수 있다.- 제약은
SM_CLASS::constraints에 캐시 되지만 권위 있는 자리 는SM_CLASS::properties다.constraints리스트는classobj_cache_constraints가properties로부터 lazy 하게 디코드한다. DDL 쓰기는 먼저properties를 갱신한 뒤,classobj_decache_class_constraints를 호출해 다음 접근 시점에 재 디코드를 강제한다. - method 서브시스템은 현대 사용에서 거의 휴면 상태다. CUBRID 의
stored-procedure 기능 (cubrid-pl-javasp.md, cubrid-pl-plcsql.md)
이 현대적 대체이며, C method 가 아닌 Java / PL-CSQL 을 쓴다.
SM_METHOD기계는 legacy 스키마가 사용할 수 있고 파서가 여전히 문법을 받아 주기 때문에 그대로 남아 있다.
미해결 질문
섹션 제목: “미해결 질문”classobj_fixup_loaded_class가object_size를 언제 재계산하는가? 이 필드는 attribute 가 추가되거나 그 도메인의 크기가 커질 때 변한다. 추적 경로 —classobj_install_template에서 size-recompute 호출 자리를 추적.- partition 이 관여한 ALTER TABLE 의 정확한 연산 순서는? 부모
파티션 + N 개의 자식은 곧 N+1 개 클래스다. 어떤 template 이 먼저
install 되는가? 추적 경로 —
do_partition.c의do_alter_partitioning_*. catcls_compile_catalog_classesvsschema_system_catalog_install.cpp.cubrid-catalog-manager.md의 헤더 주석은 install 시점 컴파일러가 시스템 클래스를 빌드한다고 적었는데, 스키마 정의는schema_system_catalog_install.cpp에 산다. 어느 쪽이 어느 쪽을 쓰는가? 아마도 install 파일이 C++ source of truth 이고catcls_compile_catalog_classes가 그것을 소비할 가능성이 높지만, call chain 을 한 번 확인해 둘 필요가 있다.- 원격 DDL 후 workspace 캐시는
SM_CLASS를 어떻게 무효화 하는가? 다른 클라이언트가 ALTER 를 commit 하면, 이 클라이언트 의 캐시된SM_CLASS는 stale 해진다. 추적 경로 —ws_decache호출과 OID 무효화 메시지 스트림. - TDE 와 class_object 의 상호작용.
SM_CLASS::tde_algorithm은 모든 페이지 복호화에서 heap 이 읽는다. hot accessor 는 어디 이며, 매 페이지 fetch 마다 workspace 캐시를 통한 indirection 을 어떻게 피하는가? 추적 경로 —heap_file.c의 heap 페이지 fetch 가 클래스 TDE 상태에 어떻게 닿는지 추적. SM_CLASS::auth_cache의 라이프사이클. authorization 은 클래스별로void *로 캐시된다 —class_object.c입장에서는 불투명하다. 무엇이 그 안으로 컴파일되며, 언제 무효화되는가? 추적 경로 —authenticate*.cpp의au_*함수들.- 온라인 인덱스 빌드 상태 전이.
SM_INDEX_STATUS는 명명된 세 값 너머에 예약 슬롯 여섯 개를 가진다. 계획된 상태들은 무엇이 었는가? 추적 경로 — ML history /git log -- src/object/class_object.h. SM_CLASS_CONSTRAINT위 dedup-leveloptions필드. 비트 0–3 이 deduplicate 레벨 (0–14) 을 가진다. 각 레벨이 무슨 뜻이고 어디에서 소비되는가? 추적 경로 —deduplicate_key.{c,h}와IS_DEDUPLICATE_KEY_ATTR_ID.
형제 문서
섹션 제목: “형제 문서”knowledge/code-analysis/cubrid/cubrid-catalog-manager.md— 디스크 위 카탈로그 representation.DISK_REPR,CLS_INFO,CTID가 본 문서의 다리 endpoint 들과 짝이다.knowledge/code-analysis/cubrid/cubrid-heap-manager.md— 클래스 인스턴스가 heap 행으로 산다.class_->header.ch_heap가 이 레이어가 사용하는 HFID 다.knowledge/code-analysis/cubrid/cubrid-btree.md—SM_CLASS_CONSTRAINT::index_btid와 FK BTID 가 거기에 기술된 B+tree 인덱스를 가리킨다.knowledge/code-analysis/cubrid/cubrid-authentication.md—class_->owner와class_->auth_cache가 authorization 으로의 진입점이다.knowledge/code-analysis/cubrid/cubrid-pl-javasp.md와cubrid-pl-plcsql.md— 휴면 상태인SM_METHOD서브시스템의 현대적 대체물.knowledge/code-analysis/cubrid/cubrid-parser.md—SM_TEMPLATE구축을 구동하는 DDL 파스 트리.
교재 챕터 (knowledge/research/dbms-general/)
섹션 제목: “교재 챕터 (knowledge/research/dbms-general/)”- Database System Concepts (Silberschatz/Korth/Sudarshan, 7판), 24장 §Object-Based Databases — object-relational 엔진을 위한 클래스 그래프 모델과 카탈로그 캐시.
- Database Internals (Petrov), 1장 §Database storage 와 7장 §Storage Engines — 스키마 캐시와 storage 레이아웃.
- Atkinson, Bancilhon, DeWitt, Dittrich, Maier, Zdonik — The Object-Oriented Database System Manifesto (1989) — UniSQL/CUBRID 의 클래스 그래프를 만든 기능 셋.
CUBRID 소스 (/data/hgryoo/references/cubrid/)
섹션 제목: “CUBRID 소스 (/data/hgryoo/references/cubrid/)”src/object/class_object.h— 타입 정의, 1155 라인.src/object/class_object.c— 구현, 8872 라인.src/object/object_accessor.c—obj_get/obj_set와 인스턴 스 라이프사이클, 4495 라인.src/object/class_description.{cpp,hpp}— pretty-printer.src/object/identifier_store.{cpp,hpp}— 식별자 검증 유틸리티.src/object/object_template.h—OBJ_TEMPLATE(인스턴스 template) —SM_TEMPLATE(클래스 template) 와 평행.src/storage/catalog_class.{c,h}—catcls_*디스크 다리, 5823 라인.src/object/schema_manager.c(이 패스에서 직접 읽지 않음) —SM_TEMPLATE을 구축하고 install 하는 DDL driver.src/object/schema_system_catalog_install.cpp(직접 읽지 않음. CUBRID AGENTS.md §Add info schema view 에서 교차 참조) —_db_*시스템 클래스용 install 시점 하드코딩 스키마.