You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2022/10/27 00:58:51 UTC

[doris] branch branch-1.1-lts updated: [fix](MV) Fix insert negative value to table with bitmap_union MV will cause count distinct result incorrect (#13700)

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

morningman pushed a commit to branch branch-1.1-lts
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-1.1-lts by this push:
     new 06ebb5e842 [fix](MV) Fix insert negative value to table with bitmap_union MV will cause count distinct result incorrect (#13700)
06ebb5e842 is described below

commit 06ebb5e84247e8ee2e0a00a102c3b9068e35486c
Author: Zhengguo Yang <ya...@gmail.com>
AuthorDate: Thu Oct 27 08:58:46 2022 +0800

    [fix](MV) Fix insert negative value to table with bitmap_union MV will cause count distinct result incorrect (#13700)
    
    cherry-pick #13667 #13448
---
 be/src/exprs/bitmap_function.cpp                   | 24 ++++++++++
 be/src/exprs/bitmap_function.h                     |  5 +-
 be/src/olap/schema_change.cpp                      | 37 +++++++++------
 be/src/vec/exec/vunion_node.cpp                    | 30 +++++++-----
 be/src/vec/functions/function.cpp                  |  5 +-
 .../vec/functions/function_always_not_nullable.h   | 19 ++++++--
 be/src/vec/functions/function_bitmap.cpp           | 54 +++++++++++++++++++++-
 .../doris/analysis/CreateMaterializedViewStmt.java | 21 ++++++++-
 .../apache/doris/analysis/FunctionCallExpr.java    |  4 ++
 .../doris/analysis/MVColumnBitmapUnionPattern.java |  3 +-
 .../java/org/apache/doris/catalog/Function.java    |  4 ++
 .../java/org/apache/doris/catalog/FunctionSet.java |  1 +
 .../rewrite/mvrewrite/FunctionCallEqualRule.java   |  1 +
 gensrc/script/doris_builtins_functions.py          |  6 +++
 14 files changed, 175 insertions(+), 39 deletions(-)

diff --git a/be/src/exprs/bitmap_function.cpp b/be/src/exprs/bitmap_function.cpp
index 79e514bf6d..5a64a129ac 100644
--- a/be/src/exprs/bitmap_function.cpp
+++ b/be/src/exprs/bitmap_function.cpp
@@ -160,6 +160,30 @@ StringVal BitmapFunctions::to_bitmap(doris_udf::FunctionContext* ctx,
     return serialize(ctx, &bitmap);
 }
 
+StringVal BitmapFunctions::to_bitmap_with_check(doris_udf::FunctionContext* ctx,
+                                                const doris_udf::StringVal& src) {
+    BitmapValue bitmap;
+
+    if (!src.is_null) {
+        StringParser::ParseResult parse_result = StringParser::PARSE_SUCCESS;
+        uint64_t int_value = StringParser::string_to_unsigned_int<uint64_t>(
+                reinterpret_cast<char*>(src.ptr), src.len, &parse_result);
+        if (parse_result == StringParser::PARSE_SUCCESS) {
+            bitmap.add(int_value);
+        } else {
+            std::stringstream ss;
+            ss << "The input: " << src.to_string()
+               << " is not valid, to_bitmap only support bigint value from 0 to "
+                  "18446744073709551615 currently, cannot load negative values to column with"
+                  " to_bitmap MV on it.";
+            ctx->set_error(ss.str().c_str());
+            return serialize(ctx, nullptr);
+        }
+    }
+
+    return serialize(ctx, &bitmap);
+}
+
 StringVal BitmapFunctions::bitmap_hash(doris_udf::FunctionContext* ctx,
                                        const doris_udf::StringVal& src) {
     BitmapValue bitmap;
diff --git a/be/src/exprs/bitmap_function.h b/be/src/exprs/bitmap_function.h
index f2cee64823..79bb3aefe6 100644
--- a/be/src/exprs/bitmap_function.h
+++ b/be/src/exprs/bitmap_function.h
@@ -68,6 +68,7 @@ public:
 
     static StringVal bitmap_serialize(FunctionContext* ctx, const StringVal& src);
     static StringVal to_bitmap(FunctionContext* ctx, const StringVal& src);
+    static StringVal to_bitmap_with_check(FunctionContext* ctx, const StringVal& src);
     static StringVal bitmap_hash(FunctionContext* ctx, const StringVal& src);
     static StringVal bitmap_or(FunctionContext* ctx, const StringVal& src, const StringVal& dst);
     static StringVal bitmap_xor(FunctionContext* ctx, const StringVal& src, const StringVal& dst);
@@ -87,9 +88,9 @@ public:
     static BigIntVal bitmap_or_count(FunctionContext* ctx, const StringVal& lhs, int num_args,
                                      const StringVal* bitmap_strs);
     static BigIntVal bitmap_and_count(FunctionContext* ctx, const StringVal& lhs, int num_args,
-                                     const StringVal* bitmap_strs);
+                                      const StringVal* bitmap_strs);
     static BigIntVal bitmap_xor_count(FunctionContext* ctx, const StringVal& lhs, int num_args,
-                                     const StringVal* bitmap_strs);
+                                      const StringVal* bitmap_strs);
 
     static StringVal bitmap_to_string(FunctionContext* ctx, const StringVal& input);
     // Convert a comma separated string to a Bitmap
diff --git a/be/src/olap/schema_change.cpp b/be/src/olap/schema_change.cpp
index 2a7b66c10e..799a0ccdf7 100644
--- a/be/src/olap/schema_change.cpp
+++ b/be/src/olap/schema_change.cpp
@@ -303,9 +303,11 @@ bool to_bitmap(RowCursor* read_helper, RowCursor* write_helper, const TabletColu
         switch (ref_column.type()) {
         case OLAP_FIELD_TYPE_TINYINT:
             if (*(int8_t*)src < 0) {
-                LOG(WARNING) << "The input: " << *(int8_t*)src
-                             << " is not valid, to_bitmap only support bigint value from 0 to "
-                                "18446744073709551615 currently";
+                LOG(WARNING)
+                        << "The input: " << *(int8_t*)src
+                        << " is not valid, to_bitmap only support bigint value from 0 to "
+                           "18446744073709551615 currently, cannot create MV with to_bitmap on "
+                           "column with negative values.";
                 return false;
             }
             origin_value = *(int8_t*)src;
@@ -315,9 +317,11 @@ bool to_bitmap(RowCursor* read_helper, RowCursor* write_helper, const TabletColu
             break;
         case OLAP_FIELD_TYPE_SMALLINT:
             if (*(int16_t*)src < 0) {
-                LOG(WARNING) << "The input: " << *(int16_t*)src
-                             << " is not valid, to_bitmap only support bigint value from 0 to "
-                                "18446744073709551615 currently";
+                LOG(WARNING)
+                        << "The input: " << *(int16_t*)src
+                        << " is not valid, to_bitmap only support bigint value from 0 to "
+                           "18446744073709551615 currently, cannot create MV with to_bitmap on "
+                           "column with negative values.";
                 return false;
             }
             origin_value = *(int16_t*)src;
@@ -327,9 +331,11 @@ bool to_bitmap(RowCursor* read_helper, RowCursor* write_helper, const TabletColu
             break;
         case OLAP_FIELD_TYPE_INT:
             if (*(int32_t*)src < 0) {
-                LOG(WARNING) << "The input: " << *(int32_t*)src
-                             << " is not valid, to_bitmap only support bigint value from 0 to "
-                                "18446744073709551615 currently";
+                LOG(WARNING)
+                        << "The input: " << *(int32_t*)src
+                        << " is not valid, to_bitmap only support bigint value from 0 to "
+                           "18446744073709551615 currently, cannot create MV with to_bitmap on "
+                           "column with negative values.";
                 return false;
             }
             origin_value = *(int32_t*)src;
@@ -339,9 +345,11 @@ bool to_bitmap(RowCursor* read_helper, RowCursor* write_helper, const TabletColu
             break;
         case OLAP_FIELD_TYPE_BIGINT:
             if (*(int64_t*)src < 0) {
-                LOG(WARNING) << "The input: " << *(int64_t*)src
-                             << " is not valid, to_bitmap only support bigint value from 0 to "
-                                "18446744073709551615 currently";
+                LOG(WARNING)
+                        << "The input: " << *(int64_t*)src
+                        << " is not valid, to_bitmap only support bigint value from 0 to "
+                           "18446744073709551615 currently, cannot create MV with to_bitmap on "
+                           "column with negative values.";
                 return false;
             }
             origin_value = *(int64_t*)src;
@@ -501,8 +509,9 @@ OLAPStatus RowBlockChanger::change_row_block(const RowBlock* ref_block, int32_t
         if (_schema_mapping[i].ref_column >= 0) {
             if (!_schema_mapping[i].materialized_function.empty()) {
                 bool (*_do_materialized_transform)(RowCursor*, RowCursor*, const TabletColumn&, int,
-                                                   int, MemPool*);
-                if (_schema_mapping[i].materialized_function == "to_bitmap") {
+                                                   int, MemPool*) = nullptr;
+                if (_schema_mapping[i].materialized_function == "to_bitmap" ||
+                    _schema_mapping[i].materialized_function == "to_bitmap_with_check") {
                     _do_materialized_transform = to_bitmap;
                 } else if (_schema_mapping[i].materialized_function == "hll_hash") {
                     _do_materialized_transform = hll_hash;
diff --git a/be/src/vec/exec/vunion_node.cpp b/be/src/vec/exec/vunion_node.cpp
index 5f1bfefa17..75d4ea4182 100644
--- a/be/src/vec/exec/vunion_node.cpp
+++ b/be/src/vec/exec/vunion_node.cpp
@@ -20,7 +20,6 @@
 #include "gen_cpp/PlanNodes_types.h"
 #include "runtime/runtime_state.h"
 #include "util/runtime_profile.h"
-
 #include "vec/core/block.h"
 #include "vec/exprs/vexpr.h"
 #include "vec/exprs/vexpr_context.h"
@@ -125,8 +124,10 @@ Status VUnionNode::get_next_materialized(RuntimeState* state, Block* block) {
     DCHECK_LT(_child_idx, _children.size());
 
     bool mem_reuse = block->mem_reuse();
-    MutableBlock mblock = mem_reuse ? MutableBlock::build_mutable_block(block) :
-        MutableBlock(Block(VectorizedUtils::create_columns_with_type_and_name(row_desc())));
+    MutableBlock mblock =
+            mem_reuse ? MutableBlock::build_mutable_block(block)
+                      : MutableBlock(Block(
+                                VectorizedUtils::create_columns_with_type_and_name(row_desc())));
 
     Block child_block;
     while (has_more_materialized() && mblock.rows() <= state->batch_size()) {
@@ -157,9 +158,9 @@ Status VUnionNode::get_next_materialized(RuntimeState* state, Block* block) {
             // Unless we are inside a subplan expecting to call open()/get_next() on the child
             // again, the child can be closed at this point.
             // TODO: Recheck whether is_in_subplan() is right
-//            if (!is_in_subplan()) {
-//                child(_child_idx)->close(state);
-//            }
+            //            if (!is_in_subplan()) {
+            //                child(_child_idx)->close(state);
+            //            }
             ++_child_idx;
         }
     }
@@ -177,19 +178,24 @@ Status VUnionNode::get_next_const(RuntimeState* state, Block* block) {
     DCHECK_LT(_const_expr_list_idx, _const_expr_lists.size());
 
     bool mem_reuse = block->mem_reuse();
-    MutableBlock mblock = mem_reuse ? MutableBlock::build_mutable_block(block) :
-        MutableBlock(Block(VectorizedUtils::create_columns_with_type_and_name(row_desc())));
+    MutableBlock mblock =
+            mem_reuse ? MutableBlock::build_mutable_block(block)
+                      : MutableBlock(Block(
+                                VectorizedUtils::create_columns_with_type_and_name(row_desc())));
     for (; _const_expr_list_idx < _const_expr_lists.size(); ++_const_expr_list_idx) {
         Block tmp_block;
         tmp_block.insert({vectorized::ColumnUInt8::create(1),
-                    std::make_shared<vectorized::DataTypeUInt8>(), ""});
+                          std::make_shared<vectorized::DataTypeUInt8>(), ""});
         int const_expr_lists_size = _const_expr_lists[_const_expr_list_idx].size();
         std::vector<int> result_list(const_expr_lists_size);
         for (size_t i = 0; i < const_expr_lists_size; ++i) {
-            _const_expr_lists[_const_expr_list_idx][i]->execute(&tmp_block, &result_list[i]);
+            RETURN_IF_ERROR(_const_expr_lists[_const_expr_list_idx][i]->execute(&tmp_block,
+                                                                                &result_list[i]));
         }
         tmp_block.erase_not_in(result_list);
-        mblock.merge(tmp_block);
+        if (tmp_block.rows() > 0) {
+            mblock.merge(tmp_block);
+        }
     }
 
     if (!mem_reuse) {
@@ -201,7 +207,7 @@ Status VUnionNode::get_next_const(RuntimeState* state, Block* block) {
     // need add one row to make sure the union node exec const expr return at least one row
     if (block->rows() == 0) {
         block->insert({vectorized::ColumnUInt8::create(1),
-                    std::make_shared<vectorized::DataTypeUInt8>(), ""});
+                       std::make_shared<vectorized::DataTypeUInt8>(), ""});
     }
 
     return Status::OK();
diff --git a/be/src/vec/functions/function.cpp b/be/src/vec/functions/function.cpp
index 5c671284a4..231e621fe7 100644
--- a/be/src/vec/functions/function.cpp
+++ b/be/src/vec/functions/function.cpp
@@ -269,9 +269,8 @@ Status PreparedFunctionImpl::execute(FunctionContext* context, Block& block,
     //            res.column = block_without_low_cardinality.safe_get_by_position(result).column;
     //        }
     //    } else
-    execute_without_low_cardinality_columns(context, block, args, result, input_rows_count,
-                                            dry_run);
-    return Status::OK();
+    return execute_without_low_cardinality_columns(context, block, args, result, input_rows_count,
+                                                   dry_run);
 }
 
 void FunctionBuilderImpl::check_number_of_arguments(size_t number_of_arguments) const {
diff --git a/be/src/vec/functions/function_always_not_nullable.h b/be/src/vec/functions/function_always_not_nullable.h
index 76b2b2a600..e7802ca9b4 100644
--- a/be/src/vec/functions/function_always_not_nullable.h
+++ b/be/src/vec/functions/function_always_not_nullable.h
@@ -24,7 +24,7 @@
 
 namespace doris::vectorized {
 
-template <typename Function>
+template <typename Function, bool WithReturn = false>
 class FunctionAlwaysNotNullable : public IFunction {
 public:
     static constexpr auto name = Function::name;
@@ -57,14 +57,25 @@ public:
                     col_nullable->get_null_map_column_ptr().get());
 
             if (col != nullptr && col_nullmap != nullptr) {
-                Function::vector_nullable(col->get_chars(), col->get_offsets(),
-                                          col_nullmap->get_data(), column_result);
+                if constexpr (WithReturn) {
+                    RETURN_IF_ERROR(Function::vector_nullable(col->get_chars(), col->get_offsets(),
+                                                              col_nullmap->get_data(),
+                                                              column_result));
+                } else {
+                    Function::vector_nullable(col->get_chars(), col->get_offsets(),
+                                              col_nullmap->get_data(), column_result);
+                }
 
                 block.replace_by_position(result, std::move(column_result));
                 return Status::OK();
             }
         } else if (const ColumnString* col = check_and_get_column<ColumnString>(column.get())) {
-            Function::vector(col->get_chars(), col->get_offsets(), column_result);
+            if constexpr (WithReturn) {
+                RETURN_IF_ERROR(
+                        Function::vector(col->get_chars(), col->get_offsets(), column_result));
+            } else {
+                Function::vector(col->get_chars(), col->get_offsets(), column_result);
+            }
 
             block.replace_by_position(result, std::move(column_result));
             return Status::OK();
diff --git a/be/src/vec/functions/function_bitmap.cpp b/be/src/vec/functions/function_bitmap.cpp
index 69b5bd6737..a49daeff6e 100644
--- a/be/src/vec/functions/function_bitmap.cpp
+++ b/be/src/vec/functions/function_bitmap.cpp
@@ -21,12 +21,12 @@
 #include "gutil/strings/numbers.h"
 #include "gutil/strings/split.h"
 #include "util/string_parser.hpp"
+#include "vec/functions/function_always_not_nullable.h"
 #include "vec/functions/function_bitmap_min_or_max.h"
 #include "vec/functions/function_const.h"
 #include "vec/functions/function_string.h"
 #include "vec/functions/function_totype.h"
 #include "vec/functions/simple_function_factory.h"
-#include "vec/functions/function_always_not_nullable.h"
 
 namespace doris::vectorized {
 
@@ -75,6 +75,54 @@ struct ToBitmap {
     }
 };
 
+struct ToBitmapWithCheck {
+    static constexpr auto name = "to_bitmap_with_check";
+    using ReturnType = DataTypeBitMap;
+
+    static Status vector(const ColumnString::Chars& data, const ColumnString::Offsets& offsets,
+                         MutableColumnPtr& col_res) {
+        return execute<false>(data, offsets, nullptr, col_res);
+    }
+
+    static Status vector_nullable(const ColumnString::Chars& data,
+                                  const ColumnString::Offsets& offsets, const NullMap& nullmap,
+                                  MutableColumnPtr& col_res) {
+        return execute<true>(data, offsets, &nullmap, col_res);
+    }
+    template <bool arg_is_nullable>
+    static Status execute(const ColumnString::Chars& data, const ColumnString::Offsets& offsets,
+                          const NullMap* nullmap, MutableColumnPtr& col_res) {
+        auto* res_column = reinterpret_cast<ColumnBitmap*>(col_res.get());
+        auto& res_data = res_column->get_data();
+        size_t size = offsets.size();
+
+        for (size_t i = 0; i < size; ++i) {
+            if (arg_is_nullable && ((*nullmap)[i])) {
+                continue;
+            } else {
+                const char* raw_str = reinterpret_cast<const char*>(&data[offsets[i - 1]]);
+                size_t str_size = offsets[i] - offsets[i - 1] - 1;
+                StringParser::ParseResult parse_result = StringParser::PARSE_SUCCESS;
+                uint64_t int_value = StringParser::string_to_unsigned_int<uint64_t>(
+                        raw_str, str_size, &parse_result);
+                if (LIKELY(parse_result == StringParser::PARSE_SUCCESS)) {
+                    res_data[i].add(int_value);
+                } else {
+                    std::stringstream ss;
+                    ss << "The input: " << std::string(raw_str, str_size)
+                       << " is not valid, to_bitmap only support bigint value from 0 to "
+                          "18446744073709551615 currently, cannot create MV with to_bitmap on "
+                          "column with negative values or cannot load negative values to column "
+                          "with to_bitmap MV on it.";
+                    LOG(WARNING) << ss.str();
+                    return Status::InternalError(ss.str());
+                }
+            }
+        }
+        return Status::OK();
+    }
+};
+
 struct BitmapFromString {
     static constexpr auto name = "bitmap_from_string";
 
@@ -155,7 +203,7 @@ struct BitmapHash {
             const char* raw_str = reinterpret_cast<const char*>(&data[offsets[i - 1]]);
             size_t str_size = offsets[i] - offsets[i - 1] - 1;
             uint32_t hash_value =
-                        HashUtil::murmur_hash3_32(raw_str, str_size, HashUtil::MURMUR3_32_SEED);
+                    HashUtil::murmur_hash3_32(raw_str, str_size, HashUtil::MURMUR3_32_SEED);
             res_data[i].add(hash_value);
         }
     }
@@ -510,6 +558,7 @@ public:
 
 using FunctionBitmapEmpty = FunctionConst<BitmapEmpty, false>;
 using FunctionToBitmap = FunctionAlwaysNotNullable<ToBitmap>;
+using FunctionToBitmapWithCheck = FunctionAlwaysNotNullable<ToBitmapWithCheck, true>;
 using FunctionBitmapFromString = FunctionBitmapAlwaysNull<BitmapFromString>;
 using FunctionBitmapHash = FunctionAlwaysNotNullable<BitmapHash>;
 
@@ -537,6 +586,7 @@ using FunctionBitmapSubsetInRange = FunctionBitmapSubs<BitmapSubsetInRange>;
 void register_function_bitmap(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionBitmapEmpty>();
     factory.register_function<FunctionToBitmap>();
+    factory.register_function<FunctionToBitmapWithCheck>();
     factory.register_function<FunctionBitmapFromString>();
     factory.register_function<FunctionBitmapHash>();
     factory.register_function<FunctionBitmapCount>();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java
index b8125417ee..1e5a590c9d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java
@@ -206,6 +206,25 @@ public class CreateMaterializedViewStmt extends DdlStmt {
                         throw new AnalysisException(
                                 "The function " + functionName + " must match pattern:" + mvColumnPattern.toString());
                     }
+
+                    // for bitmap_union(to_bitmap(column)) function, we should check value is not negative
+                    // in vectorized schema_change mode, so we should rewrite the function to
+                    // bitmap_union(to_bitmap_with_check(column))
+                    if (functionName.equalsIgnoreCase("bitmap_union")) {
+                        if (functionCallExpr.getChildren().size() == 1
+                                && functionCallExpr.getChild(0) instanceof FunctionCallExpr) {
+                            Expr child = functionCallExpr.getChild(0);
+                            FunctionCallExpr childFunctionCallExpr = (FunctionCallExpr) child;
+                            if (childFunctionCallExpr.getFnName().getFunction().equalsIgnoreCase("to_bitmap")) {
+                                childFunctionCallExpr.setFnName(
+                                        new FunctionName(childFunctionCallExpr.getFnName().getDb(),
+                                                "to_bitmap_with_check"));
+                                childFunctionCallExpr.getFn().setName(
+                                        new FunctionName(childFunctionCallExpr.getFn().getFunctionName().getDb(),
+                                                "to_bitmap_with_check"));
+                            }
+                        }
+                    }
                 }
                 // check duplicate column
                 List<SlotRef> slots = new ArrayList<>();
@@ -444,7 +463,7 @@ public class CreateMaterializedViewStmt extends DdlStmt {
                             CastExpr castExpr = new CastExpr(new TypeDef(Type.VARCHAR), baseSlotRef);
                             List<Expr> params = Lists.newArrayList();
                             params.add(castExpr);
-                            FunctionCallExpr defineExpr = new FunctionCallExpr(FunctionSet.TO_BITMAP, params);
+                            FunctionCallExpr defineExpr = new FunctionCallExpr(FunctionSet.TO_BITMAP_WITH_CHECK, params);
                             result.put(mvColumnBuilder(functionName, baseColumnName), defineExpr);
                         } else {
                             result.put(baseColumnName, null);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index e249589f92..ff1551234c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -104,6 +104,10 @@ public class FunctionCallExpr extends Expr {
         isTableFnCall = tableFnCall;
     }
 
+    public void setFnName(FunctionName fnName) {
+        this.fnName = fnName;
+    }
+
     public Function getFn() {
         return fn;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/MVColumnBitmapUnionPattern.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/MVColumnBitmapUnionPattern.java
index 45bf9948f8..d7712eda73 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/MVColumnBitmapUnionPattern.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/MVColumnBitmapUnionPattern.java
@@ -44,7 +44,8 @@ public class MVColumnBitmapUnionPattern implements MVColumnPattern {
             }
         } else if (fnExpr.getChild(0) instanceof FunctionCallExpr) {
             FunctionCallExpr child0FnExpr = (FunctionCallExpr) fnExpr.getChild(0);
-            if (!child0FnExpr.getFnName().getFunction().equalsIgnoreCase(FunctionSet.TO_BITMAP)) {
+            if (!child0FnExpr.getFnName().getFunction().equalsIgnoreCase(FunctionSet.TO_BITMAP)
+            && !child0FnExpr.getFnName().getFunction().equalsIgnoreCase(FunctionSet.TO_BITMAP_WITH_CHECK)) {
                 return false;
             }
             SlotRef slotRef = child0FnExpr.getChild(0).unwrapSlotRef();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
index bfc29d7dc2..abfab2e349 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Function.java
@@ -199,6 +199,10 @@ public class Function implements Writable {
         location = loc;
     }
 
+    public void setName(FunctionName name) {
+        this.name = name;
+    }
+
     public TFunctionBinaryType getBinaryType() {
         return binaryType;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
index d040f46715..aa9020303a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java
@@ -839,6 +839,7 @@ public class FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo
                 .build();
 
     public static final String TO_BITMAP = "to_bitmap";
+    public static final String TO_BITMAP_WITH_CHECK = "to_bitmap_with_check";
     public static final String BITMAP_UNION = "bitmap_union";
     public static final String BITMAP_UNION_COUNT = "bitmap_union_count";
     public static final String BITMAP_UNION_INT = "bitmap_union_int";
diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/mvrewrite/FunctionCallEqualRule.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/mvrewrite/FunctionCallEqualRule.java
index aee0871851..4e9005f339 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/rewrite/mvrewrite/FunctionCallEqualRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/mvrewrite/FunctionCallEqualRule.java
@@ -41,6 +41,7 @@ public class FunctionCallEqualRule implements MVExprEqualRule {
         builder.put(FunctionSet.HLL_UNION, "hll_union");
         builder.put(FunctionSet.HLL_UNION, "hll_raw_agg");
         builder.put(FunctionSet.TO_BITMAP, FunctionSet.TO_BITMAP);
+        builder.put(FunctionSet.TO_BITMAP_WITH_CHECK, FunctionSet.TO_BITMAP_WITH_CHECK);
         builder.put(FunctionSet.HLL_HASH, FunctionSet.HLL_HASH);
         columnAggTypeMatchFnName = builder.build();
     }
diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py
index 2c44b50737..b758c00ead 100755
--- a/gensrc/script/doris_builtins_functions.py
+++ b/gensrc/script/doris_builtins_functions.py
@@ -1138,12 +1138,18 @@ visible_functions = [
     [['to_bitmap'], 'BITMAP', ['VARCHAR'],
         '_ZN5doris15BitmapFunctions9to_bitmapEPN9doris_udf15FunctionContextERKNS1_9StringValE',
         '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],
+    [['to_bitmap_with_check'], 'BITMAP', ['VARCHAR'],
+        '_ZN5doris15BitmapFunctions20to_bitmap_with_checkEPN9doris_udf15FunctionContextERKNS1_9StringValE',
+        '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],
     [['bitmap_hash'], 'BITMAP', ['VARCHAR'],
         '_ZN5doris15BitmapFunctions11bitmap_hashEPN9doris_udf15FunctionContextERKNS1_9StringValE',
         '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],
     [['to_bitmap'], 'BITMAP', ['STRING'],
         '_ZN5doris15BitmapFunctions9to_bitmapEPN9doris_udf15FunctionContextERKNS1_9StringValE',
         '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],
+    [['to_bitmap_with_check'], 'BITMAP', ['STRING'],
+        '_ZN5doris15BitmapFunctions20to_bitmap_with_checkEPN9doris_udf15FunctionContextERKNS1_9StringValE',
+        '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],
     [['bitmap_hash'], 'BITMAP', ['STRING'],
         '_ZN5doris15BitmapFunctions11bitmap_hashEPN9doris_udf15FunctionContextERKNS1_9StringValE',
         '', '', 'vec', 'ALWAYS_NOT_NULLABLE'],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org