Skip to content

CUBRID System Parameters — Tunable Registry, Conf/Env/URL Parsing, and Per-Session Scoping

Contents:

A relational DBMS has hundreds of knobs. Buffer-pool size, log-buffer size, lock-timeout, isolation-level default, error-log severity, checkpoint cadence, character set, time-zone — every subsystem brings its own pile. The configuration subsystem is the place where those knobs are named, typed, given defaults, overridden from external sources, and served back to the rest of the engine on demand. It is one of the few modules that nearly every other module calls into, which means it has to be cheap, predictable, and type-safe in a way that is unusual for engine internals.

Three properties shape every implementation:

  1. A central registry. Every parameter has a name, a type, a default, optional bounds, and a set of behavioural flags (is it client-side, server-side, both? can the user change it on-line? does it need a restart? is it deprecated?). The registry is the one place that knows the answer for every parameter, and the one place that needs to be touched when a parameter is added.
  2. Multiple sources, ordered by priority. A typical engine reads defaults from the registry, then from a configuration file, then from process environment, then from connection-time arguments, then from runtime SQL (SET statements). Each later source overrides earlier ones. The ordering matters because users expect the most-specific source — the one they explicitly typed in the SET statement — to win.
  3. Per-server vs. per-session scope. Some parameters are global: they describe the physical state of the server (buffer pool, log buffer, port number, max-clients) and can change only when the server restarts or via a privileged DBA. Others are session-scoped: they describe how the current client wants its queries to behave (isolation level, time-zone, optimizer reservations, error-log severity for this session) and a single client’s SET SYSTEM PARAMETERS should only affect that client’s own session. The configuration subsystem is responsible for knowing which is which and for routing reads/writes appropriately.

The textbook treatment is thin — Database Internals (Petrov) skips it; the Postgres GUC paper “PostgreSQL’s Configuration Engine” by Tom Lane is the closest canonical reference, and its insight is that the parameter list is itself a type-checked symbol table that the rest of the engine compiles against. Operating-system literature has the same pattern under “kernel tunables” (sysctl in Linux, MIB in BSD): both are read by hundreds of callers on hot paths and both end up using the same idiom of an enum-keyed table of typed slots with inline accessors.

The implementation degrees of freedom are: the type system (CUBRID adds BIGINT for >2 GiB byte counts, KEYWORD for named enum values, and INTEGER_LIST to int/float/bool/string); default-vs-current (CUBRID stores both per slot so the dump routine can flag drift with one comparison); conf-file sectioning (CUBRID picks INI-style sectioning so one file handles both process-level and per-database overrides); and per-session storage (CUBRID keeps a sparse array indexed by a precomputed “session-eligible” subset, prm_Def_session_idx[]).

Every relational engine has converged on roughly the same architecture for parameters; the differences are mostly in the ergonomics of the user-facing surface.

Postgres GUC (Grand Unified Configuration). Per-type subclasses (config_int, config_real, config_string, config_bool, config_enum) over struct config_generic, with separate arrays walked at startup (ConfigureNamesInt[], etc.). Each GUC carries a context enum (PGC_INTERNAL, PGC_POSTMASTER, PGC_SIGHUP, PGC_SUSET, PGC_USERSET, PGC_BACKEND) that decides who can change it and when. Reads go through GetConfigOption() or direct pointer access for hot paths. The pg_settings catalog view is a thin wrapper over the GUC table.

MySQL system variables. Registry of sys_var objects registered via Sys_var_init(). Reads use THD::sys_var_value() for session and a global lock for global. The SESSION / GLOBAL split is explicit in SET GLOBAL, SET SESSION, SHOW VARIABLES. Conf file is my.cnf with [server] / [client] / [mysqld] sections.

Oracle SPFILE/PFILE. Parameters live in either a binary SPFILE (persisted) or text PFILE (init.ora). The ALTER SYSTEM SET ... SCOPE = MEMORY|SPFILE|BOTH syntax decides in-memory vs. persistent. Catalog views: v$parameter, v$spparameter.

SQLite. Pragmas only — PRAGMA name = value. Some per-connection (journal_mode, synchronous), some per-database (page_size, set at create time only). No configuration file.

CUBRID is closest to MySQL in user-facing shape (SQL SET SYSTEM PARAMETERS '...', INI-style cubrid.conf, GLOBAL / SESSION distinction implicit in flags) but closer to Postgres in implementation (a single prm_Def[] array of typed slots accessed through inline getters, with a per-parameter flag word encoding context/scope/persistence rules).

