Skip to content

CUBRID System Catalog Classes — Data-Driven Definition, Bootstrap Install, and System View Query Specs

Contents:

A relational engine has to expose its own schema to SQL. Which classes exist? What columns? What indexes? Who owns each one? Which users have what privileges? The answers must be queryable through ordinary SELECTs — otherwise tools like \d (psql), DESCRIBE (MySQL), or any GUI client cannot work. This is the system catalog as a relation principle, traceable to System R 1979 (Astrahan et al., System R: Relational Approach to Database Management, ACM TODS) and crystallised by Hellerstein and Stonebraker in Anatomy of a Database System Ch. 4 — the catalog is just another set of tables, accessed through the same execution stack as user tables.

Three textbook ideas frame how an engine actually delivers that surface.

Schema-as-data. Every system catalog table has a fixed shape: _db_class has columns class_name, owner, class_type, inst_attr_count, etc. That shape can be expressed in two ways. The hand-coded approach scatters db_add_attribute (db_class, "class_name", "varchar(255)", NULL) calls across imperative C, one call per column per table; the resulting code is correct but unreadable, hard to evolve, and impossible to validate as a whole. The data-driven approach instead represents each class as a value — a struct holding name, an attributes vector, a constraints vector, an authorization block — and a single generic builder consumes the vector to produce the class. Codd’s relational model already says schema is data; this is the implementation expression of that idea inside the engine itself.

Bootstrap chicken-and-egg. The catalog has to exist before the catalog can be read. _db_class describes the class _db_class — to read its definition you have to already know how to read it. Every engine cuts the cycle the same way: at createdb time, the engine seeds a root anchor with hardcoded knowledge (System R’s SYSTABLES, Oracle’s bootstrap segment, PostgreSQL’s pg_class initial heap, CUBRID’s root class) and uses that anchor to install all subsequent system classes by issuing the same DDL operations a user would. The install path is therefore the same db_create_class / smt_add_attribute / sm_update_class that user CREATE TABLE calls — only the data driving the calls is privileged.

Separation of disk catalog and SQL catalog. Petrov’s Database Internals §1 distinguishes the engine-internal metadata (compact disk records the optimiser and executor consume directly — OR_CLASSREP, CLS_INFO, BTREE_STATS) from the SQL-visible catalog (_db_class, _db_attribute, _db_index) that exposes the same information to the SQL surface. The two are not redundant: the disk catalog is read on every plan compile and must be cheap to deserialise; the SQL catalog is queried through ordinary scans and joins, so it must look like any other table. CUBRID makes the split explicit by keeping cubrid-catalog-manager (disk side) under DDL infrastructure and this module (SQL-visible system classes) as the catalog facade — two layers of the same metadata, kept in sync by the DDL pipeline.

The three combine into one design: encode each system class as a system_catalog_definition value, install it through a generic builder that issues normal DDL, and run the install once at createdb against the root-class anchor. schema_system_catalog_install.cpp is the data; schema_system_catalog_builder.cpp is the engine; catcls_install is the orchestration.

The four major engines reach the same SQL-visible catalog surface through different bootstrap mechanics.

PostgreSQL uses pg_class, pg_attribute, pg_index, pg_constraint as ordinary heap tables under the pg_catalog schema. Bootstrap is run by genbki.pl: a Perl script reads C structs annotated with BKI_* macros from src/include/catalog/pg_*.h, emits a postgres.bki script of create/insert directives, and the standalone --bootstrap mode of the postgres backend executes that script against an empty cluster to seed every system table. After bootstrap, the catalog is read through standard RelationCacheLookup over the same heap-and-btree access methods that user tables use. The point: schema-as-data is solved at build time by a code generator, with no runtime framework.

MySQL ships two parallel surfaces. INFORMATION_SCHEMA.* is the SQL-standard self-description: each INFORMATION_SCHEMA table is implemented by a custom storage engine (ha_information_schema) that synthesises rows from the data dictionary on demand. The actual on-disk catalog (since 8.0) lives in InnoDB tables under the mysql schema (mysql.tables, mysql.columns, …) and is bootstrapped by SQL DDL scripts run by mysqld --initialize. performance_schema is a separate runtime-state surface implemented as ring-buffer storage engine. Three layers — bootstrap DDL, InnoDB-backed data dictionary, virtual INFORMATION_SCHEMA — for the same conceptual catalog.

