Skip to content

CUBRID Class Object — In-Memory Schema, Attributes, Methods, Partitions, and the OODB Heritage

Contents:

In a relational database, the catalog tells the engine what shape every row has. But the engine never reads catalog rows from disk on every attribute access — that would slow every SELECT down to two heap fetches per column. Instead each engine builds an in-memory schema representation: a runtime data structure, keyed by class identity, that the parser, planner, executor, and locator consult cheaply. Database System Concepts (Silberschatz/Korth/Sudarshan, 7th ed., Ch. 24 “Object-Based Databases”) frames this as the “metadata cache” and observes that for object and object-relational engines the cache must hold not only column shapes but also class hierarchies, methods, and inheritance lattices.

CUBRID’s heritage forces this cache to be richer than a pure relational engine’s. CUBRID started life as UniSQL/X, a true object-oriented database (OODB) with classes, single inheritance, methods (C functions linked into the server), shared attributes, class attributes, and a metaclass distinction between root class (the class of classes) and ordinary classes. The product later transitioned to ORDB and then to a SQL-first relational engine, but it never deleted the OODB scaffolding — it shadowed the relational features on top of it. Every CUBRID table is implemented as a class; every row is an instance object with an OID; every column is an attribute slot in a memory layout determined by an SM_REPRESENTATION. This is why class_object.c is 220 KB and class_object.h is 46 KB: the file holds, in one graph, the union of relational schema (columns, constraints, indexes, partitions) and OODB schema (methods, signatures, resolutions, class hierarchy).

Two textbook ingredients shape the rest of this document:

  1. Class as a graph of components. The conventional treatment (Database System Concepts, Ch. 24 §“Object-Relational Databases”, and Atkinson et al., “The Object-Oriented Database System Manifesto”) models a class as a labelled graph: the class node carries header metadata, and edges run out to attribute nodes, method nodes, signature nodes, partition nodes, constraint nodes, and (transitively) to super- and sub-classes. CUBRID’s SM_CLASS is exactly this graph, allocated on the client-side workspace heap rather than the server heap.
  2. Schema versioning by representation. When ALTER TABLE adds a column, existing rows must remain readable. The textbook answer is to keep a list of historical representations keyed by REPR_ID, and have each row carry its REPR_ID in its header. CUBRID’s SM_REPRESENTATION chain is the in-memory mirror of the on-disk DISK_REPR chain in cubrid-catalog-manager.md — the class object holds the latest representation eagerly and prior ones lazily for instance conversion.

The remainder of this document tracks how CUBRID realizes this in-memory class graph, how it gets populated from disk via catcls_*, how obj_get/obj_set walk it for instance attribute access, and how DDL mutates it through the SM_TEMPLATE shadow structure.

Every relational engine that respects performance reaches for the same set of patterns around in-memory schema.

The engine wants class_oid → SM_CLASS * lookups in O(1) and the result memoized for the lifetime of the process (or until DDL invalidates). Postgres calls it the relcache (Relation struct in src/include/utils/rel.h); the cache is per-backend, populated on first access and invalidated through shared-memory invalidation messages (RelationCacheInvalidateEntry). MySQL (the InnoDB layer) calls it the dictionary cache (dict_table_t from storage/innobase/dict/dict0dict.cc). CUBRID’s equivalent is the workspace (work_space.h): the class object is a real MOP (Most Persistent Object Pointer / “managed object pointer”) managed by the same machinery that manages user instances, so its lifecycle and invalidation reuse instance-level workspace logic rather than a bespoke schema cache.

Tuple descriptor as a flat record-decoding table

Section titled “Tuple descriptor as a flat record-decoding table”

To decode a heap row, the engine needs a flat array of (offset, length, type, nullable) per column. Postgres calls it a TupleDesc; SQL Server calls it the row schema. The engine does not want to walk a linked list of attribute structs every time it decodes a row, because hot loops decode billions of rows. CUBRID keeps the decode-friendly form on the on-disk side (DISK_REPR from system_catalog.h, with fixed[] and variable[] packed arrays ready to drive a row-decoding loop) and the manipulation-friendly form (linked list of SM_ATTRIBUTE) in the in-memory class object, synchronized through catcls_*.

ODMG (the Object Database Management Group) standardized the OODB data model in the early 1990s. UniSQL (CUBRID’s predecessor), Itasca, ObjectStore, GemStone, and O2 all implemented variants of it. The runtime structures looked alike across all of them: a class node with separate lists for instance attributes, shared attributes (single value across all instances of the class), class attributes (per-class values reachable through the metaclass), and methods (callable behavior). When SQL became the dominant query language, these engines did not delete the OODB scaffolding — they layered SQL on top. CUBRID is the surviving open-source representative of that lineage. SM_CLASS reflects this: it has all four lists even though SM_VCLASS_CT (virtual class) and modern relational tables only use two of them.