Theoretical conceptCUBRID name
Parameter ID enumenum param_id / PARAM_ID (system_parameter.h)
Per-parameter slotstruct sysprm_param / SYSPRM_PARAM (system_parameter.h)
Registry arrayprm_Def[] (system_parameter.c)
Type tagSYSPRM_DATATYPE (int/float/bool/keyword/bigint/string/intlist)
Static (compile-time) flag wordSYSPRM_PARAM::static_flag (PRM_FOR_CLIENT, PRM_FOR_SERVER, …)
Dynamic (runtime) flag wordSYSPRM_PARAM::dynamic_flag (PRM_SET, PRM_ALLOCATED, …)
Tagged valueSYSPRM_PARAM_VALUE { is_null, v } over SYSPRM_VALUE union
Default-vs-currentdefault_value and value fields side-by-side
Boundslower_limit, upper_limit
Forced overrideforce_value (string) + prm_set_force
Unit translatorset_dup / get_dup callbacks
Conf-file loadersysprm_load_and_initprm_read_and_parse_ini_file
Section dispatcherprm_load_by_section
Env overrideprm_check_environment (uppercases name → envvar_get)
Cross-parameter tuningprm_tune_parameters (post-load)
Compound parameterPRM_COMPOUND flag + prm_set_compound (e.g. compat_mode)
Aliased parameter pairPARAM_VALUE_SHARE[] table + sysprm_find_shared_system_parameter
Session-scoped subsetPRM_FOR_SESSION flag + prm_Def_session_idx[]
Per-session value arraySESSION_PARAM array on SESSION_STATE::session_parameters
Per-session getter detourprm_get_value / PRM_SERVER_SESSION macro
Runtime change listSYSPRM_ASSIGN_VALUE linked list
User SQL → registrydb_set_system_parameterssysprm_validate_change_parameters
Server-side parameter changexsysprm_change_server_parameters (x = server stub)
Server-forced refresh on connectPRM_FORCE_SERVER + xsysprm_get_force_server_parameters
Reloadsysprm_reload_and_init (only PRM_RELOADABLE parameters)
Final teardownsysprm_final

The configuration subsystem is built on five moving parts: the parameter table that names every tunable, the resolution pipeline that pulls values from defaults, the conf file, the environment, and runtime SQL, the per-session detour that lets a session override a subset of parameters without affecting other sessions, the propagation channel that synchronises values between client and server when scopes overlap, and the dump/print machinery that serialises the table for diagnostics. We walk them in that order.

flowchart LR
  subgraph SRC["External sources"]
    DEF["Compile-time<br/>defaults<br/>(prm_Def[].default_value)"]
    CONF["cubrid.conf<br/>(INI sections)"]
    HACONF["cubrid_ha.conf<br/>(HA-only)"]
    ENV["Environment<br/>variables<br/>UPPERCASED name"]
    URL["URL / connect<br/>params (cci/jdbc)"]
    SQL["SET SYSTEM<br/>PARAMETERS '...'"]
  end
  subgraph LOAD["Resolution (system_parameter.c)"]
    SLI["sysprm_load_and_init<br/>→ _internal"]
    PRP["prm_read_and_parse_ini_file"]
    PLBS["prm_load_by_section"]
    PCE["prm_check_environment"]
    PTP["prm_tune_parameters"]
  end
  subgraph TBL["Registry"]
    PRM["prm_Def[] array<br/>indexed by PARAM_ID"]
    IDX["prm_Def_session_idx[]<br/>sparse session map"]
    SHARE["PARAM_VALUE_SHARE[]<br/>aliased pairs"]
  end
  subgraph SCOPE["Per-process state"]
    GLOBAL["Per-server values<br/>(prm_Def[].value)"]
    SESS["Per-session values<br/>(SESSION_PARAM array<br/>on SESSION_STATE)"]
  end
  subgraph READ["Read API"]
    GETI["prm_get_integer_value (inline)"]
    GETB["prm_get_bool_value (inline)"]
    GETS["prm_get_string_value (inline)"]
    GETV["prm_get_value (per-session detour)"]
  end

  DEF --> SLI
  CONF --> PRP
  HACONF --> PRP
  PRP --> PLBS
  PLBS --> SLI
  ENV --> PCE
  PCE --> SLI
  SLI --> PTP
  PTP --> PRM
  PRM --> IDX
  PRM --> SHARE
  PRM --> GLOBAL

  URL --> SQL
  SQL --> SESS
  SQL --> GLOBAL

  GLOBAL --> GETI
  GLOBAL --> GETB
  GLOBAL --> GETS
  SESS --> GETV
  GETI --> GETV
  GETB --> GETV
  GETS --> GETV

Every tunable in CUBRID is one entry in the prm_Def[] array (system_parameter.c:1010). The struct shape is fixed by SYSPRM_PARAM in the header:

// sysprm_param — system_parameter.h
struct sysprm_param
{
PARAM_ID id; // enum tag
const char *name; // string the parser expects
unsigned int static_flag; // compile-time scope/role flags
SYSPRM_DATATYPE datatype; // int/float/bool/keyword/bigint/string/intlist
unsigned int dynamic_flag; // runtime: SET / ALLOCATED / ...
const SYSPRM_PARAM_VALUE default_value; // compile-time default
SYSPRM_PARAM_VALUE value; // current value (mutated)
const SYSPRM_PARAM_VALUE upper_limit; // optional bound
const SYSPRM_PARAM_VALUE lower_limit; // optional bound
char *force_value; // string forced from runtime API
DUP_PRM_FUNC set_dup; // input -> storage unit
DUP_PRM_FUNC get_dup; // storage -> display unit
};

Three points are worth lingering on.

