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 0babc422d4f branch-4.1: [fix](be) Validate sequence pattern event 
numbers #64930 (#65038)
0babc422d4f is described below

commit 0babc422d4f5f5379a4cd9070957c22e84b246fb
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jul 1 11:33:44 2026 +0800

    branch-4.1: [fix](be) Validate sequence pattern event numbers #64930 
(#65038)
    
    Cherry-picked from #64930
    
    Co-authored-by: HappenLee <[email protected]>
---
 .../aggregate/aggregate_function_sequence_match.h  | 10 ++--
 .../exprs/aggregate/vec_sequence_match_test.cpp    | 63 ++++++++++++++++++++++
 .../functions/agg/SequenceFunction.java            | 19 +++++++
 .../nereids_syntax_p0/test_nereids_function.groovy | 27 ++++++++++
 4 files changed, 115 insertions(+), 4 deletions(-)

diff --git a/be/src/exprs/aggregate/aggregate_function_sequence_match.h 
b/be/src/exprs/aggregate/aggregate_function_sequence_match.h
index bb308159328..3f07cedf3a3 100644
--- a/be/src/exprs/aggregate/aggregate_function_sequence_match.h
+++ b/be/src/exprs/aggregate/aggregate_function_sequence_match.h
@@ -216,6 +216,7 @@ private:
         const char* pos = pattern.data();
         const char* begin = pos;
         const char* end = pos + pattern.size();
+        const size_t event_count = arg_count - 2;
 
         // Pattern is checked in fe, so pattern should be valid here, we check 
it and if pattern is invalid, we return.
         auto fail_parse = [&]() {
@@ -299,17 +300,18 @@ private:
                         return;
                     }
 
-                    if (event_number > arg_count - 1) {
+                    if (event_number == 0 || event_number > event_count) {
                         throw_exception("Event number " + 
std::to_string(event_number) +
                                         " is out of range");
                         return;
                     }
 
-                    actions.emplace_back(PatternActionType::SpecificEvent, 
event_number - 1);
+                    const auto event_index = event_number - 1;
+                    actions.emplace_back(PatternActionType::SpecificEvent, 
event_index);
                     dfa_states.back().transition = 
DFATransition::SpecificEvent;
-                    dfa_states.back().event = 
static_cast<uint32_t>(event_number - 1);
+                    dfa_states.back().event = 
static_cast<uint32_t>(event_index);
                     dfa_states.emplace_back();
-                    conditions_in_pattern.set(event_number - 1);
+                    conditions_in_pattern.set(event_index);
                 }
 
                 if (!match(")")) {
diff --git a/be/test/exprs/aggregate/vec_sequence_match_test.cpp 
b/be/test/exprs/aggregate/vec_sequence_match_test.cpp
index 7ce30e7eb1c..3518a3e06bb 100644
--- a/be/test/exprs/aggregate/vec_sequence_match_test.cpp
+++ b/be/test/exprs/aggregate/vec_sequence_match_test.cpp
@@ -19,6 +19,7 @@
 #include <gtest/gtest-test-part.h>
 
 #include <memory>
+#include <vector>
 
 #include "core/column/column_string.h"
 #include "core/column/column_vector.h"
@@ -29,6 +30,7 @@
 #include "core/types.h"
 #include "core/value/vdatetime_value.h"
 #include "exprs/aggregate/aggregate_function.h"
+#include "exprs/aggregate/aggregate_function_sequence_match.h"
 #include "exprs/aggregate/aggregate_function_simple_factory.h"
 #include "gtest/gtest_pred_impl.h"
 
@@ -61,6 +63,39 @@ public:
 
     void TearDown() {}
 
+    template <typename ResultColumn>
+    void assert_invalid_pattern_returns_zero(const AggregateFunctionPtr& 
agg_function,
+                                             const std::string& pattern, 
size_t event_count) {
+        auto column_pattern = ColumnString::create();
+        column_pattern->insert(Field::create_field<TYPE_STRING>(pattern));
+
+        auto column_timestamp = ColumnDateTimeV2::create();
+        VecDateTimeValue time_value;
+        time_value.unchecked_set_time(2024, 1, 1, 0, 0, 0);
+        column_timestamp->insert_data((char*)&time_value, 0);
+
+        std::vector<ColumnUInt8::MutablePtr> event_columns;
+        event_columns.reserve(event_count);
+        std::vector<const IColumn*> columns = {column_pattern.get(), 
column_timestamp.get()};
+        columns.reserve(event_count + 2);
+        for (size_t i = 0; i < event_count; ++i) {
+            auto column_event = ColumnUInt8::create();
+            column_event->insert(Field::create_field<TYPE_BOOLEAN>(1));
+            columns.push_back(column_event.get());
+            event_columns.push_back(std::move(column_event));
+        }
+
+        std::unique_ptr<char[]> memory(new char[agg_function->size_of_data()]);
+        AggregateDataPtr place = memory.get();
+        agg_function->create(place);
+        EXPECT_NO_THROW(agg_function->add(place, columns.data(), 0, arena));
+
+        ResultColumn column_result;
+        agg_function->insert_result_into(place, column_result);
+        EXPECT_EQ(column_result.get_data()[0], 0);
+        agg_function->destroy(place);
+    }
+
     Arena arena;
 };
 
@@ -122,6 +157,34 @@ TEST_F(VSequenceMatchTest, testCountEmpty) {
     agg_function_sequence_count->destroy(place2);
 }
 
+TEST_F(VSequenceMatchTest, testMatchInvalidEventNumber) {
+    
assert_invalid_pattern_returns_zero<ColumnUInt8>(agg_function_sequence_match, 
"(?0)", 3);
+
+    AggregateFunctionSimpleFactory factory = 
AggregateFunctionSimpleFactory::instance();
+    DataTypes data_types = {std::make_shared<DataTypeString>(),
+                            std::make_shared<DataTypeDateTimeV2>()};
+    for (size_t i = 0; i < MAX_EVENTS; ++i) {
+        data_types.push_back(std::make_shared<DataTypeUInt8>());
+    }
+    auto agg_function = factory.get("sequence_match", data_types, nullptr, 
false, -1);
+    ASSERT_NE(agg_function, nullptr);
+    assert_invalid_pattern_returns_zero<ColumnUInt8>(agg_function, "(?33)", 
MAX_EVENTS);
+}
+
+TEST_F(VSequenceMatchTest, testCountInvalidEventNumber) {
+    
assert_invalid_pattern_returns_zero<ColumnInt64>(agg_function_sequence_count, 
"(?0)", 3);
+
+    AggregateFunctionSimpleFactory factory = 
AggregateFunctionSimpleFactory::instance();
+    DataTypes data_types = {std::make_shared<DataTypeString>(),
+                            std::make_shared<DataTypeDateTimeV2>()};
+    for (size_t i = 0; i < MAX_EVENTS; ++i) {
+        data_types.push_back(std::make_shared<DataTypeUInt8>());
+    }
+    auto agg_function = factory.get("sequence_count", data_types, nullptr, 
false, -1);
+    ASSERT_NE(agg_function, nullptr);
+    assert_invalid_pattern_returns_zero<ColumnInt64>(agg_function, "(?33)", 
MAX_EVENTS);
+}
+
 TEST_F(VSequenceMatchTest, testMatchSerialize) {
     const int NUM_CONDS = 4;
     auto column_pattern = ColumnString::create();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
index 6bbf4e6e992..3f3b4e53906 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/SequenceFunction.java
@@ -23,8 +23,13 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.FunctionTrait;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /** SequenceFunction */
 public interface SequenceFunction extends FunctionTrait {
+    Pattern EVENT_PATTERN = Pattern.compile("\\(\\?(\\d+)\\)");
+
     @Override
     default void checkLegalityBeforeTypeCoercion() {
         String functionName = getName();
@@ -42,6 +47,20 @@ public interface SequenceFunction extends FunctionTrait {
         if (!FunctionCallExpr.parsePattern(pattern)) {
             throw new AnalysisException("The format of pattern params is 
wrong: " + this.toSql());
         }
+        int eventCount = arity() - 2;
+        Matcher matcher = EVENT_PATTERN.matcher(pattern);
+        while (matcher.find()) {
+            long eventNumber;
+            try {
+                eventNumber = Long.parseLong(matcher.group(1));
+            } catch (NumberFormatException e) {
+                throw new AnalysisException("Event number " + matcher.group(1) 
+ " is out of range");
+            }
+            if (eventNumber == 0 || eventNumber > eventCount) {
+                throw new AnalysisException("Event number " + eventNumber
+                        + " is out of range, valid range is [1, " + eventCount 
+ "]");
+            }
+        }
 
         for (int i = 2; i < arity(); i++) {
             if (!getArgumentType(i).isBooleanType()) {
diff --git 
a/regression-test/suites/nereids_syntax_p0/test_nereids_function.groovy 
b/regression-test/suites/nereids_syntax_p0/test_nereids_function.groovy
index 4bfb3839dc0..4d2fe2a8f32 100644
--- a/regression-test/suites/nereids_syntax_p0/test_nereids_function.groovy
+++ b/regression-test/suites/nereids_syntax_p0/test_nereids_function.groovy
@@ -55,4 +55,31 @@ suite("test_nereids_function") {
         SELECT sequence_match('(?1)(?t>3600)(?2)', date, number = 1, number = 
7) FROM sequence_match_test1;
     """
 
+    test {
+        sql """
+            SELECT sequence_match('(?0)', date, number = 1, number = 7) FROM 
sequence_match_test1;
+        """
+        exception "Event number 0 is out of range"
+    }
+
+    test {
+        sql """
+            SELECT sequence_count('(?0)', date, number = 1, number = 7) FROM 
sequence_match_test1;
+        """
+        exception "Event number 0 is out of range"
+    }
+
+    test {
+        sql """
+            SELECT sequence_match('(?3)', date, number = 1, number = 7) FROM 
sequence_match_test1;
+        """
+        exception "Event number 3 is out of range"
+    }
+
+    test {
+        sql """
+            SELECT sequence_count('(?3)', date, number = 1, number = 7) FROM 
sequence_match_test1;
+        """
+        exception "Event number 3 is out of range"
+    }
 }


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

Reply via email to