Oracle keeps its data dictionary in a fixed set of SYS-owned tables (OBJ$, COL$, IND$, USER$) plus a layer of fixed tables (X$KQFTA, X$KQFCO) that project C arrays as relations. The user-facing surface is the DBA_*/ALL_*/USER_* view family, all defined in $ORACLE_HOME/rdbms/admin/catalog.sql — a 100K-line SQL script run at CREATE DATABASE that builds the SYS.* tables, populates the seed rows, then defines the dictionary views as SQL CREATE VIEW statements layered on top.

SQL Server uses the same shape: a fixed set of base catalog tables (mostly inaccessible — sys.sysschobjs) plus a published surface of sys.* catalog views and INFORMATION_SCHEMA.* defined in mssqlsystemresource.mdf, the read-only resource database that ships with the server install.

The engines differ in where the schema-as-data lives — Postgres’s BKI files (build time), MySQL’s mysql/InnoDB tables (initialize time), Oracle’s catalog.sql (createdb time), SQL Server’s resource db (install time) — but the move is the same: encode the meta-schema as data, run a one-time install that uses normal DDL primitives, and from then on treat the catalog like any other table.

Where CUBRID lands. CUBRID encodes the meta-schema in C++ code at runtime: a cubschema::system_catalog_definition value per class, registered into a std::vector in catcls_init, consumed by a single system_catalog_builder in catcls_install. The data lives next to the engine (no external scripts to ship and version), and the install runs through ordinary db_create_class / smt_add_attribute / sm_update_class calls — the same code path a user CREATE TABLE takes. System views (db_class, db_attribute, …) carry their query spec as a SQL string literal compiled into the binary, installed via db_add_query_spec after the underlying class is built. The result is a catalog facade that is part of the engine, evolved with the rest of the C++ tree under one PR, and reachable for inspection through SELECT * FROM _db_class like any other table.

The module is split into four pieces that together implement the data-driven framework.

flowchart TB
  subgraph constants["Names — schema_system_catalog_constants.h"]
    CN["CT_CLASS_NAME = '_db_class'<br/>CT_ATTRIBUTE_NAME = '_db_attribute'<br/>CT_INDEX_NAME = '_db_index'<br/>... 24 class names + 22 view names"]
  end
  subgraph defs["Meta-schema — schema_system_catalog_definition.hpp"]
    A["struct attribute<br/>(COLUMN | CLASS_METHOD | QUERY_SPEC)"]
    C["struct constraint<br/>(PK | UNIQUE | INDEX | NOT_NULL)"]
    G["struct grant<br/>+ struct authorization"]
    SD["struct system_catalog_definition<br/>{ name, attributes[], constraints[],<br/>auth, row_initializer }"]
    A --> SD
    C --> SD
    G --> SD
  end
  subgraph install["Data — schema_system_catalog_install.cpp + _query_spec.cpp"]
    INIT["system_catalog_initializer::<br/>get_class() / get_attribute() / get_index() / ..."]
    CLIST["std::vector clist  (24 classes)"]
    VCLIST["std::vector vclist (22 views)"]
    INIT --> CLIST
    INIT --> VCLIST
  end
  subgraph builder["Engine — schema_system_catalog_builder.cpp"]
    B["system_catalog_builder::<br/>create_and_mark_system_class<br/>build_class<br/>build_vclass"]
  end
  subgraph entry["Entry — schema_system_catalog.cpp + install.cpp"]
    INITFN["catcls_init()"]
    INSTALLFN["catcls_install()"]
    INITFN --> CLIST
    INITFN --> VCLIST
    INSTALLFN --> B
  end
  SD --> INIT
  CN --> INIT
  CLIST --> INSTALLFN
  VCLIST --> INSTALLFN

Three properties make this design legible.

One representation, two consumers. The same system_catalog_definition describes a class (consumed by build_class) and a view (consumed by build_vclass). The attribute_kind discriminator selects the path: COLUMN and CLASS_METHOD go through smt_add_attribute / smt_add_class_method; QUERY_SPEC goes through db_add_query_spec. The view’s query spec is just another attribute kind, so views and tables share the same definition shape — a quiet but expressive choice.

Definitions are values, not functions. Legacy CUBRID code had per-class functions (boot_define_class, boot_define_attribute, …) that issued a long sequence of imperative db_add_* calls. The current module replaces those with system_catalog_initializer::get_class() returning a system_catalog_definition value. The builder then re-derives the same imperative sequence from the value. The redundancy is intentional: when the meta-schema needs a new feature (say, default values, expressed via the dvalue_func lambda already present), it goes once into the definition struct and once into the builder, rather than into 46 sites.