The ID enum is the canonical anchor. PARAM_ID (header:97) is declared in the same order as prm_Def[] is initialised, and the debug-build sanity check sysprm_check_id_order (the immediately- invoked lambda at system_parameter.c:5965) asserts that GET_PRM(i)->id == i for every slot. That invariant lets every caller dereference prm_Def[id] in O(1) without ever calling prm_find for a by-name lookup. The macro GET_PRM(id) (header:679) is just (&prm_Def[(id)]).

The static flag word is the policy field. A handful of bits encode every policy decision the engine ever makes about a parameter: PRM_USER_CHANGE (allow runtime SET), PRM_FOR_CLIENT / PRM_FOR_SERVER (which side hosts a copy), PRM_HIDDEN (suppress from dumps), PRM_RELOADABLE (sysprm_reload_and_init may reset), PRM_COMPOUND (setting it cascades to others), PRM_TEST_CHANGE (only changeable while test_mode=on), PRM_FOR_HA (read from cubrid_ha.conf), PRM_FOR_SESSION (session-scoped), PRM_FORCE_SERVER (client must pull from server on connect), PRM_FOR_QRY_STRING (include in plan-cache key), PRM_CLIENT_SESSION (client/server session, do not affect server), PRM_SIZE_UNIT / PRM_TIME_UNIT / PRM_DIFFER_UNIT (unit handling), PRM_FOR_HA_CONTEXT (replicate via HA log applier), PRM_GET_SERVER (dual-scope, read from server), PRM_FOR_PL_CONTEXT (forward to PL/SP context), PRM_DEPRECATED, PRM_OBSOLETED.

A typical table entry encodes its full policy in a few ORed flags. For data_buffer_size, the row sets static_flag = PRM_FOR_SERVER | PRM_SIZE_UNIT | PRM_DIFFER_UNIT | PRM_RELOADABLE, type PRM_INTEGER, default and “current” both {.i = 32768} (pages), no upper limit, lower limit 1024 pages, and set_dup / get_dup callbacks prm_size_to_io_pages / prm_io_pages_to_size. The flags say “server-only, accepts a K/M/G suffix on input, displays back in human-readable units, may be reloaded; the storage unit is pages but the user-facing unit is bytes”. The set_dup / get_dup pair is how that unit translation is plumbed in.

The default-and-current pair is duplicated for a reason. Storing both default_value and value per slot looks wasteful, but it lets sysprm_dump_parameters (5847) flag every non-default value with a single comparison, and it lets prm_set_default_internal (9489) restore a parameter to its compile-time baseline with one structure assignment (prm->value = prm->default_value;) for the scalar types.

The whole loader is bottle-necked through one private function, sysprm_load_and_init_internal (system_parameter.c:6015), which is called by three public entry points: sysprm_load_and_init for the server side, sysprm_load_and_init_client for the CS/SA client, and sysprm_reload_and_init (with the reload=true flag) for runtime re-reads.

sequenceDiagram
  participant Caller as boot_restart_server / boot_cl
  participant SLI as sysprm_load_and_init_internal
  participant Reset as PRM_RELOADABLE reset loop
  participant File as prm_read_and_parse_ini_file
  participant Sec as prm_load_by_section
  participant DefLoop as missing-default loop
  participant Env as prm_check_environment
  participant Tune as prm_tune_parameters
  participant Force as force_value loop

  Caller->>SLI: db_name, conf_file, load_flags
  Note right of SLI: if reload, reset all PRM_RELOADABLE
  SLI->>Reset: for each prm if PRM_RELOADABLE: prm_set_default
  SLI->>File: ${CUBRID}/conf/cubrid.conf or $CONF_FILE
  File->>Sec: section "common" (ignore_section=true)
  File->>Sec: section "@<dbname>"
  File->>Sec: section "service" (only on first pass)
  Note right of File: if HA mode on: also load cubrid_ha.conf
  File->>Sec: section "%<host>|*", "%<host>|<user>"
  SLI->>DefLoop: for each prm without PRM_SET: prm_set_default
  SLI->>Env: for each prm: envvar_get(UPPERCASE name)
  SLI->>Tune: cross-parameter consistency adjustments
  SLI->>Force: for each prm with force_value: prm_set
  SLI-->>Caller: NO_ERROR

The loader has three layered concerns: which file to read, which sections of that file apply, and what to do when a key has no value yet.

Which file. The default is ${CUBRID}/conf/cubrid.conf, resolved by envvar_confdir_file against the install layout. The environment variable CONF_FILE overrides the path; the optional HA_CONF_FILE chooses a separate file for HA-only parameters when ha_mode != off. The argument conf_file to sysprm_load_and_init exists for callers that want to inject a specific path; in practice it is always passed NULL and the internal asserts that.

Which sections. The conf file is INI-formatted, parsed by the ini_parser library, and walked once per section by prm_load_by_section (system_parameter.c:6350). The sections that apply, in order, are:

  • [common] — every database, every host. Read first so per- database overrides win.
  • [@<dbname>] — settings specific to one database (only if db_name != NULL, which is the server’s normal case).
  • [standalone] — only consulted in SA_MODE (csql -S, utilities).
  • [service] — service-launcher settings (service, server keys used by the cubrid shell wrapper). The ignore_section=false flag here is what tells the loader to keep the service:: prefix in the parameter name; that is why two of the parameter defines in the registry have the explicit prefix "service::service" and "service::server".
  • [%<host>|*] and [%<host>|<user>] — per-host or per-host- per-user overrides, only consulted when HA is enabled and the host name resolves.