Single-inheritance OODBs need both directions of the hierarchy walked: super-classes for attribute resolution (which class owns which attribute), sub-classes for invalidation when a super-class changes. The convention is two OBJLISTs on the class node: one for super-classes (inheritance) and one for direct sub-classes (users). Multiple inheritance, when supported, complicates name resolution; CUBRID’s SM_RESOLUTION records hold the disambiguation rules. Modern CUBRID tables don’t use multiple inheritance, but the machinery remains.

DDL mutates schema. The engine needs to assemble the new class state, validate it (constraints don’t conflict, types are compatible, partitions are well-formed), and then atomically swap it in. Engines do this with a template or builder: a parallel struct that mirrors SM_CLASS but is mutable. Postgres uses AlteredTableInfo; CUBRID uses SM_TEMPLATE. The original class object is held read-only until the template commits via classobj_install_template.

The catalog rows on disk and the class object in memory have different shapes: the disk side packs for compactness and versioning (DISK_REPR.fixed[] and variable[] arrays plus inline BTREE_STATS), the memory side is a graph optimized for manipulation. Translating between the two is its own subsystem. Postgres has RelationBuildDesc plus pg_class, pg_attribute, pg_index reads. CUBRID has the catcls_* family in catalog_class.c, working through 4-5 K of source to walk the on-disk _db_* records and assemble an OR_VALUE tree that the client side then turns into SM_CLASS.

Theoretical conceptCUBRID name
Class header / metaclass tagSM_CLASS_HEADER { ch_obj_header, ch_type, ch_name, ch_rep_dir, ch_heap }
Class nodeSM_CLASS (class_object.h)
Instance attributeSM_ATTRIBUTE (linked through SM_COMPONENT::next)
Shared attribute (single value per class)class_->shared list
Class attribute (metaclass slot)class_->class_attributes list
Method definitionSM_METHOD
Method signatureSM_METHOD_SIGNATURE
Method argumentSM_METHOD_ARGUMENT
Method file (C source/object loaded at link)SM_METHOD_FILE
Inheritance (super classes)class_->inheritance (DB_OBJLIST)
Sub-classesclass_->users (DB_OBJLIST)
Multiple-inheritance disambiguationSM_RESOLUTION
Constraint cacheSM_CLASS_CONSTRAINT
Foreign-key infoSM_FOREIGN_KEY_INFO
Partial-index predicateSM_PREDICATE_INFO
Function index infoSM_FUNCTION_INFO
PartitioningSM_PARTITION
Trigger cachetr_schema_cache *triggers
View / virtual class definitionSM_QUERY_SPEC list
Old-row decoding scaffoldingSM_REPRESENTATION + SM_REPR_ATTRIBUTE
DDL builderSM_TEMPLATE
Read-only browsing copySM_CLASS_INFO
Repeated-access shortcutSM_DESCRIPTOR + SM_DESCRIPTOR_LIST
Disk ↔ memory bridge familycatcls_* (catalog_class.c)
Top-level disk OR_VALUE buildercatcls_get_or_value_from_class
Instance attribute getterobj_get (object_accessor.c)
Instance attribute setterobj_set
Generic instance createobj_create
Generic instance deleteobj_delete
Class pretty-printerclass_description::init (class_description.cpp)

The class object subsystem has six moving parts: the SM_CLASS graph, the attribute / method component lists, the constraint and partition annotations, the SM_TEMPLATE builder, the catcls_* disk bridge, and the obj_get/obj_set instance accessors. We walk them in that order. Throughout, keep in mind that this code is client-side onlyclass_object.h opens with #if defined (SERVER_MODE) #error Does not belong to server module. The class object lives in workspace memory on the client; the server sees only OIDs, heap rows, and disk representations.

