콘텐츠로 이동

(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, 클래스 계층) 의 합집합을 함께 담는다.

본 문서의 골격을 만드는 두 교과서적 재료가 있다.

  1. 컴포넌트의 그래프로서의 클래스. 통상적인 처리 (Database System Concepts, 24장 §Object-Relational Databases, 그리고 Atkinson 외 “The Object-Oriented Database System Manifesto”) 는 클래스를 라벨 그래프로 모델링한다. 클래스 노드가 헤더 메타데이터를 들고 있고, attribute 노드, method 노드, signature 노드, partition 노드, 제약 노드, 그리고 (전이적으로) 슈퍼클래스 / 서브클래스로 엣지가 나간다. CUBRID의 SM_CLASS 가 정확히 이 그래프 다 — 다만 서버 heap 이 아니라 클라이언트 측 workspace heap 위에 할당된다.
  2. 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 그림자 구조로 그것을 어떻게 변형하는지를 추적한다.

성능을 챙기는 모든 관계형 엔진은 인메모리 스키마 주변에서 비슷한 패턴을 채택한다.

클래스 식별자를 키로 한 relcache

섹션 제목: “클래스 식별자를 키로 한 relcache”

엔진은 class_oid → SM_CLASS * lookup을 O(1) 로 하고, 그 결과를 프로세스 라이프타임 동안 (또는 DDL 무효화가 일어날 때까지) 메모이즈 하고 싶다. PostgreSQL은 이를 relcache 라고 부른다 (src/include/utils/rel.hRelation 구조체). 백엔드별이며 처음 접근 시점에 채워지고, shared-memory 무효화 메시지 (RelationCacheInvalidateEntry) 로 무효화된다. MySQL의 InnoDB 계층은 dictionary cache (storage/innobase/dict/dict0dict.ccdict_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.hDISK_REPR, 행 디코딩 루프를 곧장 굴릴 수 있는 fixed[]variable[] 패킹된 배열) 에 두고, 편집 친화적인 형태 (SM_ATTRIBUTE 의 linked list) 는 인메모리 클래스 객체에 두며, catcls_* 로 둘을 동기화한다.

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) 와 현대 관계형 테이블이 그중 둘만 쓰는데도 네 리스트를 모두 들고 있다.

단일 상속 OODB는 계층의 양방향이 모두 필요하다 — attribute 해소 (어떤 attribute를 어느 클래스가 소유하는지) 를 위해 상위로 올라가야 하고, 상위 클래스가 변할 때 무효화하기 위해 하위로도 내려가야 한다. 관습은 클래스 노드에 두 개의 OBJLIST 를 두는 것이다 — 슈퍼클래스 용 (inheritance) 과 직속 서브클래스용 (users). 다중 상속을 지원 하면 이름 해소가 복잡해진다. CUBRID의 SM_RESOLUTION 레코드가 그 disambiguation 규칙을 들고 있다. 현대 CUBRID 테이블은 다중 상속을 쓰지 않지만 기계는 남아 있다.

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은 RelationBuildDescpg_class, pg_attribute, pg_index 읽기를 가진다. CUBRID은 catalog_class.ccatcls_* 패밀리를 가진다 — 4–5 K 라인의 코드가 디스크 위 _db_* 레코드를 걷고 OR_VALUE 트리를 조립하면, 클라이언트 측이 그것을 다시 SM_CLASS 로 바꾼다.

이론적 개념CUBRID 명칭
클래스 헤더 / metaclass 태그SM_CLASS_HEADER { ch_obj_header, ch_type, ch_name, ch_rep_dir, ch_heap }
클래스 노드SM_CLASS (class_object.h)
Instance attributeSM_ATTRIBUTE (SM_COMPONENT::next 로 연결)
Shared attribute (클래스 단위 단일 값)class_->shared 리스트
Class attribute (metaclass 슬롯)class_->class_attributes 리스트
Method 정의SM_METHOD
Method signatureSM_METHOD_SIGNATURE
Method 인자SM_METHOD_ARGUMENT
Method 파일 (링크 시 로드되는 C 소스/오브젝트)SM_METHOD_FILE
상속 (슈퍼클래스)class_->inheritance (DB_OBJLIST)
서브클래스class_->users (DB_OBJLIST)
다중 상속 disambiguationSM_RESOLUTION
제약 캐시SM_CLASS_CONSTRAINT
외래 키 정보SM_FOREIGN_KEY_INFO
부분 인덱스 predicateSM_PREDICATE_INFO
함수 인덱스 정보SM_FUNCTION_INFO
PartitionSM_PARTITION
트리거 캐시tr_schema_cache *triggers
View / virtual class 정의SM_QUERY_SPEC 리스트
옛 행 디코딩 골격SM_REPRESENTATION + SM_REPR_ATTRIBUTE
DDL builderSM_TEMPLATE
읽기 전용 browse 사본SM_CLASS_INFO
반복 접근 단축 경로SM_DESCRIPTOR + SM_DESCRIPTOR_LIST
디스크 ↔ 메모리 다리 패밀리catcls_* (catalog_class.c)
최상위 디스크 OR_VALUE buildercatcls_get_or_value_from_class
Instance attribute getterobj_get (object_accessor.c)
Instance attribute setterobj_set
일반 인스턴스 생성obj_create
일반 인스턴스 삭제obj_delete
클래스 pretty-printerclass_description::init (class_description.cpp)

