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

yiguolei 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 d0536c04c51 [refine](column) use strong-typed ColumnUInt8 for null_map 
in ColumnNullable (#63491)
d0536c04c51 is described below

commit d0536c04c510100372be455785866d7daa028c5b
Author: Mryange <[email protected]>
AuthorDate: Tue May 26 15:52:54 2026 +0800

    [refine](column) use strong-typed ColumnUInt8 for null_map in 
ColumnNullable (#63491)
    
    ### What problem does this PR solve?
    
    
    The null_map column in `ColumnNullable` was stored as a generic
    `IColumn::WrappedPtr`, requiring `assert_cast<ColumnUInt8>` at every
    call site that accessed it. This had two problems:
    1. **Redundant runtime casts**: Every access to `get_null_map_column()`
    or `get_null_map_column_ptr()` performed an `assert_cast`, adding
    unnecessary overhead on hot paths (filter evaluation, null propagation,
    etc.).
    2. **Type safety**: The generic `WrappedPtr` type provided no
    compile-time guarantee that the null map is always a `ColumnUInt8`,
    making it possible to accidentally store a wrong column type.
    
    Root cause: The `_null_map` member was typed as a generic
    `IColumn::WrappedPtr` instead of the concrete `ColumnUInt8::WrappedPtr`.
    
    Fix: Change `_null_map` to `ColumnUInt8::WrappedPtr` so the type is
    fixed at the member level. The constructor now validates and converts to
    `ColumnUInt8::MutablePtr` on entry via a helper
    `assert_mutable_null_map()`. All call sites that previously did
    `assert_cast<ColumnUInt8>` now get the correct type directly.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/src/core/column/column_array.h                  |  4 +-
 be/src/core/column/column_const.h                  |  2 +-
 be/src/core/column/column_map.h                    |  6 +-
 be/src/core/column/column_nullable.cpp             | 64 ++++++++++++++--------
 be/src/core/column/column_nullable.h               | 33 +++++++----
 be/src/core/column/column_struct.h                 |  2 +-
 be/src/core/column/column_variant.cpp              |  2 +-
 be/src/core/column/column_variant.h                |  6 +-
 be/src/core/cow.h                                  |  5 +-
 be/src/exec/operator/table_function_operator.cpp   |  9 +--
 .../exprs/function/function_always_not_nullable.h  |  3 +-
 be/src/exprs/function/function_hll.cpp             |  3 +-
 be/src/exprs/function/function_quantile_state.cpp  |  3 +-
 be/src/exprs/function/if.cpp                       | 24 ++++----
 .../exprs/lambda_function/varray_map_function.cpp  |  5 +-
 .../exprs/table_function/python_udtf_function.cpp  |  7 +--
 be/src/exprs/table_function/udf_table_function.cpp |  7 +--
 be/src/exprs/table_function/vexplode.cpp           |  7 +--
 be/src/exprs/table_function/vexplode_bitmap.cpp    |  8 +--
 .../exprs/table_function/vexplode_json_object.cpp  |  7 +--
 be/src/exprs/table_function/vexplode_map.cpp       |  7 +--
 be/src/exprs/table_function/vexplode_numbers.cpp   |  4 +-
 be/src/exprs/table_function/vexplode_numbers.h     | 13 ++---
 be/src/exprs/table_function/vexplode_v2.cpp        | 12 ++--
 be/src/exprs/table_function/vjson_each.cpp         |  6 +-
 be/src/exprs/vcase_expr.h                          |  5 +-
 be/src/exprs/vcompound_pred.h                      |  5 +-
 be/src/exprs/vcondition_expr.cpp                   | 22 +++++---
 be/src/storage/segment/column_reader.cpp           | 52 +++++++++---------
 .../exec/operator/table_function_operator_test.cpp |  3 +-
 be/test/exprs/function/function_test_util.cpp      |  6 +-
 31 files changed, 178 insertions(+), 164 deletions(-)

diff --git a/be/src/core/column/column_array.h 
b/be/src/core/column/column_array.h
index 06eb3d2123e..27dfbdc893f 100644
--- a/be/src/core/column/column_array.h
+++ b/be/src/core/column/column_array.h
@@ -266,8 +266,8 @@ public:
 private:
     // [2,1,5,9,1]\n[1,2,4] --> data column [2,1,5,9,1,1,2,4], offset[-1] = 0, 
offset[0] = 5, offset[1] = 8
     // [[2,1,5],[9,1]]\n[[1,2]] --> data column [3 column array], offset[-1] = 
0, offset[0] = 2, offset[1] = 3
-    WrappedPtr data;
-    WrappedPtr offsets;
+    IColumn::WrappedPtr data;
+    IColumn::WrappedPtr offsets;
 };
 
 } // namespace doris