prm_load_by_section does not blindly call prm_set on every key — it first filters by mode and flags. Under CS_MODE a server-only parameter (no PRM_FOR_CLIENT) is silently skipped; under SERVER_MODE the inverse is true. Reloads skip parameters without PRM_RELOADABLE. Without SYSPRM_IGNORE_HA in the load-flags, only PRM_FOR_HA parameters are considered (this is how cubrid_ha.conf is layered onto cubrid.conf from a single loader path). PRM_OBSOLETED is silently skipped, PRM_DEPRECATED warns but proceeds. The pattern is the static flags decide whether the value is even considered; the same conf file is read by both binaries and only the relevant subset applies to each.

Default-fill and environment. After all sections have been applied, every parameter that was not touched is filled from its compile-time default by the loop at system_parameter.c:6144. The environment pass prm_check_environment (7091) walks the table, upper-cases each parameter name, and asks envvar_get whether the corresponding env variable is exported; if so, the value is fed through prm_set. The order is “default → conf file → environment → runtime force”; each later phase calls prm_set with set_flag=true, setting PRM_SET in the dynamic flag word so the next phase can do “set if not yet set” decisions correctly.

Cross-parameter tuning. prm_tune_parameters (system_parameter.c:9865) is the post-load fixup pass. It materialises invariants that depend on multiple parameters jointly: clamp max_clients to min(CSS_MAX_CLIENT_COUNT, css_get_max_socket_fds()), override task_worker if the user left the default and the host has a known core count, force HA-related parameters into self-consistent shapes (ha_server_state defaults to STANDBY when ha_mode != off, ACTIVE otherwise), set ha_node_list to <host>@<host> when the user did not provide one, and so on. This is where the configuration subsystem does its reluctant role of engine policy — most of the function is “if the user did not say otherwise, here is what the system thinks is sensible”.

Forced overrides. The very last thing the loader does is loop over the table and, for any slot with a non-NULL force_value string, call prm_set with set_flag=false to replay it. The force_value field is populated only by prm_set_force (9471), exposed as the public sysprm_set_force API. It is used by callers that need a value to survive a subsequent reload — notably the boot module when it injects URL-style param=val overrides early during client attach. Because set_flag=false, the dynamic flag PRM_SET is not raised, so the dump routine still considers the slot “default” for diagnostic purposes.

The hardest design choice in the configuration subsystem is what happens when the same parameter is settable both globally and per session. CUBRID’s answer is: it is a flag on the parameter (PRM_FOR_SESSION), and the read path takes a detour for those parameters only.

The mechanics rest on three pieces of state.

The session index. A flat int[], sized once to DIM (prm_Def), that maps each PARAM_ID to its position in the per-session value array, or -1 for parameters that are not session-scoped:

// prm_Def_session_idx fill — system_parameter.c
SYSPRM_INDIRECT_POS prm_Def_session_idx[DIM (prm_Def)];
static int num_session_parameters = 0;
if (num_session_parameters == 0) {
for (i = 0; i < MAX_SYSTEM_PARAMS; i++)
prm_Def_session_idx[i] = (PRM_IS_FOR_SESSION (GET_PRM (i)))
? num_session_parameters++ : -1;
}

The hash-on-demand approach was rejected because the read path is too hot — prm_get_*_value is called on every page touch, every lock acquisition, every plan generation, and the per-session detour must compile to pointer arithmetic + one indirect array load, not a hash lookup.

The per-session value array. SESSION_PARAM is a small struct-of-typed-values (prm_id, flag, datatype, value), one entry per session-eligible parameter. The session module (see cubrid-server-session.md) hangs an array of NUM_SESSION_PRM of these on SESSION_STATE::session_parameters at first attach. sysprm_session_init_session_parameters (11447) is the attach-time reconciler: if the session-state already has its parameter array (a reconnect), keep the server-side values and discard the values just unpacked from the client packet; if not (a fresh client), use the client’s values and call update_session_state_from_sys_params so derived caches like the session timezone region pick them up. This is the mechanism that lets a TCP-level reconnect recover the per-session settings the client had set before disconnecting.

The detour in the read path. The inline getter prm_get_integer_value (header:834-846) and its siblings check the PRM_SERVER_SESSION macro (header:681-683) and, only on SERVER_MODE and only when the parameter is actually session- scoped, dereference through the current thread’s session via prm_get_value (10486). The macro itself is

#define SERVER_SESSION_MASK ((PRM_FOR_SESSION | PRM_FOR_SERVER) | PRM_CLIENT_SESSION)
#define SERVER_SESSION_CHCK ((PRM_FOR_SESSION | PRM_FOR_SERVER) & ~PRM_CLIENT_SESSION)
#define PRM_SERVER_SESSION(id) \
(((GET_PRM (id))->static_flag & SERVER_SESSION_MASK) == SERVER_SESSION_CHCK)

— the one place the “is this parameter both session-scoped and server-side, but not client-only-session” question is encoded. The branch is predicted heavily toward “no detour”; the slow path itself is a BO_IS_SERVER_RESTARTED guard plus one session_get_session_parameter lookup in the per-session array, falling back to the global value if the session does not yet have its array attached.

