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]