Skip to content

CUBRID csql — Interactive SQL Client, Two-Binary Launcher Split, Session-Command Prefix, and Single-Line Execution Mode

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:

  1. Connection model. The client either dials a running server over the network (PostgreSQL psql → libpq → TCP/UDS; MySQL mysql → client library → TCP/UDS; Oracle sqlplus → 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-mode vs --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).

  2. Statement-completeness detection. SQL has no fixed statement terminator until the user types one (or the implementation imposes one). PostgreSQL psql requires ; and uses libpq’s tokenizer to detect when the buffer ends inside a string, comment, or dollar-quoted block. MySQL mysql uses ; (or \g / \G) and a similar in-block tracker. Oracle sqlplus ships 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.

  3. 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”: \ for psql and mysql, @ / & / : for sqlplus, : 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.

ClientLinkageStatement boundaryMeta-command prefixNotable trait
PostgreSQL psqllibpq, network only; with in-block tracking\ (\d, \timing, \copy)readline / libedit; pset for output formatting
MySQL mysqlclient library, network only; or \g/\G\ for system commands; ; ends statement--batch for non-interactive output; --raw to suppress escaping
Oracle sqlplusOCI, network only; or / (PL/SQL)@, &, :, SET, SHOWthe SET family controls 30+ output / behaviour knobs
SQL Server sqlcmdODBC / TDS, network onlyGO on its own line:!, :r, :setvarbatches end on GO, not ;
SQLite shellembedded SQLite library; with in-block tracking. (.tables, .mode, .import)the same binary embeds the engine; no client/server choice
CUBRID csqldlopen 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.

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.c
GETOPT_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 csql against cubridsa or cubridcs, the entire session uses that engine library. There’s no in-session switch to the other mode; reconnect would require restarting the binary. (The ;connect session 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 both libcubridsa and libcubridcs with different conditional defines (SA_MODE vs CS_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).

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 conflict

The 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 — defaults
csql_arg.auto_commit = true; // statements commit per-statement
csql_arg.single_line_execution = true; // submit on first complete statement
csql_arg.string_width = 0; // unbounded column truncation
csql_arg.trigger_action_flag = true; // triggers fire normally
csql_arg.column_delimiter = -1; // unset; -q sets ',' if also unset
csql_arg.column_enclosure = -1; // unset; -q sets '\'' if also unset
csql_arg.midxkey_print = false; // hidden ;midxkey toggle

After the launcher resolves the symbol, the csql() function (entry point at csql.c:3161) drives the rest:

// csql — csql.c
csql_exit_init (); // setjmp env for severe errors
if (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 audit
logddl_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 loop

The 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.

The interactive loop lives in start_csql (csql.c:665). Three input paths converge here:

  1. -c "stmt" — a single statement passed on the CLI; executed once via csql_execute_statements (..., STRING_INPUT, command, -1), then csql_exit_session.
  2. -i file in non-single-line mode — the entire file is fed to csql_execute_statements (..., FILE_INPUT, csql_Input_fp, -1) in one shot.
  3. 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 swapcsql_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.

The ;-prefixed commands are defined in a single table in csql_session.c:

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_no walks 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 ;ru is ;run (single match), ;c is ambiguous (clear, commit, checkpoint, cd, connect, column-width).
  • Empty-tail special case. A bare ; (no name after it) maps to S_CMD_XRUN — execute the buffer and clear it. This is the most common operation, optimized to one keystroke.
  • ! as ;shell alias. The single character ! after the prefix invokes the shell; ;! and ;shell are equivalent.
  • CMD_CHECK_CONNECT flag. Commands flagged with this bit are refused (in CS_MODE only) when db_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 flagged CMD_EMPTY_FLAG and run regardless of connection state.
  • .hist / .clear_hist / .dump_hist / .x_hist are 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).

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.

The four mutually-exclusive output styles correspond to the launcher’s check_output_style validation:

StyleFlagFormat
Default (column)(none)Aligned columns with header bar; truncation at --string-width; the human-readable mode
Line-output--line-output / -lOne column per line per row (column = value); useful for very wide rows
Plain-pNo headers, no separators, columns space-separated; for shell pipelines
Query-qCSV-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 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-vacuum is 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-catalog is a separate, narrower variant that forces SA mode and requires -i or -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 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:

  1. 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.
  2. Statement-execution histograms;.hist enables capture of per-statement performance counters (lock waits, page accesses, query plan switches); ;.dump_hist writes them out; ;.clear_hist resets; ;.x_hist is 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.

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.

SymbolRole
mainEntry point; getopt loop populating CSQL_ARGUMENT; validation chain; dlopen/dlsym of the engine library; calls resolved csql()
utility_csql_usageLoads cubridsa and dlsyms csql_get_message; renders usage from message catalog
utility_csql_printVariadic printer for non-fatal launcher messages from the message catalog
csql_option[]The 27-entry GETOPT_LONG table
SymbolRole
csqlLibrary 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_initInstalls the setjmp longjmp env for fatal-exit shortcuts
csql_exit / csql_exit_session / csql_exit_cleanupLayered exit paths; the session form commits or rolls back per auto_commit
start_csqlRead-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_completePredicates over the lexer state
csql_edit_contents_append / _clear / _finalizeManages the in-memory editor buffer between submissions
csql_execute_statementsSubmission 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
SymbolRole
csql_Session_cmd_table[]The 47-entry table of ;-commands
csql_get_session_cmd_noPrefix-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_handlerSIGPIPE 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)”
SymbolRole
csql_resultsTop-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 formattersPer-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
SymbolRole
csql_walk_statementThe stateful lexer (called per line in start_csql)
csql_pipe_to_pager / csql_pipe_handlerPager integration
csql_get_message / csql_get_help_messageMessage catalog accessors
csql_check_server_downPeriodic probe used during the read loop to detect server crashes and switch to offline prompt
SymbolPath
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_csqlsrc/executables/csql.c:665
csql_exit_initsrc/executables/csql.c:3032
csql_print_buffersrc/executables/csql.c:1927
csql_print_databasesrc/executables/csql.c:2592
csql_display_tracesrc/executables/csql.c:3723
display_buffersrc/executables/csql.c:438
csql_Session_cmd_table[]src/executables/csql_session.c:75
csql_get_session_cmd_nosrc/executables/csql_session.c:150
csql_help_menusrc/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.

  • 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 for dlopen; engine checks defend against the rare path where a caller invokes the csql symbol directly (e.g., from a custom embedded tool).
  • CMD_CHECK_CONNECT is #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 ;commit issued in SA-mode csql always reaches the engine; the engine itself decides if there’s a tran to commit.
  • db_Connect_status is 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 — once dlopen-ed, a library is permanent for the life of the launcher invocation. Switching modes requires exec()-ing a fresh csql.
  • Hidden ;midxkey is in the table but excluded from ;help rendering. It toggles internal multi-index-key trace output and is intended for engine developers, not end users.
  • stifle_history is on the readline side, not on the engine-side query history. The two are independent; ;historyread / ;historylist operate on the editor buffer history, not on the shell-side command history readline maintains.
  • tty_fp in start_csql is set to csql_Error_fp only when interactive; in batch (-i or piped stdin) it’s NULL, which suppresses prompt and conversational messages. The doc-comment on start_csql is the canonical reference for this.
  • 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_complete returns true on the first complete statement, even if the line contains more after it. The behaviour for a typed line like SELECT 1; SELECT 2; is to execute the first and discard the rest (the csql_edit_contents_clear after csql_execute_statements drops 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_output style. The pager forwarding in csql_pipe_to_pager is unconditional on terminal output; piping -q output through the pager produces double formatting. Operators normally pair -q with --no-pager, but the launcher doesn’t enforce this.
  • Session-command help internationalisation. csql_help_menu reads 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.
  • src/executables/csql_launcher.c — argv parser, validation, library loader
  • src/executables/csql.c — engine entry, main loop, session-command dispatcher, buffer management, exit handling
  • src/executables/csql.hCSQL_ARGUMENT struct, SESSION_CMD enum, message constants, function prototypes
  • src/executables/csql_session.c — session-command table and the prefix-match resolver, ;help rendering, killtran formatting
  • src/executables/csql_result.c — result iterator and pagination
  • src/executables/csql_result_format.cDB_VALUE to text formatters, per-type rendering, output style application
  • src/executables/csql_support.c — the stateful per-line lexer (csql_walk_statement), pager integration, message-catalog helpers, server-down probe
  • src/executables/AGENTS.md — agent guide naming the csql responsibilities and the build-target mapping (cs/CMakeLists.txt for 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 csql client_type), cubrid-monitoring.md (the PERFMON facility behind ;.hist), cubrid-broker.md (broker process; CS-mode csql talks through the broker to reach cub_server), cubrid-cdc.md / cubrid-ha-replication.md (the contexts where --write-on-standby matters)