This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new b350f8cc433 [refine](expr) Check type and column match in 
VExpr::execute_column (branch-4.2) #62614 (#69661)
b350f8cc433 is described below

commit b350f8cc43326ec9086caafd07ad9f5350fae7b7
Author: Mryange <[email protected]>
AuthorDate: Wed Oct 28 09:22:53 2026 +0800

    [refine](expr) Check type and column match in VExpr::execute_column 
(branch-4.2) #62614 (#69661)
    
    ### What problem does this PR solve?
    
    Issue Number: N/A
    
    Related PR: #62614
    
    Problem Summary: Backport #62614 to branch-4.2. Expression evaluation
    could return a column whose size or physical type does not match the
    declared result, allowing inconsistencies to propagate downstream. Add
    validation in `VExpr::execute_column` and move subclass implementations
    to `execute_column_impl` with read-only selectors. Adapt type validation
    to the 4.2 APIs, including valid non-nullable results for nullable
    declared types.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test:
    - Validation: `BUILD_TYPE=ASAN sh build.sh --be --clean -j48` passed.
        - Unit and regression tests were not run.
    - Behavior changed: Yes. Invalid expression result sizes or types now
    return an error.
    - Does this need documentation: No.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 be/src/exprs/lambda_function/lambda_function.h     |   2 +-
 .../lambda_function/varray_filter_function.cpp     |   6 +-
 .../exprs/lambda_function/varray_map_function.cpp  |   4 +-
 .../exprs/lambda_function/varray_sort_function.cpp |   4 +-
 be/src/exprs/short_circuit_evaluation_expr.cpp     |  32 +--
 be/src/exprs/short_circuit_evaluation_expr.h       |  16 +-
 be/src/exprs/vbloom_predicate.cpp                  |   8 +-
 be/src/exprs/vbloom_predicate.h                    |   6 +-
 be/src/exprs/vcase_expr.cpp                        |   5 +-
 be/src/exprs/vcase_expr.h                          |   4 +-
 be/src/exprs/vcast_expr.cpp                        |  10 +-
 be/src/exprs/vcast_expr.h                          |   8 +-
 be/src/exprs/vcolumn_ref.h                         |   4 +-
 be/src/exprs/vcompound_pred.h                      |   7 +-
 be/src/exprs/vcondition_expr.cpp                   |  18 +-
 be/src/exprs/vcondition_expr.h                     |  12 +-
 be/src/exprs/vdirect_in_predicate.h                |   6 +-
 be/src/exprs/vectorized_fn_call.cpp                |  12 +-
 be/src/exprs/vectorized_fn_call.h                  |   8 +-
 be/src/exprs/vexpr.cpp                             |  35 ++-
 be/src/exprs/vexpr.h                               |  22 +-
 be/src/exprs/vin_predicate.cpp                     |   5 +-
 be/src/exprs/vin_predicate.h                       |   4 +-
 be/src/exprs/vinfo_func.cpp                        |   5 +-
 be/src/exprs/vinfo_func.h                          |   4 +-
 be/src/exprs/virtual_slot_ref.cpp                  |   5 +-
 be/src/exprs/virtual_slot_ref.h                    |   4 +-
 be/src/exprs/vlambda_function_call_expr.h          |   4 +-
 be/src/exprs/vlambda_function_expr.h               |   4 +-
 be/src/exprs/vliteral.cpp                          |   5 +-
 be/src/exprs/vliteral.h                            |   4 +-
 be/src/exprs/vmatch_predicate.cpp                  |   6 +-
 be/src/exprs/vmatch_predicate.h                    |   4 +-
 be/src/exprs/vruntimefilter_wrapper.cpp            |   8 +-
 be/src/exprs/vruntimefilter_wrapper.h              |   4 +-
 be/src/exprs/vsearch.cpp                           |   5 +-
 be/src/exprs/vsearch.h                             |   4 +-
 be/src/exprs/vslot_ref.cpp                         |   5 +-
 be/src/exprs/vslot_ref.h                           |   4 +-
 be/src/exprs/vtopn_pred.h                          |   4 +-
 .../vruntimefilter_wrapper_sampling_test.cpp       |   4 +-
 be/test/exprs/expr_zonemap_filter_test.cpp         |   8 +-
 be/test/exprs/function/table_function_test.cpp     |   6 +-
 be/test/exprs/mock_vexpr.h                         |   4 +-
 be/test/exprs/score_runtime_test.cpp               |  11 +-
 be/test/exprs/vcondition_expr_test.cpp             |   5 +-
 be/test/exprs/vexpr_test.cpp                       | 260 +++++++++++++--------
 be/test/exprs/virtual_slot_ref_test.cpp            |  15 +-
 be/test/exprs/vsearch_expr_test.cpp                |  21 +-
 be/test/format/parquet/parquet_expr_test.cpp       |   4 +-
 .../index/ann/extract_query_vector_test.cpp        |   4 +-
 .../segment_iterator_apply_index_expr_test.cpp     |   4 +-
 .../segment/segment_iterator_expr_zonemap_test.cpp |   4 +-
 53 files changed, 385 insertions(+), 282 deletions(-)

diff --git a/be/src/exprs/lambda_function/lambda_function.h 
b/be/src/exprs/lambda_function/lambda_function.h
index b033617a88a..31976617a54 100644
--- a/be/src/exprs/lambda_function/lambda_function.h
+++ b/be/src/exprs/lambda_function/lambda_function.h
@@ -37,7 +37,7 @@ public:
     }
 
     virtual doris::Status execute(VExprContext* context, const doris::Block* 
block,
-                                  Selector* selector, size_t count, ColumnPtr& 
result_column,
+                                  const Selector* selector, size_t count, 
ColumnPtr& result_column,
                                   const DataTypePtr& result_type,
                                   const VExprSPtrs& children) const = 0;
 
diff --git a/be/src/exprs/lambda_function/varray_filter_function.cpp 
b/be/src/exprs/lambda_function/varray_filter_function.cpp
index 9d4f781a72b..3f6979b587b 100644
--- a/be/src/exprs/lambda_function/varray_filter_function.cpp
+++ b/be/src/exprs/lambda_function/varray_filter_function.cpp
@@ -52,9 +52,9 @@ public:
 
     std::string get_name() const override { return name; }
 
-    doris::Status execute(VExprContext* context, const doris::Block* block, 
Selector* expr_selector,
-                          size_t output_count, ColumnPtr& result_column,
-                          const DataTypePtr& result_type,
+    doris::Status execute(VExprContext* context, const doris::Block* block,
+                          const Selector* expr_selector, size_t output_count,
+                          ColumnPtr& result_column, const DataTypePtr& 
result_type,
                           const VExprSPtrs& children) const override {
         ///* array_filter(array, array<boolean>) *///
 
diff --git a/be/src/exprs/lambda_function/varray_map_function.cpp 
b/be/src/exprs/lambda_function/varray_map_function.cpp
index 07c6ee4a03f..e16443bce65 100644
--- a/be/src/exprs/lambda_function/varray_map_function.cpp
+++ b/be/src/exprs/lambda_function/varray_map_function.cpp
@@ -86,8 +86,8 @@ public:
                                                 _lambda_argument_binding);
     }
 