클래스 객체 서브시스템은 여섯 개의 이동 부품으로 이루어져 있다. 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.ccatcls_* 패밀리 — 서버 측 코드 — 가 읽어 OR_VALUE 트리로 만든 뒤 클라이언트로 보내고, 거기에서 클래스 객체가 구체화된다. (읽기 / 쓰기) 읽기 경로는 이미 존재하는 SM_CLASS 에서 끝난다. 쓰기 경로는 SM_TEMPLATE 을 하나 새로 짠 뒤 검증해 두고, 그제야 디스크와 메모리 표현을 한꺼번에 바꾼다. (클래스 / 인스턴스) 클래스 객체는 모양을 기술한다. obj_get / obj_set 가 그 모양을 따로 fetch된 인스턴스 메모리에 적용한다.

SM_CLASS 가 허브다. 레이아웃은 다음과 같다 (압축):

// SM_CLASS — src/object/class_object.h
struct 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 유산이다. 짚을 만한 필드들의 의미.

  • headerSM_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)
    int
    obj_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_constraintsproperties 를 디코드해 만든다. 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_loadeddlopen 류의 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 없이 할당되도록 한다.

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.h
struct 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;
};

흥미로운 필드들.

  • type vs domaintype 은 원시 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).
  • flagsSM_CLASSFLAG_* 류의 attribute 플래그 비트마스크. class_object.h 상단의 SM_IS_ATTFLAG_UNIQUE_FAMILYSM_MAP_INDEX_ATTFLAG_TO_CONSTRAINT 같은 매크로가 이를 검사하고 변환한다.
  • auto_incrementAUTO_INCREMENT 컬럼의 경우 시퀀스 상태를 들고 있는 _db_serial 인스턴스를 가리킨다. 이 MOP 는 insert 시점에 lazy하게 dereference된다.

SM_METHOD — OODB 유산을 노출한 부분

섹션 제목: “SM_METHOD — OODB 유산을 노출한 부분”
// SM_METHOD — src/object/class_object.h
struct 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.h
struct 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_INFOdelete_actionupdate_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.h
struct 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 클래스에는 두 얼굴이 있다. parentpname == NULL 이며 class_partition_type == DB_PARTITIONED_CLASSpartition 정보를 들고 있다. child partition 들은 각자 자기 SM_CLASS 를 가지며, 각각의 SM_PARTITIONpnameclass_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 — src/object/class_object.h
struct 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)
int
classobj_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.cclass_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_buffercatcls_put_or_value_into_recordOR_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)
int
obj_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_attributeau_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)
int
obj_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)
MOP
obj_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.hOBJ_TEMPLATE) 을 거친다 — SM_TEMPLATE 와 비슷하지만 클래스가 아니라 인스턴스용이다. template 이 모든 attribute 쓰기를 모은 뒤 한 패스에 제약 검증을 돌리고, 끝에 한 번의 heap insert 를 emit 한다.

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_ATTRIBUTESM_REPRESENTATION 위의 긴 주석을 참고).

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_classsm_clean_classSM_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.cpp 는 DDL 경로가 식별자 집합 (컬럼 이름, 인덱스 이름) 을 한꺼번에 검증할 때 쓰는 작은 유틸리티다. 클래스는 std::vector<std::string> 으로부터 생성된다. check_valid 옵션을 주면 각 엔트리를 걸어가며 “글자로 시작해야 하고, 나머지는 ASCII alnum + 밑줄. 단 _db 시스템 클래스 prefix 는 예외” 규칙을 적용 한다.