Install is a stable pipeline; data is the variable. catcls_install does the same three things every time: register all class definitions, build all classes, build all views. The order matters — classes must exist before views can reference them in query specs, and the first pass creates empty MOPs so the second pass can install attributes that reference other system classes (e.g. _db_class.owner is of type db_user, which has to be a known MOP first). This two-phase pattern is the same mutual-recursion fix the catalog-manager uses for its disk catalog (allocating the catalog file before writing rows that reference it).

// catcls_install — schema_system_catalog_install.cpp (condensed)
int catcls_install (void)
{
AU_DISABLE (save);
au_set_user (Au_dba_user);
// Pass 1: create empty MOPs for every system class
for (i = 0; i < clist.size (); i++)
class_mop[i] = catalog_builder::create_and_mark_system_class (clist[i].name);
// Pass 2: install attributes, constraints, grants, row initialisers
for (i = 0; i < clist.size (); i++)
catalog_builder::build_class (class_mop[i], clist[i].definition);
// Pass 3: install system views (their query specs may reference system classes)
for (i = 0; i < vclist.size (); i++) {
MOP class_mop = catalog_builder::create_and_mark_system_class (vclist[i].name);
catalog_builder::build_vclass (class_mop, vclist[i].definition);
}
AU_ENABLE (save);
clist.clear (); vclist.clear ();
return error_code;
}

The AU_DISABLE / AU_ENABLE bracket and the au_set_user (Au_dba_user) are not optional bookkeeping. The install runs DDL against tables that do not yet have authorization rows, and the DBA principal is the only user that exists at this point in createdb; suppressing the auth check for the install duration is what lets the recursion terminate.

The catalog surface CUBRID exposes splits along two axes — kind (real class on a heap vs. SQL view layered on top) and concern (schema, authorization, statistics, partitioning, PL, replication, i18n, server federation).

Class (24)ConcernNotes
_db_class, _db_attribute, _db_domain, _db_method, _db_meth_sig, _db_meth_arg, _db_meth_file, _db_query_spec, _db_index, _db_index_key, _db_data_typecore schemaSelf-describes every user class. _db_class is itself one of the 24.
db_root, db_user, db_password, db_authorizationauthorizationThe only ones without leading underscore — they are user-facing for direct DDL (CREATE USER).
_db_auth, _db_partition, _db_trigger, _db_serialfeature surfacesPrivileges per object, partition map, trigger registry, sequence state.
_db_stored_procedure, _db_stored_procedure_args, _db_stored_procedure_codePL familyStored procedure registry (consumed by cubrid-pl-javasp.md, cubrid-pl-plcsql.md).
_db_ha_apply_inforeplicationlog-applier checkpoint state.
_db_collation, _db_charseti18nLoaded from the per-platform libcubrid_collations.so (cubrid-charset-collation.md).
_db_serverfederationDBLink server catalog.
_db_synonymnamingObject aliases.
dualutilityOne-row table for SELECT 1 FROM dual — Oracle compatibility.
View (22)Underlying classNote
db_class, db_direct_super_class, db_vclass_db_classFilters by class type and ownership.
db_attribute, db_attr_setdomain_elm_db_attribute + _db_domainJoins set-domain elements through _db_domain.
db_method, db_meth_arg, db_meth_arg_setdomain_elm, db_meth_filemethod familyMostly disabled in modern usage but kept for compatibility.
db_index, db_index_keyindex familyExtracts B+Tree metadata into ordered rows.
db_auth_db_authResolves class_of/grantor/grantee MOPs to readable names.
db_trigger, db_partitiontrigger / partitionDecode catalog flags into human-readable enums.
db_stored_procedure, db_stored_procedure_argsSP familyThe signatures users \sp against.
db_serial, db_ha_apply_infofeature viewsCached state.
db_collation, db_charseti18n viewsUTF-8/ICU decoded.
db_server, db_synonymfederation viewsDBLink server map.

The 24/22 split is also the bilingual surface CUBRID inherits from Oracle vs. ANSI/ISO conventions: leading-underscore tables are the engine-private storage; non-underscore views are the user-facing read surface.

Where it sits relative to neighbouring modules