flowchart LR
  subgraph DISK["Disk (system_catalog + _db_class)"]
    DRP["DISK_REPR rows"]
    CLI["CLS_INFO rows"]
    DBC["_db_class row"]
    DBA["_db_attribute rows"]
    DBI["_db_index rows"]
    DBM["_db_method rows"]
    DBP["_db_partition rows"]
  end
  subgraph BRIDGE["Catalog 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 memory (client-side)"]
    SMC["SM_CLASS"]
    ATT["SM_ATTRIBUTE list"]
    MET["SM_METHOD list"]
    CON["SM_CLASS_CONSTRAINT list"]
    PART["SM_PARTITION"]
    REPR["SM_REPRESENTATION chain"]
    TMP["SM_TEMPLATE (DDL only)"]
  end
  subgraph CALLER["Callers"]
    OG["obj_get / obj_set"]
    DESC["class_description (CSQL print)"]
    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 -. swap on commit .- TMP
  TMP --> PUT --> INS --> DBC
  AUTH --> SMC
  OG --> SMC
  DESC --> SMC
  EXEC --> SMC

The figure encodes three boundaries. (disk / memory) the catalog rows on disk are read by the catcls_* family in catalog_class.c — server-side code that builds an OR_VALUE tree, then ships it across to the client where the class object is materialized. (read / write) read paths terminate at the existing SM_CLASS; write paths construct an SM_TEMPLATE, validate it, and only then mutate the on-disk and in-memory representations. (class / instance) the class object describes shape; obj_get/obj_set apply that shape to instance memory fetched separately.

SM_CLASS is the hub. Its layout (condensed):

// 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;
};

The structure is bigger than it has to be for a relational table — that’s the OODB heritage. Field-by-field commentary on the non-obvious parts:

  • header — embeds SM_CLASS_HEADER, which begins with WS_OBJECT_HEADER (cache hash number) plus the metaclass tag SM_METATYPE (SM_META_ROOT for the root class, SM_META_CLASS otherwise). The ch_rep_dir field is the OID of the directory record on disk that lists all DISK_REPR records for this class (cubrid-catalog-manager.md §“Per-class info”); ch_heap is the HFID of the class’s instance heap. Class-level metadata thus carries enough to reach both catalog and heap.

  • users / inheritance — the bidirectional class-hierarchy edges. users lists direct subclasses only (multi-level descent walks transitively); inheritance lists direct superclasses. These are DB_OBJLIST (linked list of MOPs), not SM_CLASS *, so the dependents are loaded lazily through au_fetch_class when needed.

  • class_type — three values: SM_CLASS_CT (regular class / table), SM_VCLASS_CT (virtual class / view), SM_ADT_CT (abstract data type / pseudo-class). The pretty-printer in class_description.cpp uses this to label output (MSGCAT_HELP_* message ids).

  • repid + representations — the latest representation id and the chain of historical SM_REPRESENTATIONs. Each historical representation holds only SM_REPR_ATTRIBUTE entries (attid, type, domain) — enough to decode old-format rows and convert them to the latest layout.

  • attributes / shared / class_attributes — the three attribute lists, each linked through SM_COMPONENT::next. The invariant: every attribute’s id is unique across all three lists for the class, so obj_locate_attribute can search any list it likes:

    // 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 — derived counts that mirror the on-disk DISK_REPR split. Hot read paths use these to skip header parsing when only the count is needed.

  • att_ids / method_ids — per-class monotonic counters used when assigning ids to newly-added components. Old representations retain their dead ids for backwards row decoding.

  • new_ — non-NULL only during DDL: points at the SM_TEMPLATE currently being edited. This is how classobj_install_template finds its target and how concurrent DDL is detected.

  • stats — lazy-loaded CLASS_STATS (cubrid-catalog-manager.md §“Statistics — separate cadence”). The optimizer asks for stats on demand and caches them here; ci_time_stamp invalidates.

  • ordered_attributes — a parallel pointer chain that walks all instance attributes (own + inherited) in definition order, hooked through the dedicated order_link field of SM_ATTRIBUTE. The base attributes list reflects storage order, not definition order; pretty-printers and DESC output need definition order. classobj_fixup_loaded_class builds this list after the class is loaded.

  • properties — a DB_SEQ holding constraint metadata in a serialized form (*U, *P, *F, *I keys for unique/primary/foreign/index families). The cached SM_CLASS_CONSTRAINT *constraints list is decoded from properties by classobj_cache_constraints on first use; that’s the entire reason for the recache_constraints:1 bit-flag.

  • virtual_cache_* fields — optimization for views: when the virtual class’s underlying tables haven’t changed schema, re-compiling the view’s query is unnecessary. The triple of local_schema_id, global_schema_id, snapshot_version is the cache-validity token compared against current schema state.

  • tde_algorithm — Transparent Data Encryption mode for the class’s heap. Stored alongside class metadata so heap row reads can decrypt without an extra catalog fetch.

  • bit-flag tailmethods_loaded records whether dlopen-style method loading has completed for this class (SM_METHOD_FILE files actually linked). triggers_validated, has_active_triggers, recache_constraints are similar validity tokens. dont_decache_constraints_or_flush is set during specific re-entrant operations to prevent invalidation loops. load_index_from_heap is consulted when an index is allocated: normally true (rebuild from existing heap rows), but TRUNCATE clears it so a fresh empty index is allocated without a 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 — the column / instance-variable record

