From ad5f2ad3e82ecaf5d19580888ec31dbc3c7ad6bc Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:40:10 +0900 Subject: [PATCH 1/8] fix: Add missing math_dep to bench_col_layout build target bench_col_layout links columnar/session.c which calls sqrt() but was missing math_dep in its dependency list, causing linker failure. --- bench/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/meson.build b/bench/meson.build index b607ac65..1df36c15 100644 --- a/bench/meson.build +++ b/bench/meson.build @@ -115,7 +115,7 @@ bench_col_layout = executable( bench_thread_src, bench_workqueue_src, include_directories: [wirelog_inc, wirelog_src_inc], - dependencies: [nanoarrow_dep, threads_dep, xxhash_dep, mbedtls_dep], + dependencies: [nanoarrow_dep, threads_dep, xxhash_dep, mbedtls_dep, math_dep], install: false, ) From 58d5d1df0268295fb48873694f34beb906eb74c7 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:40:17 +0900 Subject: [PATCH 2/8] refactor(#495): Partition exec_plan enum into universal and backend-specific ranges Add range documentation and #define WL_PLAN_OP__BACKEND_START separating universal operators (0-8, backend-agnostic) from columnar backend operators (9+, use opaque_data). All numeric values unchanged. --- wirelog/exec_plan.h | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/wirelog/exec_plan.h b/wirelog/exec_plan.h index cd0357de..908fdc92 100644 --- a/wirelog/exec_plan.h +++ b/wirelog/exec_plan.h @@ -230,6 +230,15 @@ typedef enum { * Operator types in a backend execution plan. * Explicit integer values for stable ABI across backends. * + * Range partitioning (Issue #495): + * Universal operators (0-8): Backend-agnostic relational algebra ops. + * Use flat fields in wl_plan_op_t only. + * Backend-specific ops (9+): Columnar backend optimizations. + * Use opaque_data for backend-defined metadata. + * See WL_PLAN_OP__BACKEND_START below. + * + * --- Universal operators (0-8) --- + * * WL_PLAN_OP_VARIABLE: Reference to an input collection (EDB or IDB). * WL_PLAN_OP_MAP: Column projection / rename. * WL_PLAN_OP_FILTER: Predicate filter (expr in serialized buffer). @@ -239,14 +248,20 @@ typedef enum { * WL_PLAN_OP_CONCAT: Union of multiple collections. * WL_PLAN_OP_CONSOLIDATE: Deduplication / consolidation. * WL_PLAN_OP_SEMIJOIN: Semijoin (SIP pre-filter). + * + * --- Columnar backend operators (9-11) --- + * + * WL_PLAN_OP_K_FUSION: Parallel semi-naive delta expansion (Issue #370). + * opaque_data -> wl_plan_op_k_fusion_t. * WL_PLAN_OP_LFTJ: Multi-way leapfrog triejoin on a single shared key * column across k >= 3 EDB relations (Issue #195). + * opaque_data -> wl_plan_op_lftj_t. * WL_PLAN_OP_EXCHANGE: Redistribute tuples by hash(key_columns) % W across * workers for partition-correct parallel evaluation - * (Issue #316). opaque_data points to - * wl_plan_op_exchange_t. + * (Issue #316). opaque_data -> wl_plan_op_exchange_t. */ typedef enum { + /* Universal operators (0-8): backend-agnostic */ WL_PLAN_OP_VARIABLE = 0, WL_PLAN_OP_MAP = 1, WL_PLAN_OP_FILTER = 2, @@ -256,11 +271,19 @@ typedef enum { WL_PLAN_OP_CONCAT = 6, WL_PLAN_OP_CONSOLIDATE = 7, WL_PLAN_OP_SEMIJOIN = 8, + + /* Columnar backend operators (9+): use opaque_data */ WL_PLAN_OP_K_FUSION = 9, WL_PLAN_OP_LFTJ = 10, - WL_PLAN_OP_EXCHANGE = 11, /* Redistribute tuples by hash(key) % W */ + WL_PLAN_OP_EXCHANGE = 11, } wl_plan_op_type_t; +/* First backend-specific operator value. All ops with numeric value + * >= WL_PLAN_OP__BACKEND_START carry their metadata in opaque_data + * (defined by the active backend, e.g. columnar/columnar_nanoarrow.h). + * Universal ops (< WL_PLAN_OP__BACKEND_START) use flat wl_plan_op_t fields. */ +#define WL_PLAN_OP__BACKEND_START 9 + /* ======================================================================== */ /* Operator Node */ /* ======================================================================== */ From f5deb2b665e291d97243f3946bcbb73cd087bfe8 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:42:01 +0900 Subject: [PATCH 3/8] test(#495): Add ABI assertions for all exec_plan operator values Assert all 12 enum values (0-11) plus WL_PLAN_OP__BACKEND_START boundary in test_plan_exchange.c. Locks the universal/backend-specific partition against accidental renumbering. --- tests/test_plan_exchange.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_plan_exchange.c b/tests/test_plan_exchange.c index cc322403..6a5675f5 100644 --- a/tests/test_plan_exchange.c +++ b/tests/test_plan_exchange.c @@ -393,6 +393,33 @@ test_plan_exchange_cleanup(void) PASS(); } +static void +test_plan_op_abi_values(void) +{ + TEST("exec_plan op enum ABI values (Issue #495)"); + + /* Universal operators (0-8) */ + ASSERT(WL_PLAN_OP_VARIABLE == 0, "VARIABLE should be 0"); + ASSERT(WL_PLAN_OP_MAP == 1, "MAP should be 1"); + ASSERT(WL_PLAN_OP_FILTER == 2, "FILTER should be 2"); + ASSERT(WL_PLAN_OP_JOIN == 3, "JOIN should be 3"); + ASSERT(WL_PLAN_OP_ANTIJOIN == 4, "ANTIJOIN should be 4"); + ASSERT(WL_PLAN_OP_REDUCE == 5, "REDUCE should be 5"); + ASSERT(WL_PLAN_OP_CONCAT == 6, "CONCAT should be 6"); + ASSERT(WL_PLAN_OP_CONSOLIDATE == 7, "CONSOLIDATE should be 7"); + ASSERT(WL_PLAN_OP_SEMIJOIN == 8, "SEMIJOIN should be 8"); + + /* Backend-specific boundary */ + ASSERT(WL_PLAN_OP__BACKEND_START == 9, "__BACKEND_START should be 9"); + + /* Columnar backend operators (9-11) */ + ASSERT(WL_PLAN_OP_K_FUSION == 9, "K_FUSION should be 9"); + ASSERT(WL_PLAN_OP_LFTJ == 10, "LFTJ should be 10"); + ASSERT(WL_PLAN_OP_EXCHANGE == 11, "EXCHANGE should be 11"); + + PASS(); +} + /* ---------------------------------------------------------------- * Main * ---------------------------------------------------------------- */ @@ -402,6 +429,7 @@ main(void) { printf("=== Plan Exchange Insertion Tests (#319) ===\n"); + test_plan_op_abi_values(); test_plan_tc_exchange_conservative(); test_plan_no_exchange_nonrecursive(); test_plan_exchange_key_metadata(); From 88a19e0091dae6f3a0f0b3dd613edaf778a20437 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:43:27 +0900 Subject: [PATCH 4/8] refactor(#495): Add inline helpers for universal/backend-specific op classification wl_plan_op_is_backend_specific() and wl_plan_op_is_universal() provide programmatic classification of operators against WL_PLAN_OP__BACKEND_START. --- wirelog/exec_plan.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/wirelog/exec_plan.h b/wirelog/exec_plan.h index 908fdc92..3334ddb0 100644 --- a/wirelog/exec_plan.h +++ b/wirelog/exec_plan.h @@ -284,6 +284,20 @@ typedef enum { * Universal ops (< WL_PLAN_OP__BACKEND_START) use flat wl_plan_op_t fields. */ #define WL_PLAN_OP__BACKEND_START 9 +/** Returns true if @op is a backend-specific operator (>= BACKEND_START). */ +static inline bool +wl_plan_op_is_backend_specific(wl_plan_op_type_t op) +{ + return (int)op >= WL_PLAN_OP__BACKEND_START; +} + +/** Returns true if @op is a universal (backend-agnostic) operator. */ +static inline bool +wl_plan_op_is_universal(wl_plan_op_type_t op) +{ + return (int)op < WL_PLAN_OP__BACKEND_START; +} + /* ======================================================================== */ /* Operator Node */ /* ======================================================================== */ From 87b214edfca5fb7080db3a46f9d03eca79b0063e Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:45:04 +0900 Subject: [PATCH 5/8] docs(#495): Update opaque_data and field-usage docs for backend-specific ops Reorganize field-usage comment block into Universal and Backend-specific sections. Fix opaque_data comment to reference all three backend ops (K_FUSION, LFTJ, EXCHANGE) instead of only K_FUSION. --- wirelog/exec_plan.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/wirelog/exec_plan.h b/wirelog/exec_plan.h index 3334ddb0..312169c6 100644 --- a/wirelog/exec_plan.h +++ b/wirelog/exec_plan.h @@ -311,6 +311,7 @@ wl_plan_op_is_universal(wl_plan_op_type_t op) * * Field usage by operator type: * + * Universal operators (use flat fields only, opaque_data is NULL): * VARIABLE: relation_name * MAP: project_indices, project_count (and/or map_exprs) * FILTER: filter_expr @@ -323,9 +324,11 @@ wl_plan_op_is_universal(wl_plan_op_type_t op) * CONSOLIDATE: (no fields used) * SEMIJOIN: right_relation, right_filter_expr, left_keys, right_keys, * key_count, project_indices, project_count - * K_FUSION: opaque_data (points to wl_plan_op_k_fusion_t in columnar backend) - * LFTJ: opaque_data (points to wl_plan_op_lftj_t in columnar backend) - * EXCHANGE: opaque_data (points to wl_plan_op_exchange_t in columnar backend) + * + * Backend-specific operators (>= WL_PLAN_OP__BACKEND_START, use opaque_data): + * K_FUSION: opaque_data -> wl_plan_op_k_fusion_t (columnar) + * LFTJ: opaque_data -> wl_plan_op_lftj_t (columnar) + * EXCHANGE: opaque_data -> wl_plan_op_exchange_t (columnar) */ typedef struct { wl_plan_op_type_t op; @@ -352,11 +355,10 @@ typedef struct { wl_delta_mode_t delta_mode; /* semi-naive delta/full selection control */ bool materialized; /* hint: cache this intermediate result for CSE reuse */ - /* Backend-specific metadata. NULL for all ops except K_FUSION. - * For K_FUSION: points to a wl_plan_op_k_fusion_t (defined in - * backend/columnar_nanoarrow.h) containing K operator sequences - * for parallel semi-naive evaluation. Owned by the plan; freed - * via wl_plan_free() -> free_op() path. */ + /* Backend-specific metadata. NULL for all universal ops (< BACKEND_START). + * For backend-specific ops (K_FUSION, LFTJ, EXCHANGE): points to a + * backend-defined struct in columnar/columnar_nanoarrow.h. + * Owned by the plan; freed via wl_plan_free() -> free_op() path. */ void *opaque_data; /* Filter predicate applied to right-child tuples before join probe. From a0d4daa8dd646d6fb7fdd1ee9de19838aa65a4e0 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:46:55 +0900 Subject: [PATCH 6/8] refactor(#495): Add compile-time and runtime validation for backend-specific ops Add _Static_assert verifying WL_PLAN_OP_K_FUSION == WL_PLAN_OP__BACKEND_START to catch boundary drift at compile time. Replace silent default break in dispatch switch with assert for unknown op types in debug builds. --- wirelog/columnar/eval.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wirelog/columnar/eval.c b/wirelog/columnar/eval.c index c238faac..948dc585 100644 --- a/wirelog/columnar/eval.c +++ b/wirelog/columnar/eval.c @@ -127,6 +127,11 @@ dedup_set_init_from_rel(col_rel_t *r) /* Stratum Evaluator */ /* ======================================================================== */ +/* Compile-time check: WL_PLAN_OP__BACKEND_START must match the first + * backend-specific operator (Issue #495). */ +_Static_assert(WL_PLAN_OP_K_FUSION == WL_PLAN_OP__BACKEND_START, + "WL_PLAN_OP__BACKEND_START must equal WL_PLAN_OP_K_FUSION"); + /* * col_eval_relation_plan: * Evaluate all operators for one relation plan using the eval stack. @@ -253,6 +258,7 @@ col_eval_relation_plan(const wl_plan_relation_t *rplan, eval_stack_t *stack, rc = col_op_exchange(op, stack, sess); break; default: + assert(0 && "unknown plan op type in columnar eval dispatch"); break; } if (rc != 0) From 8ba1e5c360e7fb178beab6a442ed4444d1948993 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 11:49:33 +0900 Subject: [PATCH 7/8] docs(#495): Remove fragile line-number references in ARCHITECTURE docs Replace exec_plan.h:259 line-number reference with symbol-only reference to avoid staleness when the enum is reorganized. --- docs/ARCHITECTURE.ko.md | 2 +- docs/ARCHITECTURE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ARCHITECTURE.ko.md b/docs/ARCHITECTURE.ko.md index ca507b8f..c50a48ae 100644 --- a/docs/ARCHITECTURE.ko.md +++ b/docs/ARCHITECTURE.ko.md @@ -110,7 +110,7 @@ Wirelog는 **순수 C11**로 구현된 **Timely-Differential 개념** 기반의 - 워크큐 기반 병렬 실행에 자연스러운 적합성 **코드 위치**: -- 계획 수준: `wirelog/exec_plan.h:259` (WL_PLAN_OP_K_FUSION 연산자 타입) +- 계획 수준: `wirelog/exec_plan.h` (WL_PLAN_OP_K_FUSION 연산자 타입) - 계획 생성: `wirelog/exec_plan_gen.c:1574-1746` (expand_multiway_k_fusion) - 실행: `wirelog/columnar/internal.h:1257` (col_op_k_fusion) - 워커 격리: `wirelog/columnar/diff_arrangement.h:88-98` (col_diff_arrangement_deep_copy) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6abc8648..ebf79a04 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -111,7 +111,7 @@ This document describes the **invariant architectural principles** that must be - Natural fit for workqueue-based parallel execution **Code locations**: -- Plan-level: `wirelog/exec_plan.h:259` (WL_PLAN_OP_K_FUSION operator type) +- Plan-level: `wirelog/exec_plan.h` (WL_PLAN_OP_K_FUSION operator type) - Plan generation: `wirelog/exec_plan_gen.c:1574-1746` (expand_multiway_k_fusion) - Execution: `wirelog/columnar/internal.h:1257` (col_op_k_fusion) - Worker isolation: `wirelog/columnar/diff_arrangement.h:88-98` (col_diff_arrangement_deep_copy) From 0f071924b9cd65ea70f0f2e0cd4f3260dc4775d2 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Wed, 15 Apr 2026 14:52:08 +0900 Subject: [PATCH 8/8] fix(#495): Use MSVC-compatible compile-time check for backend start boundary MSVC C mode does not support _Static_assert. Use typedef array trick for MSVC, keep _Static_assert for GCC/Clang. --- wirelog/columnar/eval.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wirelog/columnar/eval.c b/wirelog/columnar/eval.c index 948dc585..6ad7456e 100644 --- a/wirelog/columnar/eval.c +++ b/wirelog/columnar/eval.c @@ -129,8 +129,14 @@ dedup_set_init_from_rel(col_rel_t *r) /* Compile-time check: WL_PLAN_OP__BACKEND_START must match the first * backend-specific operator (Issue #495). */ +#if defined(_MSC_VER) +/* MSVC C mode: use typedef array trick for compile-time check */ +typedef char static_check_backend_start_ + [(WL_PLAN_OP_K_FUSION == WL_PLAN_OP__BACKEND_START) ? 1 : -1]; +#else _Static_assert(WL_PLAN_OP_K_FUSION == WL_PLAN_OP__BACKEND_START, "WL_PLAN_OP__BACKEND_START must equal WL_PLAN_OP_K_FUSION"); +#endif /* * col_eval_relation_plan: