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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 5bcabaa679a branch-4.1: [Fix](regexp) Fix empty constant pattern 
matching in regexp (#68179)
5bcabaa679a is described below

commit 5bcabaa679abbc9b81518d5dc62f13ffc7e9d6e8
Author: linrrarity <[email protected]>
AuthorDate: Sun Sep 20 09:47:34 2026 +0800

    branch-4.1: [Fix](regexp) Fix empty constant pattern matching in regexp 
(#68179)
    
    pick: https://github.com/apache/doris/pull/66788 and
    https://github.com/apache/doris/pull/68146
    
    ---------
    
    Co-authored-by: HappenLee <[email protected]>
---
 be/src/exprs/function/like.cpp                     | 155 +++++++++---------
 be/src/exprs/function/like.h                       |  11 +-
 be/src/exprs/function/match.cpp                    |   5 +
 be/src/exprs/function/regexps.h                    |   4 +
 .../storage/index/inverted/query/regexp_query.cpp  |   4 +
 .../query_v2/regexp_query/regexp_weight.cpp        |   4 +
 be/src/util/hyperscan_util.cpp                     |  93 +++++++++++
 be/src/util/hyperscan_util.h                       |  29 ++++
 be/test/exprs/function/function_like_test.cpp      | 174 ++++++++++++++++++++-
 be/test/exprs/function/function_match_test.cpp     |  21 +++
 .../exprs/function/function_multi_match_test.cpp   |  10 ++
 .../index/inverted/query/regexp_query_test.cpp     |  26 ++-
 .../index/inverted/query_v2/regexp_query_test.cpp  |  17 +-
 .../java/org/apache/doris/qe/SessionVariable.java  |   8 +-
 .../org/apache/doris/qe/SessionVariablesTest.java  |  11 ++
 gensrc/thrift/PaloInternalService.thrift           |   3 +
 .../test_string_function_regexp.out                |  35 ++++-
 .../test_string_function_regexp.groovy             |  43 +++++
 18 files changed, 571 insertions(+), 82 deletions(-)

diff --git a/be/src/exprs/function/like.cpp b/be/src/exprs/function/like.cpp
index b6eeff762d4..f1a7e7f91fa 100644
--- a/be/src/exprs/function/like.cpp
+++ b/be/src/exprs/function/like.cpp
@@ -19,7 +19,6 @@
 
 #include <fmt/format.h>
 #include <hs/hs_compile.h>
-#include <re2/stringpiece.h>
 
 #include <cstddef>
 #include <ostream>
@@ -34,6 +33,7 @@
 #include "core/column/column_vector.h"
 #include "core/string_ref.h"
 #include "exprs/function/simple_function_factory.h"
+#include "util/hyperscan_util.h"
 
 namespace doris {
 #include "common/compile_check_begin.h"
@@ -184,8 +184,9 @@ struct VectorEndsWithSearchState : public 
VectorPatternSearchState {
     }
 };
 
-Status LikeSearchState::clone(LikeSearchState& cloned) {
+Status LikeSearchState::clone(LikeSearchState& cloned) const {
     cloned.set_search_string(search_string);
+    cloned.enable_hyperscan_fallback = enable_hyperscan_fallback;
 
     std::string re_pattern;
     FunctionLike::convert_like_pattern(this, pattern_str, &re_pattern);
@@ -340,13 +341,44 @@ Status FunctionLikeBase::vector_equals_fn(const 
ColumnString& vals,
 Status FunctionLikeBase::constant_substring_fn(const LikeSearchState* state,
                                                const ColumnString& val, const 
StringRef& pattern,
                                                ColumnUInt8::Container& result) 
{
-    auto sz = val.size();
-    for (size_t i = 0; i < sz; i++) {
-        if (state->search_string_sv.size == 0) {
-            result[i] = true;
-            continue;
+    size_t needle_size = state->search_string_sv.size;
+    if (needle_size == 0) {
+        memset(result.data(), 1, result.size());
+        return Status::OK();
+    }
+
+    const auto& values = val.get_chars();
+    const auto& value_offsets = val.get_offsets();
+    // treat continuous multi string data as a long string data
+    const UInt8* begin = values.data();
+    const UInt8* end = begin + values.size();
+    const UInt8* pos = begin;
+
+    /// Current index in the array of strings.
+    size_t i = 0;
+
+    /// We will search for the next occurrence in all strings at once.
+    while (pos < end) {
+        // search return matched substring start offset
+        pos = (UInt8*)state->substring_pattern.search((char*)pos, end - pos);
+        if (pos >= end) {
+            break;
         }
-        result[i] = state->substring_pattern.search(val.get_data_at(i)) != -1;
+
+        /// Determine which index it refers to.
+        /// begin + value_offsets[i] is the start offset of string at i+1
+        while (i < value_offsets.size() && begin + value_offsets[i] < pos) {
+            ++i;
+        }
+
+        /// We check that the entry does not pass through the boundaries of 
strings.
+        if (pos + needle_size <= begin + value_offsets[i]) {
+            result[i] = 1;
+        }
+
+        // move to next string offset
+        pos = begin + value_offsets[i];
+        ++i;
     }
     return Status::OK();
 }
@@ -453,7 +485,8 @@ Status FunctionLikeBase::regexp_fn(const LikeSearchState* 
state, const ColumnStr
 
     hs_database_t* database = nullptr;
     hs_scratch_t* scratch = nullptr;
-    if (hs_prepare(nullptr, re_pattern.c_str(), &database, &scratch).ok()) { 
// use hyperscan
+    auto hs_status = hs_prepare(nullptr, re_pattern.c_str(), &database, 
&scratch);
+    if (hs_status.ok()) { // use hyperscan
         auto sz = val.size();
         for (size_t i = 0; i < sz; i++) {
             const auto& str_ref = val.get_data_at(i);
@@ -468,6 +501,9 @@ Status FunctionLikeBase::regexp_fn(const LikeSearchState* 
state, const ColumnStr
         hs_free_scratch(scratch);
         hs_free_database(database);
     } else { // fallback to re2
+        if (!state->enable_hyperscan_fallback) {
+            return hs_status;
+        }
         RE2::Options opts;
         opts.set_never_nl(false);
         opts.set_dot_nl(true);
@@ -488,8 +524,19 @@ Status FunctionLikeBase::regexp_fn(const LikeSearchState* 
state, const ColumnStr
 }
 
 // hyperscan compile expression to database and allocate scratch space
+bool FunctionLikeBase::should_fallback_to_re2(std::string_view regexp) {
+    return is_hyperscan_regexp_expensive(regexp);
+}
+
 Status FunctionLikeBase::hs_prepare(FunctionContext* context, const char* 
expression,
                                     hs_database_t** database, hs_scratch_t** 
scratch) {
+    if (should_fallback_to_re2(expression)) {
+        *database = nullptr;
+        *scratch = nullptr;
+        // Callers either fall back to RE2 or return this status based on the 
session variable.
+        return Status::RuntimeError<false>(HYPERSCAN_BOUNDED_REPEAT_ERROR);
+    }
+
     hs_compile_error_t* compile_err;
     auto res = hs_compile(expression, HS_FLAG_DOTALL | HS_FLAG_ALLOWEMPTY | 
HS_FLAG_UTF8,
                           HS_MODE_BLOCK, nullptr, database, &compile_err);
@@ -498,7 +545,7 @@ Status FunctionLikeBase::hs_prepare(FunctionContext* 
context, const char* expres
         *database = nullptr;
         std::string error_message = compile_err->message;
         hs_free_compile_error(compile_err);
-        // Do not call FunctionContext::set_error here, since we do not want 
to cancel the query here.
+        // Callers either fall back to RE2 or return this status based on the 
session variable.
         return Status::RuntimeError<false>("hs_compile regex pattern error:" + 
error_message);
     }
     hs_free_compile_error(compile_err);
@@ -507,7 +554,7 @@ Status FunctionLikeBase::hs_prepare(FunctionContext* 
context, const char* expres
         hs_free_database(*database);
         *database = nullptr;
         *scratch = nullptr;
-        // Do not call FunctionContext::set_error here, since we do not want 
to cancel the query here.
+        // Callers either fall back to RE2 or return this status based on the 
session variable.
         return Status::RuntimeError<false>("hs_alloc_scratch allocate scratch 
space error");
     }
 
@@ -531,71 +578,20 @@ Status FunctionLikeBase::execute_impl(FunctionContext* 
context, Block& block,
     vec_res.resize_fill(input_rows_count);
     auto* state = reinterpret_cast<LikeState*>(
             context->get_function_state(FunctionContext::THREAD_LOCAL));
-    // for constant_substring_fn, use long run length search for performance
-    if (constant_substring_fn ==
-        *(state->function
-                  .target<doris::Status (*)(const LikeSearchState* state, 
const ColumnString&,
-                                            const StringRef&, 
ColumnUInt8::Container&)>())) {
-        RETURN_IF_ERROR(execute_substring(values->get_chars(), 
values->get_offsets(), vec_res,
-                                          &state->search_state));
+    const auto pattern_col = block.get_by_position(arguments[1]).column;
+    if (const auto* str_patterns = 
check_and_get_column<ColumnString>(pattern_col.get())) {
+        RETURN_IF_ERROR(vector_non_const(*values, *str_patterns, vec_res, 
state, input_rows_count));
+    } else if (const auto* const_patterns = 
check_and_get_column<ColumnConst>(pattern_col.get())) {
+        const auto& pattern_val = const_patterns->get_data_at(0);
+        RETURN_IF_ERROR(vector_const(*values, &pattern_val, vec_res, 
state->function,
+                                     &state->search_state));
     } else {
-        const auto pattern_col = block.get_by_position(arguments[1]).column;
-        if (const auto* str_patterns = 
check_and_get_column<ColumnString>(pattern_col.get())) {
-            RETURN_IF_ERROR(
-                    vector_non_const(*values, *str_patterns, vec_res, state, 
input_rows_count));
-        } else if (const auto* const_patterns =
-                           
check_and_get_column<ColumnConst>(pattern_col.get())) {
-            const auto& pattern_val = const_patterns->get_data_at(0);
-            RETURN_IF_ERROR(vector_const(*values, &pattern_val, vec_res, 
state->function,
-                                         &state->search_state));
-        } else {
-            return Status::InternalError("Not supported input arguments 
types");
-        }
+        return Status::InternalError("Not supported input arguments types");
     }
     block.replace_by_position(result, std::move(res));
     return Status::OK();
 }
 
-Status FunctionLikeBase::execute_substring(const ColumnString::Chars& values,
-                                           const ColumnString::Offsets& 
value_offsets,
-                                           ColumnUInt8::Container& result,
-                                           LikeSearchState* search_state) 
const {
-    // treat continuous multi string data as a long string data
-    const UInt8* begin = values.data();
-    const UInt8* end = begin + values.size();
-    const UInt8* pos = begin;
-
-    /// Current index in the array of strings.
-    size_t i = 0;
-    size_t needle_size = search_state->substring_pattern.get_pattern_length();
-
-    /// We will search for the next occurrence in all strings at once.
-    while (pos < end) {
-        // search return matched substring start offset
-        pos = (UInt8*)search_state->substring_pattern.search((char*)pos, end - 
pos);
-        if (pos >= end) {
-            break;
-        }
-
-        /// Determine which index it refers to.
-        /// begin + value_offsets[i] is the start offset of string at i+1
-        while (i < value_offsets.size() && begin + value_offsets[i] < pos) {
-            ++i;
-        }
-
-        /// We check that the entry does not pass through the boundaries of 
strings.
-        if (pos + needle_size <= begin + value_offsets[i]) {
-            result[i] = 1;
-        }
-
-        // move to next string offset
-        pos = begin + value_offsets[i];
-        ++i;
-    }
-
-    return Status::OK();
-}
-
 Status FunctionLikeBase::vector_const(const ColumnString& values, const 
StringRef* pattern_val,
                                       ColumnUInt8::Container& result, const 
LikeFn& function,
                                       LikeSearchState* search_state) const {
@@ -946,12 +942,19 @@ Status 
FunctionLike::construct_like_const_state(FunctionContext* context, const
 
         hs_database_t* database = nullptr;
         hs_scratch_t* scratch = nullptr;
-        if (try_hyperscan && hs_prepare(context, re_pattern.c_str(), 
&database, &scratch).ok()) {
+        Status hs_status;
+        if (try_hyperscan) {
+            hs_status = hs_prepare(context, re_pattern.c_str(), &database, 
&scratch);
+        }
+        if (try_hyperscan && hs_status.ok()) {
             // use hyperscan
             state->search_state.hs_database.reset(database);
             state->search_state.hs_scratch.reset(scratch);
         } else {
             // fallback to re2
+            if (try_hyperscan && 
!state->search_state.enable_hyperscan_fallback) {
+                return hs_status;
+            }
             // reset hs_database to nullptr to indicate not use hyperscan
             state->search_state.hs_database.reset();
             state->search_state.hs_scratch.reset();
@@ -978,6 +981,8 @@ Status FunctionLike::open(FunctionContext* context, 
FunctionContext::FunctionSta
     }
     std::shared_ptr<LikeState> state = std::make_shared<LikeState>();
     state->is_like_pattern = true;
+    state->search_state.enable_hyperscan_fallback =
+            context->state()->query_options().enable_hyperscan_fallback;
     state->function = like_fn;
     state->scalar_function = like_fn_scalar;
     if (context->is_col_constant(2)) {
@@ -1008,6 +1013,8 @@ Status FunctionRegexpLike::open(FunctionContext* context,
     std::shared_ptr<LikeState> state = std::make_shared<LikeState>();
     context->set_function_state(scope, state);
     state->is_like_pattern = false;
+    state->search_state.enable_hyperscan_fallback =
+            context->state()->query_options().enable_hyperscan_fallback;
     state->function = regexp_fn;
     state->scalar_function = regexp_fn_scalar;
     if (context->is_col_constant(1)) {
@@ -1039,12 +1046,16 @@ Status FunctionRegexpLike::open(FunctionContext* 
context,
         } else {
             hs_database_t* database = nullptr;
             hs_scratch_t* scratch = nullptr;
-            if (hs_prepare(context, pattern_str.c_str(), &database, 
&scratch).ok()) {
+            auto hs_status = hs_prepare(context, pattern_str.c_str(), 
&database, &scratch);
+            if (hs_status.ok()) {
                 // use hyperscan
                 state->search_state.hs_database.reset(database);
                 state->search_state.hs_scratch.reset(scratch);
             } else {
                 // fallback to re2
+                if (!state->search_state.enable_hyperscan_fallback) {
+                    return hs_status;
+                }
                 // reset hs_database to nullptr to indicate not use hyperscan
                 state->search_state.hs_database.reset();
                 state->search_state.hs_scratch.reset();
diff --git a/be/src/exprs/function/like.h b/be/src/exprs/function/like.h
index 461c97956bc..9759e7e563f 100644
--- a/be/src/exprs/function/like.h
+++ b/be/src/exprs/function/like.h
@@ -29,6 +29,7 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "common/status.h"
 #include "core/block/column_numbers.h"
@@ -182,6 +183,8 @@ struct LikeSearchState {
 
     std::string pattern_str;
 
+    bool enable_hyperscan_fallback = true;
+
     /// Used for LIKE predicates if the pattern is a constant argument, and is 
either a
     /// constant string or has a constant string at the beginning or end of 
the pattern.
     /// This will be set in order to check for that pattern in the 
corresponding part of
@@ -226,7 +229,7 @@ struct LikeSearchState {
 
     LikeSearchState() = default;
 
-    Status clone(LikeSearchState& cloned);
+    Status clone(LikeSearchState& cloned) const;
 
     void set_search_string(const std::string& search_string_arg) {
         search_string = search_string_arg;
@@ -292,6 +295,8 @@ public:
     friend struct VectorEndsWithSearchState;
 
 protected:
+    static bool should_fallback_to_re2(std::string_view regexp);
+
     Status vector_const(const ColumnString& values, const StringRef* 
pattern_val,
                         ColumnUInt8::Container& result, const LikeFn& function,
                         LikeSearchState* search_state) const;
@@ -300,10 +305,6 @@ protected:
                             ColumnUInt8::Container& result, LikeState* state,
                             size_t input_rows_count) const;
 
-    Status execute_substring(const ColumnString::Chars& values,
-                             const ColumnString::Offsets& value_offsets,
-                             ColumnUInt8::Container& result, LikeSearchState* 
search_state) const;
-
     template <bool LIKE_PATTERN>
     static VPatternSearchStateSPtr pattern_type_recognition(const 
ColumnString& patterns);
 
diff --git a/be/src/exprs/function/match.cpp b/be/src/exprs/function/match.cpp
index 677a8edb836..4f35d01ff41 100644
--- a/be/src/exprs/function/match.cpp
+++ b/be/src/exprs/function/match.cpp
@@ -25,6 +25,7 @@
 #include "storage/index/index_reader_helper.h"
 #include "storage/index/inverted/analyzer/analyzer.h"
 #include "util/debug_points.h"
+#include "util/hyperscan_util.h"
 
 namespace doris {
 #include "common/compile_check_begin.h"
@@ -508,6 +509,10 @@ Status FunctionMatchRegexp::execute_match(FunctionContext* 
context, const std::s
     hs_compile_error_t* compile_err = nullptr;
     hs_scratch_t* scratch = nullptr;
 
+    if (is_hyperscan_regexp_expensive(pattern)) {
+        return 
Status::Error<ErrorCode::INDEX_INVALID_PARAMETERS>(HYPERSCAN_BOUNDED_REPEAT_ERROR);
+    }
+
     if (hs_compile(pattern.data(), HS_FLAG_DOTALL | HS_FLAG_ALLOWEMPTY | 
HS_FLAG_UTF8,
                    HS_MODE_BLOCK, nullptr, &database, &compile_err) != 
HS_SUCCESS) {
         std::string err_message = "hyperscan compilation failed: ";
diff --git a/be/src/exprs/function/regexps.h b/be/src/exprs/function/regexps.h
index 7d3d6d22d98..0aa3d1c2267 100644
--- a/be/src/exprs/function/regexps.h
+++ b/be/src/exprs/function/regexps.h
@@ -33,6 +33,7 @@
 
 #include "common/exception.h"
 #include "core/string_ref.h"
+#include "util/hyperscan_util.h"
 
 namespace doris::multiregexps {
 
@@ -146,6 +147,9 @@ Regexps constructRegexps(const std::vector<String>& 
str_patterns,
 
     for (auto& pattern : patterns) {
         LOG(INFO) << "pattern: " << pattern << "\n";
+        if (is_hyperscan_regexp_expensive(pattern)) {
+            throw 
doris::Exception(Status::InvalidArgument(HYPERSCAN_BOUNDED_REPEAT_ERROR));
+        }
     }
 
     hs_error_t err;
diff --git a/be/src/storage/index/inverted/query/regexp_query.cpp 
b/be/src/storage/index/inverted/query/regexp_query.cpp
index 4d2e3393f08..bb35a9275a2 100644
--- a/be/src/storage/index/inverted/query/regexp_query.cpp
+++ b/be/src/storage/index/inverted/query/regexp_query.cpp
@@ -23,6 +23,7 @@
 
 #include "common/logging.h"
 #include "util/debug_points.h"
+#include "util/hyperscan_util.h"
 
 namespace doris::segment_v2 {
 #include "common/compile_check_begin.h"
@@ -40,6 +41,9 @@ void RegexpQuery::add(const InvertedIndexQueryInfo& 
query_info) {
     }
 
     const std::string& pattern = query_info.term_infos[0].get_single_term();
+    if (is_hyperscan_regexp_expensive(pattern)) {
+        throw Exception(ErrorCode::INVALID_ARGUMENT, 
HYPERSCAN_BOUNDED_REPEAT_ERROR);
+    }
     auto prefix = get_regex_prefix(pattern);
 
     hs_database_t* database = nullptr;
diff --git 
a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp 
b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
index ac6a905ba47..5ecfad0d3ae 100644
--- a/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
+++ b/be/src/storage/index/inverted/query_v2/regexp_query/regexp_weight.cpp
@@ -41,6 +41,7 @@
 #include "storage/index/inverted/query_v2/nullable_scorer.h"
 #include "storage/index/inverted/query_v2/segment_postings.h"
 #include "storage/index/inverted/util/string_helper.h"
+#include "util/hyperscan_util.h"
 
 CL_NS_USE(index)
 
@@ -70,6 +71,9 @@ ScorerPtr RegexpWeight::scorer(const QueryExecutionContext& 
context,
 
 ScorerPtr RegexpWeight::regexp_scorer(const QueryExecutionContext& context,
                                       const std::string& binding_key) {
+    if (is_hyperscan_regexp_expensive(_pattern)) {
+        throw Exception(ErrorCode::INVALID_ARGUMENT, 
HYPERSCAN_BOUNDED_REPEAT_ERROR);
+    }
     auto prefix = get_regex_prefix(_pattern);
 
     hs_database_t* database = nullptr;
diff --git a/be/src/util/hyperscan_util.cpp b/be/src/util/hyperscan_util.cpp
new file mode 100644
index 00000000000..3cec0e4e68c
--- /dev/null
+++ b/be/src/util/hyperscan_util.cpp
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "util/hyperscan_util.h"
+
+#include <re2/re2.h>
+#include <re2/stringpiece.h>
+
+#include <charconv>
+
+namespace doris {
+namespace {
+
+bool is_larger_than_fifty(std::string_view str) {
+    int number = 0;
+    auto [_, error] = std::from_chars(str.data(), str.data() + str.size(), 
number);
+    return error == std::errc() && number > 50;
+}
+
+class SlowWithHyperscanChecker {
+public:
+    SlowWithHyperscanChecker()
+            : _searcher_one_repeat(R"(\{\s*([\d]+)\s*,?\s*})"),
+              _searcher_two_repeats(R"(\{\s*([\d]+)\s*,\s*([\d]+)\s*\})") {}
+
+    bool is_slow(std::string_view regexp) const {
+        return is_slow_one_repeat(regexp) || is_slow_two_repeats(regexp);
+    }
+
+private:
+    bool is_slow_one_repeat(std::string_view regexp) const {
+        re2::StringPiece haystack(regexp.data(), regexp.size());
+        re2::StringPiece matches[2];
+        size_t start_pos = 0;
+        while (start_pos < haystack.size()) {
+            if (!_searcher_one_repeat.Match(haystack, start_pos, 
haystack.size(),
+                                            re2::RE2::Anchor::UNANCHORED, 
matches, 2)) {
+                break;
+            }
+
+            start_pos = matches[0].data() - haystack.data() + 
matches[0].size();
+            if (is_larger_than_fifty({matches[1].data(), matches[1].size()})) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool is_slow_two_repeats(std::string_view regexp) const {
+        re2::StringPiece haystack(regexp.data(), regexp.size());
+        re2::StringPiece matches[3];
+        size_t start_pos = 0;
+        while (start_pos < haystack.size()) {
+            if (!_searcher_two_repeats.Match(haystack, start_pos, 
haystack.size(),
+                                             re2::RE2::Anchor::UNANCHORED, 
matches, 3)) {
+                break;
+            }
+
+            start_pos = matches[0].data() - haystack.data() + 
matches[0].size();
+            if (is_larger_than_fifty({matches[1].data(), matches[1].size()}) ||
+                is_larger_than_fifty({matches[2].data(), matches[2].size()})) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    re2::RE2 _searcher_one_repeat;
+    re2::RE2 _searcher_two_repeats;
+};
+
+} // namespace
+
+bool is_hyperscan_regexp_expensive(std::string_view regexp) {
+    static const SlowWithHyperscanChecker slow_with_hyperscan_checker;
+    return slow_with_hyperscan_checker.is_slow(regexp);
+}
+
+} // namespace doris
diff --git a/be/src/util/hyperscan_util.h b/be/src/util/hyperscan_util.h
new file mode 100644
index 00000000000..27ef9fdfe17
--- /dev/null
+++ b/be/src/util/hyperscan_util.h
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <string_view>
+
+namespace doris {
+
+inline constexpr std::string_view HYPERSCAN_BOUNDED_REPEAT_ERROR =
+        "Skip hyperscan compilation because bounded repetition exceeds 50";
+
+bool is_hyperscan_regexp_expensive(std::string_view regexp);
+
+} // namespace doris
diff --git a/be/test/exprs/function/function_like_test.cpp 
b/be/test/exprs/function/function_like_test.cpp
index 8e484f1735f..1c0d40044a5 100644
--- a/be/test/exprs/function/function_like_test.cpp
+++ b/be/test/exprs/function/function_like_test.cpp
@@ -16,8 +16,12 @@
 // under the License.
 
 #include <cstdint>
+#include <memory>
 #include <string>
+#include <vector>
 
+#include "core/block/block.h"
+#include "core/column/column_const.h"
 #include "core/column/column_string.h"
 #include "core/column/column_vector.h"
 #include "core/data_type/data_type_nullable.h"
@@ -26,11 +30,95 @@
 #include "core/types.h"
 #include "exprs/function/function_test_util.h"
 #include "exprs/function/like.h"
+#include "exprs/function_context.h"
 #include "gtest/gtest_pred_impl.h"
+#include "runtime/runtime_state.h"
 #include "testutil/any_type.h"
 
 namespace doris {
 
+class FunctionLikeTestHelper : public FunctionLikeBase {
+public:
+    using FunctionLikeBase::should_fallback_to_re2;
+};
+
+template <typename Function>
+Status execute_pattern_with_fallback_disabled(const std::string& value, const 
std::string& pattern,
+                                              bool constant_known_at_open) {
+    TQueryOptions query_options;
+    query_options.__set_enable_hyperscan_fallback(false);
+    RuntimeState runtime_state(query_options, TQueryGlobals {});
+
+    auto string_type = std::make_shared<DataTypeString>();
+    auto context = FunctionContext::create_context(
+            &runtime_state, std::make_shared<DataTypeUInt8>(), {string_type, 
string_type});
+
+    auto values = ColumnString::create();
+    values->insert_data(value.data(), value.size());
+    auto patterns = ColumnString::create();
+    patterns->insert_data(pattern.data(), pattern.size());
+    ColumnPtr pattern_column = ColumnConst::create(std::move(patterns), 1);
+
+    std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
+    if (constant_known_at_open) {
+        constant_columns[1] = 
std::make_shared<ColumnPtrWrapper>(pattern_column);
+    }
+    context->set_constant_cols(constant_columns);
+
+    Function function;
+    RETURN_IF_ERROR(function.open(context.get(), 
FunctionContext::THREAD_LOCAL));
+
+    Block block;
+    block.insert({std::move(values), string_type, "value"});
+    block.insert({std::move(pattern_column), string_type, "pattern"});
+    block.insert({nullptr, std::make_shared<DataTypeUInt8>(), "result"});
+    return function.execute_impl(context.get(), block, {0, 1}, 2, 1);
+}
+
+template <typename Function>
+void check_constant_pattern_batch(const std::vector<std::string>& values,
+                                  const std::string& pattern,
+                                  const std::vector<uint8_t>& expected) {
+    ASSERT_EQ(values.size(), expected.size());
+
+    TQueryOptions query_options;
+    RuntimeState runtime_state(query_options, TQueryGlobals {});
+    auto string_type = std::make_shared<DataTypeString>();
+    auto result_type = std::make_shared<DataTypeUInt8>();
+    auto context = FunctionContext::create_context(&runtime_state, result_type,
+                                                   {string_type, string_type});
+
+    auto value_column = ColumnString::create();
+    for (const auto& value : values) {
+        value_column->insert_data(value.data(), value.size());
+    }
+    auto pattern_data = ColumnString::create();
+    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);
+    context->set_constant_cols(constant_columns);
+
+    Function function;
+    auto status = function.open(context.get(), FunctionContext::THREAD_LOCAL);
+    ASSERT_TRUE(status.ok()) << status.to_string();
+
+    Block block;
+    block.insert({std::move(value_column), string_type, "value"});
+    block.insert({std::move(pattern_column), string_type, "pattern"});
+    block.insert({nullptr, result_type, "result"});
+    status = function.execute_impl(context.get(), block, {0, 1}, 2, 
values.size());
+    ASSERT_TRUE(status.ok()) << status.to_string();
+
+    const auto* result = 
check_and_get_column<ColumnUInt8>(block.get_by_position(2).column.get());
+    ASSERT_NE(result, nullptr);
+    ASSERT_EQ(result->size(), expected.size());
+    for (size_t i = 0; i < expected.size(); ++i) {
+        EXPECT_EQ(result->get_element(i), expected[i]) << "row " << i;
+    }
+}
+
 TEST(FunctionLikeTest, like) {
     std::string func_name = "like";
 
@@ -210,6 +298,90 @@ TEST(FunctionLikeTest, regexp) {
     }
 }
 
+TEST(FunctionLikeTest, regexp_empty_constant_pattern) {
+    DataSet data_set = {{{std::string("abc"), std::string("")}, uint8_t(1)},
+                        {{std::string(""), std::string("")}, uint8_t(1)}};
+    InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR, Consted 
{PrimitiveType::TYPE_VARCHAR}};
+
+    for (const auto& func_name : {"regexp", "rlike"}) {
+        for (const auto& line : data_set) {
+            DataSet const_pattern_dataset = {line};
+            static_cast<void>(check_function<DataTypeUInt8, true>(func_name, 
input_types,
+                                                                  
const_pattern_dataset));
+        }
+    }
+
+    check_constant_pattern_batch<FunctionRegexpLike>({"abc", ""}, "", {1, 1});
+}
+
+TEST(FunctionLikeTest, regexp_constant_substring_boundaries) {
+    check_constant_pattern_batch<FunctionRegexpLike>({"ab", "c", "", "abc", 
"", "xabcx"}, "abc",
+                                                     {0, 0, 0, 1, 0, 1});
+}
+
+TEST(FunctionLikeTest, hyperscan_bounded_repeat_fallback) {
+    std::string func_name = "regexp";
+    std::string matching_value = "prompt_rewrite.h03" + std::string(500, 'x') 
+ "429";
+
+    DataSet data_set = {
+            {{matching_value, 
std::string(R"(prompt_rewrite\.h03.{0,1000}429)")}, uint8_t(1)},
+            {{std::string("prompt_rewrite.h03") + std::string(500, 'x') + 
"430",
+              std::string(R"(prompt_rewrite\.h03.{0,1000}429)")},
+             uint8_t(0)}};
+
+    InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR, 
PrimitiveType::TYPE_VARCHAR};
+    check_function_all_arg_comb<DataTypeUInt8, true>(func_name, input_types, 
data_set);
+}
+
+TEST(FunctionLikeTest, hyperscan_bounded_repeat_threshold) {
+    EXPECT_FALSE(FunctionLikeTestHelper::should_fallback_to_re2("a*"));
+    EXPECT_FALSE(FunctionLikeTestHelper::should_fallback_to_re2("a{50}"));
+    EXPECT_FALSE(FunctionLikeTestHelper::should_fallback_to_re2("a{0,50}"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("a{51}"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("a{51,}"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("a{0,51}"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("a{51,51}"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("a{ 0, 1000 
}"));
+    EXPECT_FALSE(FunctionLikeTestHelper::should_fallback_to_re2(R"(a\{51\})"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("[a{51}]"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("(?# 
note{51})a"));
+    EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("(?# 
[)(ab?c?d){1000,5000}"));
+    
EXPECT_TRUE(FunctionLikeTestHelper::should_fallback_to_re2("[^^](ab?c?d){1000,5000}"));
+}
+
+TEST(FunctionLikeTest, hyperscan_bounded_repeat_fallback_disabled) {
+    for (bool constant_known_at_open : {false, true}) {
+        SCOPED_TRACE(constant_known_at_open ? "prepare during open" : "prepare 
during execute");
+        auto status = 
execute_pattern_with_fallback_disabled<FunctionRegexpLike>(
+                "prompt_rewrite.h03xxx429", 
R"(prompt_rewrite\.h03.{0,1000}429)",
+                constant_known_at_open);
+        EXPECT_FALSE(status.ok());
+        EXPECT_NE(status.to_string().find("bounded repetition exceeds 50"), 
std::string::npos);
+
+        status = execute_pattern_with_fallback_disabled<FunctionRegexpLike>(
+                "^abc", "[^^](ab?c?d){1000,5000}", constant_known_at_open);
+        EXPECT_FALSE(status.ok());
+        EXPECT_NE(status.to_string().find("bounded repetition exceeds 50"), 
std::string::npos);
+
+        status = execute_pattern_with_fallback_disabled<FunctionRegexpLike>(
+                "abcd", "(?# [)(ab?c?d){1000,5000}", constant_known_at_open);
+        EXPECT_FALSE(status.ok());
+        EXPECT_NE(status.to_string().find("bounded repetition exceeds 50"), 
std::string::npos);
+    }
+}
+
+TEST(FunctionLikeTest, 
hyperscan_bounded_repeat_literal_with_fallback_disabled) {
+    for (bool constant_known_at_open : {false, true}) {
+        SCOPED_TRACE(constant_known_at_open ? "prepare during open" : "prepare 
during execute");
+        EXPECT_TRUE(execute_pattern_with_fallback_disabled<FunctionRegexpLike>(
+                            "a{51}", R"(a\{51\})", constant_known_at_open)
+                            .ok());
+        
EXPECT_TRUE(execute_pattern_with_fallback_disabled<FunctionLike>("a{51}", 
"_{51}",
+                                                                         
constant_known_at_open)
+                            .ok());
+    }
+}
+
 TEST(FunctionLikeTest, regexp_extract) {
     std::string func_name = "regexp_extract";
 
@@ -717,7 +889,7 @@ TEST(FunctionLikeTest, error_handling) {
 TEST(FunctionLikeTest, substring_optimization_performance) {
     std::string func_name = "like";
 
-    // Test cases that should trigger execute_substring optimization
+    // Test cases that should trigger the constant substring long-buffer 
optimization
     DataSet data_set = {// Multiple identical substrings in long text
                         {{std::string("aaabbbaaabbbaaabbb"), 
std::string("%bbb%")}, uint8_t(1)},
                         {{std::string("aaacccaaacccaaaccc"), 
std::string("%bbb%")}, uint8_t(0)},
diff --git a/be/test/exprs/function/function_match_test.cpp 
b/be/test/exprs/function/function_match_test.cpp
index 738381a04ef..e8969f88a82 100644
--- a/be/test/exprs/function/function_match_test.cpp
+++ b/be/test/exprs/function/function_match_test.cpp
@@ -28,6 +28,7 @@
 #include "core/column/column_string.h"
 #include "core/column/column_vector.h"
 #include "exprs/function/match.h"
+#include "runtime/runtime_state.h"
 #include "storage/index/inverted/analyzer/analyzer.h"
 
 namespace doris {
@@ -85,6 +86,26 @@ TEST(FunctionMatchTest, analyse_query_str) {
     }
 }
 
+TEST(FunctionMatchTest, regexp_rejects_expensive_bounded_repeat) {
+    TQueryOptions query_options;
+    query_options.__set_enable_match_without_inverted_index(true);
+    RuntimeState runtime_state(query_options, TQueryGlobals {});
+    auto context = FunctionContext::create_context(&runtime_state, {}, {});
+
+    auto string_col = ColumnString::create();
+    string_col->insert_data("abcd", 4);
+    ColumnUInt8::Container result(1, 0);
+
+    FunctionMatchRegexp function;
+    for (const char* pattern : {"(ab?c?d){1000,5000}", "(?# 
[)(ab?c?d){1000,5000}"}) {
+        SCOPED_TRACE(pattern);
+        Status status = function.execute_match(context.get(), "test_column", 
pattern, 1,
+                                               string_col.get(), nullptr, 
nullptr, result);
+        EXPECT_FALSE(status.ok());
+        EXPECT_NE(status.to_string().find("bounded repetition exceeds 50"), 
std::string::npos);
+    }
+}
+
 // Test FunctionMatchAny::execute_match
 TEST(FunctionMatchTest, match_any_execute) {
     FunctionMatchAny func_match_any;
diff --git a/be/test/exprs/function/function_multi_match_test.cpp 
b/be/test/exprs/function/function_multi_match_test.cpp
index d184dd5afa5..ab7e8ea5368 100644
--- a/be/test/exprs/function/function_multi_match_test.cpp
+++ b/be/test/exprs/function/function_multi_match_test.cpp
@@ -22,6 +22,7 @@
 #include "core/block/column_with_type_and_name.h"
 #include "core/block/columns_with_type_and_name.h"
 #include "core/data_type/data_type_string.h"
+#include "exprs/function/regexps.h"
 #include "storage/index/inverted/inverted_index_reader.h"
 
 namespace doris {
@@ -73,4 +74,13 @@ TEST_F(FunctionMultiMatchTest, 
EvaluateInvertedIndexWithNullIterator) {
             << "Error message should contain column name. Actual message: " << 
error_msg;
 }
 
+TEST_F(FunctionMultiMatchTest, RejectsExpensiveBoundedRepeat) {
+    for (const char* pattern : {"(ab?c?d){1000,5000}", "(?# 
[)(ab?c?d){1000,5000}"}) {
+        SCOPED_TRACE(pattern);
+        std::vector<String> patterns = {pattern};
+        EXPECT_THROW((multiregexps::constructRegexps<false, false>(patterns, 
std::nullopt)),
+                     Exception);
+    }
+}
+
 } // namespace doris
diff --git a/be/test/storage/index/inverted/query/regexp_query_test.cpp 
b/be/test/storage/index/inverted/query/regexp_query_test.cpp
index e212592d2f3..40287b9dff1 100644
--- a/be/test/storage/index/inverted/query/regexp_query_test.cpp
+++ b/be/test/storage/index/inverted/query/regexp_query_test.cpp
@@ -218,6 +218,30 @@ TEST_F(RegexpQueryTest, AddWithInvalidTermsSize) {
     }
 }
 
+TEST_F(RegexpQueryTest, AddRejectsExpensiveBoundedRepeat) {
+    std::shared_ptr<lucene::search::IndexSearcher> searcher = nullptr;
+    OlapReaderStatistics stats;
+    RuntimeState runtime_state;
+    TQueryOptions query_options;
+    query_options.inverted_index_max_expansions = 50;
+    runtime_state.set_query_options(query_options);
+    io::IOContext io_ctx;
+
+    auto context = std::make_shared<IndexQueryContext>();
+    context->io_ctx = &io_ctx;
+    context->runtime_state = &runtime_state;
+    context->stats = &stats;
+    RegexpQuery regexp_query(searcher, context);
+
+    for (const char* pattern : {"(ab?c?d){1000,5000}", "(?# 
[)(ab?c?d){1000,5000}"}) {
+        SCOPED_TRACE(pattern);
+        InvertedIndexQueryInfo query_info;
+        query_info.field_name = L"test_field";
+        query_info.term_infos.push_back({pattern, 0});
+        EXPECT_THROW(regexp_query.add(query_info), Exception);
+    }
+}
+
 TEST_F(RegexpQueryTest, AddWithInvalidPattern) {
     // Create a mock searcher and query options for testing
     std::shared_ptr<lucene::search::IndexSearcher> searcher = nullptr;
@@ -422,4 +446,4 @@ TEST_F(RegexpQueryTest, AddWithBackreferencePattern) {
     EXPECT_NO_THROW(regexp_query.add(query_info));
 }
 
-} // namespace doris::segment_v2
\ No newline at end of file
+} // namespace doris::segment_v2
diff --git a/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp 
b/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
index f84972d84e8..aea4ed627ed 100644
--- a/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
+++ b/be/test/storage/index/inverted/query_v2/regexp_query_test.cpp
@@ -132,6 +132,21 @@ TEST_F(RegexpQueryV2Test, test_regexp_query_construction) {
     ASSERT_NE(regexp_weight, nullptr);
 }
 
+TEST_F(RegexpQueryV2Test, test_rejects_expensive_bounded_repeat) {
+    auto context = std::make_shared<IndexQueryContext>();
+    context->collection_statistics = std::make_shared<CollectionStatistics>();
+    context->collection_similarity = std::make_shared<CollectionSimilarity>();
+
+    std::wstring field = StringHelper::to_wstring("content");
+    for (const char* pattern : {"(ab?c?d){1000,5000}", "(?# 
[)(ab?c?d){1000,5000}"}) {
+        SCOPED_TRACE(pattern);
+        auto query = std::make_shared<query_v2::RegexpQuery>(context, field, 
pattern);
+        auto weight = query->weight(false);
+        query_v2::QueryExecutionContext exec_ctx;
+        EXPECT_THROW(weight->scorer(exec_ctx), Exception);
+    }
+}
+
 // Test regexp query with scoring enabled
 TEST_F(RegexpQueryV2Test, test_regexp_query_with_scoring) {
     auto context = std::make_shared<IndexQueryContext>();
@@ -564,4 +579,4 @@ TEST_F(RegexpQueryV2Test, 
test_make_exact_match_wildcard_pattern) {
     _CLDECDELETE(dir);
 }
 
-} // namespace doris::segment_v2
\ No newline at end of file
+} // namespace doris::segment_v2
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 4432844120b..17517993d80 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -840,6 +840,8 @@ public class SessionVariable implements Serializable, 
Writable {
 
     public static final String SHORT_CIRCUIT_EVALUATION = 
"short_circuit_evaluation";
 
+    public static final String ENABLE_HYPERSCAN_FALLBACK = 
"enable_hyperscan_fallback";
+
     public static final String 
CLOUD_PARTITIONS_TABLE_USE_CACHED_VISIBLE_VERSION =
             "cloud_partitions_table_use_cached_visible_version";
 
@@ -3770,6 +3772,10 @@ public class SessionVariable implements Serializable, 
Writable {
                     "Enable extended regular expressions, support look-around 
zero-width assertions"})
     public boolean enableExtendedRegex = false;
 
+    @VariableMgr.VarAttr(name = ENABLE_HYPERSCAN_FALLBACK, needForward = true, 
affectQueryResultInExecution = true,
+            description = "Whether to fall back to RE2 when Hyperscan cannot 
compile a regular expression")
+    public boolean enableHyperscanFallback = true;
+
     @VariableMgr.VarAttr(
             name = DEFAULT_VARIANT_SPARSE_HASH_SHARD_COUNT,
             needForward = true,
@@ -5918,7 +5924,7 @@ public class SessionVariable implements Serializable, 
Writable {
         
tResult.setAnnIndexCandidateRowsPercentThreshold(annIndexCandidateRowsPercentThreshold);
         tResult.setMergeReadSliceSize(mergeReadSliceSizeBytes);
         tResult.setEnableExtendedRegex(enableExtendedRegex);
-
+        tResult.setEnableHyperscanFallback(enableHyperscanFallback);
         if (fileCacheQueryLimitPercent > 0) {
             
tResult.setFileCacheQueryLimitPercent(Math.min(fileCacheQueryLimitPercent,
                     Config.file_cache_query_limit_max_percent));
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
index 1e47c3a4c72..eae89958c8c 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/SessionVariablesTest.java
@@ -349,4 +349,15 @@ public class SessionVariablesTest extends 
TestWithFeService {
         Assertions.assertTrue(queryOptions.isSetFileCacheQueryLimitBytes());
         Assertions.assertEquals(262144L, 
queryOptions.getFileCacheQueryLimitBytes());
     }
+
+    @Test
+    public void testHyperscanFallbackPropagatesToBackends() throws Exception {
+        SessionVariable variable = new SessionVariable();
+        Assertions.assertTrue(variable.toThrift().isEnableHyperscanFallback());
+
+        VariableMgr.setVar(variable, new SetVar(SetType.SESSION,
+                SessionVariable.ENABLE_HYPERSCAN_FALLBACK, new 
StringLiteral("false")));
+
+        
Assertions.assertFalse(variable.toThrift().isEnableHyperscanFallback());
+    }
 }
diff --git a/gensrc/thrift/PaloInternalService.thrift 
b/gensrc/thrift/PaloInternalService.thrift
index 6c298939158..9e6d34c7c39 100644
--- a/gensrc/thrift/PaloInternalService.thrift
+++ b/gensrc/thrift/PaloInternalService.thrift
@@ -508,6 +508,9 @@ struct TQueryOptions {
 
   227: optional i64 file_presigned_url_ttl_seconds = 3600;
 
+  // Fall back to RE2 when Hyperscan cannot compile a regular expression.
+  228: optional bool enable_hyperscan_fallback = true;
+
   // For cloud, to control if the content would be written into file cache
   // In write path, to control if the content would be written into file cache.
   // In read path, read from file cache or remote storage when execute query.
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function_regexp.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function_regexp.out
index d7422abb0e7..5b21c2a1bcc 100644
--- 
a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function_regexp.out
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function_regexp.out
@@ -301,6 +301,40 @@ false
 -- !regexp_fn_9 --
 true
 
+-- !regexp_empty_pattern --
+1      true    true
+2      true    true
+3      true    true
+4      \N      \N
+5      true    \N
+6      true    true
+7      true    true
+8      true    true
+9      true    true
+10     true    true
+11     true    true
+
+-- !rlike_empty_pattern --
+1      true    true
+2      true    true
+3      true    true
+4      \N      \N
+5      true    \N
+6      true    true
+7      true    true
+8      true    true
+9      true    true
+10     true    true
+11     true    true
+
+-- !regexp_constant_substring_boundaries --
+6      false   false
+7      false   false
+8      false   false
+9      true    true
+10     false   false
+11     true    true
+
 -- !sql_utf1 --
 true
 
@@ -454,4 +488,3 @@ Ben
 
 -- !sql_field5 --
 2
-
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function_regexp.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function_regexp.groovy
index 3a219a2e619..c5163875b58 100644
--- 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function_regexp.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function_regexp.groovy
@@ -151,6 +151,49 @@ suite("test_string_function_regexp") {
     qt_regexp_fn_9 'SELECT regexp(\'Hello\', \'(?i)hello\');'
     sql "set enable_extended_regex = false;"
 
+    sql "DROP TABLE IF EXISTS test_regexp_empty_pattern"
+    sql """
+        CREATE TABLE test_regexp_empty_pattern (
+            id INT,
+            value_col STRING NULL,
+            pattern_col STRING NULL
+        )
+        DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES("replication_num" = "1")
+    """
+    sql """
+        INSERT INTO test_regexp_empty_pattern VALUES
+            (1, 'abc', ''),
+            (2, '', ''),
+            (3, 'xyz', ''),
+            (4, NULL, ''),
+            (5, 'abc', NULL),
+            (6, 'ab', ''),
+            (7, 'c', ''),
+            (8, '', ''),
+            (9, 'abc', ''),
+            (10, '', ''),
+            (11, 'xabcx', '')
+    """
+
+    qt_regexp_empty_pattern """
+        SELECT id, regexp(value_col, ''), regexp(value_col, pattern_col)
+        FROM test_regexp_empty_pattern
+        ORDER BY id
+    """
+    qt_rlike_empty_pattern """
+        SELECT id, value_col RLIKE '', value_col RLIKE pattern_col
+        FROM test_regexp_empty_pattern
+        ORDER BY id
+    """
+    qt_regexp_constant_substring_boundaries """
+        SELECT id, regexp(value_col, 'abc'), value_col RLIKE 'abc'
+        FROM test_regexp_empty_pattern
+        WHERE id >= 6
+        ORDER BY id
+    """
+
     qt_sql_utf1 """ select '皖12345' REGEXP '^[皖][0-9]{5}\$'; """
     qt_sql_utf2 """ select '皖 12345' REGEXP '^[皖] [0-9]{5}\$'; """
 


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

Reply via email to