Section titled “Where it sits relative to neighbouring modules”
flowchart LR
  DDL["DDL pipeline<br/>(cubrid-ddl-execution)"]
  CM["Catalog manager<br/>(cubrid-catalog-manager)<br/>disk: OR_CLASSREP, CTID"]
  SC["This module<br/>(system-catalog-classes)<br/>SQL-visible: _db_class et al."]
  SHOW["SHOW commands<br/>(cubrid-show-commands)<br/>runtime: virtual scans"]
  SM["Schema manager<br/>(cubrid-class-object)<br/>in-memory: SM_CLASS"]
  BOOT["boot_sr / createdb<br/>(cubrid-boot)"]

  BOOT -->|catcls_init + catcls_install| SC
  SC -->|builds rows in| CM
  SC -.surfaces as.-> SHOW
  DDL -->|reads to compile| CM
  DDL -->|reads to update| SC
  SM -->|consumes _db_class rows from| SC
  • Upstream from this module: cubrid-boot calls catcls_init once during createdb to register the meta-schema, then catcls_install to actually run the DDL.
  • Builds into: cubrid-catalog-manager. Every db_create_class ultimately writes an OR_CLASSREP into the disk catalog — that is where the rows physically live. This module is the facade; catalog-manager is the storage.
  • Adjacent in DDL & Schema: cubrid-class-object (schema graph, SM_CLASS) reads the same rows on every plan compile to materialise its in-memory representation; cubrid-ddl-execution writes new rows when the user issues CREATE TABLE.
  • Surfaces upward as: cubrid-show-commands exposes runtime state through virtual scans; this module exposes static schema through real heap scans on _db_class etc. The two together form the system-catalog section’s full “self-description through SQL” surface.

The module’s symbols cluster cleanly by file role.

schema_system_catalog_constants.h — string constants for every system class and view name. Both raw class names (CT_CLASS_NAME = "_db_class") and view names (CTV_CLASS_NAME = "db_class") live here. Used everywhere the catalog is referenced by name (DDL parsing, SHOW, info schema, this module).

schema_system_catalog.hpp/cpp — top-level API. catcls_init (registers definitions), catcls_install (runs the install), sm_is_system_class / sm_is_system_vclass (name-membership tests against the sm_system_class_names / sm_system_vclass_names vectors). The catcls_add_data_type, catcls_add_charsets, catcls_add_collations row-initialiser entry points are also declared here and called from _db_data_type, _db_charset, _db_collation definitions through system_catalog_definition::row_initializer.

schema_system_catalog_definition.hpp/cpp — the meta-schema structs.

// Meta-schema — schema_system_catalog_definition.hpp (condensed)
enum class attribute_kind { COLUMN, CLASS_METHOD, QUERY_SPEC };
struct attribute {
attribute_kind kind;
std::string name;
std::string type; // SQL type string: "varchar(255)", "integer", ...
std::function<int (DB_VALUE *)> dvalue_func; // optional default
};
struct constraint {
DB_CONSTRAINT_TYPE type; // PK | UNIQUE | INDEX | NOT_NULL
std::string name;
std::vector<const char *> attribute_names;
bool is_class_attributes;
};
struct authorization {
struct db_object *owner; // typically Au_dba_user
std::vector<grant> grants; // (target_user, auth, with_grant_option)
};
struct system_catalog_definition {
std::string name;
std::vector<attribute> attributes;
std::vector<constraint> constraints;
authorization auth;
std::function<int (db_object *)> row_initializer; // optional seed data
};

The attribute::type field is a SQL type string, not a TP_DOMAIN. The string is parsed by smt_add_attribute exactly as if the user had written it in CREATE TABLE, which is what makes the install path identical to user DDL. The lambdas in dvalue_func and row_initializer capture by value — that is why the definitions can sit in a static std::vector from catcls_init until catcls_install runs.

schema_system_catalog_builder.hpp/cpp — the install engine.

  • create_and_mark_system_class (name)db_create_class or db_create_vclass based on the name lookup in sm_is_system_class / sm_is_system_vclass, then sm_mark_system_class to flag the resulting MOP as system-owned.
  • build_class (mop, def) — the heart of the framework: opens an SM_TEMPLATE via smt_edit_class_mop, walks def.attributes calling smt_add_attribute (or smt_add_class_method for CLASS_METHOD), invokes dvalue_func to set defaults, calls sm_update_class to commit the template, then walks def.constraints calling db_add_constraint, sets owner via au_change_class_owner_including_partitions, applies grants via au_grant, and finally invokes def.row_initializer if present.
  • build_vclass (mop, def) — view variant: db_add_attribute for COLUMN kinds, db_add_query_spec for QUERY_SPEC kinds, db_add_class_method for the rare CLASS_METHOD (preserved for legacy compat).

