(KO) CUBRID 스칼라 함수 — 산술·문자열·수치·JSON·정규식·암호 연산자 프리미티브
목차
학술적 배경
섹션 제목: “학술적 배경”스칼라 함수 라이브러리란 연산자와 피연산자 를 받아 하나의
DB_VALUE를 만들어 내는 계층을 의미한다. 옵티마이저가 물리화하는
모든 컬럼 참조, 호스트 파라미터, 서브쿼리 결과는 결국 이 프리미티브
중 하나로 흘러 들어간다. +는 qdata_add_dbval, SUBSTR은
db_string_substring, NUMERIC * NUMERIC은 numeric_db_value_mul,
REGEXP_COUNT는 db_string_regexp_count, SHA2는 crypt_sha_two.
이 라이브러리의 호출자는 regu-variable 평가기(cubrid-query-evaluator.md
참조)다. 평가기가 디스패처라면, 라이브러리는 그 디스패처가 디스패치
하는 대상 이다.
설계 모양을 결정하는 다섯 가지 교과서적 관심사가 있다.
내장 스칼라 함수의 디스패치. Silberschatz 외, Database System
Concepts 5장 §Functions and Procedures는 모든 내장 함수를
이름, 시그니처, 실행 본체 의 삼중주로 정의한다. 카탈로그가 이름과
시그니처를 묶고, 실행기는 시그니처를 C 함수 포인터에 묶는다. 두 가지
설계 축은 granularity (타입 조합마다 별도 루틴 vs. 타입에 따라
분기하는 다형 루틴 하나) 와 binding time (parse 시점에 함수
포인터 고정 vs. 행 단위 해소) 다. 고전적 트레이드오프는 단형화 비용
(코드 크기 증가, 디스패치 빠름) 대 인터프리터 비용이다. CUBRID는 그
중간이다. 행 단위 디스패치는 산술 모양 연산자에 대해서는
OPERATOR_TYPE, 함수 모양 연산자에 대해서는 FUNC_CODE 로 1차
분기한 뒤, 그 아래에서 DB_TYPE 으로 한 번 더 fan-out 한다.
타입 도메인 강제 변환. + 같은 연산자는 INT + INT,
INT + DOUBLE, STRING + INT, DATE + INT 를 모두 받는다. 엔진은
계산 전 에 두 피연산자를 공통 도메인 으로 강제 변환해야 한다는
점이다. Date, An Introduction to Database Systems 4장은 이를
명시적·암묵적 promotion 의 부분 순서로 정식화한다. 구현 패턴은
promotion lattice 다. 두 타입에 대한 이항 join 이 정밀도 손실
없이 결과를 담을 수 있는 가장 작은 타입을 돌려준다. CUBRID는 이
변환을 두 번 인코딩한다. 의미 검사 시점의
pt_apply_expressions_definition이 EXPRESSION_DEFINITION 오버로드를
고르고 PT_CAST 노드를 삽입하며, 평가 시점의 qdata_add_dbval은
런타임 타입이 디스패치 표와 어긋날 때를 대비해 tp_value_auto_cast
로 다시 한 번 강제 변환을 시도한다.
NULL 의미론. 대부분의 프리미티브는 NULL을 전파한다. x + NULL = NULL, SUBSTR(NULL,1,3) = NULL. 다만 규칙이 균일하지는 않다.
COALESCE(NULL,1) = 1, IS NULL은 boolean을 돌려주며, 비교는 3치
논리를 돌려준다. 모든 프리미티브는 피연산자에 손대기 전에
DB_IS_NULL(arg) 을 검사하고, NULL 입력에서는 db_make_null(result)
를 산출한다. db_string_instr (시작 위치로 NULL을 받는다) 같은 드문
예외도 있다.
콜레이션을 의식하는 문자열 연산. 모든 문자열은 codeset
(UTF-8, ISO-8859-1, …) 과 collation 을 함께 들고 다닌다. 문자를
비교하거나 변환하는 연산자는 둘 다 존중해야 한다. LOWER('Ç')는
collation에 따라 결과가 다르고, 'a' = 'A'는 case-insensitive
collation 아래에서만 참이다. CUBRID는 모든 문자열 프리미티브로
INTL_CODESET과 LANG_COLLATION을 함께 흘려보내며, 문자 단위 작업은
intl_* (in intl_support.c) 에 위임한다. collation별 알파벳은
lang_user_alphabet_w_coll 이다.
BCD 산술. SQL NUMERIC(p,s)는 binary float이 아니라 fixed-point
decimal이다. binary CPU 위에서 이를 구현하려면 다중 바이트 표현과
함께 carry를 전파하는 덧셈, 뺄셈, 긴 자리 곱셈, shift-and-subtract
긴 자리 나눗셈이 필요하다 (Knuth, TAOCP vol. 2 §4.3). CUBRID는
DB_NUMERIC을 16바이트 8비트 digit 버퍼로 저장하며
(DB_NUMERIC_BUF_SIZE = 16), LSB는 offset 15 에 둔다. 커널은
numeric_add / numeric_sub / numeric_mul / numeric_long_div
이다.
정규식 엔진의 선택. 백트래킹 엔진(std::regex, PCRE)은 최악
지수 시간이지만, NFA-시뮬레이션 엔진(RE2, Hyperscan)은 선형 시간인
대신 backreference 를 지원하지 못한다는 점이 알려져 있다 (Cox,
“Regular Expression Matching Can Be Simple And Fast”, swtch.com 2007).
CUBRID는 engine_type::LIB_RE2 와 engine_type::LIB_CPPSTD 두
백엔드를 모두 노출하며, 그 위에 하나의 façade를 두고
PRM_ID_REGEXP_ENGINE 으로 게이트한다.
DBMS 공통 설계 패턴 (Common DBMS Design)
섹션 제목: “DBMS 공통 설계 패턴 (Common DBMS Design)”모든 관계형 엔진 — Postgres, MySQL, SQLite, CUBRID — 은 같은 다섯 하위 문제를 같은 반복 패턴으로 푼다.
이름 있는 함수의 카탈로그와 디스패치 표. Postgres는 모든 내장을
pg_proc에 oid 키로 저장하며, fmgrtab.c 디스패치 표가 각 oid를
Datum (*) (FunctionCallInfo) 함수 포인터로 매핑한다. MySQL은
Item_func_* 클래스 계층을 두고 가상 메서드 val_int / val_str /
val_real을 디스패치한다. SQLite는 sqlite3_create_function으로 매번
동적 등록한다. CUBRID는 두 개 의 디스패치 표를 운영한다는 점이다.
(1) 산술 모양 연산자 — PT_ADD, PT_SUBSTRING, PT_LIKE … — 가
ARITH_TYPE 노드 하나에 최대 네 개의 피연산자 포인터를 들고
들어오는 OPERATOR_TYPE 표. (2) 가변 인자 연산자 — JSON 가족,
정규식 가족, set/multiset 생성자 등 — 가 REGU_VARIABLE_LIST로
모델링되는 FUNC_CODE 표. 이 분리는 역사적 성장의 흔적이다. 산술
모양이 먼저 등장했고, 페이로드가 세 피연산자를 넘기 시작하면서 함수
코드 레인이 추가되었다.
공통 우산 아래의 타입별 단형화. 우산 함수 qdata_add_dbval은
런타임에 어떤 단형 커널을 호출할지 결정한다.
qdata_add_int_to_dbval, qdata_add_short_to_dbval,
qdata_add_bigint_to_dbval 같은 변형들이 그것이다. Postgres는 이를
의미 검사 시점에 풀어 둔다 (각 타입이 별도 pg_proc 엔트리를 가져
행 단위 분기가 사라진다). MySQL은 템플릿 Item_func_plus와 런타임
타입 검사를 섞어 쓴다. CUBRID의 접근은 클라이언트/서버 경계를
가로지르는 XASL 직렬화의 산물이다. XASL 트리는 피연산자
타입과 무관하게 단일한 T_ADD만 들고 다닌다. 따라서 타입 식별은
서버 측에서 일어나야만 한다.
3단 강제 변환. Promotion lattice는 세 곳에서 적용된다. (1)
의미 검사 시점에 pt_apply_expressions_definition이 시그니처
표에서 오버로드를 고르고 PT_CAST를 삽입한다. (2)
qdata_add_dbval 도입부에서 DB_TYPE_ENUMERATION, string + number, date + string 등을 다시 점검해 안전망으로
tp_value_auto_cast를 부른다. (3) 페어별 커널은 예상 외의 조합을
NO_ERROR 와 함께 무할당으로 떨어진다.
Collation 스레드. 모든 문자열 프리미티브는 INTL_CODESET 과
collation 식별자를 db_get_string_codeset(string) /
db_get_string_collation(string) 으로 받는다. 문자 단위 작업은
intl_lower_string, intl_char_count, intl_char_size,
intl_nextchar_utf8을 거친다. 비교 기반 연산(LIKE, <)은
LANG_COLLATION 가상 테이블로 디스패치된다.
파라미터 시점의 라이브러리 선택. PRM_ID_REGEXP_ENGINE (값
LIB_RE2, LIB_CPPSTD) 은 정규식 컴파일마다 읽힌다. 컴파일된
cub_compiled_regex는 엔진 태그를 함께 들고 다니므로, search/replace
디스패치는 태그된 union 위의 switch로 표현된다. compiled_regex_object
는 union { cub_std_regex *std_obj; re2::RE2 *re2_obj; } 형태다.
CUBRID의 위치. Postgres나 SQLite의 라이브러리에 비해 더 낡고,
크고, 보수적이다. JIT 없음 (Postgres의 LLVM 기반 ExecCompileExpr
없음). SIMD 벡터화 없음. 693 KB 짜리 string_opfunc.c 가 한
translation unit 으로 유지되는 것은 의도다. 쪼개면 작은 qstr_*
헬퍼들의 inter-procedural inlining 이 깨진다는 점이다. 라이브러리는
누적된 고고학처럼 읽힌다. 산술이 바닥에, 그 위에 문자열, 그 위에
date, 그 위에 crypt, 그 위에 JSON, 그 위에 regex가 차곡차곡 쌓여
있다.
CUBRID의 구현
섹션 제목: “CUBRID의 구현”라이브러리는 src/query/ 안에 산다. 라인 수 기준 여섯 파일이
지배한다. arithmetic.c (수치·날짜 프리미티브), numeric_opfunc.c
(BCD 산술), string_opfunc.c (문자열 프리미티브 — 압도적으로 가장
크다), query_opfunc.c (디스패처와 산술 우산), crypt_opfunc.c
(해싱과 암호화), 그리고 string_regex_* 트리오. 횡단 글루는
fetch.c (행 단위 연산자 switch가 있는 fetch_peek_arith) 와
parser/func_type.cpp (함수 시그니처 표) 에 있다.
두 개의 디스패치 레인
섹션 제목: “두 개의 디스패치 레인”실행기는 두 표면으로 이 라이브러리에 진입한다. 첫째는 산술 모양
연산자 다. 최대 네 개의 피연산자 포인터(leftptr, rightptr,
thirdptr, 선택적 네 번째)와 OPERATOR_TYPE opcode를 든
ARITH_TYPE 노드로 감싸인 모든 것이 여기에 해당한다는 점이다. 이
경로는 fetch.c 의 fetch_peek_arith 로 라이브러리에 닿는다.
// fetch_peek_arith — src/query/fetch.c// driven by `arithptr->opcode` (an OPERATOR_TYPE).static intfetch_peek_arith (THREAD_ENTRY * thread_p, REGU_VARIABLE * regu_var, val_descr * vd, OID * obj_oid, QFILE_TUPLE tpl, DB_VALUE ** peek_dbval){ ARITH_TYPE *arithptr = regu_var->value.arithptr; // ... fast-path for REGU_VARIABLE_FETCH_ALL_CONST, recursion-depth check ...
/* Step 1: per-opcode operand fetch. Each case decides which sub-regus to fetch and in what order; T_ADD short-circuits the right operand when peek_left is NULL under PRM_ID_ORACLE_STYLE_EMPTY_STRING. */ switch (arithptr->opcode) { case T_SUBSTRING: case T_LPAD: case T_RPAD: case T_REPLACE: case T_TRANSLATE: /* Three-operand string ops: fetch left, then right, then third. */ // ... condensed ... break; case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: case T_POSITION: case T_AES_ENCRYPT: case T_AES_DECRYPT: case T_SHA_TWO: case T_POWER: case T_ROUND: case T_LOG: case T_TRUNC: case T_STRCAT: case T_BIT_AND: case T_BIT_OR: case T_BIT_XOR: case T_INTDIV: case T_INTMOD: /* Two-operand: fetch left and right. */ break; /* ... ~120 case labels for unary/ternary/quaternary shapes ... */ }
/* Step 2: per-opcode dispatch to the library. */ switch (arithptr->opcode) { case T_ADD: if (qdata_add_dbval (peek_left, peek_right, arithptr->value, regu_var->domain) != NO_ERROR) goto error; break; case T_SUB: if (qdata_subtract_dbval (peek_left, peek_right, arithptr->value, regu_var->domain) != NO_ERROR) goto error; break; case T_MUL: /* qdata_multiply_dbval */ break; case T_DIV: /* qdata_divide_dbval */ break; case T_MOD: /* db_mod_dbval */ break; case T_FLOOR: if (DB_IS_NULL (peek_right)) PRIM_SET_NULL (arithptr->value); else db_floor_dbval (arithptr->value, peek_right); break; case T_SUBSTRING: if (DB_IS_NULL (peek_left) || DB_IS_NULL (peek_right)) PRIM_SET_NULL (arithptr->value); else db_string_substring (arithptr->misc_operand, peek_left, peek_right, peek_third, arithptr->value); break; /* ... ~160 case labels covering the OPERATOR_TYPE enum ... */ }}이 두 패스 — 피연산자 fetch 후 디스패치 — 의 모양은 의도된 것이다.
fetch 패스는 opcode별 피연산자 평가 전략을 구현한다 (예: T_ADD는
PRM_ID_ORACLE_STYLE_EMPTY_STRING 아래에서 peek_left가 NULL이면
오른쪽 피연산자를 단축한다). 디스패치 패스는 라이브러리를 호출한다.
도입부의 재귀 깊이 검사(thread_inc_recursion_depth,
PRM_ID_MAX_RECURSION_SQL_DEPTH 로 상한) 가 깊은 식의 실행을
안전하게 만든다.
두 번째 레인은 함수 모양 연산자 다. REGU_VARIABLE_LIST 형태의
피연산자 리스트와 FUNC_CODE 식별자를 든 FUNCTION_TYPE 노드로
모델링되는 모든 것이 여기에 해당한다. 이쪽은 qdata_evaluate_function
으로 닿는다.
// qdata_evaluate_function — src/query/query_opfunc.c// driven by `funcp->ftype` (a FUNC_CODE).intqdata_evaluate_function (THREAD_ENTRY * thread_p, regu_variable_node * function_p, ...){ FUNCTION_TYPE *funcp = function_p->value.funcp; pr_clear_value (funcp->value);
switch (funcp->ftype) { case F_SET: case F_MULTISET: case F_SEQUENCE: case F_VID: return qdata_convert_dbvals_to_set (thread_p, /* set type */, function_p, ...); case F_TABLE_SET: case F_TABLE_MULTISET: case F_TABLE_SEQUENCE: return qdata_convert_table_to_set (thread_p, /* set type */, function_p, val_desc_p); case F_GENERIC: return qdata_evaluate_generic_function (...); case F_CLASS_OF: return qdata_get_class_of_function (...); case F_INSERT_SUBSTRING: return qdata_insert_substring_function (...); case F_ELT: return qdata_elt (...); case F_BENCHMARK: return qdata_benchmark (...);
/* JSON family: ~25 entries; each fetches operands into DB_VALUE *args[] via qdata_convert_operands_to_value_and_call and forwards to a db_evaluate_json_* function pointer. */ case F_JSON_ARRAY: return qdata_convert_operands_to_value_and_call (..., db_evaluate_json_array); case F_JSON_EXTRACT: return qdata_convert_operands_to_value_and_call (..., db_evaluate_json_extract); /* ... F_JSON_OBJECT, F_JSON_MERGE, F_JSON_INSERT, F_JSON_REPLACE, F_JSON_SET, F_JSON_REMOVE, F_JSON_KEYS, F_JSON_LENGTH, F_JSON_DEPTH, F_JSON_TYPE, F_JSON_VALID, F_JSON_QUOTE, F_JSON_UNQUOTE, F_JSON_PRETTY, F_JSON_SEARCH, F_JSON_CONTAINS, F_JSON_CONTAINS_PATH, F_JSON_ARRAY_APPEND, F_JSON_ARRAY_INSERT, F_JSON_GET_ALL_PATHS, F_JSON_MERGE_PATCH ... */
case F_REGEXP_COUNT: case F_REGEXP_INSTR: case F_REGEXP_LIKE: case F_REGEXP_REPLACE: case F_REGEXP_SUBSTR: return qdata_regexp_function (thread_p, funcp, val_desc_p, obj_oid_p, tuple);
default: er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0); return ER_FAILED; }}도입부의 주석 /* should sync with fetch_peek_dbval () */은 보수
지침이다. 모든 새 FUNC_CODE는 fetch_peek_dbval의 함수-평가 가지에도
함께 추가되어야만 regu-variable peek 경로가 같은 디스패처에 도달할
수 있다는 점이다.
flowchart LR
Regu[REGU_VARIABLE]
Regu -->|TYPE_INARITH| Arith[ARITH_TYPE]
Regu -->|TYPE_FUNC| Func[FUNCTION_TYPE]
Regu -->|TYPE_CONSTANT| Const[DB_VALUE]
Regu -->|TYPE_ATTR_ID| Attr[heap fetch]
Arith -->|opcode T_ADD| Add[qdata_add_dbval]
Arith -->|opcode T_SUB| Sub[qdata_subtract_dbval]
Arith -->|opcode T_MUL| Mul[qdata_multiply_dbval]
Arith -->|opcode T_DIV| Div[qdata_divide_dbval]
Arith -->|opcode T_MOD| Mod[db_mod_dbval]
Arith -->|opcode T_SUBSTRING| Sub2[db_string_substring]
Arith -->|opcode T_LIKE| Like[db_string_like]
Arith -->|opcode T_LOWER| Low[db_string_lower]
Arith -->|opcode T_AES_ENCRYPT| AES[db_string_aes_encrypt]
Arith -->|opcode T_RLIKE| Rl[db_string_rlike]
Arith -->|opcode T_FLOOR/T_CEIL/T_ABS/...| Math[db_floor_dbval/db_ceil_dbval/db_abs_dbval/...]
Func -->|F_SET/MULTISET/SEQUENCE| MkSet[qdata_convert_dbvals_to_set]
Func -->|F_INSERT_SUBSTRING| Ins[qdata_insert_substring_function]
Func -->|F_ELT| Elt[qdata_elt]
Func -->|F_JSON_ARRAY/MERGE/EXTRACT/...| JFan[qdata_convert_operands_to_value_and_call]
Func -->|F_REGEXP_COUNT/INSTR/LIKE/REPLACE/SUBSTR| RexF[qdata_regexp_function]
JFan --> Jeval[db_evaluate_json_*]
RexF --> Rexd[db_string_regexp_*]
산술 — qdata_add_dbval 과 페어별 커널들
섹션 제목: “산술 — qdata_add_dbval 과 페어별 커널들”qdata_add_dbval은 +의 우산이다. 세 단계로 쪼개진다. (1)
ENUMERATION 사전 강제 변환 — 다른 피연산자가 문자열이면 VARCHAR,
아니면 SHORT로 캐스팅한 뒤 재귀. (2) PRM_ID_PLUS_AS_CONCAT
단축 — 두 피연산자가 모두 character 또는 bit 타입이면
qdata_strcat_dbval로 전달 (MySQL 호환의 'a' + 'b' = 'ab'). (3)
promotion lattice 강제 변환 — STRING + NUMBER, NUMBER + DATE,
STRING + DATE 인 경우 피연산자가 정규 순서로 swap 될 수 있고,
문자열 쪽은 DOUBLE 로, date에 더해지는 숫자 쪽은 BIGINT 로
강제 변환된다는 점이다. 그 다음 DB_TYPE 별 switch가 단형 커널로
넘긴다.
// qdata_add_dbval — src/query/query_opfunc.cintqdata_add_dbval (DB_VALUE * dbval1_p, DB_VALUE * dbval2_p, DB_VALUE * result_p, tp_domain * domain_p){ DB_TYPE type1 = DB_VALUE_DOMAIN_TYPE (dbval1_p); /* Phase 1: ENUMERATION pre-coercion (recursive). */ /* Phase 2: PLUS_AS_CONCAT short-circuit -> qdata_strcat_dbval. */ /* Phase 3: mixed-type coercion via tp_value_auto_cast. */ /* Phase 4: per-DB_TYPE dispatch. */ switch (type1) { case DB_TYPE_SHORT: error = qdata_add_short_to_dbval (dbval1_p, dbval2_p, result_p, domain_p); break; case DB_TYPE_INTEGER: error = qdata_add_int_to_dbval (dbval1_p, dbval2_p, result_p, domain_p); break; case DB_TYPE_BIGINT: error = qdata_add_bigint_to_dbval (dbval1_p, dbval2_p, result_p, domain_p); break; case DB_TYPE_FLOAT: error = qdata_add_float_to_dbval (dbval1_p, dbval2_p, result_p); break; case DB_TYPE_DOUBLE: error = qdata_add_double_to_dbval (dbval1_p, dbval2_p, result_p); break; case DB_TYPE_NUMERIC: error = qdata_add_numeric_to_dbval(dbval1_p, dbval2_p, result_p); break; case DB_TYPE_MONETARY: error = qdata_add_monetary_to_dbval(dbval1_p, dbval2_p, result_p); break; case DB_TYPE_DATE: error = qdata_add_date_to_dbval (dbval1_p, dbval2_p, result_p); break; /* DATETIME, TIMESTAMP, TIME, TIMESTAMPTZ, DATETIMETZ, ... */ } return qdata_coerce_result_to_domain (result_p, domain_p);}페어별 커널 qdata_add_int_to_dbval (LHS는 INT, RHS로 분기) 는
두 번째 피연산자의 DB_TYPE를 안쪽 switch를 돌려 완전히 단형화된
헬퍼를 부른다.
// qdata_add_int_to_dbval — src/query/query_opfunc.cstatic intqdata_add_int_to_dbval (DB_VALUE * int_val_p, DB_VALUE * dbval_p, DB_VALUE * result_p, TP_DOMAIN * domain_p){ int i = db_get_int (int_val_p); switch (DB_VALUE_DOMAIN_TYPE (dbval_p)) { case DB_TYPE_SHORT: return qdata_add_int (i, db_get_short (dbval_p), result_p); case DB_TYPE_INTEGER: return qdata_add_int (i, db_get_int (dbval_p), result_p); case DB_TYPE_BIGINT: return qdata_add_bigint (i, db_get_bigint(dbval_p), result_p); case DB_TYPE_FLOAT: return qdata_add_float ((float) i, db_get_float (dbval_p), result_p); case DB_TYPE_DOUBLE: return qdata_add_double (i, db_get_double(dbval_p), result_p); case DB_TYPE_NUMERIC: return qdata_add_numeric (dbval_p, int_val_p, result_p); case DB_TYPE_TIMESTAMP: return qdata_add_int_to_utime (dbval_p, i, result_p, domain_p); case DB_TYPE_DATE: return qdata_add_int_to_date (dbval_p, i, result_p, domain_p); /* MONETARY, TIME, TIMESTAMPLTZ, TIMESTAMPTZ, DATETIME, DATETIMELTZ, DATETIMETZ */ }}단형 커널 qdata_add_int (그리고 qdata_add_short,
qdata_add_bigint, qdata_add_float, qdata_add_double) 이 실제
산술과 오버플로 검사를 수행하는 자리다. 정수 덧셈은
ER_QPROC_OVERFLOW_ADDITION 을 일으키고, bigint 덧셈은
DB_BIGINT_MAX 를 점검하며, float/double 산술은 IEEE 무한 전파에
의존한다는 점이다.
네 우산 — qdata_add_dbval, qdata_subtract_dbval,
qdata_multiply_dbval, qdata_divide_dbval — 은 같은 모양을
공유한다. qdata_unary_minus_dbval은 더 단순하지만(피연산자 1개)
같은 DB_TYPE 별 switch 패턴을 따른다. db_mod_dbval(arithmetic.c)
은 결이 다르다. qdata_* 우산보다 먼저 등장한 함수라 직접
db_mod_short, db_mod_int, db_mod_bigint, db_mod_float,
db_mod_double, db_mod_string, db_mod_numeric, db_mod_monetary
로 fan-out 한다.
런타임에서 관찰되는 promotion lattice:
graph TD
Short[SHORT] --> Int[INTEGER]
Int --> BigInt[BIGINT]
BigInt --> Numeric[NUMERIC<br/>BCD, exact]
BigInt --> Float[FLOAT<br/>IEEE 32]
Float --> Double[DOUBLE<br/>IEEE 64]
Numeric --> Double
Char[CHAR/VARCHAR] -->|tp_value_auto_cast| Double
Date[DATE] -->|+INT/BIGINT| Date
Date -->|+STRING| BigIntDate[BIGINT then DATE]
Enum[ENUMERATION] -->|cast first| Short
Enum -->|cast first| Char
Numeric (BCD) — numeric_db_value_*
섹션 제목: “Numeric (BCD) — numeric_db_value_*”DB_NUMERIC은 16바이트 binary digit 버퍼이며 two’s-complement
부호가 적용된다. numeric_opfunc.c가 이 표현 위의 산술을 구현한다.
커널들은 학교에서 배운 자릿수 계산을 닮은, 바이트 단위 carry 전파
루프다.
// numeric_add — src/query/numeric_opfunc.c// carry-propagating byte add, MSB at offset 0, LSB at offset size-1.static voidnumeric_add (DB_C_NUMERIC arg1, DB_C_NUMERIC arg2, DB_C_NUMERIC answer, int size){ unsigned int answer_bit = 0; for (int digit = size - 1; digit >= 0; digit--) { answer_bit = (arg1[digit] + arg2[digit]) + CARRYOVER (answer_bit); answer[digit] = GET_LOWER_BYTE (answer_bit); }}
/* numeric_sub negates arg2 (two's complement) then forwards to numeric_add. numeric_mul is long multiplication: outer loop shifts arg1 by `shift` bytes, inner loop computes one digit of the product, results accumulate via numeric_add. numeric_long_div is shift-and-subtract (Knuth Algorithm D variant). */공개 진입점은 numeric_db_value_add, numeric_db_value_sub,
numeric_db_value_mul, numeric_db_value_div 다. 각각 다음을
수행한다.
- 인자가
DB_TYPE_NUMERIC인지 검증. numeric_common_prec_scale로 두 피연산자를 공통 정밀도와 스케일 로 강제 변환. 작은 scale을 가진 쪽이 좌측으로 시프트 (10의 거듭제곱을 곱함) 되어 둘이 같은 scale을 갖도록 맞추고, precision은 carry 여유를 두기 위해max(p1, p2) + 1로 넓힌다.- 커널(
numeric_add등)을 호출. numeric_overflow로 오버플로 탐지 (새 precision 너머 상위 바이트가 0이 아닌지 점검). 오버플로면< DB_MAX_NUMERIC_PRECISION에서 precision 을 한 자리 더 넓히거나, 아니면ER_IT_DATA_OVERFLOW를 발생.db_make_numeric(answer, prec, scale)로 결과DB_VALUE를 짠다.
BCD 표현으로 들어오고 나가는 변환도 같은 파일 안에 있다.
numeric_coerce_int_to_num, numeric_coerce_double_to_num,
numeric_coerce_string_to_num (double 정밀 파싱은 mprec.h 위에
얹힌다) 와 그 역인 numeric_coerce_num_to_int,
numeric_coerce_num_to_double, numeric_coerce_num_to_dec_str. 16
바이트를 넘기는 중간 결과를 위해 32바이트 long-numeric 변형도
존재한다. numeric_is_longnum_value, numeric_shortnum_to_longnum,
numeric_longnum_to_shortnum.
문자열 — db_string_*
섹션 제목: “문자열 — db_string_*”string_opfunc.c는 src/query/에서 가장 큰 파일이다 (693 KB,
약 350개 함수). 대표 항목:
| 연산자 | 루틴 | 비고 |
|---|---|---|
|| / CONCAT | db_string_concatenate | codeset 검사. NULL 처리는 PRM_ID_ORACLE_STYLE_EMPTY_STRING 결정 |
SUBSTR / SUBSTRING / MID | db_string_substring | MISC_OPERAND 로 두 변형 |
INSTR / POSITION | db_string_instr | 1-기반 인덱스 |
LOWER / UPPER | db_string_lower / _upper | collation 알파벳은 lang_user_alphabet_w_coll |
LIKE | db_string_like | 4치 DB_LOGICAL. qstr_eval_like 에 위임 |
RLIKE / REGEXP | db_string_rlike | cubregex::compile 로 컴파일 |
LPAD / RPAD | db_string_lpad / _rpad | 길이는 문자 단위 |
TRIM / LTRIM / RTRIM | db_string_trim | MISC_OPERAND 로 디스패치 |
REPLACE / TRANSLATE | db_string_replace / _translate | source-find-replace 와 문자 표 치환 |
REPEAT | db_string_repeat | PRM_ID_STRING_MAX_SIZE_BYTES 사전 검사 |
INSERT | db_string_insert_substring | F_INSERT_SUBSTRING 함수 모양 |
LENGTH / CHAR_LENGTH | db_string_char_length / _bit_length | 문자 vs 비트 |
MD5 / SHA1 / SHA2 | db_string_md5 / _sha_one / _sha_two | crypt_* 래핑 |
AES_ENCRYPT / AES_DECRYPT | db_string_aes_encrypt / _decrypt | AES-128/ECB/PKCS7 |
REVERSE / SPACE / QUOTE | db_string_reverse / _space / _quote | UTF-8 인지 reverse, padding, escape |
db_string_substring이 codeset을 의식하는 모양을 잘 보여준다.
// db_string_substring — src/query/string_opfunc.cintdb_string_substring (const MISC_OPERAND substr_operand, const DB_VALUE * src_string, const DB_VALUE * start_position, const DB_VALUE * extraction_length, DB_VALUE * sub_string){ if (DB_IS_NULL (src_string) || DB_IS_NULL (start_position)) { db_make_null (sub_string); return NO_ERROR; } /* validate types: QSTR_IS_ANY_CHAR_OR_BIT, is_integer ... */
if (QSTR_IS_CHAR (src_type)) { const unsigned char *string = DB_GET_UCHAR (src_string); int string_len = db_get_string_length (src_string); int start_offset = db_get_int (start_position); int extract_nchars = extraction_length_is_null ? string_len : db_get_int (extraction_length);
/* For SUBSTR (Oracle), negative start counts from the end via codeset walk: intl_char_size advances byte_pos to the (length+start)'th char. */ if (substr_operand == SUBSTR && start_offset < 0) { int byte_pos; intl_char_size (string, string_len + start_offset, db_get_string_codeset (src_string), &byte_pos); string += byte_pos; string_len = -start_offset; }
qstr_substring (string, string_len, start_offset, extract_nchars, db_get_string_codeset (src_string), &sub, &sub_length, &sub_size); qstr_make_typed_string (result_type, sub_string, ..., (char *) sub, sub_size, db_get_string_codeset (src_string), db_get_string_collation (src_string)); sub_string->need_clear = true; } else { /* qstr_bit_substring for bit-string variant */ } return NO_ERROR;}흐름은 균일하다. 타입 검증, NULL 조기 반환, qstr_* 바이트 단위
커널에 위임, qstr_make_typed_string 으로 결과 packaging (결과
DB_VALUE 에 codeset과 collation 을 세팅).
db_string_lower는 collation 스레드를 잘 보여준다. 알파벳이
collation 별로 다르므로 같은 codepoint도 collation에 따라 소문자
결과가 달라진다는 점이다 (Turkish I -> ı under tr_TR).
// db_string_lower — src/query/string_opfunc.cintdb_string_lower (const DB_VALUE * string, DB_VALUE * lower_string){ if (DB_IS_NULL (string)) { db_make_null (lower_string); return NO_ERROR; } const ALPHABET_DATA *alphabet = lang_user_alphabet_w_coll (db_get_string_collation (string)); int lower_size = intl_lower_string_size (alphabet, DB_GET_UCHAR (string), ...); unsigned char *lower_str = (unsigned char *) db_private_alloc (NULL, lower_size + 1); intl_lower_string (alphabet, DB_GET_UCHAR (string), lower_str, src_length); qstr_make_typed_string (..., db_get_string_codeset (string), db_get_string_collation (string)); return NO_ERROR;}db_string_like 는 술어 짝꿍이다. 문자열 결과가 아니라 4치
DB_LOGICAL (V_TRUE / V_FALSE / V_UNKNOWN / V_ERROR) 를
돌려준다. 본체는 타입/codeset 검증, LANG_RT_COMMON_COLL 로 공통
collation 해소, 그 뒤 qstr_eval_like 호출 — % / _ 와일드카드와
선택적 ESCAPE 문자를 처리하는 바이트 워킹 매처 — 의 순서로
진행된다. 따라서 LIKE는 boolean을 돌려주는 스칼라 함수이며,
술어 평가기는 이를 T_LIKE_EVAL_TERM 으로 감싸 필터링에 쓴다.
날짜·시간
섹션 제목: “날짜·시간”날짜 산술은 arithmetic.c (SQL 수준 진입점 — db_round_dbval,
db_trunc_dbval, db_add_months) 과 query_opfunc.c (한쪽이
date/time, 다른 쪽이 numeric 일 때 호출되는 페어별
qdata_add_int_to_date, qdata_add_int_to_datetime,
qdata_add_int_to_utime, qdata_add_short_to_timestamptz) 에 나뉘어
있다. format/parse 는 db_string_to_date, db_string_to_datetime,
db_string_to_timestamp 와 그 역 db_date_format,
db_datetime_format. 풀 date 라이브러리는 src/base/db_date.c 에
산다. 스칼라 함수 레이어는 그 SQL façade 이다.
JSON
섹션 제목: “JSON”arithmetic.c는 약 25개의 JSON 평가기를 담는다.
(db_evaluate_json_extract, _array, _object, _merge_preserve,
_merge_patch, _search, _contains, _contains_path, _keys,
_length, _depth, _type_dbval, _valid, _quote, _unquote,
_pretty, _array_append, _array_insert, _insert, _replace,
_set, _remove, _get_all_paths, 그리고 집계 누산기
db_accumulate_json_arrayagg / _objectagg). 모두 시그니처
(DB_VALUE *result, DB_VALUE * const *args, int num_args) 를
공유하며, src/compat/db_json.cpp (rapidjson 래퍼) 에 위임한다.
JSONPath 문법은 db_json_path.hpp 가 정의한다.
qdata_evaluate_function에서의 디스패치는 균일하다. 모든 JSON
함수는 qdata_convert_operands_to_value_and_call으로 흐른다.
이 함수는 regu-variable 리스트를 스택 할당 DB_VALUE *args[] 로
fetch하고 함수 포인터를 호출한 뒤 배열을 해제한다는 점이다.
정규식
섹션 제목: “정규식”string_regex.cpp의 정규식 façade는 여섯 연산을 노출한다
(compile, search, count, instr, replace, substr). 각각은
원래 codeset과 무관하게 인자 std::string 들을 UTF-8로 변환하고,
compiled_regex::type 으로 분기해 RE2 백엔드 (string_regex_re2.cpp
의 re2_*) 또는 C++ std::regex 백엔드 (string_regex_std.cpp 의
std_*) 로 전달한다. 백엔드 선택은 컴파일 시점에 일어나며, 시스템
파라미터 PRM_ID_REGEXP_ENGINE 이 결정한다는 점이다.
// cubregex::compile — src/query/string_regex.cppint compile (REFPTR (compiled_regex, cr), const std::string &pattern_string, const std::string &opt_str, const LANG_COLLATION *collation){ opt_flag_type opt_flag = parse_match_type (opt_str); /* 'c' / 'i' */ engine_type type = static_cast<engine_type> (prm_get_integer_value (PRM_ID_REGEXP_ENGINE)); cublocale::convert_string_to_utf8 (utf8_pattern, pattern_string, collation->codeset); if (should_compile_skip (cr, utf8_pattern, type, opt_flag, collation->codeset)) return NO_ERROR; return compile_regex_internal (cr, utf8_pattern, type, opt_flag, collation);}compile-skip 경로가 행 단위 핫 패스다. 패턴이 행 사이에서 상수
일 때, 첫 행만 컴파일 비용을 치르고 그 뒤의 모든 행은 캐시된
cub_compiled_regex * 를 재사용한다. 이 캐시는
function_p->tmp_obj (또는 연산자 모양 RLIKE의 경우
arithptr->aux) 에 저장된다.
두 백엔드는 공유하는 부분이 거의 없다. RE2는 메서드 기반 API
(RE2::PartialMatch, RE2::FindAndConsume) 로 UTF-8 바이트
문자열 위에서 곧장 동작한다. std::regex는 이터레이터 기반
(std::regex_iterator, std::wstring) 이라 UTF-8 → wide → UTF-8
왕복이 필요하다. RE2는 lookaround도 backreference도 지원하지
않지만 선형 시간이고, std::regex는 둘 다 지원하는 대신 잠재적
지수 시간 비용을 진다.
stateDiagram-v2
[*] --> NotCompiled
NotCompiled --> Compiling: cubregex::compile()
Compiling --> ChooseEngine: parse opt_str, read PRM_ID_REGEXP_ENGINE
ChooseEngine --> RE2: type == LIB_RE2
ChooseEngine --> Std: type == LIB_CPPSTD
RE2 --> Compiled: re2_compile() ok
Std --> Compiled: std_compile() ok
Compiled --> Searching: search/count/instr/replace/substr
Searching --> Compiled: still cached, pattern unchanged
Compiled --> Recompile: pattern or flags changed
Recompile --> Compiling
Compiled --> [*]: ~compiled_regex
crypt_opfunc.c는 OpenSSL을 세 가족으로 감싼다. 해싱: crypt_sha_one
(SHA-1), crypt_sha_two (해시 길이로 SHA-224/256/384/512 선택),
crypt_md5_buffer_hex / crypt_md5_buffer_binary (MD5). 모두
EVP_DigestInit/Update/Final 을 호출하며, hex 인코딩은
str_to_hex_prealloced 로 자체 처리. 암호화:
crypt_default_encrypt / crypt_default_decrypt 는
CIPHER_ENCRYPTION_TYPE (AES_128_ECB / DES_ECB) 와 PKCS7
패딩을 받는다. aes_default_gen_key 는 임의 길이 키를 16바이트로
XOR-fold 한다 (MySQL의 AES_ENCRYPT 와 호환). EVP_CIPHER_CTX 는
deleted_unique_ptr 로 RAII 래핑되어 있다는 점이다. 랜덤 / CRC:
crypt_generate_random_bytes (OpenSSL RAND_bytes), crypt_crc32.
dblink 헬퍼들 (crypt_dblink_encrypt, shake_dblink_password) 은
서버간 링크 비밀번호를 고정 16바이트 transport 키로 난독화한다.
SQL 표면 (db_string_md5, _sha_one, _sha_two, _aes_encrypt,
_aes_decrypt, _des_encrypt) 은 string_opfunc.c 에 있는 얇은
래퍼들이다. NULL 처리, QSTR_IS_ANY_CHAR 타입 검사, 버퍼 소유권
관리(db_private_alloc + need_clear=true)를 거친 뒤 바이트를
crypt_* 커널로 흘려보낸다.
타입 시스템 통합
섹션 제목: “타입 시스템 통합”컴파일 시점의 표면은 parser/type_checking.c 와
parser/func_type.cpp 다. 함수 모양 연산자 (F_INSERT_SUBSTRING,
JSON, 정규식, …) 의 타입 검증은 pt_eval_function_type 가 수행한다.
pt_is_function_new_type_checking 으로 opt-in 한 코드의 경우
func_type::Node 를 만들어 그 type_checking () 메서드를 호출한다는
점이다. func_type.cpp 는 FUNC_CODE 별 시그니처 표 —
sig_of_insert_substring, sig_of_elt, sig_of_benchmark,
sig_ret_json_arg_jdoc_r_jpath, sig_of_regexp_count, … — 를 갖고
있다. 인자 타입은 가짜 타입 enum (PT_GENERIC_TYPE_JSON_DOC,
PT_GENERIC_TYPE_STRING) 으로 표현하며, 고정 인자 / 반복 인자 분할
(F_JSON_OBJECT 의 (key, value) 반복쌍이 한 시그니처) 도 지원한다.
매처는 cast 비용으로 후보를 점수 매겨 가장 좋은 것을 고르거나,
없으면 에러를 돌려준다.
산술 모양 연산자 (PT_ADD, PT_SUBSTRING, PT_LIKE, …) 는
pt_eval_expr_type 가 pt_apply_expressions_definition 을 부르는
방식으로 (오래된 형태이지만 유사하게) 검증한다. 두 레인은 결국 같은
강제 변환 인프라 (pt_coerce_expression_argument,
tp_value_auto_cast) 를 공유한다. 타입이 정해지면
xasl_generation.c 가 연산자 트리를 ARITH_TYPE (연산자) 또는
FUNCTION_TYPE (함수) 노드로 packaging 하고, TYPE_INARITH /
TYPE_FUNC 태그가 붙은 REGU_VARIABLE 로 감싼다. 런타임 디스패치는
이 태그를 키로 동작한다.
평가 시점의 도메인 강제 변환
섹션 제목: “평가 시점의 도메인 강제 변환”컴파일 시점의 강제 변환은 권고에 가깝다. 런타임은 옵티마이저의
타입을 신뢰할 수 없다는 점이다. 호스트 변수는 다르게 바인딩될 수
있고, 서브쿼리 결과는 실행 시점에야 해소되는 DB_TYPE_VARIABLE 일
수 있으며, T_DEFAULT / NULL 치환이 피연산자 종류를 바꿀 수 있기
때문이다. 그래서 모든 qdata_*_dbval 우산은 자체 강제 변환 패스를
tp_value_auto_cast 로 돌린다. 문자열 연산의 짝꿍은
tp_value_str_auto_cast_to_number 다. db_floor_dbval /
db_round_dbval 가 이를 호출하므로 FLOOR('3.7') 이 3 을
산출한다는 점이다.
소스 코드 가이드
섹션 제목: “소스 코드 가이드”산술 디스패처와 우산 (query_opfunc.c)
섹션 제목: “산술 디스패처와 우산 (query_opfunc.c)”디스패처: qdata_evaluate_function. 이항 우산: qdata_add_dbval,
qdata_subtract_dbval, qdata_multiply_dbval, qdata_divide_dbval.
단항: qdata_unary_minus_dbval. concat: qdata_strcat_dbval. LHS
타입별 fan-out: qdata_add_short_to_dbval / _int_to_dbval /
_bigint_to_dbval / _float_to_dbval / _double_to_dbval /
_numeric_to_dbval / _monetary_to_dbval / _date_to_dbval /
_datetime_to_dbval 와 qdata_subtract_*, qdata_multiply_*,
qdata_divide_* 의 평행 가족. 오버플로 검사가 들어간 단형 커널들:
qdata_add_short, qdata_add_int, qdata_add_bigint,
qdata_add_float, qdata_add_double, qdata_add_numeric,
qdata_add_monetary. 날짜·시간 헬퍼: qdata_add_int_to_utime,
qdata_add_int_to_date, qdata_add_int_to_datetime. 비트 연산:
qdata_bit_and_dbval, qdata_bit_or_dbval, qdata_bit_xor_dbval,
qdata_bit_not_dbval, qdata_bit_shift_dbval. 정수 divmod:
qdata_divmod_dbval. 결과 강제 변환: qdata_coerce_result_to_domain.
함수 모양 fan-in: qdata_regexp_function,
qdata_convert_operands_to_value_and_call,
qdata_convert_dbvals_to_set, qdata_convert_table_to_set,
qdata_insert_substring_function, qdata_elt, qdata_benchmark,
qdata_evaluate_generic_function, qdata_get_class_of_function.
산술 프리미티브 (arithmetic.c)
섹션 제목: “산술 프리미티브 (arithmetic.c)”수학: db_floor_dbval, db_ceil_dbval, db_sign_dbval,
db_abs_dbval, db_exp_dbval, db_sqrt_dbval, db_power_dbval.
MOD: db_mod_dbval 과 타입별 db_mod_short / _int / _bigint /
_float / _double / _string / _numeric / _monetary. 반올림:
db_round_dbval, db_trunc_dbval, round_double, round_date,
truncate_double, truncate_bigint, truncate_date. 로그:
db_log_dbval, db_log_generic_dbval. 난수: db_random_dbval,
db_drandom_dbval. 삼각함수: db_sin_dbval, db_cos_dbval,
db_tan_dbval, db_cot_dbval, db_acos_dbval, db_asin_dbval,
db_atan_dbval. 각도: db_degrees_dbval, db_radians_dbval. 기타:
db_bit_count_dbval, db_typeof_dbval, db_width_bucket, db_sleep,
db_least_or_greatest. JSON 가족 (~25개):
db_evaluate_json_extract, db_evaluate_json_array,
db_evaluate_json_object, db_evaluate_json_search,
db_evaluate_json_merge_preserve, db_evaluate_json_merge_patch,
db_evaluate_json_contains, db_evaluate_json_contains_path,
db_evaluate_json_keys, db_evaluate_json_length,
db_evaluate_json_depth, db_evaluate_json_type_dbval,
db_evaluate_json_valid, db_evaluate_json_quote,
db_evaluate_json_unquote, db_evaluate_json_pretty,
db_evaluate_json_array_append, db_evaluate_json_array_insert,
db_evaluate_json_insert, db_evaluate_json_replace,
db_evaluate_json_set, db_evaluate_json_remove,
db_evaluate_json_get_all_paths, db_accumulate_json_arrayagg,
db_accumulate_json_objectagg.
Numeric (BCD) (numeric_opfunc.c)
섹션 제목: “Numeric (BCD) (numeric_opfunc.c)”커널: numeric_add, numeric_sub, numeric_mul, numeric_long_div,
numeric_div. 바이트 연산: numeric_negate, numeric_negate_long,
numeric_copy, numeric_copy_long, numeric_increase,
numeric_decrease, numeric_zero, numeric_shift_byte,
numeric_double_shift_bit. 비교: numeric_compare,
numeric_compare_pos. 술어: numeric_is_negative, numeric_is_zero,
numeric_is_long, numeric_is_bigint, numeric_overflow,
numeric_is_longnum_value. 스케일: numeric_scale_by_ten,
numeric_scale_dec, numeric_common_prec_scale,
numeric_prec_scale_when_overflow. 공개 진입점:
numeric_db_value_add, numeric_db_value_sub, numeric_db_value_mul,
numeric_db_value_div, numeric_db_value_negate,
numeric_db_value_abs, numeric_db_value_compare,
numeric_db_value_is_zero, numeric_db_value_increase,
numeric_db_value_print. 변환: numeric_coerce_int_to_num,
_bigint_to_num, _double_to_num, _string_to_num, _num_to_int,
_num_to_bigint, _num_to_double, _num_to_dec_str, _num_to_num,
_dec_str_to_num. DB_VALUE 변환: numeric_db_value_coerce_to_num,
_coerce_from_num, _coerce_from_num_strict. Float→BCD:
numeric_internal_real_to_num, numeric_fast_convert. 정수·소수
분할: numeric_get_integral_part, numeric_get_fractional_part,
numeric_is_fraction_part_zero.
문자열 (string_opfunc.c)
섹션 제목: “문자열 (string_opfunc.c)”concat: db_string_concatenate, qstr_concatenate. substring:
db_string_substring, qstr_substring, qstr_bit_substring,
db_string_substring_index. repeat/space: db_string_repeat,
db_string_space. trim: db_string_trim, qstr_trim. padding:
db_string_lpad, db_string_rpad. case: db_string_lower,
db_string_upper. replace: db_string_replace,
db_string_translate. 기타: db_string_reverse, db_string_quote,
db_string_chr, db_string_escape_str, db_string_escape_char.
search: db_string_instr, db_string_position,
db_find_string_in_in_set. LIKE: db_string_like, qstr_eval_like.
정규식: db_string_rlike, db_string_regexp_count, _regexp_instr,
_regexp_like, _regexp_replace, _regexp_substr. 해싱:
db_string_md5, db_string_sha_one, db_string_sha_two. 암호화:
db_string_aes_encrypt, db_string_aes_decrypt,
db_string_des_encrypt. insert: db_string_insert_substring. 길이:
db_string_char_length, db_string_bit_length. 헬퍼:
qstr_make_typed_string, qstr_get_category, qstr_compare,
qstr_compare_with_collation.
암호 (crypt_opfunc.c)
섹션 제목: “암호 (crypt_opfunc.c)”SHA: crypt_sha_one, crypt_sha_two, crypt_sha_functions. MD5:
crypt_md5_buffer_hex, crypt_md5_buffer_binary. AES/DES:
crypt_default_encrypt, crypt_default_decrypt,
aes_default_gen_key. 랜덤: crypt_generate_random_bytes. CRC:
crypt_crc32. Hex: str_to_hex, str_to_hex_prealloced. dblink:
crypt_dblink_encrypt, _decrypt, shake_dblink_password,
reverse_shake_dblink_password, crypt_dblink_bin_to_str,
_str_to_bin.
정규식 (string_regex*.cpp)
섹션 제목: “정규식 (string_regex*.cpp)”Façade (string_regex.cpp): cubregex::compile, search, count,
instr, replace, substr. 내부 헬퍼 compile_regex_internal,
should_compile_skip, parse_match_type,
compiled_regex::~compiled_regex. RE2 백엔드 (string_regex_re2.cpp):
re2_compile, re2_search, re2_count, re2_instr, re2_replace,
re2_substr, re2_on_match (template helper),
re2_split_string_utf8, re2_distance_utf8. std::regex 백엔드
(string_regex_std.cpp): std_compile, std_search, std_count,
std_instr, std_replace, std_substr,
std_parse_regex_exception, std_parse_match_type, 그리고
cub_reg_traits (collation traits 서브클래스. [[. .]] 에서 throw,
iswblank 호환성 패치).
타입 시스템 글루
섹션 제목: “타입 시스템 글루”parser/type_checking.c: pt_eval_expr_type (PT_EXPR 연산자 타이퍼),
pt_apply_expressions_definition (표 기반 오버로드 해소),
pt_eval_function_type (PT_FUNCTION 타이퍼),
pt_evaluate_function_w_args (상수 폴딩),
pt_coerce_expression_argument (PT_CAST 삽입).
parser/func_type.cpp: func_signature::get (FUNC_CODE 별 시그니처
표 조회), func_type::Node::type_checking (새 함수 타이핑 진입점),
sig_has_json_args, is_type_with_collation,
can_signature_have_collation, 그리고 sig_of_* 표.
compat/db_function.hpp: FUNC_CODE enum,
fcode_get_lowercase_name, fcode_get_uppercase_name.
이 개정 시점의 위치 힌트
섹션 제목: “이 개정 시점의 위치 힌트”| 심볼 | 파일 | 라인 |
|---|---|---|
qdata_evaluate_function | src/query/query_opfunc.c | 6875 |
qdata_add_dbval | src/query/query_opfunc.c | 2436 |
qdata_subtract_dbval | src/query/query_opfunc.c | 4522 |
qdata_multiply_dbval | src/query/query_opfunc.c | 5225 |
qdata_divide_dbval | src/query/query_opfunc.c | 5829 |
qdata_unary_minus_dbval | src/query/query_opfunc.c | 5986 |
qdata_add_short_to_dbval | src/query/query_opfunc.c | 1701 |
qdata_add_int_to_dbval | src/query/query_opfunc.c | 1792 |
qdata_strcat_dbval | src/query/query_opfunc.c | 6111 |
qdata_regexp_function | src/query/query_opfunc.c | 8627 |
qdata_convert_operands_to_value_and_call | src/query/query_opfunc.c | 8712 |
fetch_peek_arith | src/query/fetch.c | 84 |
db_floor_dbval | src/query/arithmetic.c | 89 |
db_abs_dbval | src/query/arithmetic.c | 563 |
db_power_dbval | src/query/arithmetic.c | 832 |
db_mod_dbval | src/query/arithmetic.c | 1910 |
db_round_dbval | src/query/arithmetic.c | 2295 |
db_trunc_dbval | src/query/arithmetic.c | 3289 |
db_evaluate_json_extract | src/query/arithmetic.c | 5485 |
db_evaluate_json_search | src/query/arithmetic.c | 6242 |
numeric_add | src/query/numeric_opfunc.c | 822 |
numeric_mul | src/query/numeric_opfunc.c | 871 |
numeric_long_div | src/query/numeric_opfunc.c | 953 |
numeric_db_value_add | src/query/numeric_opfunc.c | 1596 |
numeric_db_value_mul | src/query/numeric_opfunc.c | 1797 |
numeric_db_value_div | src/query/numeric_opfunc.c | 1875 |
db_string_concatenate | src/query/string_opfunc.c | 942 |
db_string_substring | src/query/string_opfunc.c | 1727 |
db_string_sha_one | src/query/string_opfunc.c | 2397 |
db_string_sha_two | src/query/string_opfunc.c | 2462 |
db_string_md5 | src/query/string_opfunc.c | 2693 |
db_string_insert_substring | src/query/string_opfunc.c | 2763 |
db_string_lower | src/query/string_opfunc.c | 3321 |
db_string_trim | src/query/string_opfunc.c | 3504 |
db_string_like | src/query/string_opfunc.c | 4214 |
db_string_rlike | src/query/string_opfunc.c | 4371 |
db_string_replace | src/query/string_opfunc.c | 6031 |
cubregex::compile | src/query/string_regex.cpp | 91 |
cubregex::search | src/query/string_regex.cpp | 120 |
re2_compile | src/query/string_regex_re2.cpp | 56 |
std_compile | src/query/string_regex_std.cpp | 96 |
crypt_default_encrypt | src/query/crypt_opfunc.c | 216 |
crypt_sha_one | src/query/crypt_opfunc.c | 477 |
crypt_sha_two | src/query/crypt_opfunc.c | 494 |
pt_eval_function_type | src/parser/type_checking.c | 12285 |
pt_apply_expressions_definition | src/parser/type_checking.c | 5778 |
FUNC_CODE enum | src/compat/db_function.hpp | 26 |
소스 검증 노트
섹션 제목: “소스 검증 노트”호출자는 regu 평가기다 (cubrid-query-evaluator.md). 그 문서는
fetch_peek_dbval 이 REGU_VARIABLE::type 으로 분기하는 방식을
설명한다. 이 라이브러리에 관여하는 두 가지는 TYPE_INARITH → fetch_peek_arith 와 TYPE_FUNC → qdata_evaluate_function 이다. 둘
모두 이 라이브러리로 빠져 나간다는 점이다. 계약: 호출자는
도메인 (TP_DOMAIN *) 과 결과 DB_VALUE * 를 피호출자에게 넘긴다.
피호출자는 NO_ERROR 와 함께 결과를 쓰거나 에러 코드를 돌려준다.
peek-vs-copy 규율은 호출자 측에 있다. 모든 db_* / qdata_*
프리미티브는 문자열·비트·JSON 결과를 새로 할당된 값
(need_clear=true) 을 돌려주며, 호출자는 pr_clear_value(funcp->value)
로 정리한다 (qdata_evaluate_function 도입부에서 수행).
타입 해소는 의미 검사에서 일어난다 (cubrid-semantic-check.md).
그 문서는 pt_eval_expr_type 과 pt_eval_function_type 가 연산자별
시그니처 표에서 오버로드를 고르고 PT_CAST 노드를 삽입하는
방식을 설명한다. 런타임은 캐스팅 누락에 강건하다. 페어별 커널이
DB_VALUE_DOMAIN_TYPE 으로 직접 분기하고, tp_value_auto_cast 로
다시 강제 변환을 시도하기 때문이다. 이 비대칭은 의도된 것이다. 의미 검사를 우회하는 시스템 내부 질의에 대해서도 런타임이 독립적으로
동작하도록 만들어 준다는 점이다.
qdata_evaluate_function 과 fetch_peek_dbval 은 동기화 유지가
필요하다. 주석 /* should sync with fetch_peek_dbval () */ 은
load-bearing 이다. 새로 추가되는 모든 FUNC_CODE 는 두 루틴 모두에
평행 가지를 요구한다는 점이다. regu-variable peek 경로는
fetch_peek_dbval 의 함수 타입 가지를, 명시적 XASL 실행 경로는
qdata_evaluate_function 을 통과한다. 동기화가 어긋나면 어떤 질의
모양에서는 동작하고 다른 모양에서는 실패하는 함수가 생긴다.
numeric_db_value_* 는 닫힌 시스템이다. 네 우산 루틴은 두
피연산자가 모두 DB_TYPE_NUMERIC 일 것을 요구한다. 타입 간 케이스는
바깥에서 처리된다. 예컨대 qdata_add_numeric_to_dbval 가
numeric_db_value_add 를 부르기 전 에 INT → NUMERIC 캐스팅을
해 둔다는 점이다. numeric_opfunc.c 는 DB_TYPE_NUMERIC 외의 어떤
타입도 인지하지 않는다.
정규식과 RLIKE 는 컴파일된 패턴을 다른 자리에 캐시한다. 연산자
모양 RLIKE / REGEXP (db_string_rlike) 는 컴파일된 패턴을
arithptr->aux 에 저장한다. 함수 모양 F_REGEXP_* 는
function_p->tmp_obj->compiled_regex 에 저장한다. 둘 모두 상수 패턴
재사용을 위해 should_compile_skip 을 존중한다.
JSON 디스패처는 모두 convert and call 이다. 25개 JSON 가지가
모두 qdata_convert_operands_to_value_and_call 을 거쳐 고정 시그니처
(DB_VALUE *result, DB_VALUE * const *args, int num_args) 의 안쪽
포인터를 호출한다. db_evaluate_json_* 는 XASL 인지가 없는 평범한
C 함수다. arithmetic.c 안에 사는 이유는 단지 그 파일이 역사적으로
JSON 지원을 흡수했기 때문이다. 별도 json_opfunc.c 로 옮기더라도
디스패치는 바뀌지 않는다는 점이다.
미해결 질문
섹션 제목: “미해결 질문”벡터화 실행. 페어별 커널은 스칼라다. 호출당 한 쌍을 처리한다는
점이다. Postgres는 batch interpretation을 실험해 왔지만 CUBRID는 그렇지
않다. 두 단계 switch (fetch_peek_arith 와 페어별 커널) 자체가 이미
작긴 하지만, column-store 풍의 L1-locality 이점은 CUBRID 의
row-at-a-time 이터레이터에서 얻을 수 없다.
핫 함수에 대한 JIT. Postgres의 LLVM 기반 ExecCompileExpr 은
TPC-H에서 30% 이상을 절약한다. CUBRID는 eval_fnc 식의 연산자
specialization 만 수행한다. flat 한 XASL 표현은 JIT에 잘 맞는다는
점이다 (모든 regu-variable이 작고 완전히 타입화되어 있음). 게이팅
요인은 빌드 시점의 LLVM 비용이다.
Java SP 너머의 UDF. 현재 UDF 레인은 JNI 를 거치는 Java SP 엔진
(pl_engine/) 이다. in-process C 확장 SDK는 없다. F_GENERIC 은
유물이다. 네이티브 C 확장 레인은 빠듯한 수치/문자열 커스텀 함수의
호출 지연을 줄여 줄 수 있지만, DB_VALUE 에 대한 안정 ABI 가
선결 조건이다.
왜 정규식 백엔드가 두 개인가? RE2 와 std::regex 가 무조건 함께
링크되며, PRM_ID_REGEXP_ENGINE 가 세션마다 토글한다. 역사적으로는
std::regex 가 먼저였고, 예측 가능성 때문에 RE2가 추가되었으며,
backreference 호환을 위해 std::regex 가 남았다. RE2가 기본이 될지는
로드맵 질문이다.
왜 string_opfunc.c 를 쪼개지 않는가? 693 KB 라 엔진에서 가장
큰 파일이다. 프로젝트 가이드라인은 큰 파일을 쪼개지 않도록 권한다
(작은 qstr_* 헬퍼들의 inter-procedural inlining 이 깨질 수 있기
때문이다). 다만 신규 기여자에게는 인지 비용이 크다는 점이다. 어느
쪽이든 측정된 적은 없다.
상수 폴딩의 granularity. pt_evaluate_function_w_args 는 모든
인자가 PT_VALUE 인 호출을 폴딩한다. 부작용이 있는 함수
(RAND, NOW, BENCHMARK) 는 손으로 짠 술어로 제외한다는 점이다.
시그니처 표에 purity 어노테이션을 두면 이를 자동화할 수 있겠지만,
지금은 존재하지 않는다.
CUBRID 소스 파일: src/query/arithmetic.c, numeric_opfunc.c,
string_opfunc.c, query_opfunc.c, crypt_opfunc.c,
string_regex.cpp / string_regex.hpp / string_regex_constants.hpp,
string_regex_re2.cpp, string_regex_std.cpp, fetch.c;
src/parser/type_checking.c, func_type.cpp / func_type.hpp;
src/compat/db_function.hpp.
이론 참고문헌: Silberschatz, Korth, Sudarshan, Database System
Concepts, 7th ed., 4–5장; Date, An Introduction to Database
Systems, 8th ed., 4장; ISO/IEC 9075:2016 (SQL 표준); Knuth, TAOCP
vol. 2 §4.3 (다중 정밀 산술); Cox, “Regular Expression Matching Can
Be Simple And Fast”, swtch.com 2007; 엔진 간 비교를 위한 PostgreSQL
fmgr.c / clauses.c / pg_proc.h.
교차 참조: knowledge/code-analysis/cubrid/cubrid-query-evaluator.md
(이 라이브러리를 호출하는 regu-variable 평가기),
knowledge/code-analysis/cubrid/cubrid-semantic-check.md (런타임
디스패치에 자료를 공급하는 오버로드 해소).