// check_identifier_is_valid — src/object/identifier_store.cpp
bool
identifier_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 는 심볼명 이다. 라인은 흘러간다.

  • SM_CLASS — 인메모리 클래스 노드.
  • SM_CLASS_HEADER — 클래스와 root class 가 공유하는 공통 헤더 (WS_OBJECT_HEADER, SM_METATYPE, ch_name, ch_rep_dir, ch_heap 을 들고 있다).
  • SM_CLASS_TYPE enum — SM_CLASS_CT / SM_VCLASS_CT / SM_ADT_CT.
  • SM_METATYPE enum — SM_META_ROOT vs SM_META_CLASS.
  • SM_CLASS_FLAG enum — SYSTEM, WITHCHECKOPTION, LOCALCHECKOPTION, REUSE_OID, SUPPLEMENTAL_LOG.
  • SM_COMPONENT — attribute 와 method 가 공유하는 generic 헤더 (next, name, name_space).
  • SM_NAME_SPACE enum — name-space 태그 (instance attribute, shared attribute, class attribute, method 등을 식별).
  • SM_ATTRIBUTE — attribute 정의.
  • SM_DEFAULT_VALUE — initial + current default + default expr.
  • SM_CONSTRAINT (attribute별 캐시) vs SM_CLASS_CONSTRAINT (클래스 단위, 다 attribute).
  • SM_CONSTRAINT_TYPE enum — UNIQUE, INDEX, NOT_NULL, REVERSE_UNIQUE, REVERSE_INDEX, PRIMARY_KEY, FOREIGN_KEY.
  • SM_FOREIGN_KEY_INFO — 참조 클래스, 참조 attr, 액션, match 옵션, 참조 BTID.
  • SM_FOREIGN_KEY_ACTION enum — CASCADE / SET NULL / RESTRICT / NO ACTION.
  • SM_PREDICATE_INFO — partial-index WHERE 술어.
  • SM_FUNCTION_INFO — 함수 인덱스 표현식 메타데이터.
  • SM_INDEX_STATUS enum — NORMAL, INVISIBLE, ONLINE_BUILDING_IN_PROGRESS, 그리고 예약 슬롯 여섯 개.
  • SM_METHOD, SM_METHOD_SIGNATURE, SM_METHOD_ARGUMENT, SM_METHOD_FILE — method 기계.
  • METHOD_FUNC_ARG4..ARG33 typedef — 인자 개수별 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 검증 캐시.
  • classobj_area_init / classobj_area_finaldb_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.
  • 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.
  • 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.
  • 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 계산 (디버그).
  • classobj_make_descriptor / classobj_make_desclist.
  • classobj_free_descriptor / classobj_free_desclist.
  • classobj_print#if defined (CUBRID_DEBUG) 안.
  • 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.
  • obj_get / obj_set — 이름으로의 public attribute 접근.
  • obj_get_att / obj_set_attSM_ATTRIBUTE * 로의 접근.
  • obj_desc_get / obj_desc_setSM_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 — 인스턴스 라이프사이클.
  • 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).
  • 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.h281
