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

Mryange pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 5b2c066b4d5 [refine](column) Normalize constant expression results 
(#69679)
5b2c066b4d5 is described below

commit 5b2c066b4d55498d5a3e2c7c279c6e26a3ffa9b8
Author: Mryange <[email protected]>
AuthorDate: Thu Oct 29 10:09:35 2026 +0800

    [refine](column) Normalize constant expression results (#69679)
    
    ### What problem does this PR solve?
    
    Issue Number: N/A
    
    Related PR: #66614
    
    Problem Summary:
    
    Root cause: expression constness does not guarantee a physical
    `ColumnConst`. Cached constant expressions can return either a const
    wrapper or a single-row data column, while scan consumers sometimes cast
    the result directly to `ColumnConst`.
    
    Store an owned single-row data column in `ConstantExprResult`, unwrap
    `ColumnConst` without copying, and expose read-only accessors. Migrate
    scan, scalar-function, and ANN consumers, and recreate `ColumnConst`
    when broadcasting the cached value. Preserve current function
    initialization and NULL handling.
    
    ### Release note
    
    Handle constant expressions that return ordinary single-row columns
    consistently in scan predicate processing.
    
    ### Check List (For Author)
    
    - Test:
    - ASAN BE UT build passed. Ran 276 tests across 16 affected suites: 275
    passed and the existing `MathFunctionTest.random_test` was skipped.
    Coverage includes zero-copy normalization, nullable values and
    ownership, arrays with nested NULLs, invalid multi-row input,
    broadcasting, scan predicates, function constant arguments, and ANN
    extraction.
    - clang-format 16, `git diff --check`, and BE build hygiene checks
    passed.
    - clang-tidy checked all 43 modified C++ files. New diagnostics were
    fixed and rechecked; existing complexity diagnostics remain on unchanged
    lines in column and LIKE tests.
    - Behavior changed: Yes. Scan consumers accept cached single-row
    constants regardless of their original physical representation;
    malformed multi-row cache inputs fail at construction.
    - Does this need documentation: No. No SQL syntax, configuration, or
    serialization changes.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 be/src/core/column/column.cpp                      | 11 +++
 be/src/core/column/column.h                        | 15 +++-
 be/src/exec/operator/file_scan_operator.cpp        |  6 +-
 be/src/exec/operator/mock_scan_operator.h          |  6 +-
 be/src/exec/operator/olap_scan_operator.cpp        | 16 ++--
 be/src/exec/operator/scan_operator.cpp             | 43 ++--------
 be/src/exprs/function/array/function_array_index.h |  2 +-
 be/src/exprs/function/function_collection_in.h     |  2 +-
 be/src/exprs/function/function_convert_tz.cpp      |  8 +-
 .../function/function_datetime_string_to_string.h  |  2 +-
 be/src/exprs/function/function_dict_get.cpp        |  3 +-
 be/src/exprs/function/function_dict_get_many.cpp   |  2 +-
 be/src/exprs/function/function_jsonb.cpp           | 10 +--
 .../function/function_other_types_to_date.cpp      |  2 +-
 be/src/exprs/function/function_regexp.cpp          |  8 +-
 be/src/exprs/function/function_string_concat.h     |  2 +-
 be/src/exprs/function/function_string_misc.cpp     |  4 +-
 be/src/exprs/function/in.h                         |  4 +-
 be/src/exprs/function/like.cpp                     | 12 +--
 be/src/exprs/function/random.cpp                   |  2 +-
 be/src/exprs/function_context.cpp                  |  4 +-
 be/src/exprs/function_context.h                    |  8 +-
 be/src/exprs/vexpr.cpp                             | 10 +--
 be/src/exprs/vexpr.h                               |  4 +-
 be/src/storage/index/ann/ann_topn_runtime.cpp      |  9 +-
 be/test/core/column/column_const_test.cpp          | 66 +++++++++++++++
 be/test/exec/operator/olap_scan_operator_test.cpp  | 96 ++++++++++++++++++++++
 .../operator/scan_normalize_predicate_test.cpp     | 72 ++++++----------
 .../exprs/function/function_array_remove_test.cpp  |  6 +-
 .../exprs/function/function_array_shuffle_test.cpp |  6 +-
 .../exprs/function/function_array_trim_test.cpp    |  6 +-
 .../function/function_constant_argument_test.cpp   | 10 +--
 .../exprs/function/function_dict_get_many_test.cpp |  8 +-
 be/test/exprs/function/function_dict_get_test.cpp  |  8 +-
 be/test/exprs/function/function_h3_test.cpp        |  4 +-
 be/test/exprs/function/function_like_test.cpp      | 30 +++----
 be/test/exprs/function/function_math_test.cpp      |  6 +-
 be/test/exprs/function/function_string_test.cpp    |  4 +-
 be/test/exprs/function/function_test_util.h        |  6 +-
 .../index/ann/extract_query_vector_test.cpp        |  4 +-
 .../query/logical/gram/regex_gram_recall_test.cpp  |  4 +-
 be/test/testutil/mock/mock_fn_call.cpp             | 21 ++++-
 be/test/testutil/mock/mock_fn_call.h               |  6 +-
 be/test/testutil/mock/mock_literal_expr.h          |  8 +-
 44 files changed, 352 insertions(+), 214 deletions(-)

diff --git a/be/src/core/column/column.cpp b/be/src/core/column/column.cpp
index ef0d61076b1..a070b01b96c 100644
--- a/be/src/core/column/column.cpp
+++ b/be/src/core/column/column.cpp
@@ -51,6 +51,17 @@ bool contains_boolean_value_column(const IColumn& column) {
 
 } // namespace
 
+ConstantExprResult::ConstantExprResult(ColumnPtr column) {
+    DORIS_CHECK(column.get() != nullptr);
+    // Constant expressions can return either ColumnConst or a plain 
single-row column.
+    if (const auto* const_column = 
check_and_get_column<ColumnConst>(column.get())) {
+        _column_ptr = const_column->get_data_column_ptr();
+    } else {
+        _column_ptr = std::move(column);
+    }
+    DORIS_CHECK_EQ(_column_ptr->size(), 1);
+}
+
 std::string IColumn::dump_structure() const {
     std::stringstream res;
     res << get_name() << "(size = " << size();
diff --git a/be/src/core/column/column.h b/be/src/core/column/column.h
index ad2b6cba3e1..d92351001dc 100644
--- a/be/src/core/column/column.h
+++ b/be/src/core/column/column.h
@@ -893,11 +893,18 @@ bool is_column_const(const IColumn& column);
 bool is_column_nullable(const IColumn& column);
 } // namespace doris
 
-// Wrap `ColumnPtr` because `ColumnPtr` can't be used in forward declaration.
+// Hold a constant expression's single-row result column. For example, both a 
one-row Int64
+// column and ColumnConst(Int64, N) store the same Int64 row here; broadcast 
wraps it again.
+// This class also allows forward declarations where the ColumnPtr alias 
cannot be used.
 namespace doris {
-struct ColumnPtrWrapper {
-    ColumnPtr column_ptr;
+class ConstantExprResult {
+public:
+    explicit ConstantExprResult(ColumnPtr column);
+
+    const IColumn& column() const { return *_column_ptr; }
+    const ColumnPtr& column_ptr() const { return _column_ptr; }
 
-    ColumnPtrWrapper(ColumnPtr col) : column_ptr(std::move(col)) {}
+private:
+    ColumnPtr _column_ptr;
 };
 } // namespace doris
diff --git a/be/src/exec/operator/file_scan_operator.cpp 
b/be/src/exec/operator/file_scan_operator.cpp
index 5f6d41c8b1d..5eb2abf1b16 100644
--- a/be/src/exec/operator/file_scan_operator.cpp
+++ b/be/src/exec/operator/file_scan_operator.cpp
@@ -86,11 +86,9 @@ PushDownType 
FileScanLocalState::_should_push_down_binary_predicate(
     DCHECK(children.size() == 2);
     DCHECK_EQ(children[0]->node_type(), TExprNodeType::SLOT_REF);
     if (children[1]->is_constant()) {
-        std::shared_ptr<ColumnPtrWrapper> const_col_wrapper;
+        std::shared_ptr<ConstantExprResult> const_col_wrapper;
         THROW_IF_ERROR(children[1]->get_const_col(expr_ctx, 
&const_col_wrapper));
-        const auto* const_column =
-                assert_cast<const 
ColumnConst*>(const_col_wrapper->column_ptr.get());
-        constant_val = const_column->operator[](0);
+        constant_val = const_col_wrapper->column()[0];
         return PushDownType::PARTIAL_ACCEPTABLE;
     } else {
         // only handle constant value
diff --git a/be/src/exec/operator/mock_scan_operator.h 
b/be/src/exec/operator/mock_scan_operator.h
index 27f1f8ef53d..62128258e6c 100644
--- a/be/src/exec/operator/mock_scan_operator.h
+++ b/be/src/exec/operator/mock_scan_operator.h
@@ -61,11 +61,9 @@ private:
         DCHECK(children.size() == 2);
         DCHECK_EQ(children[0]->node_type(), TExprNodeType::SLOT_REF);
         if (children[1]->is_constant()) {
-            std::shared_ptr<ColumnPtrWrapper> const_col_wrapper;
+            std::shared_ptr<ConstantExprResult> const_col_wrapper;
             THROW_IF_ERROR(children[1]->get_const_col(expr_ctx, 
&const_col_wrapper));
-            const auto* const_column =
-                    assert_cast<const 
ColumnConst*>(const_col_wrapper->column_ptr.get());
-            constant_val = const_column->operator[](0);
+            constant_val = const_col_wrapper->column()[0];
             return PushDownType::ACCEPTABLE;
         } else {
             // only handle constant value
diff --git a/be/src/exec/operator/olap_scan_operator.cpp 
b/be/src/exec/operator/olap_scan_operator.cpp
index aecb90e02a8..d6239fa4cb2 100644
--- a/be/src/exec/operator/olap_scan_operator.cpp
+++ b/be/src/exec/operator/olap_scan_operator.cpp
@@ -114,11 +114,9 @@ PushDownType 
OlapScanLocalState::_should_push_down_binary_predicate(
     DCHECK(children.size() == 2);
     DCHECK_EQ(VExpr::expr_without_cast(children[0])->node_type(), 
TExprNodeType::SLOT_REF);
     if (children[1]->is_constant()) {
-        std::shared_ptr<ColumnPtrWrapper> const_col_wrapper;
+        std::shared_ptr<ConstantExprResult> const_col_wrapper;
         THROW_IF_ERROR(children[1]->get_const_col(expr_ctx, 
&const_col_wrapper));
-        const auto* const_column =
-                assert_cast<const 
ColumnConst*>(const_col_wrapper->column_ptr.get());
-        constant_val = const_column->operator[](0);
+        constant_val = const_col_wrapper->column()[0];
         return PushDownType::ACCEPTABLE;
     } else {
         // only handle constant value
@@ -585,15 +583,15 @@ Status 
OlapScanLocalState::_should_push_down_function_filter(VectorizedFnCall* f
             return Status::OK();
         } else {
             DCHECK(is_string_type(children[1 - 
i]->data_type()->get_primitive_type()));
-            std::shared_ptr<ColumnPtrWrapper> const_col_wrapper;
+            std::shared_ptr<ConstantExprResult> const_col_wrapper;
             RETURN_IF_ERROR(children[1 - i]->get_const_col(expr_ctx, 
&const_col_wrapper));
-            if (const auto* const_column =
-                        
check_and_get_column<ColumnConst>(const_col_wrapper->column_ptr.get())) {
-                *constant_str = const_column->get_data_at(0);
-            } else {
+            // LIKE/NOT LIKE with a NULL pattern must retain expression 
evaluation: the storage
+            // predicate only receives a StringRef and would interpret NULL as 
an empty pattern.
+            if (const_col_wrapper->column().is_null_at(0)) {
                 pdt = PushDownType::UNACCEPTABLE;
                 return Status::OK();
             }
+            *constant_str = const_col_wrapper->column().get_data_at(0);
         }
     }
     *fn_ctx = func_cxt;
diff --git a/be/src/exec/operator/scan_operator.cpp 
b/be/src/exec/operator/scan_operator.cpp
index e01fdc33bf0..0874c525c98 100644
--- a/be/src/exec/operator/scan_operator.cpp
+++ b/be/src/exec/operator/scan_operator.cpp
@@ -658,43 +658,14 @@ Status 
ScanLocalStateBase::_eval_const_conjuncts(VExprContext* expr_ctx, PushDow
     // Used to handle constant expressions, such as '1 = 1' 
_eval_const_conjuncts does not handle cases like 'colA = 1'
     const char* constant_val = nullptr;
     if (vexpr->is_constant()) {
-        std::shared_ptr<ColumnPtrWrapper> const_col_wrapper;
+        std::shared_ptr<ConstantExprResult> const_col_wrapper;
         RETURN_IF_ERROR(vexpr->get_const_col(expr_ctx, &const_col_wrapper));
-        if (const auto* const_column =
-                    
check_and_get_column<ColumnConst>(const_col_wrapper->column_ptr.get())) {
-            constant_val = const_column->get_data_at(0).data;
-            if (constant_val == nullptr || !*reinterpret_cast<const 
bool*>(constant_val)) {
-                *pdt = PushDownType::ACCEPTABLE;
-                _eos = true;
-                _scan_dependency->set_ready();
-            }
-        } else if (const auto* bool_column =
-                           
check_and_get_column<ColumnUInt8>(const_col_wrapper->column_ptr.get())) {
-            // TODO: If `vexpr->is_constant()` is true, a const column is 
expected here.
-            //  But now we still don't cover all predicates for const 
expression.
-            //  For example, for query `SELECT col FROM tbl WHERE 'PROMOTION' 
LIKE 'AAA%'`,
-            //  predicate `like` will return a ColumnVector<UInt8> which 
contains a single value.
-            LOG(WARNING) << "VExpr[" << vexpr->debug_string()
-                         << "] should return a const column but actually is "
-                         << const_col_wrapper->column_ptr->get_name();
-            DCHECK_EQ(bool_column->size(), 1);
-            /// TODO: There is a DCHECK here, but an additional check is still 
needed. It should return an error code.
-            if (bool_column->size() == 1) {
-                constant_val = bool_column->get_data_at(0).data;
-                if (constant_val == nullptr || !*reinterpret_cast<const 
bool*>(constant_val)) {
-                    *pdt = PushDownType::ACCEPTABLE;
-                    _eos = true;
-                    _scan_dependency->set_ready();
-                }
-            } else {
-                LOG(WARNING) << "Constant predicate in scan node should return 
a bool column with "
-                                "`size == 1` but actually is "
-                             << bool_column->size();
-            }
-        } else {
-            LOG(WARNING) << "VExpr[" << vexpr->debug_string()
-                         << "] should return a const column but actually is "
-                         << const_col_wrapper->column_ptr->get_name();
+        constant_val = const_col_wrapper->column().get_data_at(0).data;
+        // A constant NULL, like constant false, rejects every row in a WHERE 
predicate.
+        if (constant_val == nullptr || !*reinterpret_cast<const 
bool*>(constant_val)) {
+            *pdt = PushDownType::ACCEPTABLE;
+            _eos = true;
+            _scan_dependency->set_ready();
         }
     }
     return Status::OK();
diff --git a/be/src/exprs/function/array/function_array_index.h 
b/be/src/exprs/function/array/function_array_index.h
index 14e00f2ad7d..0430d062ce9 100644
--- a/be/src/exprs/function/array/function_array_index.h
+++ b/be/src/exprs/function/array/function_array_index.h
@@ -122,7 +122,7 @@ public:
         std::shared_ptr<ParamValue> state = std::make_shared<ParamValue>();
         Field field;
         if (context->get_constant_col(1)) {
-            context->get_constant_col(1)->column_ptr->get(0, field);
+            context->get_constant_col(1)->column().get(0, field);
             state->value = field;
             state->type = context->get_arg_type(1)->get_primitive_type();
             context->set_function_state(scope, state);
diff --git a/be/src/exprs/function/function_collection_in.h 
b/be/src/exprs/function/function_collection_in.h
index 0e5c29bdc8c..1099c4f2173 100644
--- a/be/src/exprs/function/function_collection_in.h
+++ b/be/src/exprs/function/function_collection_in.h
@@ -112,7 +112,7 @@ public:
             const auto& const_column_ptr = context->get_constant_col(i);
             // Types like struct, array, and map only support constant 
expressions.
             DCHECK(const_column_ptr != nullptr);
-            const auto& [col, _] = 
unpack_if_const(const_column_ptr->column_ptr);
+            const auto& col = const_column_ptr->column_ptr();
             if (const auto* null_col = 
check_and_get_column<ColumnNullable>(col.get())) {
                 if (null_col->has_null()) {
                     state->null_in_set = true;
diff --git a/be/src/exprs/function/function_convert_tz.cpp 
b/be/src/exprs/function/function_convert_tz.cpp
index e2feed2006a..e3d34c323a6 100644
--- a/be/src/exprs/function/function_convert_tz.cpp
+++ b/be/src/exprs/function/function_convert_tz.cpp
@@ -123,10 +123,10 @@ public:
     }
 
     void init_convert_tz_state(std::shared_ptr<ConvertTzState> state,
-                               const ColumnPtrWrapper* const_from_tz,
-                               const ColumnPtrWrapper* const_to_tz) {
-        auto const_data_from_tz = const_from_tz->column_ptr->get_data_at(0);
-        auto const_data_to_tz = const_to_tz->column_ptr->get_data_at(0);
+                               const ConstantExprResult* const_from_tz,
+                               const ConstantExprResult* const_to_tz) {
+        auto const_data_from_tz = const_from_tz->column().get_data_at(0);
+        auto const_data_to_tz = const_to_tz->column().get_data_at(0);
 
         // from_tz and to_tz must both be non-null.
         if (const_data_from_tz.data == nullptr || const_data_to_tz.data == 
nullptr) {
diff --git a/be/src/exprs/function/function_datetime_string_to_string.h 
b/be/src/exprs/function/function_datetime_string_to_string.h
index 89e172f1ddd..5747a8d4f01 100644
--- a/be/src/exprs/function/function_datetime_string_to_string.h
+++ b/be/src/exprs/function/function_datetime_string_to_string.h
@@ -108,7 +108,7 @@ public:
                     "The second parameter of the function {} must be a 
constant.", get_name());
         }
 
-        auto string_vale = column_string->column_ptr->get_data_at(0);
+        auto string_vale = column_string->column().get_data_at(0);
         if (string_vale.data == nullptr) {
             // func(col , null);
             state->is_valid = false;
diff --git a/be/src/exprs/function/function_dict_get.cpp 
b/be/src/exprs/function/function_dict_get.cpp
index 8ca79599902..fcc1ea8fd93 100644
--- a/be/src/exprs/function/function_dict_get.cpp
+++ b/be/src/exprs/function/function_dict_get.cpp
@@ -27,6 +27,7 @@
 #include "exprs/function/dictionary_factory.h"
 #include "exprs/function/function.h"
 #include "exprs/function/simple_function_factory.h"
+#include "runtime/exec_env.h"
 
 namespace doris {
 
@@ -71,7 +72,7 @@ public:
                                                                 
dict_fn->version_id);
         if (!dict) {
             std::string dict_name =
-                    
context->get_constant_col(0)->column_ptr->get_data_at(0).to_string();
+                    
context->get_constant_col(0)->column().get_data_at(0).to_string();
             throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
                                    "can not find dict name : {} , dict_id : {} 
, version_id : {}  ",
                                    dict_name, dict_fn->dictionary_id, 
dict_fn->version_id);
diff --git a/be/src/exprs/function/function_dict_get_many.cpp 
b/be/src/exprs/function/function_dict_get_many.cpp
index a2cb5a4d1f7..5f8f42c89be 100644
--- a/be/src/exprs/function/function_dict_get_many.cpp
+++ b/be/src/exprs/function/function_dict_get_many.cpp
@@ -77,7 +77,7 @@ public:
                                                                 
dict_fn->version_id);
         if (!dict) {
             std::string dict_name =
-                    
context->get_constant_col(0)->column_ptr->get_data_at(0).to_string();
+                    
context->get_constant_col(0)->column().get_data_at(0).to_string();
             throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
                                    "can not find dict name : {} , dict_id : {} 
, version_id : {}  ",
                                    dict_name, dict_fn->dictionary_id, 
dict_fn->version_id);
diff --git a/be/src/exprs/function/function_jsonb.cpp 
b/be/src/exprs/function/function_jsonb.cpp
index 40150754ba9..202975d0059 100644
--- a/be/src/exprs/function/function_jsonb.cpp
+++ b/be/src/exprs/function/function_jsonb.cpp
@@ -150,11 +150,11 @@ public:
                 if (state) {
                     if (context->get_num_args() == 2) {
                         if (context->is_col_constant(1)) {
-                            const auto default_value_col = 
context->get_constant_col(1)->column_ptr;
-                            if (default_value_col->is_null_at(0)) {
+                            const auto& default_value_col = 
context->get_constant_col(1)->column();
+                            if (default_value_col.is_null_at(0)) {
                                 state->default_is_null = true;
                             } else {
-                                const auto& default_value = 
default_value_col->get_data_at(0);
+                                const auto& default_value = 
default_value_col.get_data_at(0);
 
                                 state->default_value = default_value;
                                 state->has_const_default_value = true;
@@ -2540,8 +2540,8 @@ public:
         if (context->is_col_constant(2)) {
             std::shared_ptr<LikeState> state = std::make_shared<LikeState>();
             state->is_like_pattern = true;
-            const auto pattern_col = context->get_constant_col(2)->column_ptr;
-            const auto& pattern = pattern_col->get_data_at(0);
+            const auto& pattern_col = context->get_constant_col(2)->column();
+            const auto& pattern = pattern_col.get_data_at(0);
             RETURN_IF_ERROR(
                     FunctionLike::construct_like_const_state(context, pattern, 
state, false));
             context->set_function_state(scope, state);
diff --git a/be/src/exprs/function/function_other_types_to_date.cpp 
b/be/src/exprs/function/function_other_types_to_date.cpp
index 8867745fbfd..cdcdaea146b 100644
--- a/be/src/exprs/function/function_other_types_to_date.cpp
+++ b/be/src/exprs/function/function_other_types_to_date.cpp
@@ -486,7 +486,7 @@ struct DateTrunc {
         if (!context->is_col_constant(DateArgIsFirst ? 1 : 0)) {
             return Status::OK();
         }
-        const auto& unit_column = context->get_constant_col(DateArgIsFirst ? 1 
: 0)->column_ptr;
+        const auto& unit_column = context->get_constant_col(DateArgIsFirst ? 1 
: 0)->column_ptr();
         // The nullable wrapper returns NULL without calling execute when the 
constant unit is NULL.
         if (unit_column->is_null_at(0)) {
             return Status::OK();
diff --git a/be/src/exprs/function/function_regexp.cpp 
b/be/src/exprs/function/function_regexp.cpp
index 9457997f14c..b3005685531 100644
--- a/be/src/exprs/function/function_regexp.cpp
+++ b/be/src/exprs/function/function_regexp.cpp
@@ -388,7 +388,7 @@ public:
         if (scope == FunctionContext::FunctionStateScope::THREAD_LOCAL) {
             if (context->is_col_constant(1)) {
                 DCHECK(!context->get_function_state(scope));
-                const auto pattern_col = 
context->get_constant_col(1)->column_ptr;
+                const auto pattern_col = 
context->get_constant_col(1)->column_ptr();
                 const auto& pattern = pattern_col->get_data_at(0);
                 if (pattern.size == 0) {
                     return Status::OK();
@@ -484,7 +484,7 @@ public:
         if (scope == FunctionContext::FunctionStateScope::THREAD_LOCAL) {
             if (context->is_col_constant(1)) {
                 DCHECK(!context->get_function_state(scope));
-                const auto pattern_col = 
context->get_constant_col(1)->column_ptr;
+                const auto pattern_col = 
context->get_constant_col(1)->column_ptr();
                 StringRef options_value;
                 if constexpr (std::is_same_v<FourParamTypes, ParamTypes>) {
                     DCHECK_EQ(context->get_num_args(), 4);
@@ -493,7 +493,7 @@ public:
                     if (!context->is_col_constant(3)) {
                         return Status::OK();
                     }
-                    const auto options_col = 
context->get_constant_col(3)->column_ptr;
+                    const auto options_col = 
context->get_constant_col(3)->column_ptr();
                     // A NULL options argument makes the result NULL before 
regexp compilation.
                     if (options_col->is_null_at(0)) {
                         return Status::OK();
@@ -1019,7 +1019,7 @@ public:
             if (context->is_col_constant(Impl::PATTERN_ARG_IDX)) {
                 DCHECK(!context->get_function_state(scope));
                 const auto pattern_col =
-                        
context->get_constant_col(Impl::PATTERN_ARG_IDX)->column_ptr;
+                        
context->get_constant_col(Impl::PATTERN_ARG_IDX)->column_ptr();
                 const auto& pattern = pattern_col->get_data_at(0);
                 if (pattern.size == 0) {
                     return Status::OK();
diff --git a/be/src/exprs/function/function_string_concat.h 
b/be/src/exprs/function/function_string_concat.h
index d109d78a048..568f97c2b8a 100644
--- a/be/src/exprs/function/function_string_concat.h
+++ b/be/src/exprs/function/function_string_concat.h
@@ -87,7 +87,7 @@ public:
                 state->use_state = false;
                 return IFunction::open(context, scope);
             }
-            auto string_vale = column_string->column_ptr->get_data_at(0);
+            auto string_vale = column_string->column().get_data_at(0);
             if (string_vale.data == nullptr) {
                 // For concat(col, null), it is handled by 
default_implementation_for_nulls
                 state->use_state = false;
diff --git a/be/src/exprs/function/function_string_misc.cpp 
b/be/src/exprs/function/function_string_misc.cpp
index 131cc74acbc..b3cb7caa46c 100644
--- a/be/src/exprs/function/function_string_misc.cpp
+++ b/be/src/exprs/function/function_string_misc.cpp
@@ -416,7 +416,7 @@ public:
             return Status::InvalidArgument(
                     "character argument to convert function must be 
constant.");
         }
-        const auto& character_data = 
context->get_constant_col(1)->column_ptr->get_data_at(0);
+        const auto& character_data = 
context->get_constant_col(1)->column().get_data_at(0);
         if (!iequal(character_data.to_string(), "gbk")) {
             return Status::RuntimeError(
                     "Illegal second argument column of function convert. now 
only support "
@@ -1621,7 +1621,7 @@ public:
         }
 
         auto* const_col = context->get_constant_col(1);
-        auto mode_ref = const_col->column_ptr->get_data_at(0);
+        auto mode_ref = const_col->column().get_data_at(0);
         std::string lower_mode = 
doris::to_lower(std::string(doris::trim(mode_ref.to_string())));
 
         UErrorCode status = U_ZERO_ERROR;
diff --git a/be/src/exprs/function/in.h b/be/src/exprs/function/in.h
index f2a62dc48f6..ee8d8ce77ef 100644
--- a/be/src/exprs/function/in.h
+++ b/be/src/exprs/function/in.h
@@ -92,7 +92,7 @@ public:
         for (int i = 1; i < context->get_num_args(); ++i) {
             const auto& const_column_ptr = context->get_constant_col(i);
             if (const_column_ptr != nullptr) {
-                auto const_data = const_column_ptr->column_ptr->get_data_at(0);
+                auto const_data = const_column_ptr->column().get_data_at(0);
                 if (const_data.data != nullptr) {
                     sz++;
                 }
@@ -127,7 +127,7 @@ public:
         for (int i = 1; i < context->get_num_args(); ++i) {
             const auto& const_column_ptr = context->get_constant_col(i);
             if (const_column_ptr != nullptr) {
-                auto const_data = const_column_ptr->column_ptr->get_data_at(0);
+                auto const_data = const_column_ptr->column().get_data_at(0);
                 state->hybrid_set->insert((void*)const_data.data, 
const_data.size);
             } else {
                 state->use_set = false;
diff --git a/be/src/exprs/function/like.cpp b/be/src/exprs/function/like.cpp
index bdff040c190..ebdfd0b11fc 100644
--- a/be/src/exprs/function/like.cpp
+++ b/be/src/exprs/function/like.cpp
@@ -1024,8 +1024,8 @@ Status FunctionLike::open(FunctionContext* context, 
FunctionContext::FunctionSta
     state->scalar_function = like_fn_scalar;
     if (context->is_col_constant(2)) {
         state->has_custom_escape = true;
-        const auto escape_col = context->get_constant_col(2)->column_ptr;
-        const auto& escape = escape_col->get_data_at(0);
+        const auto& escape_col = context->get_constant_col(2)->column();
+        const auto& escape = escape_col.get_data_at(0);
         if (escape.size != 1) {
             return Status::InternalError("Escape character must be a single 
character, got: {}",
                                          escape.to_string());
@@ -1033,8 +1033,8 @@ Status FunctionLike::open(FunctionContext* context, 
FunctionContext::FunctionSta
         state->escape_char = escape.data[0];
     }
     if (context->is_col_constant(1)) {
-        const auto pattern_col = context->get_constant_col(1)->column_ptr;
-        const auto& pattern = pattern_col->get_data_at(0);
+        const auto& pattern_col = context->get_constant_col(1)->column();
+        const auto& pattern = pattern_col.get_data_at(0);
         RETURN_IF_ERROR(construct_like_const_state(context, pattern, state));
     }
     context->set_function_state(scope, state);
@@ -1056,8 +1056,8 @@ Status FunctionRegexpLike::open(FunctionContext* context,
     state->function = regexp_fn;
     state->scalar_function = regexp_fn_scalar;
     if (context->is_col_constant(1)) {
-        const auto pattern_col = context->get_constant_col(1)->column_ptr;
-        const auto& pattern = pattern_col->get_data_at(0);
+        const auto& pattern_col = context->get_constant_col(1)->column();
+        const auto& pattern = pattern_col.get_data_at(0);
 
         std::string pattern_str = pattern.to_string();
         std::string search_string;
diff --git a/be/src/exprs/function/random.cpp b/be/src/exprs/function/random.cpp
index 071abc1cb04..0fa0e4827b7 100644
--- a/be/src/exprs/function/random.cpp
+++ b/be/src/exprs/function/random.cpp
@@ -70,7 +70,7 @@ public:
                 // constant expression such as an arithmetic one is not 
evaluated in open. Then the
                 // generator is seeded with the first row in execute.
                 if (context->is_col_constant(0)) {
-                    
state->generator.seed(get_seed(*context->get_constant_col(0)->column_ptr));
+                    
state->generator.seed(get_seed(*context->get_constant_col(0)->column_ptr()));
                 } else {
                     state->seed_in_execute = true;
                 }
diff --git a/be/src/exprs/function_context.cpp 
b/be/src/exprs/function_context.cpp
index 74bea9af90a..d4717149eda 100644
--- a/be/src/exprs/function_context.cpp
+++ b/be/src/exprs/function_context.cpp
@@ -46,7 +46,7 @@ std::unique_ptr<doris::FunctionContext> 
FunctionContext::create_context(
 }
 
 void FunctionContext::set_constant_cols(
-        const std::vector<std::shared_ptr<doris::ColumnPtrWrapper>>& 
constant_cols) {
+        const std::vector<std::shared_ptr<doris::ConstantExprResult>>& 
constant_cols) {
     _constant_cols = constant_cols;
 }
 
@@ -119,7 +119,7 @@ bool FunctionContext::is_col_constant(int i) const {
     return _constant_cols[i] != nullptr;
 }
 
-doris::ColumnPtrWrapper* FunctionContext::get_constant_col(int i) const {
+doris::ConstantExprResult* FunctionContext::get_constant_col(int i) const {
     if (i < 0 || i >= _constant_cols.size()) {
         return nullptr;
     }
diff --git a/be/src/exprs/function_context.h b/be/src/exprs/function_context.h
index bc892991a49..66663aa1f06 100644
--- a/be/src/exprs/function_context.h
+++ b/be/src/exprs/function_context.h
@@ -30,7 +30,7 @@
 
 namespace doris {
 
-struct ColumnPtrWrapper;
+class ConstantExprResult;
 struct StringRef;
 class RuntimeState;
 
@@ -73,7 +73,7 @@ public:
     /// it.
     std::unique_ptr<doris::FunctionContext> clone();
 
-    void set_constant_cols(const 
std::vector<std::shared_ptr<doris::ColumnPtrWrapper>>& cols);
+    void set_constant_cols(const 
std::vector<std::shared_ptr<doris::ConstantExprResult>>& cols);
 
     RuntimeState* state() { return _state; }
 
@@ -159,7 +159,7 @@ public:
     // including the FunctionContext* argument). Returns nullptr if the 
argument is not
     // constant. This function can be used to obtain user-specified constants 
in a UDF's
     // Init() or Close() functions.
-    doris::ColumnPtrWrapper* get_constant_col(int arg_idx) const;
+    doris::ConstantExprResult* get_constant_col(int arg_idx) const;
 
     // Creates a StringRef, which memory is available when this function 
context is used next time
     StringRef create_temp_string_val(int64_t len);
@@ -196,7 +196,7 @@ private:
     // Type descriptors for each argument of the function.
     std::vector<DataTypePtr> _arg_types;
 
-    std::vector<std::shared_ptr<doris::ColumnPtrWrapper>> _constant_cols;
+    std::vector<std::shared_ptr<doris::ConstantExprResult>> _constant_cols;
 
     // UDF execute timer
     RuntimeProfile::Counter* _udf_execute_timer = nullptr;
diff --git a/be/src/exprs/vexpr.cpp b/be/src/exprs/vexpr.cpp
index f2cddfdd2d5..34d9130d3e3 100644
--- a/be/src/exprs/vexpr.cpp
+++ b/be/src/exprs/vexpr.cpp
@@ -830,7 +830,7 @@ bool VExpr::is_constant() const {
 }
 
 Status VExpr::get_const_col(VExprContext* context,
-                            std::shared_ptr<ColumnPtrWrapper>* column_wrapper) 
{
+                            std::shared_ptr<ConstantExprResult>* 
column_wrapper) {
     if (!is_constant()) {
         return Status::OK();
     }
@@ -844,7 +844,7 @@ Status VExpr::get_const_col(VExprContext* context,
 
     ColumnPtr result;
     RETURN_IF_ERROR(execute_column(context, nullptr, nullptr, 1, result));
-    _constant_col = std::make_shared<ColumnPtrWrapper>(result);
+    _constant_col = std::make_shared<ConstantExprResult>(result);
     if (column_wrapper != nullptr) {
         *column_wrapper = _constant_col;
     }
@@ -866,9 +866,9 @@ Status VExpr::init_function_context(RuntimeState* state, 
VExprContext* context,
                                     const FunctionBasePtr& function) const {
     FunctionContext* fn_ctx = context->fn_context(_fn_context_index);
     if (scope == FunctionContext::FunctionStateScope::FRAGMENT_LOCAL) {
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
         for (auto c : _children) {
-            std::shared_ptr<ColumnPtrWrapper> const_col;
+            std::shared_ptr<ConstantExprResult> const_col;
             RETURN_IF_ERROR(c->get_const_col(context, &const_col));
             constant_cols.push_back(const_col);
         }
@@ -924,7 +924,7 @@ uint64_t VExpr::get_digest(uint64_t seed) const {
 }
 
 ColumnPtr VExpr::get_result_from_const(size_t count) const {
-    return ColumnConst::create(_constant_col->column_ptr, count);
+    return ColumnConst::create(_constant_col->column_ptr(), count);
 }
 
 Status VExpr::_evaluate_inverted_index(VExprContext* context, const 
FunctionBasePtr& function,
diff --git a/be/src/exprs/vexpr.h b/be/src/exprs/vexpr.h
index 134b606ea69..aee8c2870b7 100644
--- a/be/src/exprs/vexpr.h
+++ b/be/src/exprs/vexpr.h
@@ -386,7 +386,7 @@ public:
     /// owned by this expr. This should only be called after Open() has been 
called on this
     /// expr.
     MOCK_FUNCTION Status get_const_col(VExprContext* context,
-                                       std::shared_ptr<ColumnPtrWrapper>* 
column_wrapper);
+                                       std::shared_ptr<ConstantExprResult>* 
column_wrapper);
 
     int fn_context_index() const { return _fn_context_index; }
 
@@ -526,7 +526,7 @@ protected:
 
     // If this expr is constant, this will store and cache the value generated 
by
     // get_const_col()
-    std::shared_ptr<ColumnPtrWrapper> _constant_col;
+    std::shared_ptr<ConstantExprResult> _constant_col;
     bool _prepared = false; // for base class VExpr
     // for concrete classes
     bool _prepare_finished = false;
diff --git a/be/src/storage/index/ann/ann_topn_runtime.cpp 
b/be/src/storage/index/ann/ann_topn_runtime.cpp
index 38998f145ac..f85f967c811 100644
--- a/be/src/storage/index/ann/ann_topn_runtime.cpp
+++ b/be/src/storage/index/ann/ann_topn_runtime.cpp
@@ -27,7 +27,6 @@
 #include "common/status.h"
 #include "core/column/column.h"
 #include "core/column/column_array.h"
-#include "core/column/column_const.h"
 #include "core/column/column_nullable.h"
 #include "core/column/column_vector.h"
 #include "core/data_type/primitive_type.h"
@@ -49,18 +48,14 @@ Result<ColumnFloat32::Ptr> 
extract_query_vector(std::shared_ptr<VExpr> arg_expr)
                                                    arg_expr->debug_string()));
     }
 
-    std::shared_ptr<ColumnPtrWrapper> column_wrapper;
+    std::shared_ptr<ConstantExprResult> column_wrapper;
     auto st = arg_expr->get_const_col(nullptr, &column_wrapper);
     if (!st.ok()) {
         return ResultError(Status::InvalidArgument("Failed to get constant 
column, error: {}",
                                                    st.to_string()));
     }
 
-    // Unwrap ColumnConst without copy to get the underlying single-row column
-    IColumn::Ptr col_ptr = column_wrapper->column_ptr;
-    if (const auto* const_col = check_and_get_column<ColumnConst>(*col_ptr)) {
-        col_ptr = const_col->get_data_column_ptr();
-    }
+    IColumn::Ptr col_ptr = column_wrapper->column_ptr();
 
     // The expected runtime column layout for the literal is:
     // Nullable(ColumnArray(Nullable(ColumnFloat32))) with exactly 1 row (one 
array literal)
diff --git a/be/test/core/column/column_const_test.cpp 
b/be/test/core/column/column_const_test.cpp
index 3056d5276a8..e3cf2fbdc71 100644
--- a/be/test/core/column/column_const_test.cpp
+++ b/be/test/core/column/column_const_test.cpp
@@ -42,6 +42,72 @@ TEST(ColumnConstTest, TestCreate) {
     EXPECT_TRUE(!is_column_const(column_const2->get_data_column()));
 }
 
+TEST(ConstantExprResultTest, StoresSingleRowDataColumn) {
+    auto data_column = ColumnHelper::create_column<DataTypeInt64>({7});
+
+    ConstantExprResult data_wrapper(data_column);
+    EXPECT_EQ(data_wrapper.column_ptr().get(), data_column.get());
+    EXPECT_EQ(data_wrapper.column().get_int(0), 7);
+    EXPECT_FALSE(is_column_const(data_wrapper.column()));
+
+    auto const_column = ColumnConst::create(data_column, 3);
+    ConstantExprResult const_wrapper(std::move(const_column));
+    EXPECT_EQ(const_wrapper.column_ptr().get(), data_column.get());
+    EXPECT_EQ(const_wrapper.column().get_int(0), 7);
+    EXPECT_FALSE(is_column_const(const_wrapper.column()));
+}
+
+TEST(ConstantExprResultTest, RejectsNonConstMultiRowColumn) {
+    auto column = ColumnHelper::create_column<DataTypeInt64>({7, 8});
+#ifdef NDEBUG
+    EXPECT_THROW(static_cast<void>(ConstantExprResult(column)), Exception);
+#elif DCHECK_IS_ON()
+    EXPECT_DEATH(static_cast<void>(ConstantExprResult(column)), "Check 
failed");
+#endif
+}
+
+static void check_nullable_constant_result(uint8_t is_null) {
+    auto data_column = 
ColumnHelper::create_nullable_column<DataTypeInt64>({7}, {is_null});
+    const auto* original = data_column.get();
+    ConstantExprResult direct_wrapper(data_column);
+    ConstantExprResult const_wrapper(ColumnConst::create(data_column, 3));
+    data_column.reset();
+
+    EXPECT_EQ(direct_wrapper.column_ptr().get(), original);
+    EXPECT_EQ(const_wrapper.column_ptr().get(), original);
+    EXPECT_TRUE(const_wrapper.column().is_nullable());
+    EXPECT_EQ(const_wrapper.column().is_null_at(0), is_null != 0);
+    auto broadcast = ColumnConst::create(const_wrapper.column_ptr(), 5);
+    EXPECT_EQ(broadcast->get_data_column_ptr().get(), original);
+    EXPECT_EQ(broadcast->size(), 5);
+    EXPECT_EQ(broadcast->is_null_at(4), is_null != 0);
+    if (!is_null) {
+        EXPECT_EQ((*broadcast)[4].get<TYPE_BIGINT>(), 7);
+    }
+}
+
+TEST(ConstantExprResultTest, PreservesNullableValuesAndOwnership) {
+    check_nullable_constant_result(0);
+    check_nullable_constant_result(1);
+}
+
+TEST(ConstantExprResultTest, PreservesArrayRowAndNestedNulls) {
+    auto elements = ColumnHelper::create_nullable_column<DataTypeInt64>({7, 
8}, {0, 1});
+    auto offsets = ColumnArray::ColumnOffsets::create();
+    offsets->insert_value(2);
+    ColumnPtr array = ColumnArray::create(elements, std::move(offsets));
+    ConstantExprResult wrapper(ColumnConst::create(array, 3));
+    EXPECT_EQ(wrapper.column_ptr().get(), array.get());
+    auto broadcast = ColumnConst::create(wrapper.column_ptr(), 5);
+    EXPECT_EQ(broadcast->size(), 5);
+    EXPECT_EQ(broadcast->get_data_column().compare_at(0, 0, *array, 1), 0);
+
+    const auto& data = assert_cast<const ColumnArray&>(wrapper.column());
+    EXPECT_EQ(data.get_data_ptr().get(), elements.get());
+    EXPECT_EQ(data.get_data()[0].get<TYPE_BIGINT>(), 7);
+    EXPECT_TRUE(data.get_data().is_null_at(1));
+}
+
 TEST(ColumnConstTest, IsExclusiveChecksNestedColumn) {
     auto column_data = ColumnHelper::create_column<DataTypeInt64>({7});
     auto column_const = ColumnConst::create(column_data, 3);
diff --git a/be/test/exec/operator/olap_scan_operator_test.cpp 
b/be/test/exec/operator/olap_scan_operator_test.cpp
index fec9ff1d5ce..068d38f982c 100644
--- a/be/test/exec/operator/olap_scan_operator_test.cpp
+++ b/be/test/exec/operator/olap_scan_operator_test.cpp
@@ -23,12 +23,19 @@
 #include <optional>
 
 #include "common/object_pool.h"
+#include "core/column/column_const.h"
+#include "core/data_type/data_type_nullable.h"
 #include "core/data_type/data_type_number.h"
+#include "core/data_type/data_type_string.h"
+#include "exprs/vexpr_context.h"
 #include "gen_cpp/PlanNodes_types.h"
 #include "gen_cpp/QueryCache_types.h"
 #include "storage/rowset/beta_rowset.h"
+#include "testutil/column_helper.h"
 #include "testutil/desc_tbl_builder.h"
+#include "testutil/mock/mock_fn_call.h"
 #include "testutil/mock/mock_runtime_state.h"
+#include "testutil/mock/mock_slot_ref.h"
 
 namespace doris {
 
@@ -95,6 +102,95 @@ TEST_F(OlapScanOperatorChangelogPushDownTest, 
AppendOnlyKeepsValuePredicatePushD
     EXPECT_TRUE(_local_state->can_push_down_column_predicate(_value_slot));
 }
 
+class OlapScanLikeConstantPatternTest : public 
OlapScanOperatorChangelogPushDownTest {
+protected:
+    void check_pattern(bool wrap_const, uint8_t is_null, const std::string& 
value, bool opposite) {
+        ColumnPtr column = 
ColumnHelper::create_nullable_column<DataTypeString>({value}, {is_null});
+        if (wrap_const) {
+            column = ColumnConst::create(column, 1);
+        }
+        auto pattern = MockFnCall::create("pattern");
+        pattern->_mock_is_constant = true;
+        pattern->_data_type = 
make_nullable(std::make_shared<DataTypeString>());
+        pattern->set_const_expr_col(std::move(column));
+        auto like = MockFnCall::create("like");
+        like->add_child(std::make_shared<MockSlotRef>(0, 
std::make_shared<DataTypeString>()));
+        like->add_child(pattern);
+        VExprSPtr root = like;
+        if (opposite) {
+            auto not_expr = MockFnCall::create("not");
+            not_expr->_node_type = TExprNodeType::COMPOUND_PRED;
+            not_expr->add_child(like);
+            root = not_expr;
+        }
+        auto ctx = VExprContext::create_shared(root);
+        like->_fn_context_index = ctx->register_function_context(
+                _state.get(), make_nullable(std::make_shared<DataTypeBool>()),
+                {std::make_shared<DataTypeString>(), pattern->data_type()});
+        auto pdt = PushDownType::ACCEPTABLE;
+        StringRef constant_str;
+        FunctionContext* fn_ctx = nullptr;
+        auto st = _local_state->_should_push_down_function_filter(like.get(), 
ctx.get(),
+                                                                  
&constant_str, &fn_ctx, pdt);
+        ASSERT_TRUE(st.ok()) << st.to_string();
+        EXPECT_EQ(pdt, is_null ? PushDownType::UNACCEPTABLE : 
PushDownType::ACCEPTABLE);
+        if (is_null) {
+            check_expression_fallback(ctx.get());
+        } else {
+            EXPECT_EQ(constant_str.to_string(), value);
+            EXPECT_EQ(fn_ctx, ctx->fn_context(like->_fn_context_index));
+        }
+    }
+
+    void check_expression_fallback(VExprContext* ctx) {
+        // Rejecting pushdown must leave both LIKE and NOT LIKE for expression 
evaluation.
+        std::vector<std::shared_ptr<ColumnPredicate>> predicates;
+        auto pdt = PushDownType::UNACCEPTABLE;
+        auto st = _local_state->_normalize_function_filters(ctx, _key_slot, 
predicates, &pdt);
+        ASSERT_TRUE(st.ok()) << st.to_string();
+        EXPECT_TRUE(predicates.empty());
+        EXPECT_EQ(pdt, PushDownType::UNACCEPTABLE);
+    }
+};
+
+TEST_F(OlapScanLikeConstantPatternTest, 
NullPatternsRetainExpressionEvaluation) {
+    for (bool wrap_const : {false, true}) {
+        for (bool opposite : {false, true}) {
+            check_pattern(wrap_const, 1, "", opposite);
+            // A nonempty NULL payload must never be read as a LIKE pattern.
+            check_pattern(wrap_const, 1, "ignored%", opposite);
+        }
+    }
+}
+
+TEST_F(OlapScanLikeConstantPatternTest, 
EmptyAndNonemptyPatternsRemainPushable) {
+    for (bool wrap_const : {false, true}) {
+        check_pattern(wrap_const, 0, "", false);
+        check_pattern(wrap_const, 0, "prefix%", false);
+    }
+}
+
+TEST_F(OlapScanLikeConstantPatternTest, BinaryPredicatePreservesNullConstant) {
+    for (bool wrap_const : {false, true}) {
+        ColumnPtr column = 
ColumnHelper::create_nullable_column<DataTypeInt32>({42}, {1});
+        if (wrap_const) {
+            column = ColumnConst::create(column, 1);
+        }
+        auto constant = MockFnCall::create("constant");
+        constant->_mock_is_constant = true;
+        constant->set_const_expr_col(std::move(column));
+        auto eq = MockFnCall::create("eq");
+        eq->add_child(std::make_shared<MockSlotRef>(0, 
std::make_shared<DataTypeInt32>()));
+        eq->add_child(constant);
+        auto ctx = VExprContext::create_shared(eq);
+        Field value;
+        EXPECT_EQ(_local_state->_should_push_down_binary_predicate(eq.get(), 
ctx.get(), value,
+                                                                   {"eq"}),
+                  PushDownType::ACCEPTABLE);
+        EXPECT_TRUE(value.is_null());
+    }
+}
+
 class OlapScanOperatorTsoPruningTest : public 
OlapScanOperatorChangelogPushDownTest {
 protected:
     void SetUp() override {
diff --git a/be/test/exec/operator/scan_normalize_predicate_test.cpp 
b/be/test/exec/operator/scan_normalize_predicate_test.cpp
index d61a0541a74..49ec1d5dcfc 100644
--- a/be/test/exec/operator/scan_normalize_predicate_test.cpp
+++ b/be/test/exec/operator/scan_normalize_predicate_test.cpp
@@ -52,6 +52,25 @@ struct ScanNormalizePredicate : public ::testing::Test {
                 std::vector<DataTypePtr> {std::make_shared<DataTypeInt64>()}, 
&pool};
         op->_output_tuple_desc = op->_row_descriptor.tuple_descriptors()[0];
     }
+    void check_nullable_constant_conjunct(bool wrap_const, uint8_t is_null, 
bool value) {
+        auto local_state = std::make_shared<MockScanLocalState>(state.get(), 
op.get());
+        local_state->_scan_dependency = Dependency::create_shared(0, 0, 
"DEPENDENCY");
+        ColumnPtr column = 
ColumnHelper::create_nullable_column<DataTypeBool>({value}, {is_null});
+        if (wrap_const) {
+            column = ColumnConst::create(column, 1);
+        }
+        auto fn = MockFnCall::create("eq");
+        fn->_mock_is_constant = true;
+        fn->set_const_expr_col(std::move(column));
+        auto ctx = VExprContext::create_shared(fn);
+        auto pdt = PushDownType::UNACCEPTABLE;
+        auto st = local_state->_eval_const_conjuncts(ctx.get(), &pdt);
+        ASSERT_TRUE(st.ok()) << st.to_string();
+        const bool rejects_rows = is_null || !value;
+        EXPECT_EQ(local_state->_eos, rejects_rows);
+        EXPECT_EQ(local_state->_scan_dependency->ready(), rejects_rows);
+        EXPECT_EQ(pdt == PushDownType::ACCEPTABLE, rejects_rows);
+    }
     ObjectPool pool;
     std::shared_ptr<MockRuntimeState> state;
     std::shared_ptr<MockScanOperatorX> op;
@@ -128,51 +147,14 @@ TEST_F(ScanNormalizePredicate, 
test_eval_const_conjuncts2) {
     EXPECT_TRUE(local_state->_eos);
 }
 
-TEST_F(ScanNormalizePredicate, test_eval_const_conjuncts3) {
-    // case
-    // Predicate false and xxx and xxx ....
-    // The returned column is not a constant column, but the size is not equal 
to 1
-    auto local_state = std::make_shared<MockScanLocalState>(state.get(), 
op.get());
-
-    auto fn_eq = MockFnCall::create("eq");
-    fn_eq->_mock_is_constant = true;
-    
fn_eq->set_const_expr_col(ColumnHelper::create_column<DataTypeBool>({false, 
false}));
-    EXPECT_TRUE(fn_eq->is_constant());
-
-    auto ctx = VExprContext::create_shared(fn_eq);
-    ctx->_prepared = true;
-    ctx->_opened = true;
-
-    VExprSPtr new_root;
-    auto conjunct_expr_root = ctx;
-    // There is a DCHECK in the code to ensure size must be equal to 1, wait 
for this part of the code to be removed later
-    // auto st = local_state->_normalize_predicate(
-    //                                             conjunct_expr_root.get(), 
new_root);
-    // EXPECT_FALSE(st.ok());
-    // std::cout << st.msg() << std::endl;
-}
-
-TEST_F(ScanNormalizePredicate, test_eval_const_conjuncts4) {
-    // case
-    // Predicate false and xxx and xxx ....
-    // The returned column is neither a constant column nor a boolean column
-    auto local_state = std::make_shared<MockScanLocalState>(state.get(), 
op.get());
-
-    auto fn_eq = MockFnCall::create("eq");
-    fn_eq->_mock_is_constant = true;
-    
fn_eq->set_const_expr_col(ColumnHelper::create_column<DataTypeInt32>({false, 
false}));
-    EXPECT_TRUE(fn_eq->is_constant());
-
-    auto ctx = VExprContext::create_shared(fn_eq);
-    ctx->_prepared = true;
-    ctx->_opened = true;
-
-    VExprSPtr new_root;
-    auto conjunct_expr_root = ctx;
-    auto st = local_state->_normalize_predicate(conjunct_expr_root.get(),
-                                                conjunct_expr_root->root(), 
new_root);
-    EXPECT_TRUE(st.ok());
-    std::cout << st.msg() << std::endl;
+TEST_F(ScanNormalizePredicate, NullableConstantConjuncts) {
+    for (bool wrap_const : {false, true}) {
+        for (uint8_t is_null : {0, 1}) {
+            for (bool value : {false, true}) {
+                check_nullable_constant_conjunct(wrap_const, is_null, value);
+            }
+        }
+    }
 }
 
 TEST_F(ScanNormalizePredicate, test_is_predicate_acting_on_slot1) {
diff --git a/be/test/exprs/function/function_array_remove_test.cpp 
b/be/test/exprs/function/function_array_remove_test.cpp
index 7be1f72bef1..a502d3b3e67 100644
--- a/be/test/exprs/function/function_array_remove_test.cpp
+++ b/be/test/exprs/function/function_array_remove_test.cpp
@@ -56,12 +56,12 @@ static void check_array_remove_case(DataTypePtr array_type, 
DataTypePtr element_
 
     ColumnNumbers arguments = {0, 1};
     std::vector<DataTypePtr> arg_types = {array_type, element_type};
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols = {nullptr, 
nullptr};
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_cols = {nullptr, 
nullptr};
     if (const_array) {
-        constant_cols[0] = 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(0).column);
+        constant_cols[0] = 
std::make_shared<ConstantExprResult>(block.get_by_position(0).column);
     }
     if (const_target) {
-        constant_cols[1] = 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column);
+        constant_cols[1] = 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column);
     }
 
     FunctionUtils fn_utils(return_type, arg_types, false);
diff --git a/be/test/exprs/function/function_array_shuffle_test.cpp 
b/be/test/exprs/function/function_array_shuffle_test.cpp
index 149cf1a7d40..0b365aff063 100644
--- a/be/test/exprs/function/function_array_shuffle_test.cpp
+++ b/be/test/exprs/function/function_array_shuffle_test.cpp
@@ -73,9 +73,9 @@ static Status run_array_shuffle(const std::vector<TestArray>& 
arrays, bool const
     block.insert({array_ptr, array_type, "array"});
     DataTypes arg_types = {array_type};
     ColumnNumbers arguments = {0};
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols = {nullptr};
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_cols = {nullptr};
     if (const_array) {
-        constant_cols[0] = std::make_shared<ColumnPtrWrapper>(array_ptr);
+        constant_cols[0] = std::make_shared<ConstantExprResult>(array_ptr);
     }
     if (!seeds.empty()) {
         MutableColumnPtr seed_column = seed_type->create_column();
@@ -86,7 +86,7 @@ static Status run_array_shuffle(const std::vector<TestArray>& 
arrays, bool const
         constant_cols.push_back(nullptr);
         if (seeds.size() == 1) {
             seed_ptr = ColumnConst::create(seed_ptr, row_size);
-            constant_cols[1] = std::make_shared<ColumnPtrWrapper>(seed_ptr);
+            constant_cols[1] = std::make_shared<ConstantExprResult>(seed_ptr);
         } else {
             EXPECT_EQ(seeds.size(), row_size);
         }
diff --git a/be/test/exprs/function/function_array_trim_test.cpp 
b/be/test/exprs/function/function_array_trim_test.cpp
index 440a5c2c7e7..910e7dac73e 100644
--- a/be/test/exprs/function/function_array_trim_test.cpp
+++ b/be/test/exprs/function/function_array_trim_test.cpp
@@ -62,12 +62,12 @@ static void check_array_trim_case(const DataSet& data_set, 
bool const_array, boo
             "trim_array", block.get_columns_with_type_and_name(), array_type);
     ASSERT_NE(function, nullptr);
 
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_columns(2);
     if (const_array) {
-        constant_columns[0] = 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(0).column);
+        constant_columns[0] = 
std::make_shared<ConstantExprResult>(block.get_by_position(0).column);
     }
     if (const_size) {
-        constant_columns[1] = 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column);
+        constant_columns[1] = 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column);
     }
 
     FunctionUtils function_utils(array_type, {array_type, size_type}, false);
diff --git a/be/test/exprs/function/function_constant_argument_test.cpp 
b/be/test/exprs/function/function_constant_argument_test.cpp
index b0fff06b039..7e432022336 100644
--- a/be/test/exprs/function/function_constant_argument_test.cpp
+++ b/be/test/exprs/function/function_constant_argument_test.cpp
@@ -56,7 +56,7 @@ protected:
     // for an argument that is not evaluated in open. The result is appended 
to each block.
     static Status execute(const std::string& name, std::vector<Block>& blocks,
                           const DataTypePtr& return_type,
-                          const 
std::vector<std::shared_ptr<ColumnPtrWrapper>>& constant_cols) {
+                          const 
std::vector<std::shared_ptr<ConstantExprResult>>& constant_cols) {
         auto arguments = blocks[0].get_columns_with_type_and_name();
         FunctionBasePtr function =
                 SimpleFunctionFactory::instance().get_function(name, 
arguments, return_type);
@@ -92,7 +92,7 @@ protected:
     }
 
     static Status execute(const std::string& name, Block& block, const 
DataTypePtr& return_type,
-                          const 
std::vector<std::shared_ptr<ColumnPtrWrapper>>& constant_cols) {
+                          const 
std::vector<std::shared_ptr<ConstantExprResult>>& constant_cols) {
         std::vector<Block> blocks;
         blocks.push_back(std::move(block));
         Status status = execute(name, blocks, return_type, constant_cols);
@@ -100,8 +100,8 @@ protected:
         return status;
     }
 
-    static std::shared_ptr<ColumnPtrWrapper> constant(const ColumnPtr& column) 
{
-        return std::make_shared<ColumnPtrWrapper>(column);
+    static std::shared_ptr<ConstantExprResult> constant(const ColumnPtr& 
column) {
+        return std::make_shared<ConstantExprResult>(column);
     }
 
     static ColumnPtr strings(const std::vector<std::string>& values) {
@@ -347,7 +347,7 @@ TEST_F(FunctionConstantArgumentTest, date_trunc_null_unit) {
             block.insert({dates, date_type, "date"});
             block.insert({unit, string_type, "unit"});
         }
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols(2);
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols(2);
         constant_cols[unit_first ? 0 : 1] = constant(unit);
         Status status = execute("date_trunc", block, return_type, 
constant_cols);
         ASSERT_TRUE(status.ok()) << status.to_string();
diff --git a/be/test/exprs/function/function_dict_get_many_test.cpp 
b/be/test/exprs/function/function_dict_get_many_test.cpp
index e7d72f210d5..6e1dd7cc537 100644
--- a/be/test/exprs/function/function_dict_get_many_test.cpp
+++ b/be/test/exprs/function/function_dict_get_many_test.cpp
@@ -40,8 +40,8 @@ TEST(FunctionDictGetManyTest, test_without_dict_function) {
 
     FunctionContext fn_context {};
     fn_context._arg_types.resize(3);
-    std::vector<std::shared_ptr<doris::ColumnPtrWrapper>> constant_cols {
-            std::make_shared<doris::ColumnPtrWrapper>(
+    std::vector<std::shared_ptr<doris::ConstantExprResult>> constant_cols {
+            std::make_shared<doris::ConstantExprResult>(
                     ColumnHelper::create_column<DataTypeString>({"test"})),
     };
 
@@ -64,8 +64,8 @@ TEST(FunctionDictGetManyTest, test_without_dict_id) {
 
     FunctionContext fn_context {};
     fn_context._arg_types.resize(3);
-    std::vector<std::shared_ptr<doris::ColumnPtrWrapper>> constant_cols {
-            std::make_shared<doris::ColumnPtrWrapper>(
+    std::vector<std::shared_ptr<doris::ConstantExprResult>> constant_cols {
+            std::make_shared<doris::ConstantExprResult>(
                     ColumnHelper::create_column<DataTypeString>({"test"})),
     };
 
diff --git a/be/test/exprs/function/function_dict_get_test.cpp 
b/be/test/exprs/function/function_dict_get_test.cpp
index 255b27846e4..8aefd272d7a 100644
--- a/be/test/exprs/function/function_dict_get_test.cpp
+++ b/be/test/exprs/function/function_dict_get_test.cpp
@@ -40,8 +40,8 @@ TEST(FunctionDictGetTest, test_without_dict_function) {
 
     FunctionContext fn_context {};
     fn_context._arg_types.resize(3);
-    std::vector<std::shared_ptr<doris::ColumnPtrWrapper>> constant_cols {
-            std::make_shared<doris::ColumnPtrWrapper>(
+    std::vector<std::shared_ptr<doris::ConstantExprResult>> constant_cols {
+            std::make_shared<doris::ConstantExprResult>(
                     ColumnHelper::create_column<DataTypeString>({"test"})),
     };
 
@@ -64,8 +64,8 @@ TEST(FunctionDictGetTest, test_without_dict_id) {
 
     FunctionContext fn_context {};
     fn_context._arg_types.resize(3);
-    std::vector<std::shared_ptr<doris::ColumnPtrWrapper>> constant_cols {
-            std::make_shared<doris::ColumnPtrWrapper>(
+    std::vector<std::shared_ptr<doris::ConstantExprResult>> constant_cols {
+            std::make_shared<doris::ConstantExprResult>(
                     ColumnHelper::create_column<DataTypeString>({"test"})),
     };
 
diff --git a/be/test/exprs/function/function_h3_test.cpp 
b/be/test/exprs/function/function_h3_test.cpp
index 215d4a799f9..b0f71019c3e 100644
--- a/be/test/exprs/function/function_h3_test.cpp
+++ b/be/test/exprs/function/function_h3_test.cpp
@@ -108,9 +108,9 @@ static void check_coordinates(bool boundary, bool constant, 
bool only_null, size
     ASSERT_NE(function, nullptr);
     FunctionUtils utils(output_type, {input_type}, false);
     auto* context = utils.get_fn_ctx();
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constants(1);
+    std::vector<std::shared_ptr<ConstantExprResult>> constants(1);
     if (constant) {
-        constants[0] = 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(0).column);
+        constants[0] = 
std::make_shared<ConstantExprResult>(block.get_by_position(0).column);
     }
     context->set_constant_cols(constants);
     ASSERT_TRUE(function->open(context, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL).ok());
diff --git a/be/test/exprs/function/function_like_test.cpp 
b/be/test/exprs/function/function_like_test.cpp
index 42fb0be73ed..018b41c4cab 100644
--- a/be/test/exprs/function/function_like_test.cpp
+++ b/be/test/exprs/function/function_like_test.cpp
@@ -82,9 +82,9 @@ Status execute_single_pattern(const TQueryOptions& 
query_options, const std::str
                                        ? ColumnPtr(std::move(patterns))
                                        : 
ColumnConst::create(std::move(patterns), 1);
 
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_columns(2);
     if (mode == PatternMode::CONST_AT_OPEN) {
-        constant_columns[1] = 
std::make_shared<ColumnPtrWrapper>(pattern_column);
+        constant_columns[1] = 
std::make_shared<ConstantExprResult>(pattern_column);
     }
     context->set_constant_cols(constant_columns);
 
@@ -144,8 +144,8 @@ void check_constant_pattern_batch(const 
std::vector<std::string>& values,
     pattern_data->insert_data(pattern.data(), pattern.size());
     ColumnPtr pattern_column = ColumnConst::create(std::move(pattern_data), 
values.size());
 
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
-    constant_columns[1] = std::make_shared<ColumnPtrWrapper>(pattern_column);
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_columns(2);
+    constant_columns[1] = std::make_shared<ConstantExprResult>(pattern_column);
     context->set_constant_cols(constant_columns);
 
     Function function;
@@ -623,7 +623,7 @@ TEST(FunctionLikeTest, regexp_extract_all_array) { // 
NOLINT(readability-functio
         FunctionUtils fn_utils({}, arg_types, false);
         auto* fn_ctx = fn_utils.get_fn_ctx();
         fn_ctx->set_constant_cols(
-                {nullptr, 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column)});
+                {nullptr, 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column)});
 
         ASSERT_EQ(Status::OK(),
                   func->open(fn_ctx, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL));
@@ -703,7 +703,7 @@ TEST(FunctionLikeTest, regexp_extract_all_array) { // 
NOLINT(readability-functio
         FunctionUtils fn_utils({}, arg_types, false);
         auto* fn_ctx = fn_utils.get_fn_ctx();
         fn_ctx->set_constant_cols(
-                {nullptr, 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column)});
+                {nullptr, 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column)});
 
         ASSERT_EQ(Status::OK(),
                   func->open(fn_ctx, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL));
@@ -743,7 +743,7 @@ TEST(FunctionLikeTest, regexp_extract_all_array) { // 
NOLINT(readability-functio
         FunctionUtils fn_utils({}, arg_types, false);
         auto* fn_ctx = fn_utils.get_fn_ctx();
         fn_ctx->set_constant_cols(
-                {nullptr, 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column)});
+                {nullptr, 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column)});
 
         ASSERT_EQ(Status::OK(),
                   func->open(fn_ctx, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL));
@@ -787,7 +787,7 @@ TEST(FunctionLikeTest, 
regexp_extract_all_boost_zero_width_match) {
         query_options.__set_enable_extended_regex(true);
         fn_ctx->state()->set_query_options(query_options);
         fn_ctx->set_constant_cols(
-                {nullptr, 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column)});
+                {nullptr, 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column)});
 
         ASSERT_EQ(Status::OK(),
                   func->open(fn_ctx, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL));
@@ -959,7 +959,7 @@ TEST(FunctionLikeTest, regexp_invalid_column_pattern_fails) 
{
         Block block;
         ColumnNumbers arguments;
         std::vector<DataTypePtr> arg_types;
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
         for (auto& arg : arg_cols) {
             arguments.push_back(static_cast<unsigned int>(block.columns()));
             arg_types.push_back(arg.type);
@@ -1082,12 +1082,12 @@ TEST(FunctionLikeTest, 
regexp_null_pattern_hides_invalid_payload) {
         Block block;
         ColumnNumbers arguments;
         std::vector<DataTypePtr> arg_types;
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
         for (auto& arg : arg_cols) {
             arguments.push_back(static_cast<unsigned int>(block.columns()));
             arg_types.push_back(arg.type);
             constant_cols.push_back(is_column_const(*arg.column)
-                                            ? 
std::make_shared<ColumnPtrWrapper>(arg.column)
+                                            ? 
std::make_shared<ConstantExprResult>(arg.column)
                                             : nullptr);
             block.insert(std::move(arg));
         }
@@ -1218,12 +1218,12 @@ TEST(FunctionLikeTest, 
regexp_count_non_nullable_arguments) {
         Block block;
         ColumnNumbers arguments;
         std::vector<DataTypePtr> arg_types;
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
         for (auto& arg : arg_cols) {
             arguments.push_back(static_cast<unsigned int>(block.columns()));
             arg_types.push_back(arg.type);
             constant_cols.push_back(is_column_const(*arg.column)
-                                            ? 
std::make_shared<ColumnPtrWrapper>(arg.column)
+                                            ? 
std::make_shared<ConstantExprResult>(arg.column)
                                             : nullptr);
             block.insert(std::move(arg));
         }
@@ -1309,12 +1309,12 @@ TEST(FunctionLikeTest, regexp_non_nullable_arguments) {
         Block block;
         ColumnNumbers arguments;
         std::vector<DataTypePtr> arg_types;
-        std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+        std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
         for (auto& arg : arg_cols) {
             arguments.push_back(static_cast<unsigned int>(block.columns()));
             arg_types.push_back(arg.type);
             constant_cols.push_back(is_column_const(*arg.column)
-                                            ? 
std::make_shared<ColumnPtrWrapper>(arg.column)
+                                            ? 
std::make_shared<ConstantExprResult>(arg.column)
                                             : nullptr);
             block.insert(std::move(arg));
         }
diff --git a/be/test/exprs/function/function_math_test.cpp 
b/be/test/exprs/function/function_math_test.cpp
index 463d9bf4a5c..7c3cc5daefd 100644
--- a/be/test/exprs/function/function_math_test.cpp
+++ b/be/test/exprs/function/function_math_test.cpp
@@ -755,9 +755,9 @@ TEST(MathFunctionTest, uniform_mixed_const_probe_test) {
 
     FunctionUtils fn_utils(return_type, {input_type, input_type, input_type}, 
false);
     auto* fn_ctx = fn_utils.get_fn_ctx();
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols {
-            
std::make_shared<ColumnPtrWrapper>(block.get_by_position(0).column),
-            
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column),
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_cols {
+            
std::make_shared<ConstantExprResult>(block.get_by_position(0).column),
+            
std::make_shared<ConstantExprResult>(block.get_by_position(1).column),
             nullptr,
     };
     fn_ctx->set_constant_cols(constant_cols);
diff --git a/be/test/exprs/function/function_string_test.cpp 
b/be/test/exprs/function/function_string_test.cpp
index 90d1ae63e5a..1b4b1806dbb 100644
--- a/be/test/exprs/function/function_string_test.cpp
+++ b/be/test/exprs/function/function_string_test.cpp
@@ -2979,8 +2979,8 @@ TEST(function_string_test, 
function_translate_mixed_const_map_test) {
         FunctionUtils function_utils(type, {type, type, type}, false);
         auto* context = function_utils.get_fn_ctx();
         context->set_constant_cols(
-                {nullptr, 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(1).column),
-                 
std::make_shared<ColumnPtrWrapper>(block.get_by_position(2).column)});
+                {nullptr, 
std::make_shared<ConstantExprResult>(block.get_by_position(1).column),
+                 
std::make_shared<ConstantExprResult>(block.get_by_position(2).column)});
         ASSERT_TRUE(
                 function->open(context, 
FunctionContext::FunctionStateScope::FRAGMENT_LOCAL).ok());
         ASSERT_TRUE(
diff --git a/be/test/exprs/function/function_test_util.h 
b/be/test/exprs/function/function_test_util.h
index 72473ba3455..3f24b42bfcc 100644
--- a/be/test/exprs/function/function_test_util.h
+++ b/be/test/exprs/function/function_test_util.h
@@ -364,15 +364,15 @@ Status check_function(const std::string& func_name, const 
InputTypeSet& input_ty
     // 1.2 prepare args for function call
     ColumnNumbers arguments;
     std::vector<DataTypePtr> arg_types;
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_col_ptrs;
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_cols;
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_col_ptrs;
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_cols;
     for (size_t i = 0; i < descs.size(); ++i) {
         auto& desc = descs[i];
         arguments.push_back(static_cast<unsigned int>(i));
         arg_types.push_back(desc.data_type);
         if (desc.is_const) {
             constant_col_ptrs.push_back(
-                    
std::make_shared<ColumnPtrWrapper>(block.get_by_position(i).column));
+                    
std::make_shared<ConstantExprResult>(block.get_by_position(i).column));
             constant_cols.push_back(constant_col_ptrs.back());
         } else {
             constant_cols.push_back(nullptr);
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 3b72e4f4ca6..cef3c45ffc9 100644
--- a/be/test/storage/index/ann/extract_query_vector_test.cpp
+++ b/be/test/storage/index/ann/extract_query_vector_test.cpp
@@ -47,8 +47,8 @@ public:
     MockConstVExpr() : VExpr(make_tnode()) {}
 
     Status get_const_col(VExprContext* /*context*/,
-                         std::shared_ptr<ColumnPtrWrapper>* output) override {
-        *output = std::make_shared<ColumnPtrWrapper>(_col);
+                         std::shared_ptr<ConstantExprResult>* output) override 
{
+        *output = std::make_shared<ConstantExprResult>(_col);
         return Status::OK();
     }
 
diff --git 
a/be/test/storage/index/inverted/query/logical/gram/regex_gram_recall_test.cpp 
b/be/test/storage/index/inverted/query/logical/gram/regex_gram_recall_test.cpp
index b4bb3d3ff64..6704fc37536 100644
--- 
a/be/test/storage/index/inverted/query/logical/gram/regex_gram_recall_test.cpp
+++ 
b/be/test/storage/index/inverted/query/logical/gram/regex_gram_recall_test.cpp
@@ -130,7 +130,7 @@ Status scalar_matches(const std::string& pattern, const 
std::vector<std::string>
         patterns->insert_data(pattern.data(), pattern.size());
     }
     ColumnPtr pattern_column;
-    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
+    std::vector<std::shared_ptr<ConstantExprResult>> constant_columns(2);
     if (dynamic_pattern) {
         // Prove vector_non_const will reach the scalar regex function, where 
RE2 is
         // constructed locally. No scalar or vector string fast path accepts 
these patterns.
@@ -144,7 +144,7 @@ Status scalar_matches(const std::string& pattern, const 
std::vector<std::string>
         pattern_column = std::move(patterns);
     } else {
         pattern_column = ColumnConst::create(std::move(patterns), rows.size());
-        constant_columns[1] = 
std::make_shared<ColumnPtrWrapper>(pattern_column);
+        constant_columns[1] = 
std::make_shared<ConstantExprResult>(pattern_column);
     }
     context->set_constant_cols(constant_columns);
 
diff --git a/be/test/testutil/mock/mock_fn_call.cpp 
b/be/test/testutil/mock/mock_fn_call.cpp
index 672ff8230b0..6459b98a793 100644
--- a/be/test/testutil/mock/mock_fn_call.cpp
+++ b/be/test/testutil/mock/mock_fn_call.cpp
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest.h>
 
+#include "core/column/column_const.h"
 #include "core/data_type/data_type_number.h"
 #include "testutil/column_helper.h"
 
@@ -28,10 +29,24 @@ TEST(MockFnCallTest, test) {
     auto fn = MockFnCall::create("test");
     EXPECT_EQ(fn->fn().name.function_name, "test");
     fn->set_const_expr_col(ColumnHelper::create_column<DataTypeInt64>({1}));
-    std::shared_ptr<ColumnPtrWrapper> column_wrapper;
+    std::shared_ptr<ConstantExprResult> column_wrapper;
     EXPECT_TRUE(fn->get_const_col(nullptr, &column_wrapper));
-    EXPECT_EQ(column_wrapper->column_ptr->size(), 1);
-    EXPECT_EQ(column_wrapper->column_ptr->get_int(0), 1);
+    EXPECT_EQ(column_wrapper->column().size(), 1);
+    EXPECT_EQ(column_wrapper->column().get_int(0), 1);
+}
+
+TEST(MockFnCallTest, BroadcastCachedConstant) {
+    auto fn = MockFnCall::create("test");
+    auto data = ColumnHelper::create_column<DataTypeInt64>({7});
+    fn->_constant_col = 
std::make_shared<ConstantExprResult>(ColumnConst::create(data, 3));
+    for (size_t count : {0, 1, 5}) {
+        auto result = fn->get_result_from_const(count);
+        const auto* broadcast = 
check_and_get_column<ColumnConst>(result.get());
+        ASSERT_NE(broadcast, nullptr);
+        EXPECT_EQ(broadcast->size(), count);
+        EXPECT_EQ(broadcast->get_data_column_ptr().get(), data.get());
+        EXPECT_EQ(broadcast->get_data_column().get_int(0), 7);
+    }
 }
 
 } // namespace doris
diff --git a/be/test/testutil/mock/mock_fn_call.h 
b/be/test/testutil/mock/mock_fn_call.h
index 952b640e82d..27e0aa413bb 100644
--- a/be/test/testutil/mock/mock_fn_call.h
+++ b/be/test/testutil/mock/mock_fn_call.h
@@ -35,7 +35,7 @@ public:
     }
 
     Status get_const_col(VExprContext* context,
-                         std::shared_ptr<ColumnPtrWrapper>* column_wrapper) 
override {
+                         std::shared_ptr<ConstantExprResult>* column_wrapper) 
override {
         *column_wrapper = _mock_const_expr_col;
         return Status::OK();
     }
@@ -54,7 +54,7 @@ public:
     }
 
     void set_const_expr_col(ColumnPtr col) {
-        _mock_const_expr_col = std::make_shared<ColumnPtrWrapper>(col);
+        _mock_const_expr_col = std::make_shared<ConstantExprResult>(col);
     }
 
     static std::shared_ptr<MockFnCall> create(const std::string& fn_name) {
@@ -65,7 +65,7 @@ public:
 
 private:
     const std::string _name = "MockFnCall";
-    std::shared_ptr<ColumnPtrWrapper> _mock_const_expr_col;
+    std::shared_ptr<ConstantExprResult> _mock_const_expr_col;
     bool _mock_is_constant = false;
 };
 
diff --git a/be/test/testutil/mock/mock_literal_expr.h 
b/be/test/testutil/mock/mock_literal_expr.h
index 6b27205cae1..566dc51bf94 100644
--- a/be/test/testutil/mock/mock_literal_expr.h
+++ b/be/test/testutil/mock/mock_literal_expr.h
@@ -44,9 +44,9 @@ public:
         _expr_name = data.name;
         if (!check_and_get_column<ColumnConst>(data.column.get())) {
             _mock_const_expr_col =
-                    
std::make_shared<ColumnPtrWrapper>(ColumnConst::create(data.column, 1));
+                    
std::make_shared<ConstantExprResult>(ColumnConst::create(data.column, 1));
         } else {
-            _mock_const_expr_col = 
std::make_shared<ColumnPtrWrapper>(data.column);
+            _mock_const_expr_col = 
std::make_shared<ConstantExprResult>(data.column);
         }
     }
 
@@ -107,14 +107,14 @@ public:
     }
 
     Status get_const_col(VExprContext* context,
-                         std::shared_ptr<ColumnPtrWrapper>* column_wrapper) 
override {
+                         std::shared_ptr<ConstantExprResult>* column_wrapper) 
override {
         *column_wrapper = _mock_const_expr_col;
         return Status::OK();
     }
 
 private:
     const std::string _name = "MockLiteral";
-    std::shared_ptr<ColumnPtrWrapper> _mock_const_expr_col;
+    std::shared_ptr<ConstantExprResult> _mock_const_expr_col;
 };
 
 } // namespace doris


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

Reply via email to