flowchart TD
  CALL["prm_get_integer_value(prm_id)"]
  CHECK{"PRM_SERVER_SESSION(prm_id)<br/>and SERVER_MODE<br/>and server-restarted"}
  GLOBAL["return prm_Def[prm_id].value.v.i"]
  SESS["session_get_session_parameter(thread, prm_id)"]
  SHASH{"found in<br/>session_parameters[]"}
  GLOBAL2["return prm_Def[prm_id].value.v.i"]
  SESSV["return SESSION_PARAM.value.i"]

  CALL --> CHECK
  CHECK -- no --> GLOBAL
  CHECK -- yes --> SESS
  SESS --> SHASH
  SHASH -- no --> GLOBAL2
  SHASH -- yes --> SESSV

A handful of parameters are dual names for the same underlying storage. The classic example is sort_buffer_size (in bytes) and sort_buffer_pages (in IO pages) — there is one storage location (integer page count) but two ways for the user to set it. CUBRID’s solution is the PARAM_VALUE_SHARE[] table (system_parameter.c:5341):

static const int *PARAM_VALUE_SHARE[] = {
/* {count, PRM_ID_a, PRM_ID_b, ...} */
(int[]) {2, PRM_ID_SR_NBUFFERS, PRM_ID_SORT_BUFFER_SIZE},
(int[]) {2, PRM_ID_PB_NBUFFERS, PRM_ID_PAGE_BUFFER_SIZE},
(int[]) {2, PRM_ID_BT_OID_NBUFFERS,PRM_ID_BT_OID_BUFFER_SIZE},
(int[]) {2, PRM_ID_LK_TIMEOUT_SECS,PRM_ID_LK_TIMEOUT},
(int[]) {2, PRM_ID_LOG_NBUFFERS, PRM_ID_LOG_BUFFER_SIZE},
/* ... */
(int[]) {2, PRM_ID_JAVA_STORED_PROCEDURE, PRM_ID_STORED_PROCEDURE},
/* ... */
NULL,
};

Each row’s first element is the count of aliased ids; the rest are the ids themselves, which the comment says must be in ascending order and consecutive. When sysprm_set_value (9335) sets a value on one of those parameters, it consults sysprm_find_shared_system_parameter and replays the same set on every other id in the row through sysprm_set_value_internal. The unit translation per parameter (the set_dup / get_dup callbacks declared in the registry entry for the bytes-flavoured twin) takes care of the actual pages-↔-bytes conversion; the share table is just the membership relation.

The PRM_COMPOUND flag is a different mechanism. A compound parameter sets several others as a side-effect, but the relationship is “one input keyword chooses N output values” rather than “one storage location with N names”. The only example today is compat_mode: when set, prm_compound_has_changed (9448) calls prm_set_compound (10310) with compat_mode_values, a 2-D array indexed by the compat target (CUBRID/MySQL/Oracle) and the parameter to update — oracle_style_outerjoin, ansi_quotes, pipes_as_concat, etc., land on different defaults depending on which target the user chose.

A CUBRID install has at least two processes that must see consistent parameter values: the client (cubridcs library, linked into the JDBC/CCI driver, or cubridsa for SA mode) and the server (cub_server). Each maintains its own prm_Def[] table, but they must agree on values for parameters tagged PRM_FOR_CLIENT | PRM_FOR_SERVER. The propagation rules are encoded in three flags:

  • PRM_FOR_CLIENT only — the parameter exists on the client and is invisible to the server. Pure client-side parameters (e.g. CSQL behaviours).
  • PRM_FOR_SERVER only — vice versa. Most physical-resource parameters (buffer sizes, log sizes, port).
  • Both flags set — the parameter exists on both sides; the rules for which side wins, and when, depend on PRM_FORCE_SERVER and PRM_GET_SERVER.

Client-initiated changes. The user-facing path is SET SYSTEM PARAMETERS '<name>=<value>;...', which lands in db_set_system_parameters (db_admin.c:2879). The pattern is “validate → maybe RPC → apply locally”: sysprm_validate_change_parameters (7196) parses the name=value;name=value payload through sysprm_generate_new_value and returns a SYSPRM_ASSIGN_VALUE linked list plus a status code (PRM_ERR_NO_ERROR if every parameter is client-only, PRM_ERR_NOT_FOR_CLIENT if at least one needs the server, PRM_ERR_NOT_FOR_CLIENT_NO_AUTH if DBA privilege is required). When the server is needed, db_set_system_parameters calls sysprm_change_server_parameters (the client-side RPC stub) which arrives at xsysprm_change_server_parameters (8124); this is a thin wrapper around sysprm_change_parameter_values with check=true so the server can reject parameters that were marked client-only. Whether the RPC fired or not, the client also calls sysprm_change_parameter_values locally.

The reciprocal direction is db_get_system_parameterssysprm_obtain_parameters (7983) → sysprm_obtain_server_parameters for parameters whose value lives on the server.