Section titled “SM_ATTRIBUTE — the column / instance-variable record”
// 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;
};

The interesting fields:

  • type vs domaintype is the primitive pr_type (int, varchar, …); domain is the parameterized domain (varchar(20) collate utf8_bin, set of int, …). The two are always consistent — domain->type == attribute->type — but kept separate so the type-handling code (object_primitive.c) can switch on type cheaply without dereferencing the domain.
  • class_mop — the class that originally defined this attribute. For inherited attributes, attribute->class_mop != containing_class->op; the pretty-printer (class_description::init) uses this to skip inherited columns when prt_type == SHOW_CREATE_TABLE.
  • id — globally unique within the class, and persistently bound: once allocated, the attribute id never changes for the lifetime of the schema (even across DROP/ADD with the same name, the id is freshly allocated). Old SM_REPRESENTATION entries carry these ids so old rows decode correctly.
  • offset — byte offset inside an instance’s memory image. Computed by the layout pass (classobj_fixup_loaded_class) when the class is loaded.
  • default_value — see the long comment in the header. Two values: original_value (initial default, used when reading rows from older representations that lacked the column) and value (current default for newly-inserted rows).
  • flags — bitmask of SM_CLASSFLAG_*-style attribute flags. Macros like SM_IS_ATTFLAG_UNIQUE_FAMILY and SM_MAP_INDEX_ATTFLAG_TO_CONSTRAINT (top of class_object.h) test and convert these.
  • auto_increment — for AUTO_INCREMENT columns, points at the _db_serial instance carrying the sequence state. The MOP is dereferenced lazily on insert.

SM_METHOD — the OODB heritage made explicit

Section titled “SM_METHOD — the OODB heritage made explicit”
// 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;
};

Methods are CUBRID’s most visible OODB tribute. A method is a C function compiled into a shared object, listed in SM_METHOD_FILE entries on the class, and dlopen’d at first invocation (methods_loaded:1 records the load). The signature chain SM_METHOD_SIGNATURE → SM_METHOD_ARGUMENT carries return-type and per-argument types. class_object.h defines METHOD_FUNC_ARG4 through METHOD_FUNC_ARG33 (28 distinct typedefs) for methods taking 4 to 33 arguments, all returning void and taking DB_VALUE * arguments. The header comments suggest “currently only one signature” — overloading was an aspirational future direction that never landed.

In modern CUBRID, methods are rarely used by application developers, but the engine still validates them through SQL grammar and continues to load the supporting structures. This is why SM_METHOD-related code makes up a non-trivial fraction of class_object.c.

SM_CLASS_CONSTRAINT — PK, FK, UNIQUE, INDEX cache

Section titled “SM_CLASS_CONSTRAINT — PK, FK, UNIQUE, INDEX cache”
// 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 */
};

Constraints are stored on disk in a serialized DB_SEQ form on SM_CLASS::properties keyed by *U, *P, *F, *I etc. classobj_cache_constraints decodes this DB_SEQ into the linked list of SM_CLASS_CONSTRAINT exactly once per class load, then sets the cache. classobj_make_class_constraints is the workhorse constructor; it walks every property name family and produces the constraint list (class_object.c:3164).

SM_FOREIGN_KEY_INFO carries delete_action and update_action (CASCADE / SET NULL / RESTRICT / NO ACTION) plus a ref_match_option. SM_PREDICATE_INFO carries the partial-index WHERE predicate as both pred_string (textual) and pred_stream (compiled XASL). SM_FUNCTION_INFO is the analogous structure for function indexes (CREATE INDEX … ON t (lower(col))).

The SM_INDEX_STATUS enum is worth noting: SM_NORMAL_INDEX = 1, SM_INVISIBLE_INDEX = 2, SM_ONLINE_INDEX_BUILDING_IN_PROGRESS = 3, plus six reserved slots through SM_LAST_INDEX_STATUS = 10. Online index build is a recent-ish feature; the reservations were added all-at-once to avoid future on-disk reformatting when more states are needed.

// 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;
};