schema_system_catalog_install.cpp — the data file. Two parts:

  1. Static helper functions (make_int_value_fn, make_double_value_fn, make_numeric_value_fn, format_varchar, format_numeric, format_sequence) that produce default-value lambdas and SQL type strings.
  2. catcls_init (registers everything into clist and vclist) and catcls_install (runs the three-pass install).
  3. The cubschema::system_catalog_initializer namespace — a static class with get_class(), get_attribute(), get_domain(), … factory functions that each return a system_catalog_definition value for one class. This is where the actual schema lives in code; ~50K of the file is these factories.

The get_class() excerpt:

// system_catalog_initializer::get_class — schema_system_catalog_install.cpp (condensed)
system_catalog_definition system_catalog_initializer::get_class () {
return system_catalog_definition (
CT_CLASS_NAME, // "_db_class"
{ // attributes
{"class_of", "object"}, // self-reference MOP
{"unique_name", format_varchar (255)},
{"class_name", format_varchar (255)},
{"class_type", "integer"},
{"is_system_class", "integer"},
{"owner", AU_USER_CLASS_NAME}, // "db_user" -> object
// ... 25 more columns ...
},
/* constraints */ { /* PK on (owner, unique_name), INDEXes on class_name, etc. */ },
/* auth */ { Au_dba_user, /* grants */ },
/* row_initializer */ nullptr // _db_class has no seed data
);
}

The same shape applies to all 24 class factories. Differences: _db_data_type carries catcls_add_data_type as its row initializer (which inserts the 24 type-name rows: INTEGER, FLOAT, …); _db_charset and _db_collation carry catcls_add_charsets / catcls_add_collations (which read libcubrid_collations.so and insert one row per loaded locale).

schema_system_catalog_install_query_spec.cpp — the view data file. One sm_define_view_* helper per view (e.g. sm_define_view_class_spec, sm_define_view_attribute_spec), each returning a static SQL string baked at compile time. These strings are the SELECT statements installed via db_add_query_spec in build_vclass. The header comment lists 13 formatting rules for the SQL literals (indent two tabs, lines end with a space, AND/OR newlines, etc.) — the rules are intentionally strict because git blame on this file is the canonical “schema-changed” signal for downstream tooling.

// sm_define_view_class_spec — schema_system_catalog_install_query_spec.cpp (condensed)
const char *sm_define_view_class_spec (void) {
static char stmt [2048];
sprintf (stmt,
"SELECT "
"[c].[class_name] AS [class_name], "
"CAST ([c].[owner].[name] AS VARCHAR(255)) AS [owner_name], "
"CASE [c].[class_type] WHEN 0 THEN 'CLASS' WHEN 1 THEN 'VCLASS' "
"ELSE 'UNKNOWN' END AS [class_type], "
// ...
"FROM [%s] [c] " // "_db_class"
"WHERE [c].[is_system_class] = 0 OR ...",
CT_CLASS_NAME);
return stmt;
}

The sprintf parameterisation with %s and CT_CLASS_NAME is what couples this module to schema_system_catalog_constants.h — renaming a class would require coordinated edits in both files.