Server-forced refresh. The flag PRM_FORCE_SERVER declares “this parameter’s authoritative value lives on the server, and every fresh client must pull it on connect”. sysprm_tune_client_parameters (10266) is the client-side hook called right after boot_register_client: it asks the server through sysprm_get_force_server_parameters, packs the response into a SYSPRM_ASSIGN_VALUE list, and applies it locally with sysprm_change_parameter_values (..., check=false, ...). The server side is xsysprm_get_force_server_parameters (8165), which walks the table and packs every parameter flagged PRM_FORCE_SERVER into the response. Typical examples are intl_collation, server_timezone, and block_ddl_statement — values the client must inherit because otherwise SQL semantics would diverge.

sequenceDiagram
  participant Client
  participant Server
  participant ClientPRM as Client prm_Def[]
  participant ServerPRM as Server prm_Def[]

  Note over Client,Server: At register time
  Client->>Server: xboot_register_client (session_params packed)
  Server->>ServerPRM: sysprm_session_init_session_parameters
  Server-->>Client: BOOT_SERVER_CREDENTIAL + session_id
  Client->>Server: sysprm_get_force_server_parameters
  Server->>ServerPRM: walk PRM_FORCE_SERVER
  Server-->>Client: SYSPRM_ASSIGN_VALUE list
  Client->>ClientPRM: sysprm_change_parameter_values

  Note over Client,Server: At runtime SET SYSTEM PARAMETERS
  Client->>ClientPRM: sysprm_validate_change_parameters
  Client->>Server: sysprm_change_server_parameters (if PRM_FOR_SERVER)
  Server->>ServerPRM: sysprm_change_parameter_values
  Client->>ClientPRM: sysprm_change_parameter_values (if PRM_FOR_CLIENT)

The CCI/JDBC drivers accept connection URLs of the shape cubrid:host:port:db:user:password:?param=val&.... The URL fragment is reformatted into name=val;name=val form, passed to db_set_system_parameters early during the DB-Connect callback, and from there everything is identical to a runtime SQL SET. For URL parameters that must survive a subsequent reload (e.g. lock_timeout chosen by URL must not be overwritten by cubrid.conf), the driver uses prm_set_force: each value is stashed in the slot’s force_value field and reapplied at the tail of every sysprm_load_and_init_internal call.

sysprm_reload_and_init is the on-the-fly re-read path: it calls _internal with reload=true, which (a) before reading the conf file resets every PRM_RELOADABLE parameter to its default and (b) inside prm_load_by_section skips any parameter that lacks PRM_RELOADABLE. Heavy physical parameters (PRM_ID_PAGE_BUFFER_SIZE, PRM_ID_LOG_NBUFFERS, PRM_ID_CSS_MAX_CLIENTS) are not reloadable — they participate in one-shot allocation. sysprm_final (9783) is the symmetric teardown that walks the table, frees any value flagged PRM_ALLOCATED, resets dynamic_flag = 0, and clears the prm_file_has_been_loaded set.

sysprm_dump_parameters (5847) is the introspection routine. Its caller passes a marker character (' ', 'C', 'S'), include/exclude flag masks, and AND-vs-OR semantics for those masks. The new-style output is [<marker><differs>] name=value (default) so a reader sees drifts at a glance; the old style prints just name=value. The flagged-on-difference comparison reads prm->value and prm->default_value straight from the slot — no re-formatting, no I/O — which is the reason the table holds both. xsysprm_dump_server_parameters is the server-side wrapper that pre-fills the marker to 'S'.

The configuration subsystem is one C file plus its public header. Below the symbols are grouped by role; the position-hint table at the end pairs each symbol with its line number as of updated:.

Header — types and policy. system_parameter.h exposes the id enum PARAM_ID, the type tag SYSPRM_DATATYPE, the variant union SYSPRM_VALUE and its tagged wrapper SYSPRM_PARAM_VALUE, the slot struct SYSPRM_PARAM, the session-scoped value type SESSION_PARAM, the change-list type SYSPRM_ASSIGN_VALUE, the static and dynamic flag definitions, the accessor macros (GET_PRM, PRM_GET_INT/BOOL/...), the policy macro PRM_SERVER_SESSION, and the STATIC_INLINE getters prm_get_integer_value / ..._bool_value / ..._float_value / ..._string_value / ..._integer_list_value / ..._bigint_value. Windows builds provide the getters out-of-line.

Registry data (system_parameter.c). One #define PRM_NAME_<ID> per parameter (120-797), then SYSPRM_PARAM prm_Def[] (1010-5331), the precomputed session index prm_Def_session_idx[], the size cache MAX_SYSTEM_PARAMS, the alias table PARAM_VALUE_SHARE[], the per-CS_MODE cache cached_session_parameters, and the keyword tables (boolean_words, er_log_level_words, isolation_level_words, ha_mode_words, compat_words, etc.).

Loader. Three public entry points (sysprm_load_and_init / ..._client / _reload_and_init) all funnel through sysprm_load_and_init_internal. Section-by-section work is prm_read_and_parse_ini_fileprm_load_by_sectionprm_section_cmp + prm_find. Post-load passes are prm_check_environment (env override), prm_tune_parameters (cross-parameter consistency), and the forced-value replay loop. init_server_timezone_parameter is the timezone-specific late-init step.

