CUBRID csql — Interactive SQL Client, Two-Binary Launcher Split, Session-Command Prefix, and Single-Line Execution Mode
Theoretical Background
Section titled “Theoretical Background”An interactive SQL client is the program a database operator uses to type SQL at a prompt, watch results scroll past, and incrementally build up a session of state (uncommitted transactions, environment settings, prepared statements). Every relational engine ships one; the design surface is small but the design choices are revealing of the engine’s connection model and runtime philosophy.
Three axes dominate the design space:
-
Connection model. The client either dials a running server over the network (PostgreSQL
psql→ libpq → TCP/UDS; MySQLmysql→ client library → TCP/UDS; Oraclesqlplus→ OCI → SQL*Net) or links the engine directly into its own process (SQLite shell, Berkeley DB shell). CUBRID supports both for the same client binary — a configuration switch (--SA-modevs--CS-mode) at launch time picks which engine library to load and consequently whether the client process is itself the database engine (standalone) or a network peer (client-server). -
Statement-completeness detection. SQL has no fixed statement terminator until the user types one (or the implementation imposes one). PostgreSQL
psqlrequires;and uses libpq’s tokenizer to detect when the buffer ends inside a string, comment, or dollar-quoted block. MySQLmysqluses;(or\g/\G) and a similar in-block tracker. Oraclesqlplusships SQL on;or/on a line by itself for PL/SQL blocks. CUBRID exposes both modes: a buffered editor model where the user types until the buffer is committed by an explicit;run/;xrun, and a single-line execution mode that auto-submits each complete statement as soon as it lexes cleanly outside any string / comment / identifier block. -
Out-of-band commands. Every interactive client reserves a command-prefix character that switches from “interpret as SQL” to “interpret as client-local meta-command”:
\forpsqlandmysql,@/&/:forsqlplus,:for SQLite,.for SQLite (newer). CUBRID uses;— the same character ordinary SQL uses as a statement terminator — and disambiguates purely by first-character position: if a typed line starts with;, it’s a session command; otherwise it’s SQL.
The csql client carries a fourth concern most modern clients have
shed: it doubles as the system administrator’s shell when invoked
with --sysadm. The same binary that everyday users connect with is
also what the DBA runs to rebuild the catalog or commit on a standby
replica — gated by argv flags and a hard check that the user is DBA.
Common DBMS Design
Section titled “Common DBMS Design”| Client | Linkage | Statement boundary | Meta-command prefix | Notable trait |
|---|---|---|---|---|
| PostgreSQL psql | libpq, network only | ; with in-block tracking | \ (\d, \timing, \copy) | readline / libedit; pset for output formatting |
| MySQL mysql | client library, network only | ; or \g/\G | \ for system commands; ; ends statement | --batch for non-interactive output; --raw to suppress escaping |
| Oracle sqlplus | OCI, network only | ; or / (PL/SQL) | @, &, :, SET, SHOW | the SET family controls 30+ output / behaviour knobs |
| SQL Server sqlcmd | ODBC / TDS, network only | GO on its own line | :!, :r, :setvar | batches end on GO, not ; |
| SQLite shell | embedded SQLite library | ; with in-block tracking | . (.tables, .mode, .import) | the same binary embeds the engine; no client/server choice |
| CUBRID csql | dlopen of cubridsa or cubridcs per --SA-mode / --CS-mode | ;run / ;xrun (buffered) or auto-detect (single-line mode) | ; (session-command prefix) | dual SA/CS launcher; the same binary runs as embedded engine or network client |
CUBRID’s two distinguishing traits — the dual SA/CS launcher and the session-command prefix sharing a character with SQL’s terminator — both descend from its OODB heritage where the line between “run the engine” and “talk to the engine” was deliberately blurred for the DBA audience.
CUBRID’s Approach
Section titled “CUBRID’s Approach”Two-binary launcher pattern
Section titled “Two-binary launcher pattern”The user invokes a single binary (csql), but two object files are
involved: csql_launcher.c is the executable’s entry point and the
real work lives in csql.c, compiled into one of two shared libraries
(cubridsa for standalone, cubridcs for client-server). The
launcher’s job is argv parsing + library selection; the actual
csql() function gets dlsym-ed out of whichever library matches
the requested mode.
// main — csql_launcher.cGETOPT_LONG csql_option[] = { {CSQL_SA_MODE_L, 0, 0, CSQL_SA_MODE_S}, {CSQL_CS_MODE_L, 0, 0, CSQL_CS_MODE_S}, {CSQL_USER_L, 1, 0, CSQL_USER_S}, {CSQL_PASSWORD_L, 1, 0, CSQL_PASSWORD_S}, /* ... 27 total options ... */ {VERSION_L, 0, 0, VERSION_S}, {0, 0, 0, 0}};
/* ... while (getopt_long(...)) populates csql_arg ... */
if (csql_arg.sa_mode) { utility_load_library (&util_library, LIB_UTIL_SA_NAME); // libcubridsa} else { utility_load_library (&util_library, LIB_UTIL_CS_NAME); // libcubridcs}
utility_load_symbol (util_library, (DSO_HANDLE *) (&csql), "csql");error = (*csql) (argv[0], &csql_arg);This split has two consequences:
- Mode selection is final at launch. Once the launcher resolves
csqlagainstcubridsaorcubridcs, the entire session uses that engine library. There’s no in-session switch to the other mode; reconnect would require restarting the binary. (The;connectsession command can target a different database, but not switch SA↔CS.) - Two
csql()symbols ship in the install. The same C source (csql.c) is compiled into bothlibcubridsaandlibcubridcswith different conditional defines (SA_MODEvsCS_MODE). The function bodies are largely identical; the differences are guarded by#if defined(SA_MODE)blocks (e.g., the connect/disconnect paths, the auth bypass for sysadm mode, the locator skip-vacuum toggle).
CLI option dispatch and validation
Section titled “CLI option dispatch and validation”The launcher’s getopt_long switch populates a flat CSQL_ARGUMENT
struct (declared in csql.h), and after the parse loop a chain of
validation rules rejects illegal combinations before any library is
loaded:
// csql_launcher.c — validation rules (paraphrased)if (csql_arg.plain_output) check_output_style++;if (csql_arg.query_output) check_output_style++;if (csql_arg.loaddb_output) check_output_style++;if (check_output_style > 1) goto print_usage; // mutually exclusive
if (csql_arg.sysadm_rebuild_catalog) { if (!csql_arg.sa_mode) goto print_usage; // rebuild requires SA if (in_file == NULL && command == NULL) goto print_usage; // and -i or -c}
if ((csql_arg.sysadm || csql_arg.sysadm_rebuild_catalog) && (csql_arg.user_name == NULL || strcasecmp (csql_arg.user_name, "DBA"))) goto print_usage; // sysadm = DBA only
if (csql_arg.sysadm == false && csql_arg.write_on_standby == true) goto print_usage; // --write-on-standby needs --sysadm
if (csql_arg.sa_mode && (csql_arg.cs_mode || csql_arg.write_on_standby)) goto print_usage; // can't mix SA with CS-only flags
if (!csql_arg.sa_mode && csql_arg.skip_vacuum) goto print_usage; // --skip-vacuum is SA-only
if (explicit_single_line && csql_arg.single_line_execution == false) goto print_usage; // -s and --no-single-line conflictThe validation is intentionally defensive: every illegal combination
is detected before db_restart_ex() runs, so a user mistake doesn’t
cost a database connection attempt.
The CSQL_ARGUMENT defaults set in the launcher are the policy
record:
// csql_launcher.c — defaultscsql_arg.auto_commit = true; // statements commit per-statementcsql_arg.single_line_execution = true; // submit on first complete statementcsql_arg.string_width = 0; // unbounded column truncationcsql_arg.trigger_action_flag = true; // triggers fire normallycsql_arg.column_delimiter = -1; // unset; -q sets ',' if also unsetcsql_arg.column_enclosure = -1; // unset; -q sets '\'' if also unsetcsql_arg.midxkey_print = false; // hidden ;midxkey toggleEngine entry: csql() function
Section titled “Engine entry: csql() function”After the launcher resolves the symbol, the csql() function
(entry point at csql.c:3161) drives the rest:
// csql — csql.ccsql_exit_init (); // setjmp env for severe errorsif (setjmp (csql_Exit_env)) { // ... longjmp here from anywhere csql_exit_cleanup (); logddl_destroy (); return csql_Exit_status;}
utility_initialize (); // message catalog
/* set up prompt: "csql> " or "csql_sysadm> " */
if (csql_arg->in_file_name != NULL) csql_Input_fp = fopen (csql_arg->in_file_name, "r");
if (csql_arg->in_file_name == NULL && isatty (fileno (stdin))) csql_Is_interactive = true;
er_init ("./csql.err", ER_NEVER_EXIT); // error log file
/* register SIGABRT/SIGILL/SIGFPE/SIGBUS/SIGSEGV/SIGSYS */
/* compute client_type from sysadm / read_only / skip_vacuum flags */client_type = DB_CLIENT_TYPE_CSQL;if (csql_arg->sysadm) client_type = DB_CLIENT_TYPE_ADMIN_CSQL;if (csql_arg->write_on_standby) client_type = DB_CLIENT_TYPE_ADMIN_CSQL_WOS;if (csql_arg->skip_vacuum) client_type = DB_CLIENT_TYPE_SKIP_VACUUM_ADMIN_CSQL;if (csql_arg->read_only) client_type = DB_CLIENT_TYPE_READ_ONLY_CSQL;
if (db_restart_ex (argv0, db_name, user_name, passwd, NULL, client_type) != NO_ERROR) { /* if interactive and password not supplied, prompt and retry once */ p = getpass (csql_get_message (CSQL_PASSWD_PROMPT_TEXT)); /* ... db_restart_ex retry ... */}
logddl_init (APP_NAME_CSQL); // DDL auditlogddl_check_ddl_audit_param ();db_set_client_ip_addr (ip_addr);
/* if -A: db_disable_trigger() *//* if --sysadm and DBA: AU_DISABLE(save) — bypass authorisation */
start_csql (csql_arg); // the read loopThe client_type matters: it’s recorded server-side in the
session table (see cubrid-server-session.md) and is what HA
replication, locator vacuum, and the lock manager use to decide
whether this connection is permitted to write on a standby, can skip
vacuum, etc. The same client binary therefore advertises different
“who am I” identities depending on its launch flags.
The setjmp/longjmp exit environment is the global escape hatch:
any code in csql can call csql_exit (status), which longjmps back
to the top and goes through cleanup. This avoids needing every level
to thread error returns up to main.
Read-execute-print loop (start_csql)
Section titled “Read-execute-print loop (start_csql)”The interactive loop lives in start_csql (csql.c:665). Three
input paths converge here:
-c "stmt"— a single statement passed on the CLI; executed once viacsql_execute_statements (..., STRING_INPUT, command, -1), thencsql_exit_session.-i filein non-single-line mode — the entire file is fed tocsql_execute_statements (..., FILE_INPUT, csql_Input_fp, -1)in one shot.- Interactive or single-line mode — the main
for(line_no=1;;line_no++)loop reading one line at a time.
The interactive branch uses GNU readline() on Unix (with
stifle_history (PRM_ID_CSQL_HISTORY_NUM) to bound history) and
fgets() on Windows. Each line is then dispatched:
// start_csql — csql.c (paraphrased)for (line_no = 1; ; line_no++) { prompt = (db_Connect_status == DB_CONNECTION_STATUS_CONNECTED) ? csql_Prompt : csql_Prompt_offline; // "csql> " vs "!csql> "
line_read = readline (prompt); // or fgets()
/* trim '\n' and '\r' */
if (CSQL_SESSION_COMMAND_PREFIX (line_read[0]) // first char is ';' && (csql_Is_interactive || !is_in_block)) { ret = csql_do_session_cmd (line_read, csql_arg); // dispatch ;-cmd if (ret == DO_CMD_EXIT) csql_exit_session (0, true); if (ret == DO_CMD_FAILURE) goto error_continue; continue; }
csql_edit_contents_append (line_read, read_whole_line); // accumulate
if (feof (csql_Input_fp)) { csql_execute = true; // end-of-input: flush is_in_block = false; } else { csql_walk_statement (line_read); // lex this line is_in_block = csql_is_statement_in_block (); // updated by walker
if (csql_arg->single_line_execution && read_whole_line == true && csql_is_statement_complete ()) { csql_execute = true; // single-line submit } }
if (csql_execute) { csql_execute_statements (csql_arg, EDITOR_INPUT, NULL, line_no); csql_edit_contents_clear (); }}csql_walk_statement is a stateful lexer that runs incrementally
across line boundaries; it updates flags about whether the buffer is
currently inside an unterminated string, an unterminated /* ... */
comment, or an unterminated identifier ("..."). The
is_in_block check is what makes it safe to type a multi-line string
literal that contains a ; — single-line execution waits for the
string to close.
The prompt swap — csql_Prompt vs csql_Prompt_offline — is the
visible signal of a dropped connection: csql> becomes !csql> when
db_Connect_status reports DB_CONNECTION_STATUS_NOT_CONNECTED,
typically after a server crash or ;restart.
Session-command dispatch table
Section titled “Session-command dispatch table”The ;-prefixed commands are defined in a single table in
csql_session.c:
static SESSION_CMD_TABLE csql_Session_cmd_table[] = { /* File stuffs */ {"read", S_CMD_READ, CMD_EMPTY_FLAG}, {"write", S_CMD_WRITE, CMD_EMPTY_FLAG}, {"append", S_CMD_APPEND, CMD_EMPTY_FLAG}, {"print", S_CMD_PRINT, CMD_EMPTY_FLAG}, {"shell", "!", S_CMD_SHELL, CMD_EMPTY_FLAG}, // !; alias {"cd", S_CMD_CD, CMD_EMPTY_FLAG}, {"exit", S_CMD_EXIT, CMD_EMPTY_FLAG}, /* Edit stuffs */ {"clear", S_CMD_CLEAR, CMD_EMPTY_FLAG}, {"edit", S_CMD_EDIT, CMD_EMPTY_FLAG}, {"list", S_CMD_LIST, CMD_EMPTY_FLAG}, /* Command stuffs */ {"run", S_CMD_RUN, CMD_CHECK_CONNECT}, {"xrun", S_CMD_XRUN, CMD_CHECK_CONNECT}, {"commit", S_CMD_COMMIT, CMD_CHECK_CONNECT}, {"rollback", S_CMD_ROLLBACK, CMD_CHECK_CONNECT}, {"autocommit", S_CMD_AUTOCOMMIT, CMD_EMPTY_FLAG}, {"checkpoint", S_CMD_CHECKPOINT, CMD_CHECK_CONNECT}, {"killtran", S_CMD_KILLTRAN, CMD_CHECK_CONNECT}, {"restart", S_CMD_RESTART, CMD_EMPTY_FLAG}, /* Environment stuffs */ {"shell_cmd", S_CMD_SHELL_CMD, CMD_EMPTY_FLAG}, {"editor_cmd", S_CMD_EDIT_CMD, CMD_EMPTY_FLAG}, {"print_cmd", S_CMD_PRINT_CMD, CMD_EMPTY_FLAG}, {"pager_cmd", S_CMD_PAGER_CMD, CMD_EMPTY_FLAG}, {"nopager", S_CMD_NOPAGER_CMD, CMD_EMPTY_FLAG}, {"formatter_cmd", S_CMD_FORMATTER_CMD,CMD_EMPTY_FLAG}, {"column-width", S_CMD_COLUMN_WIDTH, CMD_EMPTY_FLAG}, {"string-width", S_CMD_STRING_WIDTH, CMD_EMPTY_FLAG}, {"set", S_CMD_SET_PARAM, CMD_CHECK_CONNECT}, {"get", S_CMD_GET_PARAM, CMD_CHECK_CONNECT}, {"plan", S_CMD_PLAN_DUMP, CMD_CHECK_CONNECT}, {"echo", S_CMD_ECHO, CMD_EMPTY_FLAG}, {"date", S_CMD_DATE, CMD_EMPTY_FLAG}, {"time", S_CMD_TIME, CMD_EMPTY_FLAG}, {"line-output", S_CMD_LINE_OUTPUT, CMD_EMPTY_FLAG}, {".hist", S_CMD_HISTO, CMD_EMPTY_FLAG}, {".clear_hist", S_CMD_CLR_HISTO, CMD_EMPTY_FLAG}, {".dump_hist", S_CMD_DUMP_HISTO, CMD_EMPTY_FLAG}, {".x_hist", S_CMD_DUMP_CLR_HISTO, CMD_EMPTY_FLAG}, /* Help stuffs */ {"help", S_CMD_HELP, CMD_EMPTY_FLAG}, {"schema", S_CMD_SCHEMA, CMD_CHECK_CONNECT}, {"database", S_CMD_DATABASE, CMD_CHECK_CONNECT}, {"trigger", S_CMD_TRIGGER, CMD_CHECK_CONNECT}, {"info", S_CMD_INFO, CMD_EMPTY_FLAG}, /* History stuffs */ {"historyread", S_CMD_HISTORY_READ, CMD_EMPTY_FLAG}, {"historylist", S_CMD_HISTORY_LIST, CMD_EMPTY_FLAG},
{"trace", S_CMD_TRACE, CMD_CHECK_CONNECT}, {"singleline", S_CMD_SINGLELINE, CMD_EMPTY_FLAG}, {"connect", S_CMD_CONNECT, CMD_EMPTY_FLAG}, {"midxkey", S_CMD_MIDXKEY, CMD_EMPTY_FLAG}, // hidden, not in ;help {"server-output", S_CMD_SERVER_OUTPUT,CMD_CHECK_CONNECT}};The table is the complete dot-command surface — 47 entries across seven families. A few design notes worth pulling out:
- Prefix matching with longest-exact-wins.
csql_get_session_cmd_nowalks the table and counts case-insensitive prefix matches; if exactly one entry matches, it wins. If multiple entries match,CSQL_ERR_SESS_CMD_AMBIGUOUS. This means;ruis;run(single match),;cis ambiguous (clear,commit,checkpoint,cd,connect,column-width). - Empty-tail special case. A bare
;(no name after it) maps toS_CMD_XRUN— execute the buffer and clear it. This is the most common operation, optimized to one keystroke. !as;shellalias. The single character!after the prefix invokes the shell;;!and;shellare equivalent.CMD_CHECK_CONNECTflag. Commands flagged with this bit are refused (inCS_MODEonly) whendb_Connect_status != DB_CONNECTION_STATUS_CONNECTED. The flag covers all DDL-class commands (run, commit, schema, set/get param, plan, trace, server-output) and the transaction commands (commit, rollback, checkpoint, killtran). DDL-adjacent or pure local commands (cd, edit, autocommit, history) are flaggedCMD_EMPTY_FLAGand run regardless of connection state..hist/.clear_hist/.dump_hist/.x_histare the outliers — they have leading dots in their names (so they’re invoked as;.hist) because they collide with the legacy PERFMON-style histogram namespace. They dump per-call statistics for tuning runs.
csql_help_menu() (csql_session.c:206) renders the help text from
the message catalog, deliberately omitting midxkey (hidden flag —
internal only).
Statement-completeness tracking
Section titled “Statement-completeness tracking”csql_walk_statement (in csql_support.c) is the per-line lexer
called from start_csql. It maintains state across calls:
- Whether the current position is inside a string literal (
'...',"...", or backtick) — and if so, what closes it. - Whether the current position is inside a comment (
-- ...,/* ... */). - Whether the current position is inside an identifier delimited by
[ ](Microsoft-style) or backticks (MySQL-style).
csql_is_statement_in_block() returns true if any of these are
unterminated; csql_is_statement_complete() returns true when none
of them are open and the buffer ends in a ; outside any block.
This is what makes single-line execution safe across multi-line SQL:
a CREATE PROCEDURE body with ;s inside a '...' string won’t
trigger early submission, because the string-block flag stays set
until the closing quote.
Output styles
Section titled “Output styles”The four mutually-exclusive output styles correspond to the
launcher’s check_output_style validation:
| Style | Flag | Format |
|---|---|---|
| Default (column) | (none) | Aligned columns with header bar; truncation at --string-width; the human-readable mode |
| Line-output | --line-output / -l | One column per line per row (column = value); useful for very wide rows |
| Plain | -p | No headers, no separators, columns space-separated; for shell pipelines |
| Query | -q | CSV-style; column-delimiter (default ,) and column-enclosure (default ') configurable |
| Loaddb | --loaddb-output | '-enclosed, space-separated; produces a file loaddb can re-import |
The launcher disables plain, query, and loaddb styles when the
session is interactive (no -c and no -i), reverting to default
column output regardless of the flag — those styles only make sense
for batch / pipeline use.
The format itself lives in csql_result_format.c (~1980 lines). The
header pipeline is:
db_Query_result → csql_results() (csql_result.c) → for each row: csql_db_value_as_string() (csql_result_format.c) → per-DB_TYPE formatter (string, number, date, set, OID, ...)csql_db_value_as_string is the universal DB_VALUE → text
function; same code path is used by all four output styles, with
the formatter parameters (delimiter, enclosure, width) varying.
Sysadm mode
Section titled “Sysadm mode”--sysadm puts csql into a privileged shell. The launcher restricts
it to DBA (case-insensitive on the user name); the engine entry
applies further changes:
- Client type becomes
DB_CLIENT_TYPE_ADMIN_CSQL(or one of the more specialised admin variants if--write-on-standby/--skip-vacuumis also set). AU_DISABLE (save)is called to bypass authorisation entirely, but only if the connecting user actually is in the DBA group (au_is_dba_group_member (Au_user)).- The prompt becomes
csql_sysadm>so the operator visibly knows the session is privileged. --sysadm-rebuild-catalogis a separate, narrower variant that forces SA mode and requires-ior-c(no interactive use); it’s the catalog reconstruction mode used during cross-version migrations.
--write-on-standby requires --sysadm and is the only way to
mutate state on an HA standby database — used during DR drills and
emergency interventions.
History
Section titled “History”History is GNU readline–backed on Unix (the
#if !defined(WINDOWS) block in start_csql calls
stifle_history (PRM_ID_CSQL_HISTORY_NUM) and using_history()).
Two parallel concepts of “history” coexist:
- Readline command-line history — up-arrow recall of typed lines, persisted across sessions only if the user has shell-side readline config to do so.
- Statement-execution histograms —
;.histenables capture of per-statement performance counters (lock waits, page accesses, query plan switches);;.dump_histwrites them out;;.clear_histresets;;.x_histis dump-then-clear. These ride on top of the engine’s PERFMON facility (cubrid-monitoring.md) and are scoped to the current session.
The two are independent — readline history is purely client-local,
while .hist reads counters from the connected engine over the same
session.
DDL audit logging
Section titled “DDL audit logging”logddl_init (APP_NAME_CSQL) and logddl_check_ddl_audit_param ()
are called early in csql(). When the ddl_audit_log system
parameter is enabled, every DDL statement issued through this csql
session is logged to a per-database audit file with timestamp, user
ID, client IP, and the original SQL text. The DDL audit subsystem is
not csql-specific — broker / CCI also call logddl_* — but csql is a
common point of administrative DDL and therefore typically the
loudest contributor to audit logs.
get_host_ip() and db_set_client_ip_addr() populate the IP that
the audit log records; the same address is also stored in the
server-side session table for SHOW THREADS / SHOW TRANS to
display.
Source Walkthrough
Section titled “Source Walkthrough”Launcher (csql_launcher.c)
Section titled “Launcher (csql_launcher.c)”| Symbol | Role |
|---|---|
main | Entry point; getopt loop populating CSQL_ARGUMENT; validation chain; dlopen/dlsym of the engine library; calls resolved csql() |
utility_csql_usage | Loads cubridsa and dlsyms csql_get_message; renders usage from message catalog |
utility_csql_print | Variadic printer for non-fatal launcher messages from the message catalog |
csql_option[] | The 27-entry GETOPT_LONG table |
Engine entry and main loop (csql.c)
Section titled “Engine entry and main loop (csql.c)”| Symbol | Role |
|---|---|
csql | Library entry; sets up setjmp exit env, opens input file, registers signal handlers, computes client_type, calls db_restart_ex, retries password if needed, calls start_csql |
csql_exit_init | Installs the setjmp longjmp env for fatal-exit shortcuts |
csql_exit / csql_exit_session / csql_exit_cleanup | Layered exit paths; the session form commits or rolls back per auto_commit |
start_csql | Read-execute-print loop; manages prompt, single-line vs. buffered modes, in-block tracking |
csql_walk_statement (in csql_support.c) | Stateful per-line lexer; tracks string / comment / identifier blocks |
csql_is_statement_in_block / csql_is_statement_complete | Predicates over the lexer state |
csql_edit_contents_append / _clear / _finalize | Manages the in-memory editor buffer between submissions |
csql_execute_statements | Submission entrypoint; dispatches by STRING_INPUT / FILE_INPUT / EDITOR_INPUT |
csql_print_buffer | ;list / ;edit / ;print support — renders the current editor buffer |
csql_print_database | ;database support — formatted display of database name and connect state |
csql_display_trace | ;trace support — XASL trace for the most recently executed query |
change_prompt | ;set csql_prompt support — substitutes user-defined format with username |
Session-command dispatch (csql_session.c)
Section titled “Session-command dispatch (csql_session.c)”| Symbol | Role |
|---|---|
csql_Session_cmd_table[] | The 47-entry table of ;-commands |
csql_get_session_cmd_no | Prefix-match resolver returning a SESSION_CMD enum value |
csql_help_menu | ;help rendering from message catalog |
csql_do_session_cmd (in csql.c) | Top-level dispatcher; switch on SESSION_CMD calling per-command handlers |
csql_killtran | ;killtran support — list and selectively abort transactions |
csql_dump_alltran | ;killtran formatter for the listing |
csql_pipe_handler | SIGPIPE handler to keep the pager from killing the session |
Result rendering (csql_result.c, csql_result_format.c)
Section titled “Result rendering (csql_result.c, csql_result_format.c)”| Symbol | Role |
|---|---|
csql_results | Top-level result iterator; handles header, pagination, count |
csql_db_value_as_string (in csql_result_format.c) | The universal DB_VALUE → text formatter; respects width, delimiter, enclosure |
| Per-DB_TYPE formatters | Per-type rendering: numeric, datetime, string, set, sequence, multiset, JSON, OID, BLOB / CLOB locator |
csql_pipe_to_pager (in csql_support.c) | Forks the configured pager (PAGER_CMD) and pipes output into it |
Support helpers (csql_support.c)
Section titled “Support helpers (csql_support.c)”| Symbol | Role |
|---|---|
csql_walk_statement | The stateful lexer (called per line in start_csql) |
csql_pipe_to_pager / csql_pipe_handler | Pager integration |
csql_get_message / csql_get_help_message | Message catalog accessors |
csql_check_server_down | Periodic probe used during the read loop to detect server crashes and switch to offline prompt |
Position hints (as of 2026-05-05)
Section titled “Position hints (as of 2026-05-05)”| Symbol | Path |
|---|---|
main (launcher) | src/executables/csql_launcher.c:114 |
csql_option[] | src/executables/csql_launcher.c:124 |
CSQL_ARGUMENT (struct) | src/executables/csql.h:305 |
csql (engine entry) | src/executables/csql.c:3161 |
start_csql | src/executables/csql.c:665 |
csql_exit_init | src/executables/csql.c:3032 |
csql_print_buffer | src/executables/csql.c:1927 |
csql_print_database | src/executables/csql.c:2592 |
csql_display_trace | src/executables/csql.c:3723 |
display_buffer | src/executables/csql.c:438 |
csql_Session_cmd_table[] | src/executables/csql_session.c:75 |
csql_get_session_cmd_no | src/executables/csql_session.c:150 |
csql_help_menu | src/executables/csql_session.c:206 |
SESSION_CMD (enum, S_CMD_*) | src/executables/csql.h:185 |
Symbol names are the canonical anchor; line numbers are hints
scoped to the updated: date.
Cross-check Notes
Section titled “Cross-check Notes”- Launcher vs. engine validation overlap. Some flag combinations
are checked twice — once in the launcher (before any library is
loaded) and again in the engine entry (
csql()). The duplication is intentional: launcher checks reject the call early and cheaply without paying fordlopen; engine checks defend against the rare path where a caller invokes thecsqlsymbol directly (e.g., from a custom embedded tool). CMD_CHECK_CONNECTis#if defined (CS_MODE)-only. SA mode doesn’t have a “disconnected” state — the engine is in-process — so the connection check is compiled out. A;commitissued in SA-mode csql always reaches the engine; the engine itself decides if there’s a tran to commit.db_Connect_statusis a global, mutated by the engine library (db_restart_ex,db_shutdown) and read by the loop to switch prompts. This works because csql is single-threaded; if csql ever grew a background thread (e.g., for async query progress), this would need a memory barrier or mutex.- The two
csql()symbols are not selectable at runtime within one process — oncedlopen-ed, a library is permanent for the life of the launcher invocation. Switching modes requiresexec()-ing a fresh csql. - Hidden
;midxkeyis in the table but excluded from;helprendering. It toggles internal multi-index-key trace output and is intended for engine developers, not end users. stifle_historyis on the readline side, not on the engine-side query history. The two are independent;;historyread/;historylistoperate on the editor buffer history, not on the shell-side command history readline maintains.tty_fpinstart_csqlis set tocsql_Error_fponly when interactive; in batch (-ior piped stdin) it’sNULL, which suppresses prompt and conversational messages. The doc-comment onstart_csqlis the canonical reference for this.
Open Questions
Section titled “Open Questions”- Replacement for readline on Linux. The build currently links GNU readline; some distros prefer libedit for licensing reasons. Whether csql could be made portable across both (the API is largely compatible) is undocumented.
- Multi-statement single-line execution.
csql_is_statement_completereturns true on the first complete statement, even if the line contains more after it. The behaviour for a typed line likeSELECT 1; SELECT 2;is to execute the first and discard the rest (thecsql_edit_contents_clearaftercsql_execute_statementsdrops the buffer). Whether this is intentional or a bug deserving of an “execute all complete statements on the line” tweak is undecided. - Pager interaction with
query_outputstyle. The pager forwarding incsql_pipe_to_pageris unconditional on terminal output; piping-qoutput through the pager produces double formatting. Operators normally pair-qwith--no-pager, but the launcher doesn’t enforce this. - Session-command help internationalisation.
csql_help_menureads the help text from the message catalog, but the table itself (csql_Session_cmd_table[]) hard-codes English-name prefixes. A locale that wanted Korean/Japanese ;-commands would need a parallel table, which doesn’t exist.
Sources
Section titled “Sources”src/executables/csql_launcher.c— argv parser, validation, library loadersrc/executables/csql.c— engine entry, main loop, session-command dispatcher, buffer management, exit handlingsrc/executables/csql.h—CSQL_ARGUMENTstruct,SESSION_CMDenum, message constants, function prototypessrc/executables/csql_session.c— session-command table and the prefix-match resolver,;helprendering, killtran formattingsrc/executables/csql_result.c— result iterator and paginationsrc/executables/csql_result_format.c—DB_VALUEto text formatters, per-type rendering, output style applicationsrc/executables/csql_support.c— the stateful per-line lexer (csql_walk_statement), pager integration, message-catalog helpers, server-down probesrc/executables/AGENTS.md— agent guide naming the csql responsibilities and the build-target mapping (cs/CMakeLists.txtfor the CS-mode binary)- Adjacent docs:
cubrid-coverage.md(where this fits in the utilities batch),cubrid-server-session.md(server-side session table that records the csqlclient_type),cubrid-monitoring.md(the PERFMON facility behind;.hist),cubrid-broker.md(broker process; CS-mode csql talks through the broker to reachcub_server),cubrid-cdc.md/cubrid-ha-replication.md(the contexts where--write-on-standbymatters)