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 86eb9ca1ab0 branch-4.1: [fix](function) Keep the leading NULLs of 
every row in array_cum_sum #68554 (#68605)
86eb9ca1ab0 is described below

commit 86eb9ca1ab073bd513fcc336f0b62450452b8f0f
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Sep 29 17:19:07 2026 +0800

    branch-4.1: [fix](function) Keep the leading NULLs of every row in 
array_cum_sum #68554 (#68605)
    
    Cherry-picked from #68554
    
    Co-authored-by: Chenyang Sun <[email protected]>
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../function/array/function_array_cum_sum.cpp      |  14 +--
 .../exprs/function/function_array_cum_sum_test.cpp | 100 +++++++++++++++++++++
 .../test_array_cum_sum_leading_null.out            |  14 +++
 .../test_array_cum_sum_leading_null.groovy         |  51 +++++++++++
 4 files changed, 174 insertions(+), 5 deletions(-)

diff --git a/be/src/exprs/function/array/function_array_cum_sum.cpp 
b/be/src/exprs/function/array/function_array_cum_sum.cpp
index d23d54f0e03..653ebfc09ac 100644
--- a/be/src/exprs/function/array/function_array_cum_sum.cpp
+++ b/be/src/exprs/function/array/function_array_cum_sum.cpp
@@ -253,11 +253,15 @@ private:
 
             // handle null value in res_datas for first null value
             auto res_null_map_col = ColumnUInt8::create(size, 0);
-            size_t first_not_null_pos =
-                    VectorizedUtils::find_first_valid_simd(src_null_map, 0, 
size);
-            VLOG_DEBUG << "first_not_null_pos: " << 
std::to_string(first_not_null_pos);
-            
VectorizedUtils::range_set_nullmap_to_true_simd(res_null_map_col->get_data(), 0,
-                                                            
first_not_null_pos);
+            size_t prev_offset = 0;
+            for (auto cur_offset : src_offsets) {
+                // The NULLs before the first non-NULL element of each array 
stay NULL.
+                size_t first_not_null_pos = 
VectorizedUtils::find_first_valid_simd(
+                        src_null_map, prev_offset, cur_offset);
+                
VectorizedUtils::range_set_nullmap_to_true_simd(res_null_map_col->get_data(),
+                                                                prev_offset, 
first_not_null_pos);
+                prev_offset = cur_offset;
+            }
 
             res_nested_ptr = 