Write paths. prm_set parses a string into a typed value (sysprm_generate_new_value) and applies it via sysprm_set_value, which calls sysprm_set_value_internal for the actual mutation, consults sysprm_find_shared_system_parameter to propagate to aliased ids, and (for PRM_COMPOUND) calls prm_compound_has_changedprm_set_compound to cascade further sets. prm_set_default resets to baseline. prm_set_force stashes a string in force_value for replay. The typed public setters prm_set_integer_value / ..._bool / ..._float / ..._string / ..._integer_list / ..._bigint are the direct-write API.

Read paths. prm_get_value returns a pointer to the active SYSPRM_VALUE, taking the per-session detour when needed. The inline getters in the header are the hot path. prm_get_name, prm_get_master_port_id, prm_get_commit_on_shutdown are typed convenience wrappers.

SQL/URL and propagation. sysprm_validate_change_parameters parses name=val;... into a SYSPRM_ASSIGN_VALUE list with policy checks. sysprm_make_default_values reformats names → name=default; strings. sysprm_change_parameter_values applies a list. sysprm_obtain_parameters is the read counterpart. db_set_system_parameters / db_get_system_parameters (compat/db_admin.c) are the SQL-API drivers. xsysprm_change_server_parameters, xsysprm_obtain_server_parameters, xsysprm_get_force_server_parameters, xsysprm_dump_server_parameters, xsysprm_get_pl_context_parameters are the server-side RPC stubs. sysprm_tune_client_parameters is the client post-register hook that pulls PRM_FORCE_SERVER values. sysprm_session_init_session_parameters is the server-side reconcile-at-attach logic.

Wire format. sysprm_pack_sysprm_value / ..._unpack_..._value cover one SYSPRM_VALUE. The sysprm_pack_session_parameters / ..._unpack_... pair handles the per-session array shipped at xboot_register_client. The sysprm_pack_assign_values / ..._unpack_... pair handles the change-list. sysprm_free_assign_values is the freer.

Diagnostics and teardown. sysprm_dump_parameters and xsysprm_dump_server_parameters, sysprm_print_parameters_for_qry_string (plan-cache key), sysprm_print_parameters_for_ha_repl (HA replication), prm_print (per-slot formatter), prm_rewrite_int_list, plus the debug-build sanity checks sysprm_check_id_order and prm_check_parameters. sysprm_final is the process-shutdown walker.

SymbolFile / lines
enum param_id / PARAM_IDsystem_parameter.h:97-534
Static / dynamic flag macrossystem_parameter.h:606-646
SYSPRM_DATATYPE / SYSPRM_VALUE / SESSION_PARAM / SYSPRM_ASSIGN_VALUEsystem_parameter.h:539-578
SYSPRM_PARAM (slot struct)system_parameter.h:706-721
PRM_SERVER_SESSION macrosystem_parameter.h:681-683
Inline prm_get_*_value getterssystem_parameter.h:819-947
PRM_NAME_* macrossystem_parameter.c:120-797
NULL_SYSPRM_PARAM_VALUEsystem_parameter.c:1007
SYSPRM_PARAM prm_Def[]system_parameter.c:1010-5331
prm_Def_session_idx[]system_parameter.c:5333
MAX_SYSTEM_PARAMS / num_session_parameterssystem_parameter.c:5335-5339
PARAM_VALUE_SHARE[]system_parameter.c:5341-5374
cached_session_parameterssystem_parameter.c:5383
Keyword tables (boolean_words, …)system_parameter.c:5399-5530
sysprm_dump_parameterssystem_parameter.c:5847
sysprm_set_er_log_filesystem_parameter.c:5919
sysprm_check_id_ordersystem_parameter.c:5963
sysprm_load_and_init_internalsystem_parameter.c:6015
sysprm_load_and_init / ..._client / _reload_and_initsystem_parameter.c:6285-6315
prm_load_by_sectionsystem_parameter.c:6350
prm_read_and_parse_ini_filesystem_parameter.c:6574
prm_check_environmentsystem_parameter.c:7091
sysprm_validate_change_parameterssystem_parameter.c:7196
sysprm_make_default_valuessystem_parameter.c:7322
sysprm_change_parameter_valuessystem_parameter.c:7402
sysprm_obtain_parameterssystem_parameter.c:7983
xsysprm_change_server_parameterssystem_parameter.c:8124
xsysprm_obtain_server_parameterssystem_parameter.c:8140
xsysprm_get_force_server_parameterssystem_parameter.c:8165
xsysprm_get_pl_context_parameterssystem_parameter.c:8223
sysprm_get_rangesystem_parameter.c:8380
prm_setsystem_parameter.c:9167
sysprm_set_value / ..._internalsystem_parameter.c:9183-9362
sysprm_find_shared_system_parametersystem_parameter.c:9366
prm_set_forcesystem_parameter.c:9471
prm_set_default / ..._internalsystem_parameter.c:9489-9589
prm_findsystem_parameter.c:9597
sysprm_finalsystem_parameter.c:9783
prm_tune_parameterssystem_parameter.c:9865
sysprm_tune_client_parameterssystem_parameter.c:10266
prm_set_compoundsystem_parameter.c:10310
prm_get_next_param_valuesystem_parameter.c:10347
prm_get_valuesystem_parameter.c:10486
prm_set_integer_value / ..._bool / ..._float / ..._string / ..._integer_list / ..._bigintsystem_parameter.c:10533-10650
sysprm_pack_session_parameters / ..._unpack_...system_parameter.c:11118-11233
sysprm_pack_assign_values / ..._unpack_...system_parameter.c:11243-11357
sysprm_free_assign_valuessystem_parameter.c:11366
sysprm_session_init_session_parameterssystem_parameter.c:11447
sysprm_get_session_parameters_countsystem_parameter.c:12127
Conf file cubrid.confconf/cubrid.conf:25-75
db_set_system_parameterscompat/db_admin.c:2879
db_get_system_parameterscompat/db_admin.c:3014

