CUBRID Procedural Language — Section Overview
What this section covers
Section titled “What this section covers”This section describes CUBRID’s procedural language family — the
two stored-procedure surfaces a user can write against. The first is
JavaSP: a Java stored procedure where the user supplies a
pre-compiled .class or .jar and the engine dispatches into it by
reflection. The second is PL/CSQL: an Oracle-dialect procedural
SQL whose source text is parsed, type-checked, lowered to Java,
compiled with javac in memory, and stored as bytecode in the
catalog. Despite the very different user-facing syntax, the two share
almost the entire runtime. Both are persisted in the same three
catalog system classes, both ride the same Unix-domain-socket wire
protocol from cub_server to a sidecar cub_pl JVM process, and
both are dispatched by the same C++ executor and the same Java
ExecuteThread. Only the final step that turns the user’s input into
something callable differs. Read this overview first to understand
the shape of the family; then read the two detail docs to see how
each frontend plugs in.
The PL family architecture
Section titled “The PL family architecture”flowchart TB
subgraph cub_server["cub_server (C/C++ process)"]
catalog["Catalog rows<br/>_db_stored_procedure*<br/>(sp_catalog.cpp)"]
compile_h["pl_compile_handler.cpp<br/>(PL/CSQL DDL only)"]
executor["pl_executor.cpp<br/>session, invoke_java"]
connpool["pl_connection.cpp<br/>connection_pool"]
end
subgraph wire["Wire — UDS or TCP"]
proto["RequestCode-tagged<br/>binary frames<br/>(pl_comm.h)"]
end
subgraph cub_pl["cub_pl (one external JVM)"]
listener["ListenerThread.java<br/>(accept loop)"]
execthread["ExecuteThread.java<br/>(per connection)"]
subgraph javasp_fe["JavaSP frontend"]
target["TargetMethod.java<br/>reflective dispatch"]
cl["ClassLoaderManager<br/>(user JARs)"]
sec["SpSecurityManager"]
end
subgraph plcsql_fe["PL/CSQL frontend"]
antlr["ANTLR<br/>PlcLexer/PlcParser.g4"]
ast["AST<br/>Decl* / Stmt* / Expr*"]
tc["TypeChecker"]
emit["JavaCodeWriter<br/>(emit Java source)"]
javac["MemoryJavaCompiler<br/>(javax.tools)"]
end
end
catalog --- executor
compile_h --> connpool
executor --> connpool
connpool --> proto
proto --> listener
listener --> execthread
execthread -->|INVOKE_SP JavaSP| target
target --> cl
target --> sec
execthread -->|COMPILE PL/CSQL| antlr
antlr --> ast --> tc --> emit --> javac
javac -. bytecode .-> catalog
execthread -->|INVOKE_SP PL/CSQL| target
classDef shared fill:#e8f0ff,stroke:#3060a0;
class catalog,executor,connpool,proto,listener,execthread shared;
The diagram shows the three layers of the family. The blue-shaded nodes are shared between both frontends; everything else is language-specific.
Shared C-side glue (src/sp/). The catalog half lives in
sp_catalog.cpp / sp_catalog.hpp, which writes the sp_info,
sp_arg_info, and sp_code_info structs into the system classes
_db_stored_procedure, _db_stored_procedure_args, and
_db_stored_procedure_code. Attribute names are centralised in
sp_constants.hpp. The transport half lives in pl_connection.cpp
and pl_comm.c: a pooled set of Unix-domain-socket (or TCP)
connections to the sidecar JVM, wrapped in RAII connection_view
handles. The dispatch half lives in pl_executor.cpp and
pl_session.cpp: every stored-procedure call — JavaSP or PL/CSQL —
is packaged into an invoke_java message by the C++ executor
class, and the per-CUBRID-session execution stack is held by
session::create_and_push_stack. The executor does not care which
language produced the procedure; the catalog row tells it the target
method name and the wire protocol carries the args.
Shared JVM (pl_engine/pl_server). A single cub_pl process is
forked at server boot by pl_start_jvm_server() (or, in the modern
external-process model, spawned by server_manager::start() via
create_child_process("cub_pl", db_name)). Inside the JVM,
Server.java installs the security manager and binds the listening
socket; ListenerThread.java accepts incoming connections and spawns
one ExecuteThread.java per active link. ExecuteThread decodes
RequestCode headers and routes to either reflective dispatch
(INVOKE_SP for JavaSP), the in-process compiler (COMPILE for
PL/CSQL DDL), or the same reflective dispatch path again
(INVOKE_SP for PL/CSQL — once compiled it is indistinguishable from
a hand-written JavaSP).
Two frontends. JavaSP (see cubrid-pl-javasp.md) brings its
own classloader hierarchy under
pl_engine/pl_server/.../classloader/, a SpSecurityManager that
blocks System.exit() and native-library loading from user code, and
TargetMethod.java to resolve ClassName.methodName(argTypes) by
reflection. PL/CSQL (see cubrid-pl-plcsql.md) brings ANTLR 4
grammars (PlcLexer.g4, PlcParser.g4,
StaticSqlWithRecords.g4), the compiler/ast/ tree of Decl* /
Stmt* / Expr* nodes, a TypeChecker that calls back to the C
server for static-SQL semantics, a JavaCodeWriter that walks the
checked AST and emits a Java source string, and MemoryJavaCompiler
which wraps javax.tools.JavaCompiler to produce bytecode in memory.
The C-side coordinator for that compile-on-DDL round trip is
pl_compile_handler.cpp, which is the only piece of src/sp/ that
PL/CSQL adds.
Reading order
Section titled “Reading order”Read cubrid-pl-javasp.md first. It introduces the shared
infrastructure — process topology, the cub_pl boot sequence, the
SP_CODE / RequestCode wire protocol, the connection pool, the
pl_session execution stack, the catalog rows — in detail, because
JavaSP exercises every layer except the compiler. Once you have read
JavaSP you understand the entire substrate.
Then read cubrid-pl-plcsql.md. It builds on top of the
substrate and only describes what is added: the ANTLR front end,
the AST and type system, the Java emitter, the in-process javac,
and the pl_compile_handler round-trip. PL/CSQL deliberately
delegates execution to the JavaSP path — the PL/CSQL compiler’s
output is, by construction, just another Java class that lives in the
catalog. So a reader who already understands JavaSP can focus
entirely on the compiler in the PL/CSQL doc, with no need to
re-read shared sections.
Reading them in the opposite order works but forces the PL/CSQL doc to forward-reference shared infrastructure that is more naturally introduced under JavaSP.
Cross-cutting concerns
Section titled “Cross-cutting concerns”A handful of design choices are worth naming explicitly because they apply to both frontends and are easy to miss when you read the detail docs in isolation.
-
One catalog, no language tag at the storage level. Both JavaSP and PL/CSQL persist as rows in
_db_stored_procedure,_db_stored_procedure_args, and_db_stored_procedure_code. Thesp_infostruct insp_catalog.hppdoes not branch on language; the JVM-side dispatcher distinguishes a JavaSP from a PL/CSQL procedure only by where the bytecode came from (user-supplied JAR versus emitted by the in-process compiler). This means catalog utilities — schema dumps,loaddb, replication of DDL — handle both kinds uniformly. -
One wire, one socket. Both rides of the family use the same pooled Unix-domain-socket (or TCP fallback) link from
cub_servertocub_pl, the sameRequestCode-tagged frames built byCUBRIDPacker/CUBRIDUnpacker, and the sameconnection_pool/connection_viewlifetime model. There is no separate “PL/CSQL channel”; the only difference between calls is the request code in the header. -
Process isolation is the safety story. Because
cub_plis a separate OS process — not an in-process JNI embedding — a user Java class that crashes the JVM, runs out of permgen, or stalls in GC cannot take down the SQL server. A monitor task insidecub_server(server_monitor_task) reaps and respawnscub_plon death. This isolation applies equally to PL/CSQL because PL/CSQL ultimately runs as Java inside the samecub_pl. -
No language-aware fast path. The executor does not have a shortcut for PL/CSQL nor a shortcut for JavaSP. Every call pays the IPC and reflection cost. The trade-off — accepted explicitly during the migration off the legacy JNI embedding documented in
pl_sr_jvm.cpp— is reliability over peak call rate.
Detail-doc summaries
Section titled “Detail-doc summaries”| Doc | Scope | What is unique to it |
|---|---|---|
cubrid-pl-javasp.md | Java stored procedures end-to-end. Owns the shared-infrastructure exposition: cub_pl process topology, boot via pl_start_jvm_server / server_manager::start, SP_CODE and METHOD_REQUEST wire protocol, connection_pool / connection_view, pl_session execution stack, server-side JDBC callback channel. | Reflective dispatch (TargetMethod.java), classloader hierarchy (ClassLoaderManager, ContextClassLoader, SessionClassLoader, ServerClassLoader), security sandbox (SpSecurityManager), legacy in-process JNI path (pl_sr_jvm.cpp) preserved as historical context. |
cubrid-pl-plcsql.md | The Oracle-dialect compile-on-DDL pipeline. Builds on JavaSP’s runtime; everything below the AST is borrowed. | ANTLR 4 grammars (PlcLexer.g4, PlcParser.g4, StaticSqlWithRecords.g4), AST (Decl*, Stmt*, Expr*, Unit), TypeChecker with ServerAPI callback for embedded static SQL, type system (Type, TypeChar, TypeNumeric, TypeRecord, TypeVariadic, Coercion, CoercionScheme), JavaCodeWriter emitter, MemoryJavaCompiler, and the C-side pl_compile_handler.cpp round-trip. |
Adjacent sections
Section titled “Adjacent sections”The PL family does not stand alone; the following sections supply context that the detail docs assume you already know.
-
DDL & Schema.
cubrid-ddl-execution.mdand the catalog notes explain howCREATE PROCEDUREreaches the catalog manager in the first place — including transactional semantics for catalog inserts. The PL/CSQL compile-on-DDL hook plugs into that path; the PL family doc takes the catalog rows as given. -
Server architecture.
cubrid-architecture-overview.mdexplains the SA-vs-CS runtime split (standalone vs client-server modes) and wherecub_plsits relative tocub_server.cubrid-network-protocol.mdexplains the client-facing wire protocol; the PL family runs a separate, internal protocol on a different socket, but reuses the sameCUBRIDPackerframing primitives. -
Query processing. PL
CALLstatements enter through the same XASL execution path as ordinary SQL — aCALLnode in the execution plan invokespl_executor.cpp::invoke_java. So everything the query-processing section says about XASL, predicate evaluation, and result fetching applies before and after a PL invocation; the PL family doc only covers what happens inside the call.
When in doubt, read this overview, then JavaSP, then PL/CSQL — and treat DDL, server architecture, and query processing as the surrounding chapters that the PL family deliberately offloads work to.