ColumnNullable::create(std::move(res_nested_mut_ptr),
                                                     
std::move(res_null_map_col));
diff --git a/be/test/exprs/function/function_array_cum_sum_test.cpp 
b/be/test/exprs/function/function_array_cum_sum_test.cpp
new file mode 100644
index 00000000000..d990528d93e
--- /dev/null
+++ b/be/test/exprs/function/function_array_cum_sum_test.cpp
@@ -0,0 +1,100 @@
+// 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 <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "core/block/block.h"
+#include "core/data_type/data_type_array.h"
+#include "core/data_type/data_type_nullable.h"
+#include "core/data_type/data_type_number.h"
+#include "exprs/function/function_test_util.h"
+#include "exprs/function/simple_function_factory.h"
+
+namespace doris {
+
+// Runs array_cum_sum on one block with one row per element of arrays, and 
returns each result
+// row as a string.
+static std::vector<std::string> run_array_cum_sum(const 
std::vector<TestArray>& arrays) {
+    auto array_type =
+            
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeInt32>()));
+    auto return_type =
+            
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeInt64>()));
+    const size_t row_size = arrays.size();
+    // Empty rows make a failed call fail the checks instead of reading past 
the end.
+    std::vector<std::string> results(row_size);
+
+    MutableColumnPtr array_column = array_type->create_column();
+    for (const auto& array : arrays) {
+        EXPECT_TRUE(insert_cell(array_column, array_type, array));
+    }
+    Block block;
+    block.insert({std::move(array_column), array_type, "array"});
+
+    DataTypePtr result_type = return_type;
+    FunctionBasePtr func = SimpleFunctionFactory::instance().get_function(
+            "array_cum_sum", block.get_columns_with_type_and_name(), 
result_type);
+    EXPECT_NE(func, nullptr);
+
+    FunctionUtils fn_utils(result_type, {array_type}, false);
+    auto* fn_ctx = fn_utils.get_fn_ctx();
+    fn_ctx->set_constant_cols({nullptr});
+    EXPECT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
+    EXPECT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
+
+    block.insert({nullptr, result_type, "result"});
+    auto result_idx = block.columns() - 1;
+    auto st = func->execute(fn_ctx, block, {0}, result_idx, row_size);
+    EXPECT_TRUE(st.ok()) << st;
+    static_cast<void>(func->close(fn_ctx, FunctionContext::THREAD_LOCAL));
+    static_cast<void>(func->close(fn_ctx, FunctionContext::FRAGMENT_LOCAL));
+    if (!st.ok()) {
+        return results;
+    }
+
+    const auto& result_column = *block.get_by_position(result_idx).column;
+    for (size_t i = 0; i < row_size; ++i) {
+        results[i] = result_type->to_string(result_column, i);
+    }
+    return results;
+}
+
+// The NULLs before the first non-NULL element of each array stay NULL, and a 
later NULL keeps the
+// running sum. This holds for every row of a block, so each row gives the 
same result in a block
+// with other rows as alone.
+TEST(function_array_cum_sum_test, leading_null_per_row) {
+    const std::vector<TestArray> arrays = {
+            {Int32(1), Int32(2)},
+            {Null(), Int32(2)},
+            {Null(), Null(), Int32(3)},
+            {Int32(5), Null(), Int32(1)},
+            {Null(), Null()},
+            {},
+            {Null(), Int32(1), Null(), Int32(2), Int32(3)},
+    };
+    auto results = run_array_cum_sum(arrays);
+    for (size_t i = 0; i < arrays.size(); ++i) {
+        EXPECT_EQ(results[i], run_array_cum_sum({arrays[i]})[0]) << "row " << 
i;
+    }
+    EXPECT_EQ(results[1], "[null, 2]");
+    EXPECT_EQ(results[6], "[null, 1, 1, 3, 6]");
+}
+
+} // namespace doris
diff --git 
a/regression-test/data/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.out
 
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.out
new file mode 100644
index 00000000000..a7419dbc53e
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.out
@@ -0,0 +1,14 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !column --
+1      [1, 2]  [1, 3]
+2      [null, 2]       [null, 2]
+3      [null, null, 3] [null, null, 3]
+4      [5, null, 1]    [5, 5, 6]
+5      [null, null]    [null, null]
+6      [null, 1, null, 2, 3]   [null, 1, 1, 3, 6]
+7      []      []
+8      \N      \N
+
+-- !literal --
+[1, 3] [null, 2]       [null, null, 3] [5, 5, 6]       [null, null]    [null, 
1, 1, 3, 6]
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.groovy
 
b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.groovy
new file mode 100644
index 00000000000..36e5c5596e6
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/array_functions/test_array_cum_sum_leading_null.groovy
@@ -0,0 +1,51 @@
+// 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.
+
+suite("test_array_cum_sum_leading_null") {
+    sql "DROP TABLE IF EXISTS test_array_cum_sum_leading_null"
+    sql """
+        CREATE TABLE test_array_cum_sum_leading_null (
+            id INT,
+            a ARRAY<INT> NULL
+        ) DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_num" = "1")
+    """
+    // The rows are loaded together and read in one block, so every row after 
the first one checks
+    // that the NULLs at the start of its own array stay NULL.
+    sql """
+        INSERT INTO test_array_cum_sum_leading_null VALUES
+            (1, [1, 2]),
+            (2, [NULL, 2]),
+            (3, [NULL, NULL, 3]),
+            (4, [5, NULL, 1]),
+            (5, [NULL, NULL]),
+            (6, [NULL, 1, NULL, 2, 3]),
+            (7, []),
+            (8, NULL)
+    """
+
+    // The NULLs before the first non-NULL element of each array stay NULL, 
and a later NULL keeps
+    // the running sum.
+    order_qt_column "SELECT id, a, array_cum_sum(a) FROM 
test_array_cum_sum_leading_null"
+    // Each row gives the same result as the same array written as a literal.
+    order_qt_literal """
+        SELECT array_cum_sum([1, 2]), array_cum_sum([NULL, 2]), 
array_cum_sum([NULL, NULL, 3]),
+               array_cum_sum([5, NULL, 1]), array_cum_sum([NULL, NULL]),
+               array_cum_sum([NULL, 1, NULL, 2, 3])
+    """
+}


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

Reply via email to