-    Status execute(VExprContext* context, const Block* block, Selector* 
expr_selector, size_t count,
-                   ColumnPtr& result_column, const DataTypePtr& result_type,
+    Status execute(VExprContext* context, const Block* block, const Selector* 
expr_selector,
+                   size_t count, ColumnPtr& result_column, const DataTypePtr& 
result_type,
                    const VExprSPtrs& children) const override {
         LambdaArgs args_info;
 
diff --git a/be/src/exprs/lambda_function/varray_sort_function.cpp 
b/be/src/exprs/lambda_function/varray_sort_function.cpp
index 3c786feee25..f071c2d7791 100644
--- a/be/src/exprs/lambda_function/varray_sort_function.cpp
+++ b/be/src/exprs/lambda_function/varray_sort_function.cpp
@@ -89,8 +89,8 @@ public:
         return Status::OK();
     }
 
-    Status execute(VExprContext* context, const Block* block, Selector* 
expr_selector, size_t count,
-                   ColumnPtr& result_column, const DataTypePtr& result_type,
+    Status execute(VExprContext* context, const Block* block, const Selector* 
expr_selector,
+                   size_t count, ColumnPtr& result_column, const DataTypePtr& 
result_type,
                    const VExprSPtrs& children) const override {
         ///* array_sort(lambda, arg) *///
 
diff --git a/be/src/exprs/short_circuit_evaluation_expr.cpp 
b/be/src/exprs/short_circuit_evaluation_expr.cpp
index 1ebc4bfa325..40ebed3a069 100644
--- a/be/src/exprs/short_circuit_evaluation_expr.cpp
+++ b/be/src/exprs/short_circuit_evaluation_expr.cpp
@@ -89,8 +89,8 @@ std::string ShortCircuitExpr::debug_string() const {
 // These are issues with the exprs themselves, but for convenience, we handle 
this case uniformly in short-circuit expr.
 /// TODO: Once all exprs support size-0 columns in the future, this function 
can be removed.
 [[nodiscard]] Status try_early_return_on_empty(const VExprSPtr& expr, 
VExprContext* context,
-                                               const Block* block, Selector* 
selector, size_t count,
-                                               ColumnPtr& result_columnn) {
+                                               const Block* block, const 
Selector* selector,
+                                               size_t count, ColumnPtr& 
result_columnn) {
     if (count == 0) {
         result_columnn = expr->execute_type(block)->create_column();
         DCHECK_EQ(result_columnn->size(), 0);
@@ -177,9 +177,9 @@ void execute_if_selector(const ColumnPtr& cond_column, 
const Selector* selector,
     condition_view.for_each(null_func, true_func, false_func);
 }
 
-Status ShortCircuitIfExpr::execute_column(VExprContext* context, const Block* 
block,
-                                          Selector* selector, size_t count,
-                                          ColumnPtr& result_column) const {
+Status ShortCircuitIfExpr::execute_column_impl(VExprContext* context, const 
Block* block,
+                                               const Selector* selector, 
size_t count,
+                                               ColumnPtr& result_column) const 
{
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK(selector == nullptr || selector->size() == count);
     ColumnPtr cond_column;
@@ -255,9 +255,9 @@ void execute_case_selector(const ColumnPtr& cond_column, 
const Selector* executo
     }
 }
 
-Status ShortCircuitCaseExpr::execute_column(VExprContext* context, const 
Block* block,
-                                            Selector* selector, size_t count,
-                                            ColumnPtr& result_column) const {
+Status ShortCircuitCaseExpr::execute_column_impl(VExprContext* context, const 
Block* block,
+                                                 const Selector* selector, 
size_t count,
+                                                 ColumnPtr& result_column) 
const {
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK(selector == nullptr || selector->size() == count);
 
@@ -274,7 +274,7 @@ Status ShortCircuitCaseExpr::execute_column(VExprContext* 
context, const Block*
     std::vector<ColumnAndSelector> columns_and_selectors;
     columns_and_selectors.resize(num_branches);
 
-    Selector* executor_selector = selector;
+    const Selector* executor_selector = selector;
     size_t executor_count = count;
 
     Selector* current_selector = nullptr;
@@ -374,9 +374,9 @@ void execute_ifnull_selector(const ColumnPtr& cond_column, 
const Selector* selec
     condition_view.for_each(null_func, not_null_func);
 }
 
-Status ShortCircuitIfNullExpr::execute_column(VExprContext* context, const 
Block* block,
-                                              Selector* selector, size_t count,
-                                              ColumnPtr& result_column) const {
+Status ShortCircuitIfNullExpr::execute_column_impl(VExprContext* context, 
const Block* block,
+                                                   const Selector* selector, 
size_t count,
+                                                   ColumnPtr& result_column) 
const {
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK(selector == nullptr || selector->size() == count);
     ColumnPtr expr1_column;
@@ -440,9 +440,9 @@ void execute_coalesce_selector(const ColumnPtr& 
cond_column, const Selector* exe
     }
 }
 
-Status ShortCircuitCoalesceExpr::execute_column(VExprContext* context, const 
Block* block,
-                                                Selector* selector, size_t 
count,
-                                                ColumnPtr& result_column) 
const {
+Status ShortCircuitCoalesceExpr::execute_column_impl(VExprContext* context, 
const Block* block,
+                                                     const Selector* selector, 
size_t count,
+                                                     ColumnPtr& result_column) 
const {
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK(selector == nullptr || selector->size() == count);
 
@@ -450,7 +450,7 @@ Status 
ShortCircuitCoalesceExpr::execute_column(VExprContext* context, const Blo
     columns_and_selectors.resize(_children.size() +
                                  1); // the last one represents the selector 
for null output
 
-    Selector* executor_selector = selector;
+    const Selector* executor_selector = selector;
     size_t executor_count = count;
 
     Selector* current_selector = nullptr;
diff --git a/be/src/exprs/short_circuit_evaluation_expr.h 
b/be/src/exprs/short_circuit_evaluation_expr.h
index 050cdba34c8..7240207aaca 100644
--- a/be/src/exprs/short_circuit_evaluation_expr.h
+++ b/be/src/exprs/short_circuit_evaluation_expr.h
@@ -71,8 +71,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
 private:
     inline static const std::string IF_NAME = "if";
@@ -95,8 +95,8 @@ public:
         *cloned_expr = ShortCircuitCaseExpr::create_shared(node);
         return Status::OK();
     }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
 private:
     const bool _has_else_expr;
@@ -117,8 +117,8 @@ public:
         *cloned_expr = ShortCircuitIfNullExpr::create_shared(node);
         return Status::OK();
     }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
 private:
     inline static const std::string IFNULL_NAME = "ifnull";
@@ -137,8 +137,8 @@ public:
         *cloned_expr = ShortCircuitCoalesceExpr::create_shared(node);
         return Status::OK();
     }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
 private:
     inline static const std::string COALESCE_NAME = "coalesce";
diff --git a/be/src/exprs/vbloom_predicate.cpp 
b/be/src/exprs/vbloom_predicate.cpp
index 996d2f1031f..45ba780944f 100644
--- a/be/src/exprs/vbloom_predicate.cpp
+++ b/be/src/exprs/vbloom_predicate.cpp
@@ -73,7 +73,7 @@ void VBloomPredicate::close(VExprContext* context, 
FunctionContext::FunctionStat
 }
 
 Status VBloomPredicate::_do_execute(VExprContext* context, const Block* block,
-                                    const uint8_t* __restrict filter, 
Selector* selector,
+                                    const uint8_t* __restrict filter, const 
Selector* selector,
                                     size_t count, ColumnPtr& result_column) 
const {
     DCHECK(_open_finished || block == nullptr);
     DCHECK(!(filter != nullptr && selector != nullptr))
@@ -97,9 +97,9 @@ Status VBloomPredicate::_do_execute(VExprContext* context, 
const Block* block,
     return Status::OK();
 }
 
-Status VBloomPredicate::execute_column(VExprContext* context, const Block* 
block,
-                                       Selector* selector, size_t count,
-                                       ColumnPtr& result_column) const {
+Status VBloomPredicate::execute_column_impl(VExprContext* context, const 
Block* block,
+                                            const Selector* selector, size_t 
count,
+                                            ColumnPtr& result_column) const {
     return _do_execute(context, block, nullptr, selector, count, 
result_column);
 }
 
diff --git a/be/src/exprs/vbloom_predicate.h b/be/src/exprs/vbloom_predicate.h
index 31824ae740e..964b8bd67e4 100644
--- a/be/src/exprs/vbloom_predicate.h
+++ b/be/src/exprs/vbloom_predicate.h
@@ -42,8 +42,8 @@ public:
     VBloomPredicate(const TExprNode& node);
     ~VBloomPredicate() override = default;
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
     Status execute_runtime_filter(VExprContext* context, const Block* block,
                                   const uint8_t* __restrict filter, size_t 
count,
@@ -82,7 +82,7 @@ public:
 
 private:
     Status _do_execute(VExprContext* context, const Block* block, const 
uint8_t* __restrict filter,
-                       Selector* selector, size_t count, ColumnPtr& 
result_column) const;
+                       const Selector* selector, size_t count, ColumnPtr& 
result_column) const;
 
     std::shared_ptr<BloomFilterFuncBase> _filter;
     inline static const std::string EXPR_NAME = "bloom_predicate";
diff --git a/be/src/exprs/vcase_expr.cpp b/be/src/exprs/vcase_expr.cpp
index 9c38f6a55fb..ca8621f2194 100644
--- a/be/src/exprs/vcase_expr.cpp
+++ b/be/src/exprs/vcase_expr.cpp
@@ -76,8 +76,9 @@ void VCaseExpr::close(VExprContext* context, 
FunctionContext::FunctionStateScope
     VExpr::close(context, scope);
 }
 
-Status VCaseExpr::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                 size_t count, ColumnPtr& result_column) const 
{
+Status VCaseExpr::execute_column_impl(VExprContext* context, const Block* 
block,
+                                      const Selector* selector, size_t count,
+                                      ColumnPtr& result_column) const {
     if (is_const_and_have_executed()) { // const have execute in open function
         result_column = get_result_from_const(count);
         return Status::OK();
diff --git a/be/src/exprs/vcase_expr.h b/be/src/exprs/vcase_expr.h
index 7e7cdd11f59..806ada9e379 100644
--- a/be/src/exprs/vcase_expr.h
+++ b/be/src/exprs/vcase_expr.h
@@ -55,8 +55,8 @@ class VCaseExpr final : public VExpr {
 public:
     VCaseExpr(const TExprNode& node);
     ~VCaseExpr() override = default;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
diff --git a/be/src/exprs/vcast_expr.cpp b/be/src/exprs/vcast_expr.cpp
index 755be44b545..98d3303829b 100644
--- a/be/src/exprs/vcast_expr.cpp
+++ b/be/src/exprs/vcast_expr.cpp
@@ -107,8 +107,9 @@ void VCastExpr::close(VExprContext* context, 
FunctionContext::FunctionStateScope
     VExpr::close(context, scope);
 }
 
-Status VCastExpr::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                 size_t count, ColumnPtr& result_column) const 
{
+Status VCastExpr::execute_column_impl(VExprContext* context, const Block* 
block,
+                                      const Selector* selector, size_t count,
+                                      ColumnPtr& result_column) const {
     DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
     if (is_const_and_have_executed()) { // const have executed in open function
         result_column = get_result_from_const(count);
@@ -148,8 +149,9 @@ DataTypePtr TryCastExpr::original_cast_return_type() const {
     }
 }
 
-Status TryCastExpr::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                   size_t count, ColumnPtr& result_column) 
const {
+Status TryCastExpr::execute_column_impl(VExprContext* context, const Block* 
block,
+                                        const Selector* selector, size_t count,
+                                        ColumnPtr& result_column) const {
     DCHECK(_open_finished || block == nullptr) << _open_finished << _expr_name;
     if (is_const_and_have_executed()) { // const have executed in open function
         result_column = get_result_from_const(count);
diff --git a/be/src/exprs/vcast_expr.h b/be/src/exprs/vcast_expr.h
index abed5e3d3b1..f049811634b 100644
--- a/be/src/exprs/vcast_expr.h
+++ b/be/src/exprs/vcast_expr.h
@@ -47,8 +47,8 @@ public:
     VCastExpr(const TExprNode& node)
             : VExpr(node), _is_strict_cast(node.__isset.is_strict_cast && 
node.is_strict_cast) {}
     ~VCastExpr() override = default;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
@@ -100,8 +100,8 @@ public:
 
     TryCastExpr(const TExprNode& node)
             : VCastExpr(node), 
_original_cast_return_is_nullable(node.is_cast_nullable) {}
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     ~TryCastExpr() override = default;
     std::string cast_name() const override { return "TRY CAST"; }
     bool is_safe_to_execute_on_selected_rows() const override {
diff --git a/be/src/exprs/vcolumn_ref.h b/be/src/exprs/vcolumn_ref.h
index ad9624d1239..253c50f0627 100644
--- a/be/src/exprs/vcolumn_ref.h
+++ b/be/src/exprs/vcolumn_ref.h
@@ -59,8 +59,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         DCHECK(_open_finished || block == nullptr);
         const int column_position = _get_column_position(context, block);
         if (column_position < 0 || column_position >= block->columns()) {
diff --git a/be/src/exprs/vcompound_pred.h b/be/src/exprs/vcompound_pred.h
index 42e5f1f772c..6d783a2e468 100644
--- a/be/src/exprs/vcompound_pred.h
+++ b/be/src/exprs/vcompound_pred.h
@@ -398,13 +398,14 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         if (fast_execute(context, selector, count, result_column)) {
             return Status::OK();
         }
         if (get_num_children() == 1 || _has_const_child()) {
-            return VectorizedFnCall::execute_column(context, block, selector, 
count, result_column);
+            return VectorizedFnCall::execute_column_impl(context, block, 
selector, count,
+                                                         result_column);
         }
 
         ColumnPtr lhs_column;
diff --git a/be/src/exprs/vcondition_expr.cpp b/be/src/exprs/vcondition_expr.cpp
index f4066b06940..057cadf0b2f 100644
--- a/be/src/exprs/vcondition_expr.cpp
+++ b/be/src/exprs/vcondition_expr.cpp
@@ -451,9 +451,9 @@ Status VectorizedIfExpr::_execute_impl_internal(Block& 
block, const ColumnNumber
     }
 }
 
-Status VectorizedIfExpr::execute_column(VExprContext* context, const Block* 
block,
-                                        Selector* selector, size_t count,
-                                        ColumnPtr& result_column) const {
+Status VectorizedIfExpr::execute_column_impl(VExprContext* context, const 
Block* block,
+                                             const Selector* selector, size_t 
count,
+                                             ColumnPtr& result_column) const {
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK_EQ(_children.size(), 3) << "IF expr must have three children";
 
@@ -492,9 +492,9 @@ Status VectorizedIfExpr::execute_column(VExprContext* 
context, const Block* bloc
     return Status::OK();
 }
 
-Status VectorizedIfNullExpr::execute_column(VExprContext* context, const 
Block* block,
-                                            Selector* selector, size_t count,
-                                            ColumnPtr& result_column) const {
+Status VectorizedIfNullExpr::execute_column_impl(VExprContext* context, const 
Block* block,
+                                                 const Selector* selector, 
size_t count,
+                                                 ColumnPtr& result_column) 
const {
     DCHECK(_open_finished || block == nullptr) << debug_string();
     DCHECK_EQ(_children.size(), 2) << "IFNULL expr must have two children";
 
@@ -652,9 +652,9 @@ Status filled_result_column(const DataTypePtr& data_type, 
MutableColumnPtr& resu
     return Status::OK();
 }
 
-Status VectorizedCoalesceExpr::execute_column(VExprContext* context, const 
Block* block,
-                                              Selector* selector, size_t count,
-                                              ColumnPtr& return_column) const {
+Status VectorizedCoalesceExpr::execute_column_impl(VExprContext* context, 
const Block* block,
+                                                   const Selector* selector, 
size_t count,
+                                                   ColumnPtr& return_column) 
const {
     DataTypePtr result_type = _data_type;
     const auto input_rows_count = count;
 
diff --git a/be/src/exprs/vcondition_expr.h b/be/src/exprs/vcondition_expr.h
index a6c7bd68a2c..6beade74b3e 100644
--- a/be/src/exprs/vcondition_expr.h
+++ b/be/src/exprs/vcondition_expr.h
@@ -61,8 +61,8 @@ class VectorizedIfExpr : public VConditionExpr {
 public:
     VectorizedIfExpr(const TExprNode& node) : VConditionExpr(node) {}
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
     const std::string& expr_name() const override { return IF_NAME; }
     Status clone_node(VExprSPtr* cloned_expr) const override {
@@ -135,16 +135,16 @@ public:
     }
     inline static const std::string IF_NULL_NAME = "ifnull";
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 };
 
 class VectorizedCoalesceExpr : public VConditionExpr {
     ENABLE_FACTORY_CREATOR(VectorizedCoalesceExpr);
 
 public:
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     VectorizedCoalesceExpr(const TExprNode& node) : VConditionExpr(node) {}
     const std::string& expr_name() const override { return NAME; }
     Status clone_node(VExprSPtr* cloned_expr) const override {
diff --git a/be/src/exprs/vdirect_in_predicate.h 
b/be/src/exprs/vdirect_in_predicate.h
index 5eb0087c95a..93e7095574c 100644
--- a/be/src/exprs/vdirect_in_predicate.h
+++ b/be/src/exprs/vdirect_in_predicate.h
@@ -85,8 +85,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         return _do_execute(context, block, nullptr, selector, count, 
result_column, nullptr);
     }
 
@@ -285,7 +285,7 @@ private:
     }
 
     Status _do_execute(VExprContext* context, const Block* block, const 
uint8_t* __restrict filter,
-                       Selector* selector, size_t count, ColumnPtr& 
result_column,
+                       const Selector* selector, size_t count, ColumnPtr& 
result_column,
                        ColumnPtr* arg_column) const {
         DCHECK(_open_finished || block == nullptr);
         DCHECK(!(filter != nullptr && selector != nullptr))
diff --git a/be/src/exprs/vectorized_fn_call.cpp 
b/be/src/exprs/vectorized_fn_call.cpp
index 6e31d80d2c4..137fe34e97d 100644
--- a/be/src/exprs/vectorized_fn_call.cpp
+++ b/be/src/exprs/vectorized_fn_call.cpp
@@ -376,9 +376,9 @@ bool VectorizedFnCall::can_evaluate_bloom_filter() const {
            _function->can_evaluate_bloom_filter(_children);
 }
 
-Status VectorizedFnCall::_do_execute(VExprContext* context, const Block* 
block, Selector* selector,
-                                     size_t count, ColumnPtr& result_column,
-                                     ColumnPtr* arg_column) const {
+Status VectorizedFnCall::_do_execute(VExprContext* context, const Block* block,
+                                     const Selector* selector, size_t count,
+                                     ColumnPtr& result_column, ColumnPtr* 
arg_column) const {
     if (is_const_and_have_executed()) { // const have executed in open function
         result_column = get_result_from_const(count);
         return Status::OK();
@@ -467,9 +467,9 @@ Status 
VectorizedFnCall::execute_runtime_filter(VExprContext* context, const Blo
     return _do_execute(context, block, nullptr, count, result_column, 
arg_column);
 }
 
-Status VectorizedFnCall::execute_column(VExprContext* context, const Block* 
block,
-                                        Selector* selector, size_t count,
-                                        ColumnPtr& result_column) const {
+Status VectorizedFnCall::execute_column_impl(VExprContext* context, const 
Block* block,
+                                             const Selector* selector, size_t 
count,
+                                             ColumnPtr& result_column) const {
     return _do_execute(context, block, selector, count, result_column, 
nullptr);
 }
 
diff --git a/be/src/exprs/vectorized_fn_call.h 
b/be/src/exprs/vectorized_fn_call.h
index 24d576ff0d8..05a2178fb30 100644
--- a/be/src/exprs/vectorized_fn_call.h
+++ b/be/src/exprs/vectorized_fn_call.h
@@ -53,8 +53,8 @@ public:
 #endif
     VectorizedFnCall(const TExprNode& node);
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     Status execute_runtime_filter(VExprContext* context, const Block* block,
                                   const uint8_t* __restrict filter, size_t 
count,
                                   ColumnPtr& result_column, ColumnPtr* 
arg_column) const override;
@@ -134,8 +134,8 @@ protected:
     std::string _function_name;
 
 private:
-    Status _do_execute(VExprContext* context, const Block* block, Selector* 
selector, size_t count,
-                       ColumnPtr& result_column, ColumnPtr* arg_column) const;
+    Status _do_execute(VExprContext* context, const Block* block, const 
Selector* selector,
+                       size_t count, ColumnPtr& result_column, ColumnPtr* 
arg_column) const;
 };
 
 #include "common/compile_check_end.h"
diff --git a/be/src/exprs/vexpr.cpp b/be/src/exprs/vexpr.cpp
index f8a04eae516..cd4bb2d9940 100644
--- a/be/src/exprs/vexpr.cpp
+++ b/be/src/exprs/vexpr.cpp
@@ -34,6 +34,7 @@
 #include "common/config.h"
 #include "common/exception.h"
 #include "common/status.h"
+#include "core/column/column_nothing.h"
 #include "core/column/column_vector.h"
 #include "core/data_type/data_type_array.h"
 #include "core/data_type/data_type_decimal.h"
@@ -1107,7 +1108,39 @@ size_t VExpr::estimate_memory(const size_t rows) {
     return estimate_size;
 }
 
-bool VExpr::fast_execute(VExprContext* context, Selector* selector, size_t 
count,
+Status VExpr::execute_column(VExprContext* context, const Block* block, const 
Selector* selector,
+                             size_t count, ColumnPtr& result_column) const {
+    RETURN_IF_ERROR(execute_column_impl(context, block, selector, count, 
result_column));
+    if (result_column->size() != count) {
+        return Status::InternalError("Expr {} return column size {} not equal 
to expected size {}",
+                                     expr_name(), result_column->size(), 
count);
+    }
+    DCHECK(selector == nullptr || selector->size() == count);
+    // Validate type match. ColumnNothing is exempt (used as a placeholder in 
tests/stubs).
+    if (!check_and_get_column<ColumnNothing>(result_column.get())) {
+        auto result_type = execute_type(block);
+        if (result_type != nullptr) {
+            Status st = result_type->check_column(*result_column);
+            if (!st.ok()) {
+                // Nullable(T) may legitimately produce a non-nullable T 
column when all rows are
+                // non-null (use_default_implementation_for_nulls 
optimization). Allow this.
+                const auto* nullable_type =
+                        
check_and_get_data_type<DataTypeNullable>(result_type.get());
+                if (nullable_type && 
!check_and_get_column<ColumnNullable>(result_column.get())) {
+                    st = 
nullable_type->get_nested_type()->check_column(*result_column);
+                }
+            }
+            if (!st.ok()) {
+                return Status::InternalError(
+                        "Expr {} return column type mismatch: declared={}, 
actual={}", expr_name(),
+                        result_type->get_name(), result_column->get_name());
+            }
+        }
+    }
+    return Status::OK();
+}
+
+bool VExpr::fast_execute(VExprContext* context, const Selector* selector, 
size_t count,
                          ColumnPtr& result_column) const {
     if (context->get_index_context() &&
         
context->get_index_context()->get_index_result_column().contains(this)) {
diff --git a/be/src/exprs/vexpr.h b/be/src/exprs/vexpr.h
index bee792db27e..dd149e3b86b 100644
--- a/be/src/exprs/vexpr.h
+++ b/be/src/exprs/vexpr.h
@@ -161,26 +161,12 @@ public:
     // In the future this interface will add an additional parameter, 
Selector, which specifies
     // which rows in the block should be evaluated.
     // If expr is executing constant expressions, then block should be nullptr.
-    virtual Status execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                  size_t count, ColumnPtr& result_column) 
const {
-        return execute_column_impl(context, block, selector, count, 
result_column);
-    }
-
-    template <typename SelectorType, 
std::enable_if_t<std::is_const_v<SelectorType>, int> = 0>
-    Status execute_column(VExprContext* context, const Block* block, 
SelectorType* selector,
-                          size_t count, ColumnPtr& result_column) const {
-        // format_v2 treats selectors as read-only; the branch-4.1 expression 
API still exposes
-        // a mutable pointer even though implementations must not modify it.
-        return execute_column(context, block, const_cast<Selector*>(selector), 
count,
-                              result_column);
-    }
+    Status execute_column(VExprContext* context, const Block* block, const 
Selector* selector,
+                          size_t count, ColumnPtr& result_column) const;
 
     virtual Status execute_column_impl(VExprContext* context, const Block* 
block,
                                        const Selector* selector, size_t count,
-                                       ColumnPtr& result_column) const {
-        return execute_column(context, block, const_cast<Selector*>(selector), 
count,
-                              result_column);
-    }
+                                       ColumnPtr& result_column) const = 0;
 
     // Currently, due to fe planning issues, for slot-ref expressions the type 
of the returned Column may not match data_type.
     // Therefore we need a function like this to return the actual type 
produced by execution.
@@ -434,7 +420,7 @@ public:
     virtual std::shared_ptr<HybridSetBase> get_set_func() const { return 
nullptr; }
 
     // fast_execute can direct copy expr filter result which build by apply 
index in segment_iterator
-    bool fast_execute(VExprContext* context, Selector* selector, size_t count,
+    bool fast_execute(VExprContext* context, const Selector* selector, size_t 
count,
                       ColumnPtr& result_column) const;
 
     virtual bool can_push_down_to_index() const { return false; }
diff --git a/be/src/exprs/vin_predicate.cpp b/be/src/exprs/vin_predicate.cpp
index 96058dd4fb9..223b953f174 100644
--- a/be/src/exprs/vin_predicate.cpp
+++ b/be/src/exprs/vin_predicate.cpp
@@ -309,8 +309,9 @@ Status VInPredicate::execute_on_raw_binary_values(const 
StringRef* values, size_
     return Status::OK();
 }
 
-Status VInPredicate::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                    size_t count, ColumnPtr& result_column) 
const {
+Status VInPredicate::execute_column_impl(VExprContext* context, const Block* 
block,
+                                         const Selector* selector, size_t 
count,
+                                         ColumnPtr& result_column) const {
     if (is_const_and_have_executed()) { // const have execute in open function
         result_column = get_result_from_const(count);
         return Status::OK();
diff --git a/be/src/exprs/vin_predicate.h b/be/src/exprs/vin_predicate.h
index 2fbbf8393f4..6e708915025 100644
--- a/be/src/exprs/vin_predicate.h
+++ b/be/src/exprs/vin_predicate.h
@@ -46,8 +46,8 @@ public:
     VInPredicate() = default;
 #endif
     ~VInPredicate() override = default;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     size_t estimate_memory(const size_t rows) override;
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
diff --git a/be/src/exprs/vinfo_func.cpp b/be/src/exprs/vinfo_func.cpp
index 90d0469194a..e220e5946d3 100644
--- a/be/src/exprs/vinfo_func.cpp
+++ b/be/src/exprs/vinfo_func.cpp
@@ -54,8 +54,9 @@ VInfoFunc::VInfoFunc(const TExprNode& node) : VExpr(node) {
     this->_column_ptr = _data_type->create_column_const(1, field);
 }
 
-Status VInfoFunc::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                 size_t count, ColumnPtr& result_column) const 
{
+Status VInfoFunc::execute_column_impl(VExprContext* context, const Block* 
block,
+                                      const Selector* selector, size_t count,
+                                      ColumnPtr& result_column) const {
     result_column = _column_ptr->clone_resized(count);
     DCHECK_EQ(result_column->size(), count);
     return Status::OK();
diff --git a/be/src/exprs/vinfo_func.h b/be/src/exprs/vinfo_func.h
index ee55c1c8340..750b3a89010 100644
--- a/be/src/exprs/vinfo_func.h
+++ b/be/src/exprs/vinfo_func.h
@@ -38,8 +38,8 @@ public:
     ~VInfoFunc() override = default;
 
     const std::string& expr_name() const override { return _expr_name; }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
 private:
     const std::string _expr_name = "vinfofunc expr";
diff --git a/be/src/exprs/virtual_slot_ref.cpp 
b/be/src/exprs/virtual_slot_ref.cpp
index 01f8f708cab..8cad15961dd 100644
--- a/be/src/exprs/virtual_slot_ref.cpp
+++ b/be/src/exprs/virtual_slot_ref.cpp
@@ -103,8 +103,9 @@ Status VirtualSlotRef::open(RuntimeState* state, 
VExprContext* context,
     return Status::OK();
 }
 
-Status VirtualSlotRef::execute_column(VExprContext* context, const Block* 
block, Selector* selector,
-                                      size_t count, ColumnPtr& result_column) 
const {
+Status VirtualSlotRef::execute_column_impl(VExprContext* context, const Block* 
block,
+                                           const Selector* selector, size_t 
count,
+                                           ColumnPtr& result_column) const {
     if (_column_id >= 0 && _column_id >= block->columns()) {
         return Status::Error<ErrorCode::INTERNAL_ERROR>(
                 "input block not contain slot column {}, column_id={}, 
block={}", *_column_name,
diff --git a/be/src/exprs/virtual_slot_ref.h b/be/src/exprs/virtual_slot_ref.h
index 0969049670e..0e160e6242a 100644
--- a/be/src/exprs/virtual_slot_ref.h
+++ b/be/src/exprs/virtual_slot_ref.h
@@ -31,8 +31,8 @@ public:
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     const std::string& expr_name() const override;
     std::string expr_label() override;
     std::string debug_string() const override;
diff --git a/be/src/exprs/vlambda_function_call_expr.h 
b/be/src/exprs/vlambda_function_call_expr.h
index 41b46d97ba7..a3764fc13fd 100644
--- a/be/src/exprs/vlambda_function_call_expr.h
+++ b/be/src/exprs/vlambda_function_call_expr.h
@@ -63,8 +63,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         DCHECK(_open_finished || block == nullptr);
         return _lambda_function->execute(context, block, selector, count, 
result_column, _data_type,
                                          _children);
diff --git a/be/src/exprs/vlambda_function_expr.h 
b/be/src/exprs/vlambda_function_expr.h
index 2f8c6f93af2..9b8f7474287 100644
--- a/be/src/exprs/vlambda_function_expr.h
+++ b/be/src/exprs/vlambda_function_expr.h
@@ -52,8 +52,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         DCHECK(_open_finished || block == nullptr);
         return get_child(0)->execute_column(context, block, selector, count, 
result_column);
     }
diff --git a/be/src/exprs/vliteral.cpp b/be/src/exprs/vliteral.cpp
index 048b2951fc4..6f2f178b711 100644
--- a/be/src/exprs/vliteral.cpp
+++ b/be/src/exprs/vliteral.cpp
@@ -43,8 +43,9 @@ Status VLiteral::prepare(RuntimeState* state, const 
RowDescriptor& desc, VExprCo
     return Status::OK();
 }
 
-Status VLiteral::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                size_t count, ColumnPtr& result_column) const {
+Status VLiteral::execute_column_impl(VExprContext* context, const Block* block,
+                                     const Selector* selector, size_t count,
+                                     ColumnPtr& result_column) const {
     DCHECK(selector == nullptr || selector->size() == count);
     result_column = _column_ptr->clone_resized(count);
     DCHECK_EQ(result_column->size(), count);
diff --git a/be/src/exprs/vliteral.h b/be/src/exprs/vliteral.h
index 678927c895c..89988e2ba31 100644
--- a/be/src/exprs/vliteral.h
+++ b/be/src/exprs/vliteral.h
@@ -59,8 +59,8 @@ public:
 #endif
 
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
 
     const std::string& expr_name() const override { return _expr_name; }
     std::string debug_string() const override;
diff --git a/be/src/exprs/vmatch_predicate.cpp 
b/be/src/exprs/vmatch_predicate.cpp
index 5fef6a778f4..9839dff3d15 100644
--- a/be/src/exprs/vmatch_predicate.cpp
+++ b/be/src/exprs/vmatch_predicate.cpp
@@ -181,9 +181,9 @@ const std::string& VMatchPredicate::get_analyzer_key() 
const {
     return _analyzer_ctx->analyzer_key;
 }
 
-Status VMatchPredicate::execute_column(VExprContext* context, const Block* 
block,
-                                       Selector* selector, size_t count,
-                                       ColumnPtr& result_column) const {
+Status VMatchPredicate::execute_column_impl(VExprContext* context, const 
Block* block,
+                                            const Selector* selector, size_t 
count,
+                                            ColumnPtr& result_column) const {
     DCHECK(_open_finished || block == nullptr);
     if (fast_execute(context, selector, count, result_column)) {
         return Status::OK();
diff --git a/be/src/exprs/vmatch_predicate.h b/be/src/exprs/vmatch_predicate.h
index 20c647390b8..4c513733105 100644
--- a/be/src/exprs/vmatch_predicate.h
+++ b/be/src/exprs/vmatch_predicate.h
@@ -47,8 +47,8 @@ class VMatchPredicate final : public VExpr {
 public:
     VMatchPredicate(const TExprNode& node);
     ~VMatchPredicate() override;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
diff --git a/be/src/exprs/vruntimefilter_wrapper.cpp 
b/be/src/exprs/vruntimefilter_wrapper.cpp
index 94a2b2221de..7ee29ecf943 100644
--- a/be/src/exprs/vruntimefilter_wrapper.cpp
+++ b/be/src/exprs/vruntimefilter_wrapper.cpp
@@ -118,10 +118,10 @@ void VRuntimeFilterWrapper::close(VExprContext* context,
     _impl->close(context, scope);
 }
 
-Status VRuntimeFilterWrapper::execute_column(VExprContext* context, const 
Block* block,
-                                             Selector* selector, size_t count,
-                                             ColumnPtr& result_column) const {
-    return Status::InternalError("Not implement 
VRuntimeFilterWrapper::execute_column");
+Status VRuntimeFilterWrapper::execute_column_impl(VExprContext* context, const 
Block* block,
+                                                  const Selector* selector, 
size_t count,
+                                                  ColumnPtr& result_column) 
const {
+    return Status::InternalError("Not implement 
VRuntimeFilterWrapper::execute_column_impl");
 }
 
 const std::string& VRuntimeFilterWrapper::expr_name() const {
diff --git a/be/src/exprs/vruntimefilter_wrapper.h 
b/be/src/exprs/vruntimefilter_wrapper.h
index d6b736eeebe..da0cce9f60e 100644
--- a/be/src/exprs/vruntimefilter_wrapper.h
+++ b/be/src/exprs/vruntimefilter_wrapper.h
@@ -56,8 +56,8 @@ public:
                           bool null_aware, int filter_id,
                           int sampling_frequency = 
RuntimeFilterSelectivity::DISABLE_SAMPLING);
     ~VRuntimeFilterWrapper() override = default;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     Status prepare(RuntimeState* state, const RowDescriptor& desc, 
VExprContext* context) override;
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
diff --git a/be/src/exprs/vsearch.cpp b/be/src/exprs/vsearch.cpp
index 8d292ae2836..dac696c1c5a 100644
--- a/be/src/exprs/vsearch.cpp
+++ b/be/src/exprs/vsearch.cpp
@@ -350,8 +350,9 @@ const std::string& VSearchExpr::expr_name() const {
     return name;
 }
 
-Status VSearchExpr::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                   size_t count, ColumnPtr& result_column) 
const {
+Status VSearchExpr::execute_column_impl(VExprContext* context, const Block* 
block,
+                                        const Selector* selector, size_t count,
+                                        ColumnPtr& result_column) const {
     if (fast_execute(context, selector, count, result_column)) {
         return Status::OK();
     }
diff --git a/be/src/exprs/vsearch.h b/be/src/exprs/vsearch.h
index d662c9d3bbe..bb4f8170905 100644
--- a/be/src/exprs/vsearch.h
+++ b/be/src/exprs/vsearch.h
@@ -27,8 +27,8 @@ class VSearchExpr : public VExpr {
 public:
     VSearchExpr(const TExprNode& node);
     ~VSearchExpr() override = default;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     const std::string& expr_name() const override;
     Status evaluate_inverted_index(VExprContext* context, uint32_t 
segment_num_rows) override;
 
diff --git a/be/src/exprs/vslot_ref.cpp b/be/src/exprs/vslot_ref.cpp
index 35b9f5468cb..f02ef50d575 100644
--- a/be/src/exprs/vslot_ref.cpp
+++ b/be/src/exprs/vslot_ref.cpp
@@ -104,8 +104,9 @@ Status VSlotRef::execute(VExprContext* context, Block* 
block, int* result_column
     return Status::OK();
 }
 
-Status VSlotRef::execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                                size_t count, ColumnPtr& result_column) const {
+Status VSlotRef::execute_column_impl(VExprContext* context, const Block* block,
+                                     const Selector* selector, size_t count,
+                                     ColumnPtr& result_column) const {
     if (_column_id >= 0 && _column_id >= block->columns()) {
         return Status::Error<ErrorCode::INTERNAL_ERROR>(
                 "input block not contain slot column {}, column_id={}, 
block={}", *_column_name,
diff --git a/be/src/exprs/vslot_ref.h b/be/src/exprs/vslot_ref.h
index f98c8f0f523..680d8b36761 100644
--- a/be/src/exprs/vslot_ref.h
+++ b/be/src/exprs/vslot_ref.h
@@ -49,8 +49,8 @@ public:
     Status open(RuntimeState* state, VExprContext* context,
                 FunctionContext::FunctionStateScope scope) override;
     Status execute(VExprContext* context, Block* block, int* result_column_id) 
const override;
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override;
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override;
     DataTypePtr execute_type(const Block* block) const override;
 
     const std::string& expr_name() const override;
diff --git a/be/src/exprs/vtopn_pred.h b/be/src/exprs/vtopn_pred.h
index d3f07754f0e..7ed15bb5e08 100644
--- a/be/src/exprs/vtopn_pred.h
+++ b/be/src/exprs/vtopn_pred.h
@@ -88,8 +88,8 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         if (!_predicate->has_value()) {
             result_column = create_always_true_column(count, 
_data_type->is_nullable());
             return Status::OK();
diff --git 
a/be/test/exec/runtime_filter/vruntimefilter_wrapper_sampling_test.cpp 
b/be/test/exec/runtime_filter/vruntimefilter_wrapper_sampling_test.cpp
index 45d0cb93098..1044189ca64 100644
--- a/be/test/exec/runtime_filter/vruntimefilter_wrapper_sampling_test.cpp
+++ b/be/test/exec/runtime_filter/vruntimefilter_wrapper_sampling_test.cpp
@@ -40,8 +40,8 @@ public:
 
     Status execute(VExprContext*, Block*, int*) const override { return 
Status::OK(); }
 
-    Status execute_column(VExprContext*, const Block*, Selector*, size_t,
-                          ColumnPtr&) const override {
+    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t,
+                               ColumnPtr&) const override {
         return Status::OK();
     }
 
diff --git a/be/test/exprs/expr_zonemap_filter_test.cpp 
b/be/test/exprs/expr_zonemap_filter_test.cpp
index b3ab962d574..03b5159262b 100644
--- a/be/test/exprs/expr_zonemap_filter_test.cpp
+++ b/be/test/exprs/expr_zonemap_filter_test.cpp
@@ -232,8 +232,8 @@ public:
 
     const std::string& expr_name() const override { return _expr_name; }
 
-    Status execute_column(VExprContext*, const Block*, Selector*, size_t,
-                          ColumnPtr&) const override {
+    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t,
+                               ColumnPtr&) const override {
         return Status::InternalError("FixedZonemapExpr is only used by zonemap 
tests");
     }
 
@@ -299,8 +299,8 @@ public:
 
     const std::string& expr_name() const override { return _expr_name; }
 
-    Status execute_column(VExprContext*, const Block*, Selector*, size_t,
-                          ColumnPtr&) const override {
+    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t,
+                               ColumnPtr&) const override {
         return Status::InternalError("UnsupportedSingleSlotExpr is only used 
by zonemap tests");
     }
 
diff --git a/be/test/exprs/function/table_function_test.cpp 
b/be/test/exprs/function/table_function_test.cpp
index 19e9b96034b..f5b70595b77 100644
--- a/be/test/exprs/function/table_function_test.cpp
+++ b/be/test/exprs/function/table_function_test.cpp
@@ -67,9 +67,9 @@ protected:
             EXPECT_CALL(*_children[i], execute(_, _, _))
                     .WillRepeatedly(DoAll(SetArgPointee<2>(_column_ids[i]), 
Return(Status::OK())));
             const int col_id = _column_ids[i];
-            EXPECT_CALL(*_children[i], execute_column(_, _, _, _, _))
-                    .WillRepeatedly(Invoke([col_id](VExprContext*, const 
Block* block, Selector*,
-                                                    size_t, ColumnPtr& result) 
{
+            EXPECT_CALL(*_children[i], execute_column_impl(_, _, _, _, _))
+                    .WillRepeatedly(Invoke([col_id](VExprContext*, const 
Block* block,
+                                                    const Selector*, size_t, 
ColumnPtr& result) {
                         result = block->get_by_position(col_id).column;
                         return Status::OK();
                     }));
diff --git a/be/test/exprs/mock_vexpr.h b/be/test/exprs/mock_vexpr.h
index 87f2ddae7e8..73fe3c1c755 100644
--- a/be/test/exprs/mock_vexpr.h
+++ b/be/test/exprs/mock_vexpr.h
@@ -29,8 +29,8 @@ public:
     MOCK_CONST_METHOD0(clone, VExprSPtr());
     MOCK_CONST_METHOD0(expr_name, const std::string&());
     MOCK_CONST_METHOD3(execute, Status(VExprContext* context, Block* block, 
int* result_column_id));
-    MOCK_CONST_METHOD5(execute_column,
-                       Status(VExprContext* context, const Block* block, 
Selector* selector,
+    MOCK_CONST_METHOD5(execute_column_impl,
+                       Status(VExprContext* context, const Block* block, const 
Selector* selector,
                               size_t count, ColumnPtr& result_column));
 }; // class MockVExpr
 
diff --git a/be/test/exprs/score_runtime_test.cpp 
b/be/test/exprs/score_runtime_test.cpp
index 682710870ab..6d5b068664d 100644
--- a/be/test/exprs/score_runtime_test.cpp
+++ b/be/test/exprs/score_runtime_test.cpp
@@ -24,6 +24,8 @@
 
 #include <memory>
 
+#include "core/column/column_nothing.h"
+#include "core/data_type/data_type_nothing.h"
 #include "exprs/vexpr_context.h"
 #include "exprs/virtual_slot_ref.h"
 
@@ -40,9 +42,14 @@ public:
         return kName;
     }
 
+    DataTypePtr execute_type(const Block* block) const override {
+        return std::make_shared<DataTypeNothing>();
+    }
+
     Status execute(VExprContext*, Block*, int*) const override { return 
Status::OK(); }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
+        result_column = ColumnNothing::create(count);
         return Status::OK();
     }
 };
diff --git a/be/test/exprs/vcondition_expr_test.cpp 
b/be/test/exprs/vcondition_expr_test.cpp
index 8605ca496b0..e111f7f65ce 100644
--- a/be/test/exprs/vcondition_expr_test.cpp
+++ b/be/test/exprs/vcondition_expr_test.cpp
@@ -84,8 +84,9 @@ public:
         return Status::OK();
     }
 
-    Status execute_column(VExprContext* /*context*/, const Block* /*block*/, 
Selector* /*selector*/,
-                          size_t /*count*/, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* /*context*/, const Block* 
/*block*/,
+                               const Selector* /*selector*/, size_t /*count*/,
+                               ColumnPtr& result_column) const override {
         result_column = _column;
         return Status::OK();
     }
diff --git a/be/test/exprs/vexpr_test.cpp b/be/test/exprs/vexpr_test.cpp
index 5c67746cfbb..3ece04b06b2 100644
--- a/be/test/exprs/vexpr_test.cpp
+++ b/be/test/exprs/vexpr_test.cpp
@@ -36,7 +36,11 @@
 
 #include "common/object_pool.h"
 #include "core/block/block.h"
+#include "core/column/column_nothing.h"
+#include "core/column/column_vector.h"
 #include "core/data_type/data_type_factory.hpp"
+#include "core/data_type/data_type_nothing.h"
+#include "core/data_type/data_type_number.h"
 #include "core/data_type/define_primitive_type.h"
 #include "core/field.h"
 #include "core/types.h"
@@ -421,106 +425,92 @@ TEST(TEST_VEXPR, LITERALTEST) {
     // bool
     {
         VLiteral literal(create_literal<TYPE_BOOLEAN>(true));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_BOOLEAN>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_BOOLEAN>();
         EXPECT_EQ(v, true);
         EXPECT_EQ("1", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_BOOLEAN, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_BOOLEAN, 0, 
0), true);
         EXPECT_EQ("1", node->value());
     }
     // smallint
     {
         VLiteral literal(create_literal<TYPE_SMALLINT>(1024));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_SMALLINT>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_SMALLINT>();
         EXPECT_EQ(v, 1024);
         EXPECT_EQ("1024", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_SMALLINT, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_SMALLINT, 0, 
0), true);
         EXPECT_EQ("1024", node->value());
     }
     // int
     {
         VLiteral literal(create_literal<TYPE_INT>(1024));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_INT>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_INT>();
         EXPECT_EQ(v, 1024);
         EXPECT_EQ("1024", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_INT, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_INT, 0, 0), 
true);
         EXPECT_EQ("1024", node->value());
     }
     // bigint
     {
         VLiteral literal(create_literal<TYPE_BIGINT>(1024));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_BIGINT>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_BIGINT>();
         EXPECT_EQ(v, 1024);
         EXPECT_EQ("1024", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_BIGINT, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_BIGINT, 0, 
0), true);
         EXPECT_EQ("1024", node->value());
     }
     // large int
     {
         VLiteral literal(create_literal<TYPE_LARGEINT, __int128_t>(1024));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_LARGEINT>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_LARGEINT>();
         EXPECT_EQ(v, 1024);
         EXPECT_EQ("1024", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_LARGEINT, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_LARGEINT, 0, 
0), true);
         EXPECT_EQ("1024", node->value());
     }
     // float
     {
         VLiteral literal(create_literal<TYPE_FLOAT, float>(1024.0f));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_FLOAT>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_FLOAT>();
         EXPECT_FLOAT_EQ(v, 1024.0f);
         EXPECT_EQ("1024", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_FLOAT, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_FLOAT, 0, 0), 
true);
         EXPECT_EQ("1024", node->value());
     }
     // double
     {
         VLiteral literal(create_literal<TYPE_DOUBLE, double>(1024.0));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_DOUBLE>();
-        EXPECT_FLOAT_EQ(v, 1024.0) << ctn.column->get_name();
-        EXPECT_EQ("1024", literal.value()) << ctn.column->get_name();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_DOUBLE>();
+        EXPECT_FLOAT_EQ(v, 1024.0) << result_column->get_name();
+        EXPECT_EQ("1024", literal.value()) << result_column->get_name();
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DOUBLE, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_DOUBLE, 0, 
0), true);
         EXPECT_EQ("1024", node->value());
     }
     // datetime
@@ -535,14 +525,12 @@ TEST(TEST_VEXPR, LITERALTEST) {
         }
         std::cout << data_time_value.type() << std::endl;
         VLiteral literal(create_literal<TYPE_DATETIME, 
std::string>(std::string(date)));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
         EXPECT_EQ("2021-04-07 00:00:00", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DATETIME, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_DATETIME, 0, 
0), true);
         EXPECT_EQ("2021-04-07 00:00:00", node->value());
     }
     // datetimev2
@@ -559,14 +547,12 @@ TEST(TEST_VEXPR, LITERALTEST) {
         std::string date = datetime_v2.to_string();
 
         VLiteral literal(create_literal<TYPE_DATETIMEV2, std::string>(date, 
4));
-        Block block;
-        int ret = -1;
-        EXPECT_TRUE(literal.execute(nullptr, &block, &ret).ok());
+        ColumnPtr result_column;
+        EXPECT_TRUE(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column).ok());
         EXPECT_EQ("1997-11-18 09:12:47.0000", literal.value());
 
-        auto ctn = block.safe_get_by_position(ret);
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DATETIMEV2, 0, 
4), true);
+                create_texpr_node_from((*result_column)[0], TYPE_DATETIMEV2, 
0, 4), true);
         EXPECT_EQ("1997-11-18 09:12:47.0000", node->value());
     }
     // timestamptz
@@ -590,14 +576,12 @@ TEST(TEST_VEXPR, LITERALTEST) {
         std::string tz_value_str = tz_value.to_string(tz, scale);
 
         VLiteral literal(create_literal<TYPE_TIMESTAMPTZ, 
std::string>(tz_value_str, scale));
-        Block block;
-        int ret = -1;
-        EXPECT_TRUE(literal.execute(nullptr, &block, &ret).ok());
+        ColumnPtr result_column;
+        EXPECT_TRUE(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column).ok());
         EXPECT_EQ("1997-11-18 01:12:46.999999+00:00", literal.value());
 
-        auto ctn = block.safe_get_by_position(ret);
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_TIMESTAMPTZ, 0, 
scale), true);
+                create_texpr_node_from((*result_column)[0], TYPE_TIMESTAMPTZ, 
0, scale), true);
         EXPECT_EQ("1997-11-18 01:12:46.999999+00:00", node->value());
 
         node = std::make_shared<VLiteral>(
@@ -618,14 +602,12 @@ TEST(TEST_VEXPR, LITERALTEST) {
         __int64_t dt;
         memcpy(&dt, &date_time_value, sizeof(__int64_t));
         VLiteral literal(create_literal<TYPE_DATE, 
std::string>(std::string(date)));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
         EXPECT_EQ("2021-04-07", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DATE, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_DATE, 0, 0), 
true);
         EXPECT_EQ("2021-04-07", node->value());
     }
     // datev2
@@ -640,16 +622,14 @@ TEST(TEST_VEXPR, LITERALTEST) {
         uint32_t dt;
         memcpy(&dt, &data_time_value, sizeof(uint32_t));
         VLiteral literal(create_literal<TYPE_DATEV2, 
std::string>(std::string(date)));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_DATEV2>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_DATEV2>();
         EXPECT_EQ(v, dt);
         EXPECT_EQ("2021-04-07", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DATEV2, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_DATEV2, 0, 
0), true);
         EXPECT_EQ("2021-04-07", node->value());
     }
     config::allow_zero_date = true;
@@ -711,26 +691,22 @@ TEST(TEST_VEXPR, LITERALTEST) {
     {
         std::string j = R"([null,true,false,100,6.18,"abc"])";
         VLiteral literal(create_literal<TYPE_JSONB, std::string>(j));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
         EXPECT_EQ(j, literal.value());
     }
     // string
     {
         std::string s = "I am Amory, 24";
         VLiteral literal(create_literal<TYPE_STRING, 
std::string>(std::string("I am Amory, 24")));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_STRING>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_STRING>();
         EXPECT_EQ(v, s);
         EXPECT_EQ(s, literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_STRING, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_STRING, 0, 
0), true);
         EXPECT_EQ(s, node->value());
     }
     // varbinary
@@ -760,31 +736,27 @@ TEST(TEST_VEXPR, LITERALTEST) {
     // decimalv2
     {
         VLiteral literal(create_literal<TYPE_DECIMALV2, 
std::string>(std::string("1234.56")));
-        Block block;
-        int ret = -1;
-        static_cast<void>(literal.execute(nullptr, &block, &ret));
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_DECIMALV2>();
+        ColumnPtr result_column;
+        static_cast<void>(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column));
+        auto v = (*result_column)[0].get<TYPE_DECIMALV2>();
         EXPECT_FLOAT_EQ(((double)v.value()) / (std::pow(10, 9)), 1234.56);
         EXPECT_EQ("1234.560000000", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_DECIMALV2, 27, 
9), true);
+                create_texpr_node_from((*result_column)[0], TYPE_DECIMALV2, 
27, 9), true);
         EXPECT_EQ("1234.560000000", node->value());
     }
     // timev2
     {
         VLiteral literal(create_literal<TYPE_TIMEV2, double>(12123400, 4));
-        Block block;
-        int ret = -1;
-        EXPECT_TRUE(literal.execute(nullptr, &block, &ret).ok());
-        auto ctn = block.safe_get_by_position(ret);
-        auto v = (*ctn.column)[0].get<TYPE_TIMEV2>();
+        ColumnPtr result_column;
+        EXPECT_TRUE(literal.execute_column(nullptr, nullptr, nullptr, 1, 
result_column).ok());
+        auto v = (*result_column)[0].get<TYPE_TIMEV2>();
         EXPECT_FLOAT_EQ(v / 1000000, 12.1234);
         EXPECT_EQ("00:00:12.1234", literal.value());
 
         auto node = std::make_shared<VLiteral>(
-                create_texpr_node_from((*ctn.column)[0], TYPE_TIMEV2, 0, 0), 
true);
+                create_texpr_node_from((*result_column)[0], TYPE_TIMEV2, 0, 
0), true);
         EXPECT_EQ("00:00:12", node->value());
     }
     // deciaml32
@@ -862,3 +834,107 @@ TEST(TEST_VEXPR, LITERALTEST) {
     }
     config::allow_zero_date = false;
 }
+
+namespace doris {
+
+// A concrete VExpr that returns a column provided at construction time.
+// The declared type is set via set_data_type(); the returned column may 
differ.
+class FakeVExpr final : public VExpr {
+public:
+    FakeVExpr() = default;
+    const std::string& expr_name() const override {
+        static const std::string name = "FakeVExpr";
+        return name;
+    }
+    Status execute_column_impl(VExprContext* /*context*/, const Block* 
/*block*/,
+                               const Selector* /*selector*/, size_t /*count*/,
+                               ColumnPtr& result_column) const override {
+        result_column = _column;
+        return Status::OK();
+    }
+    void set_column(ColumnPtr col) { _column = std::move(col); }
+    void set_data_type(DataTypePtr type) { _data_type = std::move(type); }
+
+private:
+    ColumnPtr _column;
+};
+
+} // namespace doris
+
+// Tests for VExpr::execute_column post-condition checks.
+TEST(VExprExecuteColumnTest, SizeCheckFails) {
+    using namespace doris;
+    FakeVExpr expr;
+    auto type = std::make_shared<DataTypeInt32>();
+    expr.set_data_type(type);
+
+    // Column has 2 rows but count == 1 → size mismatch
+    auto col = ColumnInt32::create();
+    col->insert_value(1);
+    col->insert_value(2);
+    expr.set_column(std::move(col));
+
+    ColumnPtr result;
+    auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result);
+    EXPECT_FALSE(st.ok());
+    EXPECT_TRUE(st.to_string().find("not equal to expected size") != 
std::string::npos);
+}
+
+TEST(VExprExecuteColumnTest, TypeMismatchFails) {
+    using namespace doris;
+    FakeVExpr expr;
+    // Declare type as Int32, but return a Float64 column — clear type mismatch
+    expr.set_data_type(std::make_shared<DataTypeInt32>());
+
+    auto col = ColumnFloat64::create();
+    col->insert_value(3.14);
+    expr.set_column(std::move(col));
+
+    ColumnPtr result;
+    auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result);
+    EXPECT_FALSE(st.ok());
+}
+
+TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnPasses) {
+    using namespace doris;
+    FakeVExpr expr;
+    // Declared type is Nullable(Int32) but result is Int32 (non-nullable).
+    // This mirrors the use_default_implementation_for_nulls optimization and 
must pass.
+    
expr.set_data_type(std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()));
+
+    auto col = ColumnInt32::create();
+    col->insert_value(7);
+    expr.set_column(std::move(col));
+
+    ColumnPtr result;
+    auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result);
+    EXPECT_TRUE(st.ok());
+}
+
+TEST(VExprExecuteColumnTest, ColumnNothingPassesTypeCheck) {
+    using namespace doris;
+    FakeVExpr expr;
+    // Declare type as Int32, but return ColumnNothing — check should be 
skipped
+    expr.set_data_type(std::make_shared<DataTypeInt32>());
+    expr.set_column(ColumnNothing::create(1));
+
+    ColumnPtr result;
+    auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result);
+    EXPECT_TRUE(st.ok());
+}
+
+TEST(VExprExecuteColumnTest, CorrectColumnPasses) {
+    using namespace doris;
+    FakeVExpr expr;
+    auto type = std::make_shared<DataTypeInt32>();
+    expr.set_data_type(type);
+
+    auto col = ColumnInt32::create();
+    col->insert_value(42);
+    expr.set_column(std::move(col));
+
+    ColumnPtr result;
+    auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result);
+    EXPECT_TRUE(st.ok());
+    EXPECT_EQ(result->size(), 1u);
+}
diff --git a/be/test/exprs/virtual_slot_ref_test.cpp 
b/be/test/exprs/virtual_slot_ref_test.cpp
index d96f64675a7..171e0fbf698 100644
--- a/be/test/exprs/virtual_slot_ref_test.cpp
+++ b/be/test/exprs/virtual_slot_ref_test.cpp
@@ -191,8 +191,9 @@ TEST_F(VirtualSlotRefTest, 
EqualsFunction_WithDifferentTypes) {
         Status execute(VExprContext* context, Block* block, int* 
result_column_id) const override {
             return Status::OK();
         }
-        Status execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                              size_t count, ColumnPtr& result_column) const 
override {
+        Status execute_column_impl(VExprContext* context, const Block* block,
+                                   const Selector* selector, size_t count,
+                                   ColumnPtr& result_column) const override {
             return Status::OK();
         }
 
@@ -312,8 +313,9 @@ TEST_F(VirtualSlotRefTest, EqualsFunction_TestAllBranches) {
             return Status::OK();
         }
 
-        Status execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                              size_t count, ColumnPtr& result_column) const 
override {
+        Status execute_column_impl(VExprContext* context, const Block* block,
+                                   const Selector* selector, size_t count,
+                                   ColumnPtr& result_column) const override {
             return Status::OK();
         }
 
@@ -337,8 +339,9 @@ TEST_F(VirtualSlotRefTest, EqualsFunction_TestAllBranches) {
         Status execute(VExprContext* context, Block* block, int* 
result_column_id) const override {
             return Status::OK();
         }
-        Status execute_column(VExprContext* context, const Block* block, 
Selector* selector,
-                              size_t count, ColumnPtr& result_column) const 
override {
+        Status execute_column_impl(VExprContext* context, const Block* block,
+                                   const Selector* selector, size_t count,
+                                   ColumnPtr& result_column) const override {
             return Status::OK();
         }
         const std::string& expr_name() const override {
diff --git a/be/test/exprs/vsearch_expr_test.cpp 
b/be/test/exprs/vsearch_expr_test.cpp
index afcf69384cd..7d120066c33 100644
--- a/be/test/exprs/vsearch_expr_test.cpp
+++ b/be/test/exprs/vsearch_expr_test.cpp
@@ -25,7 +25,9 @@
 #include <vector>
 
 #include "core/block/block.h"
+#include "core/column/column_nothing.h"
 #include "core/column/column_vector.h"
+#include "core/data_type/data_type_nothing.h"
 #include "core/data_type/data_type_number.h"
 #include "core/data_type/data_type_string.h"
 #include "exprs/vexpr_context.h"
@@ -79,8 +81,8 @@ public:
     }
 
     Status execute(VExprContext*, Block*, int*) const override { return 
Status::OK(); }
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         return Status::OK();
     }
 };
@@ -853,21 +855,6 @@ TEST_F(VSearchExprTest, TestSingleChildBooleanClause) {
     ASSERT_NE(nullptr, vsearch_expr);
 }
 
-TEST_F(VSearchExprTest, TestExecuteWithNullBlock) {
-    auto vsearch_expr = VSearchExpr::create_shared(test_node);
-
-    // Create a basic VExprContext without inverted index context
-    auto dummy_expr = VSearchExpr::create_shared(test_node);
-    VExprContext context(dummy_expr);
-
-    // Test with null block (should not crash)
-
-    ColumnPtr result_column;
-    auto status = vsearch_expr->execute_column(&context, nullptr, nullptr, 0, 
result_column);
-    EXPECT_FALSE(status.ok());
-    EXPECT_TRUE(status.code() == ErrorCode::INTERNAL_ERROR);
-}
-
 TEST_F(VSearchExprTest, TestEvaluateInvertedIndexWithWhitespaceOnlyDSL) {
     TExprNode whitespace_node;
     whitespace_node.node_type = TExprNodeType::SEARCH_EXPR;
diff --git a/be/test/format/parquet/parquet_expr_test.cpp 
b/be/test/format/parquet/parquet_expr_test.cpp
index be0c7200550..cc09f4e02db 100644
--- a/be/test/format/parquet/parquet_expr_test.cpp
+++ b/be/test/format/parquet/parquet_expr_test.cpp
@@ -122,8 +122,8 @@ public:
 
     const std::string& expr_name() const override { return _expr_name; }
 
-    Status execute_column(VExprContext*, const Block*, Selector*, size_t,
-                          ColumnPtr&) const override {
+    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t,
+                               ColumnPtr&) const override {
         return Status::InternalError("TestPageZonemapExpr is only used by 
zonemap tests");
     }
 
diff --git a/be/test/storage/index/ann/extract_query_vector_test.cpp 
b/be/test/storage/index/ann/extract_query_vector_test.cpp
index 8fd6850218e..3f55c43627e 100644
--- a/be/test/storage/index/ann/extract_query_vector_test.cpp
+++ b/be/test/storage/index/ann/extract_query_vector_test.cpp
@@ -54,8 +54,8 @@ public:
 
     bool is_constant() const override { return _is_constant; }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         return Status::OK();
     }
 
diff --git a/be/test/storage/segment/segment_iterator_apply_index_expr_test.cpp 
b/be/test/storage/segment/segment_iterator_apply_index_expr_test.cpp
index ed51625e325..affef9bb943 100644
--- a/be/test/storage/segment/segment_iterator_apply_index_expr_test.cpp
+++ b/be/test/storage/segment/segment_iterator_apply_index_expr_test.cpp
@@ -66,8 +66,8 @@ public:
 
     Status execute(VExprContext*, Block*, int*) const override { return 
Status::OK(); }
 
-    Status execute_column(VExprContext* context, const Block* block, Selector* 
selector,
-                          size_t count, ColumnPtr& result_column) const 
override {
+    Status execute_column_impl(VExprContext* context, const Block* block, 
const Selector* selector,
+                               size_t count, ColumnPtr& result_column) const 
override {
         return Status::OK();
     }
 
diff --git a/be/test/storage/segment/segment_iterator_expr_zonemap_test.cpp 
b/be/test/storage/segment/segment_iterator_expr_zonemap_test.cpp
index 6739cad82e0..e1008084e89 100644
--- a/be/test/storage/segment/segment_iterator_expr_zonemap_test.cpp
+++ b/be/test/storage/segment/segment_iterator_expr_zonemap_test.cpp
@@ -60,8 +60,8 @@ public:
 
     const std::string& expr_name() const override { return _expr_name; }
 
-    Status execute_column(VExprContext*, const Block*, Selector*, size_t,
-                          ColumnPtr&) const override {
+    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t,
+                               ColumnPtr&) const override {
         return Status::InternalError("IntMaxAtLeastExpr is only used by 
zonemap tests");
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to