The partitioned-class story has two flavours: the parent (which holds the partition info with pname == NULL and class_partition_type == DB_PARTITIONED_CLASS) and the child partitions, each its own SM_CLASS with its own SM_PARTITION carrying pname and class_partition_type == DB_PARTITION_CLASS. The relationship is encoded through class_->users (parent → children) and inheritance (child → parent). The pretty-printer’s describe_partition_info / describe_partition_parts (in object_printer.cpp) walks both faces.

// 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;
};

A template is built (classobj_make_template / classobj_make_template_like), mutated by the DDL parser, then flattened (inheritance fully resolved, super-class attributes copied in, conflicts disambiguated by resolutions). The flattened template becomes the new SM_CLASS content via classobj_install_template:

// 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 ...
}

The saverep flag controls whether the previous representation is appended to class_->representations — done when attribute storage layout changed, skipped when only metadata (comments, collation, owner) changed.

Catalog ↔ memory bridge — the catcls_* family

Section titled “Catalog ↔ memory bridge — the catcls_* family”

The bridge runs in the server, unlike everything else in src/object/. catalog_class.c is conceptually paired with class_object.c but is server-side because the disk records live in the server’s heap files. The bridge has two directions.

Disk → memory (read direction). Triggered when the client fetches a class through au_fetch_class:

// 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.
}

The output OR_VALUE tree is essentially the same shape as SM_CLASS would be, but in a generic “sequence-of-attribute-name-and-DB_VALUE” form. The result is shipped to the client (server-mode) or used in-process (SA-mode, where parser and server share an address space). On the client side, schema-manager code (in schema_manager.c, not covered in detail here) walks the OR_VALUE tree and constructs the SM_CLASS.

Memory → disk (write direction). DDL goes the other way:

  • catcls_insert_catalog_classes — for a newly-created class, write the new _db_class row plus subordinate rows in _db_attribute, _db_index, _db_method, _db_partition.
  • catcls_update_catalog_classes — for ALTER, replace the existing rows.
  • catcls_delete_catalog_classes — for DROP.

These run server-side under the same system-op bracket as the internal catalog updates (cubrid-catalog-manager.md §“One ALTER, end to end”). The serialization helpers catcls_put_or_value_into_buffer and catcls_put_or_value_into_record translate OR_VALUE trees back into raw RECDES bytes.

Two server-resident caches matter:

  • catcls_class_oid_to_oid_hash_table — maps _db_class class oid (the OID that points at _db_class itself) to the instance oid of the row in _db_class that describes a given user class. Without it, every catalog class lookup would scan _db_class. Initialized by catcls_initialize_class_oid_to_oid_hash_table; finalized by catcls_finalize_class_oid_to_oid_hash_table.
  • catcls_Btid — cached BTID of the index on _db_class.class_name, found via catcls_find_btid_of_class_name once and reused.
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

Object accessor — obj_get / obj_set on instances

Section titled “Object accessor — obj_get / obj_set on instances”

The class object describes instance shape; object_accessor.c applies that shape to actual instance memory. Two entry points:

// 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_attribute does an au_fetch_class plus a name lookup through the three attribute lists (attributes, shared, class_attributes). obj_get_att then walks to the instance memory through au_fetch_instance and copies the value out using the attribute’s offset and type. The setter mirror is:

// 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 (large function, ~200 lines) handles four sub-cases keyed on the attribute’s name_space:

  • instance attribute — find the byte offset, run validation through tp_value_cast, write into instance memory, mark dirty.
  • shared attribute — write into the class object’s per-attribute default_value.value (shared attrs have a single store on the class, not per-instance).
  • class attribute — same as shared but reached via the metaclass.
  • set / multiset / sequence — recurse into collection update; may take a path through obj_set again on a referenced object.

The “descriptor” variant (obj_desc_get / obj_desc_set) is the fast path: a pre-computed SM_DESCRIPTOR that caches the (class_mop, attribute) pair so repeated access on the same attribute over many instances avoids name lookups. The descriptor keeps a SM_DESCRIPTOR_LIST of subclass-specific overrides for when an instance happens to be of a derived class.

obj_create / obj_delete are the lifecycle pair:

// 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’s instance creation goes through an object template (OBJ_TEMPLATE from object_template.h) — like SM_TEMPLATE but for instances rather than classes. The template gathers all the attribute writes, runs constraint validation in a single pass, and emits one heap insert at the end.

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

Two invariants worth highlighting. First, SM_TEMPLATE is flattened before installation: super-class attributes are copied into the template’s instance_attributes list, and resolutions are applied to disambiguate names. The template thus carries the complete schema as the class will see it, not just the locally-declared parts. Second, classobj_install_template saves the prior representation when storage layout changed; the saved SM_REPRESENTATION will be needed later if the engine encounters old-format rows during scans (see SM_REPR_ATTRIBUTE and the long comment on SM_REPRESENTATION).