diff --git a/be/src/core/column/column_const.h 
b/be/src/core/column/column_const.h
index 7f648ece468..cc8b94ff234 100644
--- a/be/src/core/column/column_const.h
+++ b/be/src/core/column/column_const.h
@@ -103,7 +103,7 @@ class ColumnConst final : public COWHelper<IColumn, 
ColumnConst> {
 private:
     friend class COWHelper<IColumn, ColumnConst>;
     using Self = ColumnConst;
-    WrappedPtr data;
+    IColumn::WrappedPtr data;
     size_t s;
 
     ColumnConst(const ColumnPtr& data, size_t s_, bool create_with_empty = 
false,
diff --git a/be/src/core/column/column_map.h b/be/src/core/column/column_map.h
index fa67caa654e..3566ab8759d 100644
--- a/be/src/core/column/column_map.h
+++ b/be/src/core/column/column_map.h
@@ -240,9 +240,9 @@ public:
 private:
     friend class COWHelper<IColumn, ColumnMap>;
 
-    WrappedPtr keys_column;    // nullable
-    WrappedPtr values_column;  // nullable
-    WrappedPtr offsets_column; // offset
+    IColumn::WrappedPtr keys_column;    // nullable
+    IColumn::WrappedPtr values_column;  // nullable
+    IColumn::WrappedPtr offsets_column; // offset
 
     ColumnMap(MutableColumnPtr&& keys, MutableColumnPtr&& values, 
MutableColumnPtr&& offsets);
     ColumnMap(SharedTag, ColumnPtr keys, ColumnPtr values, ColumnPtr offsets);
diff --git a/be/src/core/column/column_nullable.cpp 
b/be/src/core/column/column_nullable.cpp
index 03674fb0917..119c165c136 100644
--- a/be/src/core/column/column_nullable.cpp
+++ b/be/src/core/column/column_nullable.cpp
@@ -41,6 +41,10 @@ const ColumnUInt8& check_nullable_null_map_column(const 
IColumn& null_map) {
     return *concrete;
 }
 
+ColumnUInt8::Ptr check_nullable_null_map_column_ptr(const ColumnPtr& null_map) 
{
+    return 
ColumnUInt8::cast_to_column_ptr(&check_nullable_null_map_column(*null_map));
+}
+
 void check_nullable_sizes(const IColumn& nested_column, const IColumn& 
null_map) {
     const auto& null_map_concrete = check_nullable_null_map_column(null_map);
     if (nested_column.size() != null_map_concrete.size()) {
@@ -53,24 +57,36 @@ void check_nullable_sizes(const IColumn& nested_column, 
const IColumn& null_map)
 
 } // namespace
 
+namespace {
+ColumnUInt8::MutablePtr assert_mutable_null_map(MutableColumnPtr&& null_map) {
+    if (is_column_const(*null_map)) [[unlikely]] {
+        throw doris::Exception(ErrorCode::INTERNAL_ERROR,
+                               "ColumnNullable cannot have constant null map");
+    }
+    auto mutable_null_map = ColumnUInt8::cast_to_column_mutptr(
+            assert_cast<ColumnUInt8*, 
TypeCheckOnRelease::DISABLE>(null_map.get()));
+    null_map = nullptr;
+    return mutable_null_map;
+}
+} // namespace
+
 ColumnNullable::ColumnNullable(MutableColumnPtr&& nested_column_, 
MutableColumnPtr&& null_map_)
+        : ColumnNullable(std::move(nested_column_), 
assert_mutable_null_map(std::move(null_map_))) {
+}
+
+ColumnNullable::ColumnNullable(MutableColumnPtr&& nested_column_,
+                               ColumnUInt8::MutablePtr&& null_map_)
         : _nested_column(std::move(nested_column_)), 
_null_map(std::move(null_map_)) {
     check_const_only_in_top_level();
     // after convert const column to full column, it may be a nullable column
     if (_nested_column->is_nullable()) {
-        assert_cast<ColumnNullable&>(*_nested_column)
-                .apply_null_map(static_cast<const 
ColumnUInt8&>(get_null_map_column()));
-        _null_map = 
assert_cast<ColumnNullable&>(*_nested_column).get_null_map_column_ptr();
-        _nested_column = 
assert_cast<ColumnNullable&>(*_nested_column).get_nested_column_ptr();
-    }
-
-    if (is_column_const(get_null_map_column())) [[unlikely]] {
-        throw doris::Exception(ErrorCode::INTERNAL_ERROR,
-                               "ColumnNullable cannot have constant null map");
-        __builtin_unreachable();
+        auto& nested_nullable = assert_cast<ColumnNullable&>(*_nested_column);
+        nested_nullable.apply_null_map(get_null_map_column());
+        _null_map = nested_nullable._null_map;
+        _nested_column = nested_nullable.get_nested_column_ptr();
     }
     check_nullable_sizes(*static_cast<const IColumn::Ptr&>(_nested_column),
-                         *static_cast<const IColumn::Ptr&>(_null_map));
+                         *static_cast<const ColumnUInt8::Ptr&>(_null_map));
 }
 
 ColumnNullable::ColumnNullable(SharedTag, ColumnPtr nested_column_, ColumnPtr 
null_map_) {
@@ -78,8 +94,9 @@ ColumnNullable::ColumnNullable(SharedTag, ColumnPtr 
nested_column_, ColumnPtr nu
 
     if (const auto* nullable_nested = 
check_and_get_column<ColumnNullable>(nested_column_.get())) {
         auto merged_null_map = null_map_->clone_empty();
-        merged_null_map->insert_range_from(*null_map_, 0, null_map_->size());
-        auto& merged_null_map_data = 
assert_cast<ColumnUInt8&>(*merged_null_map).get_data();
+        auto merged_null_map_ptr = 
assert_mutable_null_map(std::move(merged_null_map));
+        merged_null_map_ptr->insert_range_from(*null_map_, 0, 
null_map_->size());
+        auto& merged_null_map_data = merged_null_map_ptr->get_data();
         const auto& nested_null_map_data = 
nullable_nested->get_null_map_data();
         DCHECK_EQ(merged_null_map_data.size(), nested_null_map_data.size());
         for (size_t i = 0; i != merged_null_map_data.size(); ++i) {
@@ -87,21 +104,21 @@ ColumnNullable::ColumnNullable(SharedTag, ColumnPtr 
nested_column_, ColumnPtr nu
         }
 
         static_cast<IColumn::Ptr&>(_nested_column) = 
nullable_nested->get_nested_column_ptr();
-        static_cast<IColumn::Ptr&>(_null_map) = std::move(merged_null_map);
+        static_cast<ColumnUInt8::Ptr&>(_null_map) = 
std::move(merged_null_map_ptr);
     } else {
         static_cast<IColumn::Ptr&>(_nested_column) = std::move(nested_column_);
-        static_cast<IColumn::Ptr&>(_null_map) = std::move(null_map_);
+        static_cast<ColumnUInt8::Ptr&>(_null_map) = 
check_nullable_null_map_column_ptr(null_map_);
     }
 
     check_const_only_in_top_level();
     check_nullable_sizes(*static_cast<const IColumn::Ptr&>(_nested_column),
-                         *static_cast<const IColumn::Ptr&>(_null_map));
+                         *static_cast<const ColumnUInt8::Ptr&>(_null_map));
 }
 
 void ColumnNullable::replace_columns(ColumnPtr nested_column, ColumnPtr 
null_map) {
     check_nullable_sizes(*nested_column, *null_map);
     static_cast<IColumn::Ptr&>(_nested_column) = std::move(nested_column);
-    static_cast<IColumn::Ptr&>(_null_map) = std::move(null_map);
+    static_cast<ColumnUInt8::Ptr&>(_null_map) = 
check_nullable_null_map_column_ptr(null_map);
     check_const_only_in_top_level();
 }
 
@@ -178,7 +195,8 @@ void ColumnNullable::update_crc32c_batch(uint32_t* 
__restrict hashes,
         // before this normalized nested column is written back.
         auto nested_mut = std::move(*static_cast<const 
IColumn::Ptr&>(_nested_column)).mutate();
         nested_mut->replace_column_null_data(real_null_data);
-        static_cast<IColumn::Ptr&>(const_cast<WrappedPtr&>(_nested_column)) = 
std::move(nested_mut);
+        
static_cast<IColumn::Ptr&>(const_cast<IColumn::WrappedPtr&>(_nested_column)) =
+                std::move(nested_mut);
         _nested_column->update_crc32c_batch(hashes, nullptr);
     } else {
         auto s = size();
@@ -412,10 +430,10 @@ void 
ColumnNullable::append_data_by_selector(IColumn::MutablePtr& res,
                                              const IColumn::Selector& 
selector, size_t begin,
                                              size_t end) const {
     auto& res_column = assert_cast<ColumnNullable&>(*res);
-    auto res_nested_column = res_column.get_nested_column_ptr();
+    MutableColumnPtr res_nested_column = res_column.get_nested_column_ptr();
     get_nested_column().append_data_by_selector(res_nested_column, selector, 
begin, end);
-    auto res_null_map = res_column.get_null_map_column_ptr();
-    get_null_map_column().append_data_by_selector(res_null_map, selector, 
begin, end);
+    IColumn::MutablePtr res_null_map_column = 
res_column.get_null_map_column_ptr();
+    get_null_map_column().append_data_by_selector(res_null_map_column, 
selector, begin, end);
 }
 
 void ColumnNullable::pop_back(size_t n) {
@@ -440,8 +458,8 @@ Status ColumnNullable::filter_by_selector(const uint16_t* 
sel, size_t sel_size,
     auto* nullable_col_ptr = assert_cast<ColumnNullable*>(col_ptr);
     // Access the nested column via const path to avoid assert_mutable_ref 
(which requires
     // exclusive ownership). The output col_ptr was just created, so its 
nested column is exclusive.
-    IColumn* nest_col_raw = const_cast<IColumn*>(
-            static_cast<const 
WrappedPtr&>(nullable_col_ptr->_nested_column).get());
+    auto nest_col_raw = const_cast<IColumn*>(
+            static_cast<const 
IColumn::WrappedPtr&>(nullable_col_ptr->_nested_column).get());
 
     /// `get_null_map_data` will set `_need_update_has_null` to true
     auto& res_nullmap = nullable_col_ptr->get_null_map_data();
diff --git a/be/src/core/column/column_nullable.h 
b/be/src/core/column/column_nullable.h
index 769c0a0a05f..371edf8fc63 100644
--- a/be/src/core/column/column_nullable.h
+++ b/be/src/core/column/column_nullable.h
@@ -26,11 +26,13 @@
 #include "core/column/column_vector.h"
 #include "core/cow.h"
 #include "core/data_type/define_primitive_type.h"
+#include "core/data_type/primitive_type.h"
 #include "core/field.h"
 #include "core/string_ref.h"
 #include "core/typeid_cast.h"
 #include "core/types.h"
 #include "storage/olap_common.h"
+#include "util/defer_op.h"
 
 class SipHash;
 
@@ -57,6 +59,7 @@ private:
     ColumnNullable(MutableColumnPtr&& nested_column_, MutableColumnPtr&& 
null_map_);
     struct SharedTag {};
     ColumnNullable(SharedTag, ColumnPtr nested_column_, ColumnPtr null_map_);
+    ColumnNullable(MutableColumnPtr&& nested_column_, 
ColumnUInt8::MutablePtr&& null_map_);
     ColumnNullable(const ColumnNullable&) = default;
 
 public:
@@ -68,6 +71,11 @@ public:
         return Base::create(SharedTag {}, nested_column_, null_map_);
     }
 
+    static MutablePtr create(MutableColumnPtr&& nested_column_,
+                             ColumnUInt8::MutablePtr&& null_map_) {
+        return Base::create(std::move(nested_column_), std::move(null_map_));
+    }
+
     template <typename... Args>
     static MutablePtr create(Args&&... args)
         requires IsMutableColumns<Args...>::value
@@ -246,7 +254,12 @@ public:
 
     void for_each_subcolumn(ColumnCallback callback) override {
         callback(_nested_column);
-        callback(_null_map);
+
+        IColumn::WrappedPtr 
null_map(std::move(static_cast<ColumnUInt8::Ptr&>(_null_map)));
+        Defer defer([&] {
+            _null_map = cast_to_column<ColumnUInt8>(static_cast<const 
IColumn::Ptr&>(null_map));
+        });
+        callback(null_map);
     }
 
     bool structure_equals(const IColumn& rhs) const override {
@@ -380,16 +393,16 @@ public:
     }
 
     // return the column that represents the byte map. if want use null_map, 
just call this.
-    const ColumnPtr& get_null_map_column_ptr() const { return _null_map; }
-    const ColumnUInt8& get_null_map_column() const {
-        return assert_cast<const ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*_null_map);
-    }
+    const ColumnUInt8::Ptr& get_null_map_column_ptr() const { return 
_null_map; }
+    const ColumnUInt8& get_null_map_column() const { return *_null_map; }
     const NullMap& get_null_map_data() const { return 
get_null_map_column().get_data(); }
 
-    MutableColumnPtr get_null_map_column_ptr() { return 
_null_map->assert_mutable(); }
-    ColumnUInt8& get_null_map_column() {
-        return assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*_null_map);
+    ColumnUInt8::MutablePtr get_null_map_column_ptr() {
+        auto null_map = _null_map->assert_mutable();
+        return ColumnUInt8::cast_to_column_mutptr(
+                assert_cast<ColumnUInt8*, 
TypeCheckOnRelease::DISABLE>(null_map.get()));
     }
+    ColumnUInt8& get_null_map_column() { return *_null_map; }
     NullMap& get_null_map_data() { return get_null_map_column().get_data(); }
 
     // push not null value wouldn't change the nullity. no need to update 
_has_null
@@ -403,8 +416,8 @@ private:
     template <bool negative>
     void apply_null_map_impl(const ColumnUInt8& map);
 
-    WrappedPtr _nested_column;
-    WrappedPtr _null_map;
+    IColumn::WrappedPtr _nested_column;
+    ColumnUInt8::WrappedPtr _null_map;
 };
 
 ColumnPtr make_nullable(const ColumnPtr& column, bool is_nullable = false);
diff --git a/be/src/core/column/column_struct.h 
b/be/src/core/column/column_struct.h
index ca77868d056..bee55370b85 100644
--- a/be/src/core/column/column_struct.h
+++ b/be/src/core/column/column_struct.h
@@ -51,7 +51,7 @@ class ColumnStruct final : public COWHelper<IColumn, 
ColumnStruct> {
 private:
     friend class COWHelper<IColumn, ColumnStruct>;
 
-    using TupleColumns = std::vector<WrappedPtr>;
+    using TupleColumns = std::vector<IColumn::WrappedPtr>;
     TupleColumns columns;
 
     template <bool positive>
diff --git a/be/src/core/column/column_variant.cpp 
b/be/src/core/column/column_variant.cpp
index 37921e26989..88cd501acb3 100644
--- a/be/src/core/column/column_variant.cpp
+++ b/be/src/core/column/column_variant.cpp
@@ -2118,7 +2118,7 @@ void ColumnVariant::clear_sparse_column() {
 }
 
 void ColumnVariant::ensure_binary_columns_rows() {
-    auto resize_if_empty = [this](WrappedPtr& column) {
+    auto resize_if_empty = [this](IColumn::WrappedPtr& column) {
         const auto& const_column = static_cast<const IColumn::Ptr&>(column);
         if (const_column->size() == num_rows) {
             return;
diff --git a/be/src/core/column/column_variant.h 
b/be/src/core/column/column_variant.h
index 1ae92afd54c..1d5c4eed137 100644
--- a/be/src/core/column/column_variant.h
+++ b/be/src/core/column/column_variant.h
@@ -247,7 +247,7 @@ public:
         /// Parts of column. Parts should be in increasing order in terms of 
subtypes/supertypes.
         /// That means that the least common type for i-th prefix is the type 
of i-th part
         /// and it's the supertype for all type of column from 0 to i-1.
-        std::vector<WrappedPtr> data;
+        std::vector<IColumn::WrappedPtr> data;
         std::vector<DataTypePtr> data_types;
         std::vector<DataTypeSerDeSPtr> data_serdes;
         /// Until we insert any non-default field we don't know further
@@ -275,10 +275,10 @@ private:
 
     // It's filled when the number of subcolumns reaches the limit.
     // It has type Map(String, String) and stores a map (path, binary 
serialized subcolumn value) for each row.
-    WrappedPtr serialized_sparse_column = ColumnMap::create(
+    IColumn::WrappedPtr serialized_sparse_column = ColumnMap::create(
             ColumnString::create(), ColumnString::create(), 
ColumnArray::ColumnOffsets::create());
 
-    WrappedPtr serialized_doc_value_column = ColumnMap::create(
+    IColumn::WrappedPtr serialized_doc_value_column = ColumnMap::create(
             ColumnString::create(), ColumnString::create(), 
ColumnArray::ColumnOffsets::create());
 
     // if `_max_subcolumns_count == 0`, all subcolumns are materialized.
diff --git a/be/src/core/cow.h b/be/src/core/cow.h
index afa0cab3d5b..7fb4cb4f754 100644
--- a/be/src/core/cow.h
+++ b/be/src/core/cow.h
@@ -351,13 +351,13 @@ protected:
                 : value(std::forward<std::initializer_list<U>>(arg)) {}
 
         const T* get() const { return value.get(); }
-        T* get() { return &value->assert_mutable_ref(); }
+        T* get() { return &static_cast<T&>(value->assert_mutable_ref()); }
 
         const T* operator->() const { return get(); }
         T* operator->() { return get(); }
 
         const T& operator*() const { return *value; }
-        T& operator*() { return value->assert_mutable_ref(); }
+        T& operator*() { return static_cast<T&>(value->assert_mutable_ref()); }
 
         operator const immutable_ptr<T>&() const { return value; }
         operator immutable_ptr<T>&() { return value; }
@@ -419,6 +419,7 @@ public:
     static_assert(std::is_base_of_v<doris::IColumn, Base>, "COWHelper only use 
in IColumn");
     using Ptr = typename Base::template immutable_ptr<Derived>;
     using MutablePtr = typename Base::template mutable_ptr<Derived>;
+    using WrappedPtr = typename Base::template chameleon_ptr<Derived>;
 
 #include "common/compile_check_avoid_begin.h"
 
diff --git a/be/src/exec/operator/table_function_operator.cpp 
b/be/src/exec/operator/table_function_operator.cpp
index 397a9754620..bb4f8945359 100644
--- a/be/src/exec/operator/table_function_operator.cpp
+++ b/be/src/exec/operator/table_function_operator.cpp
@@ -282,8 +282,7 @@ Status 
TableFunctionLocalState::_get_expanded_block_block_fast_path(
         if (out_col->is_nullable()) {
             auto* nullable = assert_cast<ColumnNullable*>(out_col.get());
             struct_col_ptr = 
assert_cast<ColumnStruct*>(nullable->get_nested_column_ptr().get());
-            outer_struct_nullmap_ptr =
-                    
assert_cast<ColumnUInt8*>(nullable->get_null_map_column_ptr().get());
+            outer_struct_nullmap_ptr = 
nullable->get_null_map_column_ptr().get();
         } else {
             struct_col_ptr = assert_cast<ColumnStruct*>(out_col.get());
         }
@@ -343,8 +342,7 @@ Status 
TableFunctionLocalState::_get_expanded_block_block_fast_path(
             val_nullable->get_nested_column_ptr()->insert_range_from(
                     *_block_fast_path_ctx.nested_col, 
segment_ctx.seg_nested_start,
                     segment_ctx.seg_nested_count);
-            auto* val_nullmap =
-                    
assert_cast<ColumnUInt8*>(val_nullable->get_null_map_column_ptr().get());
+            auto* val_nullmap = val_nullable->get_null_map_column_ptr().get();
             auto& val_nullmap_data = val_nullmap->get_data();
             const size_t old_size = val_nullmap_data.size();
             val_nullmap_data.resize(old_size + segment_ctx.seg_nested_count);
@@ -365,8 +363,7 @@ Status 
TableFunctionLocalState::_get_expanded_block_block_fast_path(
             out_nullable->get_nested_column_ptr()->insert_range_from(
                     *_block_fast_path_ctx.nested_col, 
segment_ctx.seg_nested_start,
                     segment_ctx.seg_nested_count);
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(out_nullable->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
out_nullable->get_null_map_column_ptr().get();
             auto& nullmap_data = nullmap_column->get_data();
             const size_t old_size = nullmap_data.size();
             nullmap_data.resize(old_size + segment_ctx.seg_nested_count);
diff --git a/be/src/exprs/function/function_always_not_nullable.h 
b/be/src/exprs/function/function_always_not_nullable.h
index dc9b1eb3073..0f9790fb135 100644
--- a/be/src/exprs/function/function_always_not_nullable.h
+++ b/be/src/exprs/function/function_always_not_nullable.h
@@ -51,8 +51,7 @@ public:
             const ColumnNullable* col_nullable = assert_cast<const 
ColumnNullable*>(column.get());
             const ColumnType* col =
                     
check_and_get_column<ColumnType>(col_nullable->get_nested_column_ptr().get());
-            const ColumnUInt8* col_nullmap =
-                    assert_cast<const 
ColumnUInt8*>(col_nullable->get_null_map_column_ptr().get());
+            const ColumnUInt8* col_nullmap = 
col_nullable->get_null_map_column_ptr().get();
 
             if (col != nullptr) {
                 if constexpr (WithReturn) {
diff --git a/be/src/exprs/function/function_hll.cpp 
b/be/src/exprs/function/function_hll.cpp
index ec47b07744b..a3e10bdc433 100644
--- a/be/src/exprs/function/function_hll.cpp
+++ b/be/src/exprs/function/function_hll.cpp
@@ -99,8 +99,7 @@ public:
                     check_and_get_column<ColumnNullable>(column.get())) {
             const ColumnHLL* col =
                     
check_and_get_column<ColumnHLL>(col_nullable->get_nested_column_ptr().get());
-            const ColumnUInt8* col_nullmap =
-                    assert_cast<const 
ColumnUInt8*>(col_nullable->get_null_map_column_ptr().get());
+            const ColumnUInt8* col_nullmap = 
col_nullable->get_null_map_column_ptr().get();
 
             if (col != nullptr) {
                 Function::vector_nullable(col->get_data(), 
col_nullmap->get_data(), column_result);
diff --git a/be/src/exprs/function/function_quantile_state.cpp 
b/be/src/exprs/function/function_quantile_state.cpp
index eed78b97da9..4019e84e65e 100644
--- a/be/src/exprs/function/function_quantile_state.cpp
+++ b/be/src/exprs/function/function_quantile_state.cpp
@@ -91,8 +91,7 @@ public:
         const NullMap* nullmap = nullptr;
         if constexpr (is_nullable) {
             col_nullable = assert_cast<const ColumnNullable*>(column.get());
-            col_nullmap =
-                    assert_cast<const 
ColumnUInt8*>(col_nullable->get_null_map_column_ptr().get());
+            col_nullmap = col_nullable->get_null_map_column_ptr().get();
             col = assert_cast<const 
ColumnFloat64*>(col_nullable->get_nested_column_ptr().get());
 
             nullmap = &col_nullmap->get_data();
diff --git a/be/src/exprs/function/if.cpp b/be/src/exprs/function/if.cpp
index d00b0f3edbb..d571eae5a91 100644
--- a/be/src/exprs/function/if.cpp
+++ b/be/src/exprs/function/if.cpp
@@ -361,19 +361,23 @@ public:
             // b. create a const_nullmap_column: it's a not nullable column or 
a const nullable column, contain a const value
             Block temporary_block;
             temporary_block.insert(arg_cond);
-            auto then_nested_null_map =
-                    (then_type_is_nullable && !then_column_is_const_nullable)
-                            ? then_is_nullable->get_null_map_column_ptr()
-                            : 
DataTypeUInt8().create_column_const_with_default_value(
-                                      input_rows_count);
+            ColumnPtr then_nested_null_map;
+            if (then_type_is_nullable && !then_column_is_const_nullable) {
+                then_nested_null_map = 
then_is_nullable->get_null_map_column_ptr();
+            } else {
+                then_nested_null_map =
+                        
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+            }
             temporary_block.insert({then_nested_null_map, 
std::make_shared<DataTypeUInt8>(),
                                     "then_column_null_map"});
 
-            auto else_nested_null_map =
-                    (else_type_is_nullable && !else_column_is_const_nullable)
-                            ? else_is_nullable->get_null_map_column_ptr()
-                            : 
DataTypeUInt8().create_column_const_with_default_value(
-                                      input_rows_count);
+            ColumnPtr else_nested_null_map;
+            if (else_type_is_nullable && !else_column_is_const_nullable) {
+                else_nested_null_map = 
else_is_nullable->get_null_map_column_ptr();
+            } else {
+                else_nested_null_map =
+                        
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+            }
             temporary_block.insert({else_nested_null_map, 
std::make_shared<DataTypeUInt8>(),
                                     "else_column_null_map"});
             temporary_block.insert(
diff --git a/be/src/exprs/lambda_function/varray_map_function.cpp 
b/be/src/exprs/lambda_function/varray_map_function.cpp
index 79fbb5f5dfd..b669830c5bb 100644
--- a/be/src/exprs/lambda_function/varray_map_function.cpp
+++ b/be/src/exprs/lambda_function/varray_map_function.cpp
@@ -146,9 +146,8 @@ public:
                                      ->get_nested_type();
 
                 // need to union nullmap from all columns
-                VectorizedUtils::update_null_map(
-                        outside_null_map->get_data(),
-                        assert_cast<const 
ColumnUInt8&>(*column_array_nullmap).get_data());
+                VectorizedUtils::update_null_map(outside_null_map->get_data(),
+                                                 
column_array_nullmap->get_data());
             }
 
             // here is the array column
diff --git a/be/src/exprs/table_function/python_udtf_function.cpp 
b/be/src/exprs/table_function/python_udtf_function.cpp
index 4bcd8ae4636..eaaf4d5227a 100644
--- a/be/src/exprs/table_function/python_udtf_function.cpp
+++ b/be/src/exprs/table_function/python_udtf_function.cpp
@@ -195,9 +195,9 @@ void 
PythonUDTFFunction::get_same_many_values(MutableColumnPtr& column, int leng
         if (_is_nullable) {
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto nested_column = nullable_column->get_nested_column_ptr();
-            auto nullmap_column = nullable_column->get_null_map_column_ptr();
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nested_column->insert_many_from(*_array_column_detail.nested_col, 
pos, length);
-            
assert_cast<ColumnUInt8*>(nullmap_column.get())->insert_many_defaults(length);
+            nullmap_column->insert_many_defaults(length);
         } else {
             column->insert_many_from(*_array_column_detail.nested_col, pos, 
length);
         }
@@ -215,8 +215,7 @@ int PythonUDTFFunction::get_value(MutableColumnPtr& column, 
int max_step) {
         if (_is_nullable) {
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto nested_column = nullable_column->get_nested_column_ptr();
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
 
             nested_column->insert_range_from(*_array_column_detail.nested_col, 
pos, max_step);
             size_t old_size = nullmap_column->size();
diff --git a/be/src/exprs/table_function/udf_table_function.cpp 
b/be/src/exprs/table_function/udf_table_function.cpp
index 414766ef915..67f727ec297 100644
--- a/be/src/exprs/table_function/udf_table_function.cpp
+++ b/be/src/exprs/table_function/udf_table_function.cpp
@@ -161,9 +161,9 @@ void 
UDFTableFunction::get_same_many_values(MutableColumnPtr& column, int length
         if (_is_nullable) {
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto nested_column = nullable_column->get_nested_column_ptr();
-            auto nullmap_column = nullable_column->get_null_map_column_ptr();
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nested_column->insert_many_from(*_array_column_detail.nested_col, 
pos, length);
-            
assert_cast<ColumnUInt8*>(nullmap_column.get())->insert_many_defaults(length);
+            nullmap_column->insert_many_defaults(length);
         } else {
             column->insert_many_from(*_array_column_detail.nested_col, pos, 
length);
         }
@@ -180,8 +180,7 @@ int UDFTableFunction::get_value(MutableColumnPtr& column, 
int max_step) {
         if (_is_nullable) {
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto nested_column = nullable_column->get_nested_column_ptr();
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nested_column->insert_range_from(*_array_column_detail.nested_col, 
pos, max_step);
             size_t old_size = nullmap_column->size();
             nullmap_column->resize(old_size + max_step);
diff --git a/be/src/exprs/table_function/vexplode.cpp 
b/be/src/exprs/table_function/vexplode.cpp
index 0b8556229a4..efbfdb335fc 100644
--- a/be/src/exprs/table_function/vexplode.cpp
+++ b/be/src/exprs/table_function/vexplode.cpp
@@ -138,8 +138,8 @@ void 
VExplodeTableFunction::get_same_many_values(MutableColumnPtr& column, int l
             assert_cast<ColumnNullable*>(column.get())
                     ->get_nested_column_ptr()
                     ->insert_many_from(*_detail.nested_col, pos, length);
-            assert_cast<ColumnUInt8*>(
-                    
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+            assert_cast<ColumnNullable*>(column.get())
+                    ->get_null_map_column_ptr()
                     ->insert_many_defaults(length);
         } else {
             column->insert_many_from(*_detail.nested_col, pos, length);
@@ -157,8 +157,7 @@ int VExplodeTableFunction::get_value(MutableColumnPtr& 
column, int max_step) {
         if (_is_nullable) {
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto nested_column = nullable_column->get_nested_column_ptr();
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nested_column->insert_range_from(*_detail.nested_col, pos, 
max_step);
             size_t old_size = nullmap_column->size();
             nullmap_column->resize(old_size + max_step);
diff --git a/be/src/exprs/table_function/vexplode_bitmap.cpp 
b/be/src/exprs/table_function/vexplode_bitmap.cpp
index 80ba79ce9d2..b21f099dde7 100644
--- a/be/src/exprs/table_function/vexplode_bitmap.cpp
+++ b/be/src/exprs/table_function/vexplode_bitmap.cpp
@@ -75,8 +75,8 @@ void 
VExplodeBitmapTableFunction::get_same_many_values(MutableColumnPtr& column,
             assert_cast<ColumnInt64*>(
                     
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get())
                     ->insert_many_vals(**_cur_iter, length);
-            assert_cast<ColumnUInt8*>(
-                    
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+            assert_cast<ColumnNullable*>(column.get())
+                    ->get_null_map_column_ptr()
                     ->insert_many_defaults(length);
         } else {
             
assert_cast<ColumnInt64*>(column.get())->insert_many_vals(**_cur_iter, length);
@@ -114,8 +114,8 @@ int 
VExplodeBitmapTableFunction::get_value(MutableColumnPtr& column, int max_ste
         if (_is_nullable) {
             target = assert_cast<ColumnInt64*>(
                     
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get());
-            assert_cast<ColumnUInt8*>(
-                    
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+            assert_cast<ColumnNullable*>(column.get())
+                    ->get_null_map_column_ptr()
                     ->insert_many_defaults(max_step);
         } else {
             target = assert_cast<ColumnInt64*>(column.get());
diff --git a/be/src/exprs/table_function/vexplode_json_object.cpp 
b/be/src/exprs/table_function/vexplode_json_object.cpp
index 0e33c503e38..4611034f082 100644
--- a/be/src/exprs/table_function/vexplode_json_object.cpp
+++ b/be/src/exprs/table_function/vexplode_json_object.cpp
@@ -107,8 +107,8 @@ void 
VExplodeJsonObjectTableFunction::get_same_many_values(MutableColumnPtr& col
         // make map kv value into struct
         ret = assert_cast<ColumnStruct*>(
                 
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get());
-        assert_cast<ColumnUInt8*>(
-                
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+        assert_cast<ColumnNullable*>(column.get())
+                ->get_null_map_column_ptr()
                 ->insert_many_defaults(length);
     } else if (is_column<ColumnStruct>(column.get())) {
         ret = assert_cast<ColumnStruct*>(column.get());
@@ -137,8 +137,7 @@ int 
VExplodeJsonObjectTableFunction::get_value(MutableColumnPtr& column, int max
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             struct_column =
                     
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             // here nullmap_column insert max_step many defaults as if 
MAP[row_idx] is NULL
             // will be not update value, _cur_size = 0, means current_empty;
             // so here could insert directly
diff --git a/be/src/exprs/table_function/vexplode_map.cpp 
b/be/src/exprs/table_function/vexplode_map.cpp
index 0245eee3156..cff60eaa3d5 100644
--- a/be/src/exprs/table_function/vexplode_map.cpp
+++ b/be/src/exprs/table_function/vexplode_map.cpp
@@ -101,8 +101,8 @@ void 
VExplodeMapTableFunction::get_same_many_values(MutableColumnPtr& column, in
         // make map kv value into struct
         ret = assert_cast<ColumnStruct*>(
                 
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get());
-        assert_cast<ColumnUInt8*>(
-                
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+        assert_cast<ColumnNullable*>(column.get())
+                ->get_null_map_column_ptr()
                 ->insert_many_defaults(length);
     } else if (is_column<ColumnStruct>(column.get())) {
         ret = assert_cast<ColumnStruct*>(column.get());
@@ -131,8 +131,7 @@ int VExplodeMapTableFunction::get_value(MutableColumnPtr& 
column, int max_step)
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             struct_column =
                     
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             // here nullmap_column insert max_step many defaults as if 
MAP[row_idx] is NULL
             // will be not update value, _cur_size = 0, means current_empty;
             // so here could insert directly
diff --git a/be/src/exprs/table_function/vexplode_numbers.cpp 
b/be/src/exprs/table_function/vexplode_numbers.cpp
index 3746cabd0bb..0f93dec02f1 100644
--- a/be/src/exprs/table_function/vexplode_numbers.cpp
+++ b/be/src/exprs/table_function/vexplode_numbers.cpp
@@ -103,8 +103,8 @@ void 
VExplodeNumbersTableFunction::get_same_many_values(MutableColumnPtr& column
             assert_cast<ColumnInt32*>(
                     
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get())
                     ->insert_many_vals(static_cast<int32_t>(_cur_offset), 
length);
-            assert_cast<ColumnUInt8*>(
-                    
assert_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+            assert_cast<ColumnNullable*>(column.get())
+                    ->get_null_map_column_ptr()
                     ->insert_many_defaults(length);
         } else {
             assert_cast<ColumnInt32*>(column.get())
diff --git a/be/src/exprs/table_function/vexplode_numbers.h 
b/be/src/exprs/table_function/vexplode_numbers.h
index 1b715a67d10..9a862b666bb 100644
--- a/be/src/exprs/table_function/vexplode_numbers.h
+++ b/be/src/exprs/table_function/vexplode_numbers.h
@@ -45,11 +45,11 @@ public:
         max_step = std::min(max_step, (int)(_cur_size - _cur_offset));
         if (_is_const) {
             if (_is_nullable) {
-                static_cast<ColumnInt32*>(
-                        
static_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get())
+                assert_cast<ColumnInt32*>(
+                        
assert_cast<ColumnNullable*>(column.get())->get_nested_column_ptr().get())
                         ->insert_range_from(*_elements_column, _cur_offset, 
max_step);
-                static_cast<ColumnUInt8*>(
-                        
static_cast<ColumnNullable*>(column.get())->get_null_map_column_ptr().get())
+                assert_cast<ColumnNullable*>(column.get())
+                        ->get_null_map_column_ptr()
                         ->insert_many_defaults(max_step);
             } else {
                 static_cast<ColumnInt32*>(column.get())
@@ -66,9 +66,8 @@ public:
                     target = 
assert_cast<ColumnInt32*>(assert_cast<ColumnNullable*>(column.get())
                                                                
->get_nested_column_ptr()
                                                                .get());
-                    
assert_cast<ColumnUInt8*>(assert_cast<ColumnNullable*>(column.get())
-                                                      
->get_null_map_column_ptr()
-                                                      .get())
+                    assert_cast<ColumnNullable*>(column.get())
+                            ->get_null_map_column_ptr()
                             ->insert_many_defaults(max_step);
                 } else {
                     target = assert_cast<ColumnInt32*>(column.get());
diff --git a/be/src/exprs/table_function/vexplode_v2.cpp 
b/be/src/exprs/table_function/vexplode_v2.cpp
index 62a4ab1d66a..e256c2aaa82 100644
--- a/be/src/exprs/table_function/vexplode_v2.cpp
+++ b/be/src/exprs/table_function/vexplode_v2.cpp
@@ -163,8 +163,7 @@ void 
VExplodeV2TableFunction::get_same_many_values(MutableColumnPtr& column, int
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             struct_column =
                     
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nullmap_column->insert_many_defaults(length);
 
         } else {
@@ -192,8 +191,7 @@ void 
VExplodeV2TableFunction::get_same_many_values(MutableColumnPtr& column, int
             struct_field.insert_many_defaults(length);
         } else {
             auto* nullable_column = 
assert_cast<ColumnNullable*>(struct_field.get_ptr().get());
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             // only need to check if the value at position pos is null
             if (element_size < _cur_offset ||
                 (detail.nested_nullmap_data && 
detail.nested_nullmap_data[pos])) {
@@ -223,8 +221,7 @@ int VExplodeV2TableFunction::get_value(MutableColumnPtr& 
column, int max_step) {
                 auto* nullable_column = 
assert_cast<ColumnNullable*>(column.get());
                 struct_column =
                         
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
-                auto* nullmap_column =
-                        
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+                auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
                 nullmap_column->insert_many_defaults(max_step);
 
             } else {
@@ -253,8 +250,7 @@ int VExplodeV2TableFunction::get_value(MutableColumnPtr& 
column, int max_step) {
                 struct_field.insert_many_defaults(max_step);
             } else {
                 auto* nullable_column = 
assert_cast<ColumnNullable*>(struct_field.get_ptr().get());
-                auto* nullmap_column =
-                        
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+                auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
                 if (element_size >= _cur_offset + max_step) {
                     
nullable_column->get_nested_column_ptr()->insert_range_from(*detail.nested_col,
                                                                                
 pos, max_step);
diff --git a/be/src/exprs/table_function/vjson_each.cpp 
b/be/src/exprs/table_function/vjson_each.cpp
index 4b233ae9f85..aa978f75e7c 100644
--- a/be/src/exprs/table_function/vjson_each.cpp
+++ b/be/src/exprs/table_function/vjson_each.cpp
@@ -162,8 +162,7 @@ void 
VJsonEachTableFunction<TEXT_MODE>::get_same_many_values(MutableColumnPtr& c
     if (_is_nullable) {
         auto* nullable = assert_cast<ColumnNullable*>(column.get());
         ret = 
assert_cast<ColumnStruct*>(nullable->get_nested_column_ptr().get());
-        assert_cast<ColumnUInt8*>(nullable->get_null_map_column_ptr().get())
-                ->insert_many_defaults(length);
+        nullable->get_null_map_column_ptr()->insert_many_defaults(length);
     } else {
         ret = assert_cast<ColumnStruct*>(column.get());
     }
@@ -184,8 +183,7 @@ int 
VJsonEachTableFunction<TEXT_MODE>::get_value(MutableColumnPtr& column, int m
         if (_is_nullable) {
             auto* nullable_col = assert_cast<ColumnNullable*>(column.get());
             struct_col = 
assert_cast<ColumnStruct*>(nullable_col->get_nested_column_ptr().get());
-            
assert_cast<ColumnUInt8*>(nullable_col->get_null_map_column_ptr().get())
-                    ->insert_many_defaults(max_step);
+            
nullable_col->get_null_map_column_ptr()->insert_many_defaults(max_step);
         } else {
             struct_col = assert_cast<ColumnStruct*>(column.get());
         }
diff --git a/be/src/exprs/vcase_expr.h b/be/src/exprs/vcase_expr.h
index b8e274be82a..fef3a3462f9 100644
--- a/be/src/exprs/vcase_expr.h
+++ b/be/src/exprs/vcase_expr.h
@@ -277,10 +277,7 @@ private:
                     continue;
                 }
                 const auto* __restrict cond_raw_nullmap =
-                        assert_cast<const ColumnUInt8*, 
TypeCheckOnRelease::DISABLE>(
-                                
column_nullable_ptr->get_null_map_column_ptr().get())
-                                ->get_data()
-                                .data();
+                        
column_nullable_ptr->get_null_map_column_ptr()->get_data().data();
                 for (int row_idx = 0; row_idx < rows_count; row_idx++) {
                     then_idx_ptr[row_idx] |= (!then_idx_ptr[row_idx] * 
cond_raw_data[row_idx] *
                                               !cond_raw_nullmap[row_idx]) *
diff --git a/be/src/exprs/vcompound_pred.h b/be/src/exprs/vcompound_pred.h
index e82fa04ba8e..0c8fd46d8bd 100644
--- a/be/src/exprs/vcompound_pred.h
+++ b/be/src/exprs/vcompound_pred.h
@@ -406,10 +406,7 @@ private:
                     assert_cast<const 
ColumnUInt8*>(nullable_column->get_nested_column_ptr().get())
                             ->get_data()
                             .data();
-            auto* null_map = assert_cast<const ColumnUInt8*>(
-                                     
nullable_column->get_null_map_column_ptr().get())
-                                     ->get_data()
-                                     .data();
+            auto* null_map = 
nullable_column->get_null_map_column_ptr()->get_data().data();
             return std::make_pair(data_column, null_map);
         } else {
             auto* data_column = assert_cast<const 
ColumnUInt8*>(column.get())->get_data().data();
diff --git a/be/src/exprs/vcondition_expr.cpp b/be/src/exprs/vcondition_expr.cpp
index ef462f4cde9..2f2ffddebe5 100644
--- a/be/src/exprs/vcondition_expr.cpp
+++ b/be/src/exprs/vcondition_expr.cpp
@@ -311,17 +311,23 @@ Status 
VectorizedIfExpr::execute_for_nullable_then_else(Block& block,
         // b. create a const_nullmap_column: it's a not nullable column or a 
const nullable column, contain a const value
         Block temporary_block;
         temporary_block.insert(arg_cond);
-        auto then_nested_null_map =
-                (then_type_is_nullable && !then_column_is_const_nullable)
-                        ? then_is_nullable->get_null_map_column_ptr()
-                        : 
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+        ColumnPtr then_nested_null_map;
+        if (then_type_is_nullable && !then_column_is_const_nullable) {
+            then_nested_null_map = then_is_nullable->get_null_map_column_ptr();
+        } else {
+            then_nested_null_map =
+                    
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+        }
         temporary_block.insert(
                 {then_nested_null_map, std::make_shared<DataTypeUInt8>(), 
"then_column_null_map"});
 
-        auto else_nested_null_map =
-                (else_type_is_nullable && !else_column_is_const_nullable)
-                        ? else_is_nullable->get_null_map_column_ptr()
-                        : 
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+        ColumnPtr else_nested_null_map;
+        if (else_type_is_nullable && !else_column_is_const_nullable) {
+            else_nested_null_map = else_is_nullable->get_null_map_column_ptr();
+        } else {
+            else_nested_null_map =
+                    
DataTypeUInt8().create_column_const_with_default_value(input_rows_count);
+        }
         temporary_block.insert(
                 {else_nested_null_map, std::make_shared<DataTypeUInt8>(), 
"else_column_null_map"});
         temporary_block.insert(
diff --git a/be/src/storage/segment/column_reader.cpp 
b/be/src/storage/segment/column_reader.cpp
index 02c59ce6cb6..7fc5d5e1466 100644
--- a/be/src/storage/segment/column_reader.cpp
+++ b/be/src/storage/segment/column_reader.cpp
@@ -979,12 +979,12 @@ Status MapFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, bool*
         size_t num_read = *n;
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
             // schema-change: column became nullable but old segment has no 
null data
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
         // fill nested ColumnMap with empty (zero-element) maps
@@ -1039,11 +1039,11 @@ Status MapFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, bool*
         // if so, we should set null_map to all null by default
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
     }
@@ -1064,12 +1064,12 @@ Status MapFileColumnIterator::read_by_rowids(const 
rowid_t* rowids, const size_t
         auto& nullable_col = assert_cast<ColumnNullable&>(*dst);
         if (_null_iterator) {
             auto null_map_ptr = nullable_col.get_null_map_column_ptr();
-            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_ptr));
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
+            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_column));
         } else {
             // schema-change: column became nullable but old segment has no 
null data
             auto null_map_ptr = nullable_col.get_null_map_column_ptr();
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, count);
+            null_map_ptr->insert_many_vals(0, count);
         }
         // fill nested ColumnMap with empty (zero-element) maps
         auto& column_map = assert_cast<ColumnMap&, 
TypeCheckOnRelease::DISABLE>(
@@ -1099,12 +1099,13 @@ Status MapFileColumnIterator::read_by_rowids(const 
rowid_t* rowids, const size_t
         }
         auto null_map_ptr = 
static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
         size_t null_before = null_map_ptr->size();
-        RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_ptr));
+        auto* null_map_col = null_map_ptr.get();
+        MutableColumnPtr null_map_column = std::move(null_map_ptr);
+        RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_column));
         // extract a light-weight view to decide element reads
-        auto& null_map_col = assert_cast<ColumnUInt8&>(*null_map_ptr);
         null_mask.reserve(count);
         for (size_t i = 0; i < count; ++i) {
-            null_mask.push_back(null_map_col.get_element(null_before + i));
+            null_mask.push_back(null_map_col->get_element(null_before + i));
         }
     } else if (dst->is_nullable()) {
         // in not-null to null linked-schemachange mode,
@@ -1112,8 +1113,7 @@ Status MapFileColumnIterator::read_by_rowids(const 
rowid_t* rowids, const size_t
         // so may dst from changed meta which is nullable but old data is not 
nullable,
         // if so, we should set null_map to all null by default
         auto null_map_ptr = 
static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr();
-        auto& null_map = assert_cast<ColumnUInt8&>(*null_map_ptr);
-        null_map.insert_many_vals(0, count);
+        null_map_ptr->insert_many_vals(0, count);
     }
 
     // 2. bulk read start ordinals for requested rows
@@ -1397,12 +1397,12 @@ Status StructFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, bo
         size_t num_read = *n;
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
             // schema-change: column became nullable but old segment has no 
null data
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
         // fill nested ColumnStruct with defaults to maintain consistent 
column sizes
@@ -1435,11 +1435,11 @@ Status StructFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, bo
         // if so, we should set null_map to all null by default
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
     }
@@ -1762,12 +1762,12 @@ Status ArrayFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, boo
         size_t num_read = *n;
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
             // schema-change: column became nullable but old segment has no 
null data
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
         // fill nested ColumnArray with empty (zero-length) arrays
@@ -1817,11 +1817,11 @@ Status ArrayFileColumnIterator::next_batch(size_t* n, 
MutableColumnPtr& dst, boo
         // if so, we should set null_map to all null by default
         if (_null_iterator) {
             bool null_signs_has_null = false;
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
             RETURN_IF_ERROR(
-                    _null_iterator->next_batch(&num_read, null_map_ptr, 
&null_signs_has_null));
+                    _null_iterator->next_batch(&num_read, null_map_column, 
&null_signs_has_null));
         } else {
-            auto& null_map = assert_cast<ColumnUInt8&, 
TypeCheckOnRelease::DISABLE>(*null_map_ptr);
-            null_map.insert_many_vals(0, num_read);
+            null_map_ptr->insert_many_vals(0, num_read);
         }
         DCHECK(num_read == *n);
     }
diff --git a/be/test/exec/operator/table_function_operator_test.cpp 
b/be/test/exec/operator/table_function_operator_test.cpp
index b6057ede0f5..913687414be 100644
--- a/be/test/exec/operator/table_function_operator_test.cpp
+++ b/be/test/exec/operator/table_function_operator_test.cpp
@@ -145,8 +145,7 @@ public:
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             
nullable_column->get_nested_column_ptr()->insert_range_from(*_detail.nested_col,
 pos,
                                                                         
max_step);
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             const size_t old_size = nullmap_column->size();
             nullmap_column->resize(old_size + max_step);
             if (_detail.nested_nullmap_data != nullptr) {
diff --git a/be/test/exprs/function/function_test_util.cpp 
b/be/test/exprs/function/function_test_util.cpp
index d0e6092fed6..b37e452b69a 100644
--- a/be/test/exprs/function/function_test_util.cpp
+++ b/be/test/exprs/function/function_test_util.cpp
@@ -390,8 +390,7 @@ bool insert_cell(MutableColumnPtr& column, DataTypePtr 
type_ptr, const AnyType&
         auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
         auto col_type = remove_nullable(type_ptr);
         auto col = nullable_column->get_nested_column_ptr();
-        auto* nullmap_column =
-                
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+        auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
         bool ok = insert_cell(col, col_type, cell, datetime_is_string_format);
         nullmap_column->insert_value(ok ? 0 : 1);
     } else {
@@ -553,8 +552,7 @@ bool insert_cell(MutableColumnPtr& column, DataTypePtr 
type_ptr, const AnyType&
             auto* nullable_column = assert_cast<ColumnNullable*>(column.get());
             auto* struct_column =
                     
assert_cast<ColumnStruct*>(nullable_column->get_nested_column_ptr().get());
-            auto* nullmap_column =
-                    
assert_cast<ColumnUInt8*>(nullable_column->get_null_map_column_ptr().get());
+            auto* nullmap_column = 
nullable_column->get_null_map_column_ptr().get();
             nullmap_column->insert_default();
             for (size_t i = 0; i < v.size(); ++i) {
                 auto& field = v[i];


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

Reply via email to