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 1705f1ad8c2 branch-4.1: [fix](exec) Retain sliding window rows during 
eviction #67274 (#67329)
1705f1ad8c2 is described below

commit 1705f1ad8c23ae615662aca904c40c0f6f9a2e65
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sun Sep 6 05:57:16 2026 +0800

    branch-4.1: [fix](exec) Retain sliding window rows during eviction #67274 
(#67329)
    
    Cherry-picked from #67274
    
    Co-authored-by: Mryange <[email protected]>
---
 be/src/exec/operator/analytic_sink_operator.cpp    | 14 +++++
 be/src/exec/operator/analytic_sink_operator.h      |  9 +++-
 .../exec/operator/analytic_sink_operator_test.cpp  | 60 ++++++++++++++++++++--
 3 files changed, 76 insertions(+), 7 deletions(-)

diff --git a/be/src/exec/operator/analytic_sink_operator.cpp 
b/be/src/exec/operator/analytic_sink_operator.cpp
index 133b4ec9e60..5f9ba79efab 100644
--- a/be/src/exec/operator/analytic_sink_operator.cpp
+++ b/be/src/exec/operator/analytic_sink_operator.cpp
@@ -65,8 +65,10 @@ Status AnalyticSinkLocalState::init(RuntimeState* state, 
LocalSinkStateInfo& inf
     } else {
         if (!p._has_window_start) {
             _executor.get_next_impl = 
&AnalyticSinkLocalState::_get_next_for_unbounded_rows;
+            _rows_window_type = RowsWindowType::UNBOUNDED_START;
         } else {
             _executor.get_next_impl = 
&AnalyticSinkLocalState::_get_next_for_sliding_rows;
+            _rows_window_type = RowsWindowType::SLIDING;
         }
         _streaming_mode = true;
         _support_incremental_calculate = (p._has_window_start && 
p._has_window_end);
@@ -845,6 +847,18 @@ void AnalyticSinkLocalState::_remove_unused_rows() {
         if (idx < 0 || _input_block_first_row_positions[idx] <= 
unused_rows_pos) {
             return;
         }
+        if (_rows_window_type != RowsWindowType::NONE) {
+            // Sliding frames need the outgoing row; unbounded-start frames 
need the next unread row.
+            const int64_t earliest_required_row =
+                    _rows_window_type == RowsWindowType::SLIDING
+                            ? std::max(_partition_by_pose.start,
+                                       _current_row_position + 
_rows_start_offset - 1)
+                            : std::max(_partition_by_pose.start,
+                                       _current_row_position + 
_rows_end_offset);
+            if (_have_removed_rows + earliest_required_row < unused_rows_pos) {
+                return;
+            }
+        }
     } else {
         if (_have_removed_rows + _partition_by_pose.start <= unused_rows_pos) {
             return;
diff --git a/be/src/exec/operator/analytic_sink_operator.h 
b/be/src/exec/operator/analytic_sink_operator.h
index a7466761e18..af37bae331c 100644
--- a/be/src/exec/operator/analytic_sink_operator.h
+++ b/be/src/exec/operator/analytic_sink_operator.h
@@ -20,6 +20,8 @@
 
 #include <stdint.h>
 
+#include <algorithm>
+
 #include "exec/operator/operator.h"
 #include "exec/pipeline/dependency.h"
 
@@ -32,8 +34,8 @@ struct BoundaryPose {
     int64_t end = 0;
     bool is_ended = false;
     void remove_unused_rows(int64_t cnt) {
-        start -= cnt;
-        end -= cnt;
+        start = std::max<int64_t>(0, start - cnt);
+        end = std::max<int64_t>(0, end - cnt);
     }
 };
 
@@ -73,6 +75,8 @@ public:
     Status close(RuntimeState* state, Status exec_status) override;
 
 private:
+    enum class RowsWindowType { NONE, UNBOUNDED_START, SLIDING };
+
     friend class AnalyticSinkOperatorX;
     Status _execute_impl(RuntimeState* state);
     // over(partition by k1 order by k2 range|rows unbounded preceding and 
unbounded following)
@@ -145,6 +149,7 @@ private:
     std::vector<uint8_t> _use_null_result;
     std::vector<uint8_t> _could_use_previous_result;
     bool _streaming_mode = false;
+    RowsWindowType _rows_window_type = RowsWindowType::NONE;
     bool _support_incremental_calculate = true;
     bool _need_more_data = false;
     int64_t _current_row_position = 0;
diff --git a/be/test/exec/operator/analytic_sink_operator_test.cpp 
b/be/test/exec/operator/analytic_sink_operator_test.cpp
index b5e5787e8a6..ec3934540c6 100644
--- a/be/test/exec/operator/analytic_sink_operator_test.cpp
+++ b/be/test/exec/operator/analytic_sink_operator_test.cpp
@@ -83,6 +83,15 @@ private:
 
 } // namespace
 
+TEST(BoundaryPoseTest, RemoveUnusedRowsKeepsPhysicalCoordinatesNonNegative) {
+    BoundaryPose pose {.start = 1, .end = 5};
+
+    pose.remove_unused_rows(2);
+
+    EXPECT_EQ(pose.start, 0);
+    EXPECT_EQ(pose.end, 3);
+}
+
 struct AnalyticSinkOperatorTest : public ::testing::Test {
     void Initialize(int batch_size) {
         sink = std::make_unique<AnalyticSinkOperatorX>(&pool);
@@ -441,7 +450,48 @@ TEST_F(AnalyticSinkOperatorTest, AggFunction3) {
     std::cout << "######### AggFunction with row_number test end #########" << 
std::endl;
 }
 
-TEST_F(AnalyticSinkOperatorTest, AggFunction4) {
+TEST_F(AnalyticSinkOperatorTest, 
UnboundedRowsRetainsNextUnreadRowDuringEviction) {
+    constexpr int batch_size = 2;
+    Initialize(batch_size);
+    create_operator(true, 1, "sum", {std::make_shared<DataTypeInt64>()},
+                    std::make_shared<DataTypeInt64>());
+    sink->_agg_expr_ctxs.resize(1);
+    sink->_agg_expr_ctxs[0] =
+            MockSlotRef::create_mock_contexts(0, 
std::make_shared<DataTypeInt64>());
+    TAnalyticWindow window;
+    window.type = TAnalyticWindowType::ROWS;
+    TAnalyticWindowBoundary window_end;
+    window_end.type = TAnalyticWindowBoundaryType::PRECEDING;
+    window_end.__set_rows_offset_value(5);
+    window.__set_window_end(window_end);
+    create_window_type(false, true, window);
+    create_local_state();
+
+    for (int row = 1; row <= 8; row += batch_size) {
+        Block block = ColumnHelper::create_block<DataTypeInt64>({row, row + 
1});
+        auto status = sink->sink(state.get(), &block, row + batch_size > 8);
+        EXPECT_TRUE(status.ok()) << status.msg();
+    }
+
+    const std::vector<int64_t> expected_sums {0, 0, 0, 0, 0, 1, 3, 6};
+    for (int row = 1; row <= 8; row += batch_size) {
+        Block block = ColumnHelper::create_block<DataTypeInt64>({});
+        bool eos = false;
+        auto status = source->get_block(state.get(), &block, &eos);
+        EXPECT_TRUE(status.ok()) << status.msg();
+        EXPECT_TRUE(ColumnHelper::block_equal(
+                block, ColumnHelper::create_block<DataTypeInt64>(
+                               {row, row + 1}, {expected_sums[row - 1], 
expected_sums[row]})));
+    }
+
+    Block block = ColumnHelper::create_block<DataTypeInt64>({});
+    bool eos = false;
+    auto status = source->get_block(state.get(), &block, &eos);
+    EXPECT_TRUE(status.ok()) << status.msg();
+    EXPECT_TRUE(eos);
+}
+
+TEST_F(AnalyticSinkOperatorTest, 
SlidingRowsSumRetainsOutgoingRowDuringEviction) {
     int batch_size = 2;
     Initialize(batch_size);
     create_operator(true, 1, "sum", {std::make_shared<DataTypeInt64>()},
@@ -453,14 +503,14 @@ TEST_F(AnalyticSinkOperatorTest, AggFunction4) {
     temp_window.type = TAnalyticWindowType::ROWS;
     TAnalyticWindowBoundary window_start;
     window_start.type = TAnalyticWindowBoundaryType::PRECEDING;
-    window_start.__set_rows_offset_value(1);
+    window_start.__set_rows_offset_value(4);
     temp_window.__set_window_start(window_start);
     TAnalyticWindowBoundary window_end;
     window_end.type = TAnalyticWindowBoundaryType::CURRENT_ROW;
     temp_window.__set_window_end(window_end);
     create_window_type(true, true, temp_window);
     create_local_state();
-    // test with row_number agg function and has window: 
_get_next_for_unbounded_rows
+    // The frame is wider than one buffered block, so eviction must retain its 
outgoing row.
 
     auto sink_data = [&](int row_count, bool eos) {
         std::vector<int64_t> data_vals;
@@ -504,7 +554,7 @@ TEST_F(AnalyticSinkOperatorTest, AggFunction4) {
     {
         int row_count = 0;
         std::vector<int64_t> data_vals {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
-        std::vector<int64_t> expect_vals {0, 1, 3, 5, 7, 9, 11, 13, 15, 17}; 
//sum
+        std::vector<int64_t> expect_vals {0, 1, 3, 6, 10, 15, 20, 25, 30, 35}; 
//sum
         for (int i = 0; i < 5; i++) {
             compare_block_result(row_count, data_vals, expect_vals);
             row_count += batch_size;
@@ -516,7 +566,7 @@ TEST_F(AnalyticSinkOperatorTest, AggFunction4) {
         EXPECT_EQ(block2.rows(), 0);
         EXPECT_TRUE(eos2);
     }
-    std::cout << "######### AggFunction with row_number test end #########" << 
std::endl;
+    std::cout << "######### sliding rows sum eviction test end #########" << 
std::endl;
 }
 
 TEST_F(AnalyticSinkOperatorTest, AggFunction5) {


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

Reply via email to