class_description.cpp is the human-readable face. It produces two flavours through the type enum:

  • CSQL_SCHEMA_COMMAND — the output of CSQL’s ;sc <name> and ;schema <name> commands. Includes inherited attributes, triggers, partitions.
  • SHOW_CREATE_TABLE — SQL-compatible SHOW CREATE TABLE output. Excludes inherited attributes (they belong to the super-class’s CREATE TABLE), bracket-quotes identifiers, formats default expressions as SQL.

The flow is: class_description::init(op, prt_type)au_fetch_classsm_clean_class → walk the SM_CLASS lists, calling object_printer::describe_attribute, describe_method, describe_constraint, describe_partition_info etc. for each component, accumulating into class_description’s string-array fields (attributes[], methods[], constraints[], partition[]). The walk respects ordered_attributes for stable output ordering, and uses class_mop != op to distinguish own vs inherited components.

Identifier validation — identifier_store

Section titled “Identifier validation — identifier_store”

identifier_store.cpp is a small utility used by DDL paths to validate sets of identifiers (column names, index names) at once. The class is constructed from a std::vector<std::string>, optionally with check_valid which walks each entry and applies the rule “must begin with letter, ASCII alnum + underscore for the rest, exception for _db system class 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;
}

The internal storage is cubbase::string_set_ci_lower — a case-insensitive set, so is_exists accepts any-case lookups. The class is immutable and not thread-safe (built once during a DDL operation, consulted to detect duplicate identifiers, then dropped).