SymbolFileLine
catcls_initsrc/object/schema_system_catalog_install.cpp257
catcls_installsrc/object/schema_system_catalog_install.cpp315
system_catalog_initializer::get_classsrc/object/schema_system_catalog_install.cpp~415
system_catalog_builder::create_and_mark_system_classsrc/object/schema_system_catalog_builder.cpp33
system_catalog_builder::build_classsrc/object/schema_system_catalog_builder.cpp64
system_catalog_builder::build_vclasssrc/object/schema_system_catalog_builder.cpp204
sm_is_system_classsrc/object/schema_system_catalog.cpp126
sm_system_class_names (24 entries)src/object/schema_system_catalog.cpp33
sm_system_vclass_names (22 entries)src/object/schema_system_catalog.cpp86
sm_define_view_class_specsrc/object/schema_system_catalog_install_query_spec.cpp62
  • Definitions are runtime values, not compile-time constants. The 24 class definitions and 22 view definitions are constructed when catcls_init runs, not at static initialisation. The static std::vector<catcls_function> clist declarations in install.cpp are populated by catcls_init’s ADD_TABLE_DEFINITION macros at runtime, then drained by catcls_install. Consequence: catcls_init must be called before catcls_install, and both must be called inside the createdb boot path — there is no static-initialiser shortcut. The clist.clear () / vclist.clear () at the end of catcls_install is intentional: the definitions are freed once installed, since they exist only to feed the builder.
  • The framework is invoked exactly once, at createdb. catcls_install is called from boot_create_db (cubrid-boot.md) on first-time database creation. On every subsequent restart, the system classes are already on disk in the catalog, and the engine reads them through cubrid-class-object’s SM_CLASS graph machinery — this module is dormant after createdb.
  • AU_DISABLE is required, not optional. During install the auth tables (db_user, db_authorization, _db_auth) themselves are being created; running auth checks against an in-construction catalog would wedge the install. The install runs as Au_dba_user with AU_DISABLE precisely because the access checks would otherwise loop on tables that do not yet exist.
  • Two-phase MOP creation matters because of cross-class references. _db_class.owner is of type db_user, _db_attribute.class_of is of type _db_class, etc. If the install built each class fully before moving to the next, the very first class would fail trying to declare an attribute of a not-yet-existent type. Pass 1 creates empty MOPs for all 24 classes so that Pass 2’s smt_add_attribute calls always find the type names they need.
  • The view query specs reference table names through sprintf %s. This is fragile: changing CT_CLASS_NAME from _db_class to anything else requires editing both the constant and any view query spec that names the table directly. The strict formatting comment at the top of install_query_spec.cpp is partly to ensure such changes are visible in git diff.
  • Legacy boot_define_* functions still exist. boot_sr.c retains a boot_define_class, boot_define_attribute, etc. family that predates this module. They are no longer called from the install path, but git blame shows they were deprecated incrementally — some are still referenced by older tooling. The // new routine comments in catcls_install mark where the new builder replaced the old direct calls.
  • row_initializer is the only place where seed data lives. Schema (columns, constraints, grants) is data inside the definition struct, but actual seed rows (the 24 type-name rows in _db_data_type, the locale rows in _db_collation) live as imperative C++ inside the row-initializer callbacks. There is no declarative way to express “and here are the initial rows” — the framework stops at schema and hands off row population to a lambda.
  • Why not generate the install code at build time (Postgres BKI style) rather than at runtime? The runtime approach pays a one-time cost on createdb but keeps the meta-schema in plain C++ next to the rest of the engine, which a Perl-style code-gen does not. The trade-off is real — the BKI approach can validate the meta-schema at compile time — but CUBRID has chosen runtime for unification with the normal DDL path.
  • Should _db_charset / _db_collation be elsewhere? Their row initializers read the i18n shared libraries; they are catalog tables but their content is owned by cubrid-charset-collation.md and cubrid-timezone.md (no _db_timezone exists; timezones surface only through SHOW). The current placement keeps the schema-bootstrap story unified.
  • The 13 query-spec formatting rules are partly self-enforced. No CI lint runs against install_query_spec.cpp shape; the rules live as a header comment. A strict diff in code review is the only enforcement.
  • dual and db_root are exposed names. Most catalog classes use the leading-underscore convention (_db_class); db_user, db_password, db_authorization, db_root, dual do not. The boundary is historical (Oracle dual for compatibility, the auth tables predating the underscore convention) but it is observable by users and worth keeping in mind when reading client code that filters system tables by name prefix.
  • src/object/schema_system_catalog.hpp/cpp — top-level API, catcls_init/catcls_install, name-membership predicates
  • src/object/schema_system_catalog_builder.hpp/cpp — generic install engine (create_and_mark_system_class, build_class, build_vclass)
  • src/object/schema_system_catalog_definition.hpp/cpp — meta-schema structs (attribute, constraint, grant, system_catalog_definition)
  • src/object/schema_system_catalog_constants.h — class and view name constants
  • src/object/schema_system_catalog_install.cpp — definition data for all 24 system classes + the 3-pass install orchestration
  • src/object/schema_system_catalog_install_query_spec.cpp — SQL literal query specs for all 22 system views
  • src/object/schema_manager.h, src/object/schema_template.h — DDL primitives the builder calls into
  • src/transaction/boot_sr.cboot_create_db invokes catcls_init + catcls_install once at createdb
  • Adjacent CUBRID docs: cubrid-catalog-manager.md, cubrid-class-object.md, cubrid-ddl-execution.md, cubrid-show-commands.md, cubrid-boot.md, cubrid-charset-collation.md
  • Textbook references: System R 1979 (Astrahan et al.); Hellerstein and Stonebraker, Anatomy of a Database System Ch. 4; Petrov, Database Internals Ch. 1; Selinger 1979 (Access Path Selection in a Relational DBMS)