SM_CLASS_TYPE (enum)class_object.h296
SM_CLASS_FLAG (enum)class_object.h305
SM_METATYPE (enum)class_object.h318
SM_CLASS_HEADER (struct)class_object.h351
SM_CONSTRAINT (struct)class_object.h370
SM_COMPONENT (struct)class_object.h387
SM_DEFAULT_VALUE (struct)class_object.h397
SM_ATTRIBUTE (struct)class_object.h445
SM_FOREIGN_KEY_INFO (struct)class_object.h478
SM_PREDICATE_INFO (struct)class_object.h497
SM_FUNCTION_INFO (struct)class_object.h508
SM_INDEX_STATUS (enum)class_object.h518
SM_CLASS_CONSTRAINT (struct)class_object.h553
SM_METHOD_ARGUMENT (struct)class_object.h591
SM_METHOD_SIGNATURE (struct)class_object.h603
SM_METHOD (struct)class_object.h626
SM_METHOD_FILE (struct)class_object.h650
SM_RESOLUTION (struct)class_object.h663
SM_REPR_ATTRIBUTE (struct)class_object.h683
SM_REPRESENTATION (struct)class_object.h702
SM_QUERY_SPEC (struct)class_object.h718
SM_PARTITION (struct)class_object.h728
SM_CLASS (struct)class_object.h746
SM_TEMPLATE (struct)class_object.h819
SM_CLASS_INFO (struct)class_object.h867
SM_DESCRIPTOR_LIST (struct)class_object.h903
SM_VALIDATION (struct)class_object.h916
SM_DESCRIPTOR (struct)class_object.h933
classobj_cache_constraintsclass_object.c2528
classobj_make_class_constraintclass_object.c2631
classobj_free_class_constraintsclass_object.c2690
classobj_make_class_constraintsclass_object.c3164
classobj_make_classclass_object.c6832
classobj_free_classclass_object.c6928
classobj_fixup_loaded_classclass_object.c7109
classobj_install_templateclass_object.c7349
classobj_make_partition_infoclass_object.c8638
obj_locate_attributeobject_accessor.c321
obj_assign_valueobject_accessor.c645
obj_set_attobject_accessor.c721
obj_setobject_accessor.c931
obj_set_sharedobject_accessor.c1012
obj_get_valueobject_accessor.c1307
obj_get_attobject_accessor.c1387
obj_desc_getobject_accessor.c1470
obj_getobject_accessor.c1508
obj_get_sharedobject_accessor.c1549
obj_allocobject_accessor.c1863
obj_createobject_accessor.c1921
obj_copyobject_accessor.c1983
obj_deleteobject_accessor.c2090
class_description::init (3-arg)class_description.cpp192
catcls_finalize_class_oid_to_oid_hash_tablecatalog_class.c286
catcls_find_oidcatalog_class.c321
catcls_put_entrycatalog_class.c353
catcls_remove_entrycatalog_class.c391
catcls_get_or_value_from_classcatalog_class.c999
catcls_get_or_value_from_attributecatalog_class.c1273
catcls_get_or_value_from_attridcatalog_class.c1622
catcls_get_or_value_from_domaincatalog_class.c1690
catcls_get_or_value_from_methodcatalog_class.c1834
identifier_store::check_identifier_is_valididentifier_store.cpp95
  • 디스크와 메모리 representation 은 의도적으로 같은 모양이 아니 다. cubrid-catalog-manager.mdDISK_REPR 을 빠른 행 디코딩 을 위한 packed fixed[] / variable[] 배열로 기술한다. 인메모리 의 SM_ATTRIBUTE 사슬은 storage 순서로 SM_COMPONENT::next 로 연결된 linked list 다. 두 representation 은 서로 다른 소비자 (디코더 vs 에디터) 를 위해 존재하며, catcls_* 패밀리로 만 동기화된다. 버그나 불일치는 항상 그 다리 위에서만 발생한다. 한쪽 안에서만 일어날 수 없다.
  • SM_CLASS::statsstatistics.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_constraintsproperties 로부터 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 스키마가 사용할 수 있고 파서가 여전히 문법을 받아 주기 때문에 그대로 남아 있다.
  1. classobj_fixup_loaded_classobject_size 를 언제 재계산하는가? 이 필드는 attribute 가 추가되거나 그 도메인의 크기가 커질 때 변한다. 추적 경로 — classobj_install_template 에서 size-recompute 호출 자리를 추적.
  2. partition 이 관여한 ALTER TABLE 의 정확한 연산 순서는? 부모 파티션 + N 개의 자식은 곧 N+1 개 클래스다. 어떤 template 이 먼저 install 되는가? 추적 경로 — do_partition.cdo_alter_partitioning_*.
  3. catcls_compile_catalog_classes vs schema_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 을 한 번 확인해 둘 필요가 있다.
  4. 원격 DDL 후 workspace 캐시는 SM_CLASS 를 어떻게 무효화 하는가? 다른 클라이언트가 ALTER 를 commit 하면, 이 클라이언트 의 캐시된 SM_CLASS 는 stale 해진다. 추적 경로 — ws_decache 호출과 OID 무효화 메시지 스트림.
  5. TDE 와 class_object 의 상호작용. SM_CLASS::tde_algorithm 은 모든 페이지 복호화에서 heap 이 읽는다. hot accessor 는 어디 이며, 매 페이지 fetch 마다 workspace 캐시를 통한 indirection 을 어떻게 피하는가? 추적 경로 — heap_file.c 의 heap 페이지 fetch 가 클래스 TDE 상태에 어떻게 닿는지 추적.
  6. SM_CLASS::auth_cache 의 라이프사이클. authorization 은 클래스별로 void * 로 캐시된다 — class_object.c 입장에서는 불투명하다. 무엇이 그 안으로 컴파일되며, 언제 무효화되는가? 추적 경로 — authenticate*.cppau_* 함수들.
  7. 온라인 인덱스 빌드 상태 전이. SM_INDEX_STATUS 는 명명된 세 값 너머에 예약 슬롯 여섯 개를 가진다. 계획된 상태들은 무엇이 었는가? 추적 경로 — ML history / git log -- src/object/class_object.h.
  8. SM_CLASS_CONSTRAINT 위 dedup-level options 필드. 비트 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.mdSM_CLASS_CONSTRAINT::index_btid 와 FK BTID 가 거기에 기술된 B+tree 인덱스를 가리킨다.
  • knowledge/code-analysis/cubrid/cubrid-authentication.mdclass_->ownerclass_->auth_cache 가 authorization 으로의 진입점이다.
  • knowledge/code-analysis/cubrid/cubrid-pl-javasp.mdcubrid-pl-plcsql.md — 휴면 상태인 SM_METHOD 서브시스템의 현대적 대체물.
  • knowledge/code-analysis/cubrid/cubrid-parser.mdSM_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.cobj_get / obj_set 와 인스턴 스 라이프사이클, 4495 라인.
  • src/object/class_description.{cpp,hpp} — pretty-printer.
  • src/object/identifier_store.{cpp,hpp} — 식별자 검증 유틸리티.
  • src/object/object_template.hOBJ_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 시점 하드코딩 스키마.