A few details surface only by reading prm_Def[] against subsystem call sites:

  • PRM_ID_OPTIMIZER_RESERVE_02 … _20 are placeholder slots carrying PRM_OBSOLETED. New optimizer parameters reuse a reserved slot rather than bumping PRM_LAST_ID, keeping the enum-id ↔ network-protocol mapping stable across versions.
  • PRM_ID_HA_MODE_FOR_SA_UTILS_ONLY is a back-channel: under SA-mode prm_tune_parameters forcibly resets ha_mode to OFF, but stashes the configured value here so the utility’s HA-aware logging path can still find it. There is no user-facing way to set it directly.
  • HA conf is loaded twice. The first prm_read_and_parse_ini_file call passes SYSPRM_IGNORE_HA to skip PRM_FOR_HA; the second passes the opposite to read cubrid_ha.conf. This is the only place the loader runs twice in one boot.
  • PRM_FOR_HA_CONTEXT is replication-aware: when a master changes such a parameter via SET SYSTEM PARAMETERS, the HA log applier walks the table and reapplies on the replica.
  • PRM_FOR_PL_CONTEXT is forwarded once at PL/SP attach via xsysprm_get_pl_context_parameters. Subsequent changes to those parameters are not pushed to the JVM; callers that need live updates must rebind.
  • Compound vs. share — the lines blur. PRM_COMPOUND sets several others by side-effect; PARAM_VALUE_SHARE binds two ids to the same storage. sysprm_set_value orders them as “write → alias propagate → compound cascade”. A parameter that is both compound and half of a share pair would loop without the recursive guard in prm_set_compound; today no parameter is both, so the case is unexercised.
  • Environment outranks conf. prm_check_environment runs after the conf file, so an exported CUBRID_DATA_BUFFER_SIZE=1G beats data_buffer_size=512M in cubrid.conf. This matches Postgres’ PGOPTIONS precedence and inverts the MySQL convention.
  • prm_tune_parameters mutates without PRM_SET. The tuner calls prm_set with set_flag=false, so a tuned value changes but the PRM_DEFAULT_USED flag remains, and the dump routine still describes the parameter as “default”. This is deliberate — the tuner is engine policy, not user intent — but it means the default_marker in a dump is not always the compile-time default.
  • PRM_FOR_QRY_STRING plan-cache key cost. Iterating the whole table to build the plan-cache key string is hot; a precomputed bitmap would help, but no evidence of that work in tree.
  • Should PRM_FOR_HA_CONTEXT parameters be auto-derived? Today every HA-context parameter is hand-flagged. The real invariant is “any parameter that affects deterministic SQL semantics must be replicated”; encoding that automatically would be brittle but valuable.
  • Are _RESERVE slots required for ABI? Their cost is table space, dump-output noise, and developer attention; the alternative (append-and-bump-version) is what most other engines do. CUBRID’s rationale is defensible but a one-time format break could drop the slots.
  • Reload + session parameters. sysprm_reload_and_init resets only the global table; sessions that have local copies are not notified. A reload of PRM_ID_LK_TIMEOUT_SECS is invisible to existing sessions until they SET SYSTEM PARAMETERS or reconnect. Whether that is intended is uncommented.
  • force_value × PRM_FOR_SESSION is fragile. A force value is reapplied at the tail of every load via prm_set against the global slot, but for a session parameter that path takes the per-session detour and lands on the current thread’s session, not the global default. No caller currently exercises this case.
  • src/base/system_parameter.c — the implementation (12 272 lines).
  • src/base/system_parameter.h — public types, inline getters, flag definitions (957 lines).
  • conf/cubrid.conf — the user-facing default configuration (75 lines, sample-only; the install ships a longer one).
  • src/compat/db_admin.cdb_set_system_parameters, db_get_system_parameters, the SQL-facing wrappers.
  • Subsystem call sites grep-able via prm_get_*_value (PRM_ID_*), e.g. pgbuf_Pool.num_buffers = prm_get_integer_value (PRM_ID_PB_NBUFFERS); in src/storage/page_buffer.c, wait_msecs = prm_get_integer_value (PRM_ID_LK_TIMEOUT_SECS); in src/transaction/lock_manager.c, log_No_logging = prm_get_bool_value (PRM_ID_LOG_NO_LOGGING); in src/transaction/log_manager.c.
  • Adjacent docs: cubrid-boot.md for how the loader is wired into process startup; cubrid-server-session.md for the SESSION_STATE::session_parameters array and the xsession_check_session reconnect path.