Anchor on symbol names. Line numbers go in the position table.

  • SM_CLASS — the in-memory class node.
  • SM_CLASS_HEADER — common header for class and root class (carries 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 — generic header (next, name, name_space) for attributes and methods.
  • SM_NAME_SPACE enum — name-space tag identifying instance attribute, shared attribute, class attribute, method, etc.
  • SM_ATTRIBUTE — attribute definition.
  • SM_DEFAULT_VALUE — initial + current default + default expr.
  • SM_CONSTRAINT (per-attribute cached) vs SM_CLASS_CONSTRAINT (class-level, multi-attribute).
  • SM_CONSTRAINT_TYPE enum — UNIQUE, INDEX, NOT_NULL, REVERSE_UNIQUE, REVERSE_INDEX, PRIMARY_KEY, FOREIGN_KEY.
  • SM_FOREIGN_KEY_INFO — referenced class, ref attrs, actions, match option, ref BTID.
  • SM_FOREIGN_KEY_ACTION enum — CASCADE / SET NULL / RESTRICT / NO ACTION.
  • SM_PREDICATE_INFO — partial-index WHERE predicate.
  • SM_FUNCTION_INFO — function-index expression metadata.
  • SM_INDEX_STATUS enum — NORMAL, INVISIBLE, ONLINE_BUILDING_IN_PROGRESS, plus six reserved.
  • SM_METHOD, SM_METHOD_SIGNATURE, SM_METHOD_ARGUMENT, SM_METHOD_FILE — method machinery.
  • METHOD_FUNC_ARG4..ARG33 typedefs — variadic-arity method function pointer types (28 of them).
  • SM_RESOLUTION — multi-inheritance disambiguation.
  • SM_REPRESENTATION + SM_REPR_ATTRIBUTE — historical row layouts.
  • SM_QUERY_SPEC — virtual class query specification.
  • SM_PARTITION — partition metadata.
  • SM_TEMPLATE — DDL builder.
  • SM_CLASS_INFO — read-only browsing copy.
  • SM_DESCRIPTOR + SM_DESCRIPTOR_LIST — fast attribute-access cache for repeated access patterns.
  • SM_VALIDATION — type/domain validation cache embedded in descriptor.

Construction / destruction (in class_object.c)

Section titled “Construction / destruction (in class_object.c)”
  • classobj_area_init / classobj_area_final — area allocator setup at db_open / db_shutdown.
  • classobj_make_class — allocate empty SM_CLASS.
  • classobj_free_class — deep free of all linked lists.
  • 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 (by name) / classobj_find_attribute_id (by id).
  • classobj_find_method.
  • classobj_find_component / classobj_complist_search.
  • classobj_find_class_constraint (by 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 — decode property list into SM_CLASS_CONSTRAINT list.
  • classobj_cache_constraints / classobj_cache_class_constraints / classobj_decache_class_constraints.
  • classobj_get_cached_constraint — per-attribute cached 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 — append index entry.
  • classobj_find_prop_constraint.
  • classobj_btid_from_property_value / classobj_oid_from_property_value.
  • classobj_install_template — atomic swap on commit.
  • classobj_fixup_loaded_class — post-load layout pass; builds ordered_attributes, computes offset per attribute.
  • classobj_filter_components — split a component list by name-space.
  • classobj_annotate_method_files — set class_mop on inherited method files.
  • classobj_initialize_attributes / classobj_initialize_methods.
  • classobj_copy_attlist / classobj_copy_reslist / classobj_copy_methfiles.
  • classobj_copy_default_expr.
  • classobj_class_size — compute SM_CLASS heap footprint (debugging).
  • classobj_make_descriptor / classobj_make_desclist.
  • classobj_free_descriptor / classobj_free_desclist.
  • classobj_print — under #if defined (CUBRID_DEBUG).
  • catcls_Enable — global flag enabling catalog-class maintenance.
  • 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 — top-level read decoder.
  • 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-time schema build.
  • 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 access by name.
  • obj_get_att / obj_set_att — by SM_ATTRIBUTE *.
  • obj_desc_get / obj_desc_set — by SM_DESCRIPTOR * (fast).
  • obj_locate_attribute — by id; returns memory pointer.
  • obj_assign_value — generic setter, used by templates.
  • obj_get_value / obj_get_path / obj_get_temp / obj_set_temp — variants for collections, paths, temporary objs.
  • obj_get_shared / obj_set_shared — shared-attribute force.
  • obj_alloc — instance memory allocation given class layout.
  • obj_create / obj_create_by_name / obj_copy / obj_delete / obj_free_memory — instance lifecycle.
  • class_description::class_description() — ctor (zeroes).
  • class_description::~class_description() — dtor (frees all string arrays and vectors).
  • class_description::init(const char *name) — by class name.
  • class_description::init(db_object *, type) — by MOP.
  • class_description::init(db_object *, type, string_buffer &) — shared-buffer variant for embedded use.
  • init_triggers (file-static) — populate trigger list.
  • describe_trigger_list (file-static).

Identifier store (in identifier_store.cpp)

Section titled “Identifier store (in 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 — overall validity (set at construction).
  • identifier_store::get_size.
  • identifier_store::check_identifier_condition (private).
  • identifier_store::check_identifier_is_valid (static) — single identifier rule check.
  • identifier_store::is_enclosed (static) — quotes / brackets / backticks predicate.
SymbolFileLine
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
  • Disk vs memory representation are deliberately not the same shape. cubrid-catalog-manager.md describes DISK_REPR as having packed fixed[] and variable[] arrays for fast row-decoding; the in-memory SM_ATTRIBUTE chain is a linked list through SM_COMPONENT::next ordered by storage. The two representations exist for different consumers (decoder vs editor) and are synchronized exclusively through the catcls_* family. Any bug or inconsistency lives at that bridge, never inside one side alone.
  • SM_CLASS::stats is the same CLASS_STATS defined in statistics.h (cubrid-catalog-manager.md §“Statistics”). The field is NULL until the optimizer asks for stats; first request triggers a server fetch of the per-class DISK_ATTR::bt_stats[] and friends. Cache invalidation is by ci_time_stamp comparison identical to what the catalog manager uses.
  • class_->header.ch_heap is the canonical HFID for heap-side scans. This is the same HFID stored on CLS_INFO::ci_hfid — cubrid-heap-manager.md §“Heap file identifier”. The class object holds it inline so heap scans don’t need a catalog round-trip after the class is loaded.
  • class_->header.ch_rep_dir is the directory-record OID — the same OID CLS_INFO::ci_rep_dir stores. From it you can walk the catalog’s representation directory to enumerate all DISK_REPRs for the class.
  • Constraints are cached on SM_CLASS::constraints but authoritative on SM_CLASS::properties. The constraints list is decoded from properties lazily by classobj_cache_constraints. DDL writes update properties first, then call classobj_decache_class_constraints to force a re-decode on next access.
  • The “method” subsystem is mostly dormant in modern usage. CUBRID’s stored-procedure feature (cubrid-pl-javasp.md and cubrid-pl-plcsql.md) is the modern replacement, with Java/PL-CSQL rather than C methods. The SM_METHOD machinery still exists because legacy schemas may use it and the parser still accepts the syntax.
  1. When does classobj_fixup_loaded_class recompute object_size? The field changes when attributes are added or their domains’ size grows. Investigation path: trace classobj_install_template for the size-recompute call site.
  2. What is the exact ordering of operations during ALTER TABLE when partitions are involved? A partitioned parent + N children is N+1 classes; whose template installs first? Investigation path: do_alter_partitioning_* in do_partition.c.
  3. catcls_compile_catalog_classes vs schema_system_catalog_install.cpp. The header comment in cubrid-catalog-manager.md claims the install-time compiler builds system classes, but the schema definitions live in schema_system_catalog_install.cpp. Which writes which? Likely the install file is the C++ source of truth and catcls_compile_catalog_classes consumes it, but the call chain is worth confirming.
  4. How does the workspace cache invalidate SM_CLASS after a remote DDL? When another client commits ALTER, this client’s cached SM_CLASS becomes stale. Investigation path: ws_decache calls plus the OID invalidation message stream.
  5. TDE + class_object interaction. SM_CLASS::tde_algorithm is read by the heap on every page decrypt. Where is the hot accessor and how does it avoid an indirect through the workspace cache on every page fetch? Investigation path: heap page fetch in heap_file.c versus how class TDE state is reached.
  6. SM_CLASS::auth_cache lifecycle. Authorization is cached per-class as a void * — opaque to class_object.c. What compiles into it, and when is it invalidated? Investigation path: au_* functions in authenticate*.cpp.
  7. Online index build status transitions. SM_INDEX_STATUS has six reserved slots beyond the three named values. What were the planned states? Investigation path: ML history / git log -- src/object/class_object.h.
  8. Dedup-level options field on SM_CLASS_CONSTRAINT. Bits 0–3 hold a deduplicate level (0–14). What does each level mean and where is it consumed? Investigation path: deduplicate_key.{c,h} and IS_DEDUPLICATE_KEY_ATTR_ID.
  • knowledge/code-analysis/cubrid/cubrid-catalog-manager.md — on-disk catalog representation; DISK_REPR, CLS_INFO, CTID match the bridge endpoints in this document.
  • knowledge/code-analysis/cubrid/cubrid-heap-manager.md — class instances live as heap rows; class_->header.ch_heap is the HFID this layer uses.
  • knowledge/code-analysis/cubrid/cubrid-btree.mdSM_CLASS_CONSTRAINT::index_btid and FK BTIDs target B+tree indexes described there.
  • knowledge/code-analysis/cubrid/cubrid-authentication.mdclass_->owner and class_->auth_cache are entry points into authorization.
  • knowledge/code-analysis/cubrid/cubrid-pl-javasp.md and cubrid-pl-plcsql.md — modern stored-procedure replacement for the dormant SM_METHOD subsystem.
  • knowledge/code-analysis/cubrid/cubrid-parser.md — DDL parse trees that drive SM_TEMPLATE construction.

Textbook chapters (under knowledge/research/dbms-general/)

Section titled “Textbook chapters (under knowledge/research/dbms-general/)”
  • Database System Concepts (Silberschatz/Korth/Sudarshan, 7th ed.), Ch. 24 §“Object-Based Databases” — class graph model and catalog cache for object-relational engines.
  • Database Internals (Petrov), Ch. 1 §“Database storage” and Ch. 7 §“Storage Engines” — schema cache and storage layout.
  • Atkinson, Bancilhon, DeWitt, Dittrich, Maier, Zdonik — The Object-Oriented Database System Manifesto (1989) — the feature set that shaped UniSQL/CUBRID’s class graph.

CUBRID source (/data/hgryoo/references/cubrid/)

Section titled “CUBRID source (/data/hgryoo/references/cubrid/)”
  • src/object/class_object.h — type definitions, 1155 lines.
  • src/object/class_object.c — implementation, 8872 lines.
  • src/object/object_accessor.cobj_get / obj_set and instance lifecycle, 4495 lines.
  • src/object/class_description.{cpp,hpp} — pretty-printer.
  • src/object/identifier_store.{cpp,hpp} — identifier validation utility.
  • src/object/object_template.hOBJ_TEMPLATE (instance template), parallel to SM_TEMPLATE (class template).
  • src/storage/catalog_class.{c,h}catcls_* disk bridge, 5823 lines.
  • src/object/schema_manager.c (not directly read in this pass) — the “DDL driver” that builds and installs SM_TEMPLATEs.
  • src/object/schema_system_catalog_install.cpp (not directly read; cross-referenced from CUBRID AGENTS.md §“Add info schema view”) — install-time hard-coded schemas for _db_* system classes.