[KO] CUBRID PL/JavaSP — Java 저장 프로시저, JDBC 백채널, 그리고 PL/CSQL과 형제인 외부 PL 엔진
목차
PL 패밀리와의 관계
섹션 제목: “PL 패밀리와의 관계”이 문서는 JavaSP — Java 저장 프로시저 서브시스템 — 을 다룬다.
형제 문서인 cubrid-pl-plcsql.md 는 CUBRID 의 Oracle 방언 절차적
언어인 PL/CSQL 을 다룬다. 두 문서는 PL 패밀리 (pl-family
태그) 를 이루는데, 사용자 코드로 들어가는 마지막 디스패치 단계만
빼면 거의 모든 것을 공유하기 때문이다.
공유되는 인프라
섹션 제목: “공유되는 인프라”| 계층 | 공유 산출물 |
|---|---|
| 카탈로그 | _db_stored_procedure, _db_stored_procedure_args, _db_stored_procedure_code 라는 동일한 세 시스템 클래스의 행들 (속성 이름은 sp_constants.hpp, 그 행을 쓰는 C++ 구조체는 sp_catalog.hpp 참조) |
| 전송 (transport) | pl_connection.cpp / pl_comm.c — cub_server 에서 cub_pl 로 가는 Unix domain socket (UDS) 또는 TCP. 언어가 무엇이든 동일한 PL_CONNECTION_POOL 과 connection_view 타입이 쓰인다 |
| 실행기 (executor) | pl_executor.cpp / pl_session.cpp — C++ executor 클래스가 모든 호출을 공유 connection pool 위로 cub_pl 에게 보낸다. session 클래스가 두 언어 모두를 cursor, execution stack, per-session 파라미터를 추적한다 |
| JVM 호스팅 | Server.java, ListenerThread.java, ExecuteThread.java — 단일 cub_pl 프로세스가 JVM 하나를 띄우고, 그 JVM 이 JavaSP JAR 디스패치와 PL/CSQL 컴파일러+런타임을 함께 호스팅한다 |
JavaSP 고유
섹션 제목: “JavaSP 고유”| 관심사 | 산출물 |
|---|---|
| 사용자 JAR 위의 reflective 디스패치 | TargetMethod.java — ClassName.methodName(argTypes) 를 reflection 으로 풀고, StoredProcedure.invoke() 가 로드된 클래스 위에서 Method.invoke() 를 호출 |
| 클래스로더 위계 | classloader/ 패키지 — ClassLoaderManager, ContextClassLoader, SessionClassLoader, ServerClassLoader. 사용자 JAR 은 $CUBRID_DATABASES/<db>/java/ (동적) 또는 java_static/ (정적) 경로에서 로드된다 |
| 보안 샌드박스 | SpSecurityManager.java — 거의 모든 것을 허용하지만 System.exit() (서버 자체가 셧다운 중일 때 한정 예외) 와 사용자 클래스로더로부터의 native 라이브러리 로드 (System.loadLibrary) 를 차단하는 커스텀 SecurityManager |
| 레거시 in-process JVM 경로 | pl_sr_jvm.cpp — pl_start_jvm_server() / pl_server_port(). 외부 cub_pl 프로세스 모델 이전에 쓰이던 옛 in-process JNI 임베딩. 컴파일은 되지만 대체된 상태 |
두 PL 문서를 함께 읽을 때, 카탈로그와 wire 프로토콜은 여기서 한 번 설명되고 PL/CSQL 쪽에서는 교차 참조된다는 점을 염두에 두면 된다.
학술적 배경
섹션 제목: “학술적 배경”저장 프로시저 는 데이터베이스 엔진 안에 보관되어 SQL 또는 클라이언트 호출로 불려 가는, 이름이 붙고 매개변수화된 계산 단위다. 데이터베이스 시스템 관점에서는 구현 공간을 좌우하는 설계 선택이 셋 있다.
-
언어 런타임의 거주지. 프로시저 본체는 데이터베이스 서버 프로세스 안에서 (in-process) 돌 수도 있고, 서버가 포크해 띄운 위성 프로세스 (외부 프로세스) 에서 돌 수도 있고, 별도 애플리케이션 서버에서 돌 수도 있다. In-process 는 호출 오버헤드가 가장 낮고 내부 자료구조에 직접 접근할 수 있지만, 결함 있는 저장 프로시저 하나가 서버 전체를 죽일 수 있다. 외부 프로세스는 IPC 지연이라는 비용을 치르고 격리를 얻는다. CUBRID 은 in-process JNI 접근에서 시작했다가 격리를 위해 포크된 외부 프로세스 (
cub_pl) 로 옮겨 왔다. -
언어. SQL/PSM (SQL 표준의 절차적 확장), PL/SQL 계열 언어 (Oracle 방언, Sybase 방언), Java (IBM DB2 의 Java 루틴, CUBRID JavaSP), 그리고 JavaScript / Python (현대 NewSQL 엔진). Java 가 역사적으로 매력적이었던 이유는 강타입이고, JDBC 기반 애플리케이션 로직에서 이미 쓰이고 있었으며, 이식성 있는 바이트코드를 만들기 때문이다. 그 대가는 JVM 시작 비용과 GC pause 다. CUBRID 은 JVM 을 장기 사이드카로 살려 두는 방식으로 이를 완화한다.
-
JDBC 백채널. Java 저장 프로시저가 SQL 쿼리를 발행하면 데이터베이스 연결이 필요해진다. 그 쿼리를 평범한 클라이언트-서버 경로로 흘려 보내면 네트워크 round-trip 이 추가로 발생하고 사용자가 자격 증명을 따로 넘겨야 한다. 통상적인 패턴은 (IBM DB2 가 그렇고 CUBRID 도 같은 설계를 따른다) 네트워크를 우회하는 서버 사이드 JDBC 드라이버 를 두는 것이다. 이 드라이버는 호출을 전달했던 그 IPC 채널 위로 콜백 요청을 원래의 서버 워커 스레드로 되쏘아 보낸다.
CUBRID의 구현
섹션 제목: “CUBRID의 구현”프로세스 토폴로지
섹션 제목: “프로세스 토폴로지”cub_server (C/C++ process)││ PRM_ID_STORED_PROCEDURE = true│├── pl_server_init() ← boot_sr.c calls this during server boot│ └── server_manager::start()│ └── create_child_process("cub_pl", db_name) ← fork+exec│├── server_monitor_task (daemon thread, 1-sec loop)│ ├── is_terminated_process(pid) ← reap and restart if cub_pl crashes│ ├── do_check_connection() → do_ping_connection() ← SP_CODE_UTIL_PING│ └── do_bootstrap_request() ← SP_CODE_UTIL_BOOTSTRAP (sends sysprms)│└── connection_pool (10 connections, UDS or TCP) └── connection_view (RAII handle: claim/retire)
cub_pl (Java process — pl_engine/ Gradle artifact)│├── Server.main()│ ├── SpSecurityManager installed│ ├── ClassLoaderManager dirs created│ └── initializeSocket() → ServerSocket (UDS via junixsocket or TCP)│├── ListenerThread (accept loop)│ └── for each accepted socket → new ExecuteThread(socket).start()│└── ExecuteThread (one per active connection) ├── RequestCode.UTIL_PING → respond with server name ├── RequestCode.UTIL_BOOTSTRAP → apply sysprm settings ├── RequestCode.INVOKE_SP → processStoredProcedure() │ ├── PrepareArgs.readArgs() │ ├── makeStoredProcedure() → StoredProcedure │ └── StoredProcedure.invoke() │ ├── [JavaSP] TargetMethod.getMethod() → Method.invoke() │ └── [PL/CSQL] PlcsqlCompilerMain / compiled class dispatch ├── RequestCode.COMPILE → processCompile() └── RequestCode.DESTROY → ContextManager.destroyContext()이 그림이 보여 주는 것은 두 프로세스 사이의 비대칭이다.
cub_server 측의 무게 중심은 connection pool 과 monitor
task 두 가지에 실린다. pool 은 SP 호출이 실제로 흐르는
파이프이고, monitor task 는 자식 프로세스의 생명주기를 책임진다.
cub_pl 측의 무게 중심은 ExecuteThread 의 dispatch 분기 에
실린다. RequestCode 한 줄짜리 분기 안에 ping, bootstrap, invoke,
compile, destroy 다섯 개의 시스템 명령이 모두 들어가고, 그 중
INVOKE_SP 가 다시 JavaSP / PL/CSQL 두 갈래로 갈라진다.
발견과 랑데부 (Discovery and rendezvous)
섹션 제목: “발견과 랑데부 (Discovery and rendezvous)”cub_pl 은 자기 소켓을 바인드한 뒤 자신의 PID 와 포트 (UDS
모드라면 -1) 를 $CUBRID/var/pl_<db_name>.info 에 쓴다. C++
측은 이 파일을 pl_read_info() (pl_file.c) 로 읽어 포트
번호를 알아낸다. monitor task 가 재접속을 시도할 때마다 이
파일을 폴링한다. UDS 모드에서는 소켓 경로 자체가 합의된 파일
경로라 info 파일에 포트 필드가 따로 필요 없다.
Wire 프로토콜 (SP_CODE / METHOD_REQUEST)
섹션 제목: “Wire 프로토콜 (SP_CODE / METHOD_REQUEST)”소켓 위의 모든 메시지는 길이 prefix 가 붙은 바이트 버퍼다. 첫
필드는 세션 ID 와 요청 코드를 담은 Header 다. C++ 측은
시스템 수준 명령 (PING, BOOTSTRAP, DESTROY) 에 대해서는
SP_CODE 값 (pl_comm.h 정의) 을, SP 호출에 대해서는
METHOD_REQUEST 값 (sp_constants.hpp 정의) 을 쓴다. Java 측의
RequestCode 상수는 C++ enum 을 그대로 미러링한다.
// pl_comm.h — SP_CODESP_CODE_INVOKE = 0x01 // invoke a SPSP_CODE_RESULT = 0x02 // result from SP to server (callback)SP_CODE_ERROR = 0x04 // error from SP to serverSP_CODE_INTERNAL_JDBC = 0x08 // back-channel JDBC requestSP_CODE_DESTROY = 0x10 // session teardownSP_CODE_COMPILE = 0x80 // PL/CSQL compile requestSP_CODE_UTIL_BOOTSTRAP = 0xDD // bootstrap sysprmSP_CODE_UTIL_PING = 0xDE // liveness probeSP_CODE_UTIL_STATUS = 0xEE // status querypl_executor.cpp 의 invoke_java packable struct 가 호출
페이로드를 직렬화한다. 트랜잭션 ID, signature 문자열
(ClassName.methodName), 인증 컨텍스트, 언어 태그
(SP_LANG_JAVA 또는 SP_LANG_PLCSQL), 인자 개수, 인자별
모드와 DB 타입, 반환 타입, 트랜잭션 제어 플래그.
// invoke_java::invoke_java — pl_executor.cppsignature.assign (sig->ext.sp.target_class_name) .append (".").append (sig->ext.sp.target_method_name);lang = sig->type; // PL_TYPE_JAVA_SP or PL_TYPE_PLCSQLtransaction_control = (lang == SP_LANG_PLCSQL) ? true : tc;PL/CSQL 언어는 항상 transaction_control = true 를 받는다.
JavaSP 는 SP 정의 자체에서 그 값을 물려받는다.
카탈로그 행
섹션 제목: “카탈로그 행”저장 프로시저 메타데이터를 담는 시스템 클래스가 셋이다.
_db_stored_procedure (SP_CLASS_NAME) unique_name, sp_name, sp_type, return_type, arg_count, args, lang, pkg_name, is_system_generated, directive, target_class, target_method, ← JavaSP-only meaningful fields owner, sql_data_access, comment, created_time, updated_time
_db_stored_procedure_args (SP_ARG_CLASS_NAME) sp_of, index_of, arg_name, data_type, mode, default_value, is_optional, comment
_db_stored_procedure_code (SP_CODE_CLASS_NAME) name, created_time, owner, is_static, is_system_generated, stype (source type: PLCSQL=0 / JAVA=1), scode (source code text), otype (object code type: JAVA_CLASS / JAVA_JAR), ocode (compiled object code, base64)sp_info::lang 이 JavaSP 에 대해서는 SP_LANG_JAVA (1), PL/CSQL
에 대해서는 SP_LANG_PLCSQL (0) 이다. 이 단일 필드 하나가
ExecuteThread 안에서 호출을 적절한 핸들러로 갈라 보낸다.
sp_catalog.cpp 의 C++ 함수 sp_add_stored_procedure(),
sp_add_stored_procedure_argument(), sp_add_stored_procedure_code()
가 CREATE PROCEDURE / CREATE FUNCTION 시점에 이 행들을 쓴다.
jsp_cl.cpp 가 클라이언트 측 DDL 처리 (parse tree 에서 카탈로그
행으로) 를 담당한다. DDL 은 클라이언트 측에서 도는 일이라
#if !defined(SERVER_MODE) 가드가 걸려 있다.
C++ session 과 executor
섹션 제목: “C++ session 과 executor”pl_session.hpp 의 cubpl::session 은 CUBRID 세션별 상태
객체다.
m_stack_map/m_exec_stack— execution stack 추적 (재귀 SP 호출당execution_stack하나씩, 각자cub_plthread pool 안의 워커 스레드를 받음).m_cursor_map— JDBC 백채널 쿼리가 연 서버 사이드 커서.m_session_connections— 호출이 도는 동안 글로벌 풀에서 빌려 온connection_view의 deque.m_session_params— 세션별 파라미터 그림자 (DBMS_OUTPUT플래그 등).METHOD_CALLBACK_SET_PL_SESSION_PARAM으로cub_pl과 동기화된다.
pl_executor.hpp 의 cubpl::executor 는 단일 호출을 굴린다.
fetch_args_peek()— XASL value descriptor 또는 직접 CALL 문장의 인자 리스트에서 인자DB_VALUE들을 읽어 들임.request_invoke_command()—invoke_java를 패킹해 잡아 둔 connection 위로 전송.response_invoke_command()— 응답을 읽는 루프를 돌린다. 각 응답은 최종 결과 (SP_CODE_RESULT), 에러 (SP_CODE_ERROR), 또는 콜백 요청 (SP_CODE_INTERNAL_JDBC) 중 하나다. 콜백 요청은callback_prepare(),callback_execute(),callback_fetch()등으로 분기되어 서버의 쿼리 실행 기계로 들어가고, 결과를 같은 소켓으로cub_pl로 되돌려 쓴다.
JavaSP 고유 — reflective 디스패치와 클래스로더
섹션 제목: “JavaSP 고유 — reflective 디스패치와 클래스로더”TargetMethod 는 호출 시점에 타겟을 풀어낸다. 클래스명, 메서드명,
콤마로 구분된 인자 타입 디스크립터 문자열을 담은 Signature 로부터
생성된다. 생성자가 classesFor() 를 호출해 각 타입 이름을 정적
argClassMap 으로 Class<?> 에 매핑한다. 이 맵은 모든 primitive,
그 박스 등가물, java.math.BigDecimal,
java.sql.Date/Time/Timestamp, cubrid.sql.CUBRIDOID, 그리고
이 모두의 1차원 / 2차원 배열을 커버한다. Class<?>[] 가
풀리고 나면 getMethod() 가 reflection 으로
Class.getMethod(methodName, argsTypes) 를 호출한다.
클래스로더 위계는 다음과 같다.
JVM bootstrap classloader └── ServerClassLoader (server JARs: cubrid-jdbc, pl_server.jar) └── ContextClassLoader (per-database user JARs, dynamic path) └── SessionClassLoader (per-session isolation, if needed)ClassLoaderManager 는 동적 JAR 의 루트를
$CUBRID_DATABASES/<db>/java/ 로, 정적 JAR 의 루트를
$CUBRID_DATABASES/<db>/java_static/ 로 잡는다. 마지막 수정
타임스탬프를 추적하기 때문에 loadjava 를 다시 실행하면
cub_pl 을 재시작하지 않고도 클래스로더가 새 JAR 을 집어
올린다.
SpSecurityManager 는 Server 생성 시점에
System.setSecurityManager() 로 설치된다. 핵심 제약은 다음과
같다.
checkExit()—Server인스턴스가 이미 셧다운 상태가 아니면SecurityException을 던진다. 사용자 저장 프로시저가System.exit()을 불러 JVM 을 죽이는 것을 막기 위함이다.checkLink()— 클래스로더 체인을 살펴, 어느 한 프레임이라도ContextClassLoader또는SessionClassLoader에 속한다면 native 라이브러리 로드를 차단하면서loadjava -jni를 안내하는 메시지를 띄운다.- 그 외 모든
check*()메서드는 관대한 no-op (빈 오버라이드) 이다.
서버 사이드 JDBC 백채널
섹션 제목: “서버 사이드 JDBC 백채널”cub_pl 안에서는 CUBRIDServerSideDriver /
CUBRIDServerSideConnection 이 JDBC 드라이버로 등록되어 있다.
사용자 Java 코드가 SQL 쿼리를 발행하면 JDBC 호출은
CUBRIDServerSidePreparedStatement.execute() 로 흘러가서
METHOD_CALLBACK_QUERY_PREPARE 또는
METHOD_CALLBACK_QUERY_EXECUTE 요청을 직렬화한 뒤, 원래의
SP_CODE_INVOKE 를 전달했던 그 동일한 소켓 으로
cub_server 에게 되돌려 보낸다. C++
executor::response_callback_command() 루프가 이 콜백 코드를
인지해 적절한 서버 서브시스템 (쿼리 prepare, execute, cursor
fetch, LOB 연산 등) 으로 디스패치하고, 그 결과를 다시 cub_pl
로 쓴다.
이 구조가 단일 TCP/UDS 연결 위의 동기 콜백 루프 를 만든다.
cub_server cub_pl ──── SP_CODE_INVOKE ──────► ◄─── METHOD_CALLBACK_QUERY_PREPARE ─── ──── (prepared stmt handle) ─────────► ◄─── METHOD_CALLBACK_QUERY_EXECUTE ─── ──── (cursor, rows) ──────────────────► ◄─── METHOD_CALLBACK_FETCH ──────────── ──── (row data) ──────────────────────► ◄─── SP_CODE_RESULT ───────────────────재귀적 SP 호출 (Java SP 가 또 다른 SP 를 부르는 경우) 은 각자
새 execution_stack 엔트리를 잡고 별도의 ExecuteThread 로
처리되지만, 같은 세션을 재사용한다.
전체 오피코드 분류 체계, 서버 측 디스패처
(cubpl::executor::response_callback_command 와 12 개의 핸들러),
재귀 깊이 가드 (METHOD_MAX_RECURSION_DEPTH = 15), 그리고 공유
packed 와이어 구조체는 PL 패밀리의 세 번째 형제 문서인
cubrid-pl-server-bridge.md 에 정리되어 있다는 점이다. 그 문서는
같은 오피코드 집합을 사용하는 더 오래된 server→CAS 콜백 경로
(Path A) 도 함께 다룬다. 두 경로는 물리적으로는 분리되어 있지만
같은 METHOD_CALLBACK_* 열거형을 공유한다.
시작 FSM
섹션 제목: “시작 FSM”server_monitor_task 는 작은 상태 머신을 구현한다.
STOPPED ──fork cub_pl──► READY_TO_INITIALIZE │ ping poll (up to 10×, 1s each) │ bootstrap_request (send sysprms) │ ┌──────────────┴──────────────────┐ RUNNING FAILED_TO_INITIALIZE │ │ (monitor daemon re-checks every 1s) (after >10 failures) process exits? → STOPPED → re-forkSERVER_MODE 에서는 monitor 데몬이 cubthread::daemon (1 초
looper) 으로 돈다. SA_MODE (standalone / csql) 에서는
do_monitor() 가 동기적으로 호출되며 최대 10 회 재시도한다.
소스 코드 가이드
섹션 제목: “소스 코드 가이드”C++ 측 (src/sp/)
섹션 제목: “C++ 측 (src/sp/)”| 심볼 | 파일 | 역할 |
|---|---|---|
pl_server_init | pl_sr.cpp | 서버 부팅 시 boot_sr.c 가 호출하는 진입점. server_manager 를 만들고 cub_pl 을 fork 한다 |
pl_server_destroy | pl_sr.cpp | 서버 셧다운 시 호출. server_manager 를 삭제 |
pl_server_wait_for_ready | pl_sr.cpp | init 직후 cub_pl 이 연결을 받기 시작할 때까지 블로킹 |
get_connection_pool | pl_sr.cpp | 글로벌 PL_CONNECTION_POOL 반환. executor 가 connection 을 청구할 때 사용 |
pl_server_port_from_info | pl_sr.cpp | pl_read_info() 로 $CUBRID/var/pl_<db>.info 를 읽음 |
server_manager | pl_sr.cpp | pool + monitor task 를 소유. start / stop 라이프사이클 |
server_monitor_task | pl_sr.cpp | 데몬 태스크 — cub_pl fork, ping, bootstrap, crash 감지 |
bootstrap_request | pl_sr.cpp | Packable. 시스템 파라미터 스냅샷을 보냄 (SP_CODE_UTIL_BOOTSTRAP) |
connection_pool | pl_connection.hpp | 고정 크기의 connection 객체 풀. 재시작 시 epoch 기반 무효화 |
connection | pl_connection.hpp | SOCKET 래퍼. send_buffer_args, receive_buffer, 자동 재접속 |
pl_connect_server | pl_comm.c | 저수준 소켓 connect (UDS 또는 TCP) |
pl_writen / pl_readn | pl_comm.c | 신뢰성 있는 write / read 헬퍼 (EINTR 처리) |
SP_CODE enum | pl_comm.h | Java 측 RequestCode 와 공유되는 wire 프로토콜 코드 |
executor | pl_executor.hpp | 단일 호출 드라이버 — 인자 fetch → invoke 송신 → 콜백 처리 → 결과 반환 |
invoke_java | pl_executor.cpp | Packable 호출 페이로드 (signature, lang, args, result type) |
session | pl_session.hpp | CUBRID 세션별 — 스택, 커서, 연결, 파라미터, 인터럽트 |
sys_param | pl_session.hpp | Packable 시스템 파라미터 (DB prm 또는 PL 전용 prm) |
pl_signature | pl_signature.hpp | 풀린 SP 디스크립터 — 타입, 이름, 인증, 인자 모드 / 타입, ext (target class / method 또는 code OID) |
sp_info | sp_catalog.hpp | _db_stored_procedure 행의 C++ 표현 |
sp_arg_info | sp_catalog.hpp | _db_stored_procedure_args 행의 C++ 표현 |
sp_code_info | sp_catalog.hpp | _db_stored_procedure_code 행의 C++ 표현 |
sp_add_stored_procedure | sp_catalog.cpp | CREATE 시점에 새 SP 행을 씀 |
PL_SERVER_INFO | pl_file.h | pl_read_info / pl_write_info 로 읽고 쓰는 {pid, port} 구조체 |
jsp_create_stored_procedure | jsp_cl.cpp | 클라이언트 측 CREATE PROCEDURE / FUNCTION 핸들러 |
jsp_make_pl_signature | jsp_cl.cpp | PT_NODE 로부터 pl_signature 를 빌드 |
Java 측 (pl_engine/pl_server/)
섹션 제목: “Java 측 (pl_engine/pl_server/)”| 심볼 | 파일 | 역할 |
|---|---|---|
Server | Server.java | 진입점. SpSecurityManager 설치, 소켓 셋업, ListenerThread 시작 |
ListenerThread | ListenerThread.java | ServerSocket.accept() 루프. 연결당 ExecuteThread 생성. 에러 시 exponential backoff |
ExecuteThread | ExecuteThread.java | 연결당 스레드. Header.code 로 디스패치. processStoredProcedure() / processCompile() |
processStoredProcedure | ExecuteThread.java | 인자 read, StoredProcedure 빌드, invoke() 호출, 결과 송신 |
TargetMethod | TargetMethod.java | Signature 로부터 Class.getMethod(name, argTypes) 를 reflection 으로 풀어냄 |
TargetMethod.argClassMap | TargetMethod.java | 정적 맵 — 타입 이름 문자열 → Class<?> (primitive, boxed, SQL 타입, 배열) |
TargetMethod.getMethod | TargetMethod.java | 호출용 Method 반환. 메시지에 풀 시그니처를 담은 NoSuchMethodException 던짐 |
SpSecurityManager | SpSecurityManager.java | 커스텀 SecurityManager — 셧다운 중이 아니면 exit 차단, 사용자 클래스로더의 native lib load 차단 |
SpSecurityManager.checkLink | SpSecurityManager.java | getClassContext() 체인 점검. ContextClassLoader / SessionClassLoader 의 loadLibrary 거부 |
ClassLoaderManager | classloader/ClassLoaderManager.java | root / static / dynamic 경로 관리. 핫 JAR 리로드용 last-modified 추적 |
ClassLoaderManager.getDynamicPath | classloader/ClassLoaderManager.java | $db_path/java/ 반환 (loadjava 로 올린 사용자 JAR) |
ClassLoaderManager.getStaticPath | classloader/ClassLoaderManager.java | $db_path/java_static/ 반환 (서버 전체 JAR) |
위치 힌트 (2026-04-30 기준)
섹션 제목: “위치 힌트 (2026-04-30 기준)”| 심볼 | 파일 | 대략 라인 |
|---|---|---|
pl_server_init | src/sp/pl_sr.cpp | 696 |
server_manager::start | src/sp/pl_sr.cpp | 262 |
server_monitor_task::do_monitor | src/sp/pl_sr.cpp | 356 |
server_monitor_task::do_bootstrap_request | src/sp/pl_sr.cpp | 625 |
pl_server_port_from_info | src/sp/pl_sr.cpp | 763 |
SP_CODE enum | src/sp/pl_comm.h | 44 |
connection_pool 클래스 | src/sp/pl_connection.hpp | 64 |
executor::execute | src/sp/pl_executor.cpp | (executor 클래스 본문 안) |
invoke_java::invoke_java | src/sp/pl_executor.cpp | 45 |
session 클래스 | src/sp/pl_session.hpp | 106 |
pl_signature struct | src/sp/pl_signature.hpp | 86 |
SP_LANG_JAVA, SP_LANG_PLCSQL | src/sp/sp_constants.hpp | 147 |
SP_CLASS_NAME 매크로 | src/sp/sp_constants.hpp | 22 |
sp_info struct | src/sp/sp_catalog.hpp | 115 |
PL_SERVER_INFO struct | src/sp/pl_file.h | 35 |
Server 생성자 | pl_engine/.../Server.java | 70 |
ListenerThread.run | pl_engine/.../ListenerThread.java | 63 |
ExecuteThread.run | pl_engine/.../ExecuteThread.java | 129 |
ExecuteThread.processStoredProcedure | pl_engine/.../ExecuteThread.java | 319 |
TargetMethod.getMethod | pl_engine/.../TargetMethod.java | 243 |
SpSecurityManager.checkLink | pl_engine/.../SpSecurityManager.java | 72 |
ClassLoaderManager.getDynamicPath | pl_engine/.../classloader/ClassLoaderManager.java | 58 |
소스 검증 노트
섹션 제목: “소스 검증 노트”-
pl_sr_jvm.cpp는 레거시 잔재다.pl_start_jvm_server()와pl_server_port()는 JVM 이cub_server안에 직접 임베드되어 있던 옛 in-process JNI 경로의 잔해다. 심볼 자체는 여전히 컴파일되지만, 실제로 살아 있는 코드 경로는pl_sr.cpp의server_managerfork / exec 모델이다. JNI 경로는 죽은 코드 거나, 특정 빌드 구성용으로 남아 있을 가능성이 있다. -
SpSecurityManager는 Java 17 부터는 deprecated 다.SecurityManager는 Java 17 에서 제거를 위해 deprecated 로 표시되었다 (JEP 411). CUBRID 가 신버전 JVM 으로 옮겨 가면 이 샌드박스 메커니즘은 다른 것 (예: 제한된 클래스 로딩 정책, 또는 SP 실행마다 별도 프로세스를 두는 방식) 으로 대체되어야 한다. 현재 코드가 노리는 타깃은 JDK 1.8+ 다. -
connection_pool::CONNECTION_POOL_SIZE = 10은 하드코드 되어 있다. 풀 크기를 다스리는 시스템 파라미터가 없다. 동시 SP 호출이 많은 고동시성 시나리오에서는 풀에 큐잉이 걸린다.is_system_pool플래그가 monitor 자신의 시스템 연결 풀과 메인 풀을 구분한다. -
TargetMethod.argClassMap은 고정된 타입 집합만 다룬다. 맵에 없는 클래스 타입 (예: 사용자 정의 DTO 클래스) 을 받는 사용자 JAR 메서드는 디스패치 시점에ClassNotFoundException을 낸다.classFor()의 주석은 이 빈자리를 인정하면서ClassAccess.getClass()구현으로 가는TODO를 가리킨다. -
info 파일의 race window. monitor task 에서
fork()가 돌아온 시점과cub_pl이 자신의 포트를 info 파일에 쓰는 시점 사이에는pl_server_port_from_info()가PL_PORT_DISABLED를 반환하는 구간이 있다. monitor 는 폴링 루프 (do_check_connection(), 최대 10×, 1 초씩) 로 대응하지만, JVM 시작이 매우 느리면 그 예산을 초과할 수 있다.
미해결 질문
섹션 제목: “미해결 질문”-
SpSecurityManager의 Java 17+ 우아한 업그레이드. 현재 소스에서 대체 메커니즘이 보이지 않는다. JDK 17+ 가 타깃 베이스라인이 될 때 계획은 무엇인가? -
핫 JAR 리로드의 단위.
ClassLoaderManager.isModified()는 JAR 파일 단위로 last-modified 타임스탬프를 추적한다. 이미 클래스를 로드한 채 진행 중인 SP 호출은 다음 호출 전까지 옛 버전을 보게 되는가? 핸드오프 메커니즘이 있는가? -
pl_sr_jvm.cpp의 운명. in-process JNI 경로는 현재 어떤 제품 구성에서든 컴파일되고 있는가, 아니면 완전히 죽은 코드라 제거 예정인가? -
재귀 SP 깊이 제한.
sp_constants.hpp의METHOD_MAX_RECURSION_DEPTH가15로 정의되어 있다. 이 값을 강제하는 쪽은 C++ 측인가, Java 측인가, 아니면 둘 다 인가? JavaExecuteThread는 이를 직접 점검하지 않는 것으로 보인다. -
SP 에러 시 트랜잭션 rollback. JavaSP 가 잡히지 않은 예외를 던지면
ExecuteThread가 그것을 catch 하고 로깅한 뒤sendError()를 보낸다. C++executor::response_invoke_command()가 이를 어떻게 CUBRID 트랜잭션 rollback 으로 번역하는가? 콜백 루프가 thread 위에 rollback 을 트리거하는 에러 조건을 세팅해 줘야 하지만, 그 정확한 경로는 본 문서가 추적하지 않았다.
src/sp/— C/C++ PL 서버 브리지 (위references:에 나열된 모든 파일)pl_engine/pl_server/src/main/java/com/cubrid/jsp/— Java PL 엔진pl_engine/pl_server/src/main/java/com/cubrid/jsp/classloader/JAR 클래스로더 위계pl_engine/AGENTS.md— Gradle 빌드 구조 개요references/cubrid/CLAUDE.md— CUBRID 엔진 구조와 빌드 노트
이 지식 베이스의 형제 문서
섹션 제목: “이 지식 베이스의 형제 문서”knowledge/code-analysis/cubrid/cubrid-pl-javasp.md(영문 원본) 이 문서의 영어판.knowledge/ko/code-analysis/cubrid/cubrid-pl-plcsql.md— PL 패밀리의 형제 문서. PL/CSQL 컴파일러와 런타임이 이 문서에서 설명한 동일 카탈로그 / 전송 / 실행기 인프라를 어떻게 재사용하는지를 다룬다.