github-actions[bot] commented on code in PR #68022:
URL: https://github.com/apache/doris/pull/68022#discussion_r4015811539


##########
be/src/exprs/lambda_function/varray_map_function.cpp:
##########
@@ -144,35 +144,90 @@ class ArrayMapFunction : public LambdaFunction {
 
             // here is the array column
             const auto& col_array = assert_cast<const 
ColumnArray&>(*column_array);
+            lambda_offsets[i] = col_array.get_offsets_ptr();
 
             if (i == 0) {
                 nested_array_column_rows = col_array.get_data_ptr()->size();
-                first_array_offsets = col_array.get_offsets_ptr();
                 const auto& off_data = col_array.get_offsets_column();
                 array_column_offset = 
off_data.clone_resized(col_array.get_offsets_column().size());
                 args_info.offsets_ptr = &col_array.get_offsets();
-            } else {
-                // select array_map((x,y)->x+y,c_array1,[0,1,2,3]) from 
array_test2;
-                // c_array1: [0,1,2,3,4,5,6,7,8,9]
-                const auto& array_offsets =
-                        assert_cast<const 
ColumnArray::ColumnOffsets&>(*first_array_offsets)
-                                .get_data();
-                if (nested_array_column_rows != 
col_array.get_data_ptr()->size() ||
-                    (!array_offsets.empty() &&
-                     memcmp(array_offsets.data(), 
col_array.get_offsets().data(),
-                            sizeof(array_offsets[0]) * array_offsets.size()) 
!= 0)) {
-                    return Status::InvalidArgument(
-                            "in array map function, the input column size "
-                            "are "
-                            "not equal completely, nested column data rows 1st 
size is {}, {}th "
-                            "size is {}.",
-                            nested_array_column_rows, i + 1, 
col_array.get_data_ptr()->size());
-                }
             }
             lambda_datas[i] = col_array.get_data_ptr();
             const auto& col_type = assert_cast<const 
DataTypeArray&>(*type_array);
             lambda_argument_types[i] = col_type.get_nested_type();
         }
+
+        const auto& first_array_offsets =
+                assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[0]).get_data();
+        const auto& outside_null_map_data = outside_null_map->get_data();
+        const bool has_outer_null =
+                std::ranges::any_of(outside_null_map_data, [](uint8_t is_null) 
{ return is_null; });
+        bool has_hidden_nested_data = false;
+        if (!has_outer_null) {
+            // select array_map((x,y)->x+y,c_array1,[0,1,2,3]) from 
array_test2;
+            // c_array1: [0,1,2,3,4,5,6,7,8,9]
+            for (int i = 1; i < arguments.size(); ++i) {
+                const auto& offsets =
+                        assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[i])
+                                .get_data();
+                if (nested_array_column_rows != lambda_datas[i]->size() ||
+                    (!first_array_offsets.empty() &&
+                     memcmp(first_array_offsets.data(), offsets.data(),
+                            sizeof(first_array_offsets[0]) * 
first_array_offsets.size()) != 0)) {
+                    return Status::InvalidArgument(
+                            "in array map function, the input column size are 
not equal "
+                            "completely, nested column data rows 1st size is 
{}, {}th size is {}.",
+                            nested_array_column_rows, i + 1, 
lambda_datas[i]->size());
+                }
+            }
+        } else {
+            std::vector<size_t> previous_offsets(arguments.size(), 0);
+            for (size_t row = 0; row < count; ++row) {
+                const size_t first_row_size = first_array_offsets[row] - 
previous_offsets[0];
+                has_hidden_nested_data |= outside_null_map_data[row] != 0 && 
first_row_size > 0;
+                for (int i = 1; i < arguments.size(); ++i) {
+                    const auto& offsets =
+                            assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[i])
+                                    .get_data();
+                    const size_t row_size = offsets[row] - previous_offsets[i];
+                    has_hidden_nested_data |= outside_null_map_data[row] != 0 
&& row_size > 0;
+                    if (outside_null_map_data[row] == 0 && first_row_size != 
row_size) {
+                        return Status::InvalidArgument(
+                                "in array map function, the input column size 
are not equal "
+                                "completely at row {}, 1st size is {}, {}th 
size is {}.",
+                                row, first_row_size, i + 1, row_size);
+                    }
+                    previous_offsets[i] = offsets[row];
+                }
+                previous_offsets[0] = first_array_offsets[row];
+            }
+        }
+
+        // NULL rows are skipped. If they retain hidden payload, rebuild only 
result offsets;

Review Comment:
   [P1] This skip is confined to `ArrayMapFunction`, but the parallel 
lambda-form `array_sort` path still evaluates hidden payload from an outer-NULL 
array. `ArraySortFunction` saves `outside_null_map`, then unconditionally 
`std::sort`s every original row range and invokes the comparator. With 
`short_circuit_evaluation=false`, a row such as `array_sort((x,y) -> if(cast(x 
as int) < cast(y as int), -1, 1), if(id=1, cast(NULL as ARRAY<STRING>), 
values))` still raises on two hidden non-numeric strings even though the result 
row must be NULL. Please skip comparator execution for null rows too and add 
the corresponding strict-cast regression.



##########
be/test/exprs/lambda_function/array_map_function_test.cpp:
##########
@@ -1015,6 +1025,102 @@ TEST(ArrayMapFunctionTest, 
MultiBatchPreservesCaptureMappingAcrossSelectedArrayR
     EXPECT_EQ(values.get_element(total_nested_rows - 1), 1349);
 }
 
+TEST(ArrayMapFunctionTest, 
HiddenPayloadAfterValidRowUsesOwnOffsetsAcrossBatchesAndSelector) {
+    constexpr int lambda_batch_size = 3;
+    auto int_type = std::make_shared<DataTypeInt32>();
+    auto array_int_type = std::make_shared<DataTypeArray>(int_type);
+    auto nullable_array_int_type = 
std::make_shared<DataTypeNullable>(array_int_type);
+    std::vector<size_t> observed_batch_sizes;
+
+    auto root =

Review Comment:
   [P1] The required Clang Formatter check is failing on this new test block; 
the local clang-format 16 dry run reports violations from here through both 
added tests. Please run `build-support/clang-format.sh` on the changed C++ 
files and push the formatted result so the mandatory style gate can pass.



##########
be/src/exprs/lambda_function/varray_map_function.cpp:
##########
@@ -144,35 +144,90 @@ class ArrayMapFunction : public LambdaFunction {
 
             // here is the array column
             const auto& col_array = assert_cast<const 
ColumnArray&>(*column_array);
+            lambda_offsets[i] = col_array.get_offsets_ptr();
 
             if (i == 0) {
                 nested_array_column_rows = col_array.get_data_ptr()->size();
-                first_array_offsets = col_array.get_offsets_ptr();
                 const auto& off_data = col_array.get_offsets_column();
                 array_column_offset = 
off_data.clone_resized(col_array.get_offsets_column().size());
                 args_info.offsets_ptr = &col_array.get_offsets();
-            } else {
-                // select array_map((x,y)->x+y,c_array1,[0,1,2,3]) from 
array_test2;
-                // c_array1: [0,1,2,3,4,5,6,7,8,9]
-                const auto& array_offsets =
-                        assert_cast<const 
ColumnArray::ColumnOffsets&>(*first_array_offsets)
-                                .get_data();
-                if (nested_array_column_rows != 
col_array.get_data_ptr()->size() ||
-                    (!array_offsets.empty() &&
-                     memcmp(array_offsets.data(), 
col_array.get_offsets().data(),
-                            sizeof(array_offsets[0]) * array_offsets.size()) 
!= 0)) {
-                    return Status::InvalidArgument(
-                            "in array map function, the input column size "
-                            "are "
-                            "not equal completely, nested column data rows 1st 
size is {}, {}th "
-                            "size is {}.",
-                            nested_array_column_rows, i + 1, 
col_array.get_data_ptr()->size());
-                }
             }
             lambda_datas[i] = col_array.get_data_ptr();
             const auto& col_type = assert_cast<const 
DataTypeArray&>(*type_array);
             lambda_argument_types[i] = col_type.get_nested_type();
         }
+
+        const auto& first_array_offsets =
+                assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[0]).get_data();
+        const auto& outside_null_map_data = outside_null_map->get_data();
+        const bool has_outer_null =
+                std::ranges::any_of(outside_null_map_data, [](uint8_t is_null) 
{ return is_null; });
+        bool has_hidden_nested_data = false;
+        if (!has_outer_null) {
+            // select array_map((x,y)->x+y,c_array1,[0,1,2,3]) from 
array_test2;
+            // c_array1: [0,1,2,3,4,5,6,7,8,9]
+            for (int i = 1; i < arguments.size(); ++i) {
+                const auto& offsets =
+                        assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[i])
+                                .get_data();
+                if (nested_array_column_rows != lambda_datas[i]->size() ||
+                    (!first_array_offsets.empty() &&
+                     memcmp(first_array_offsets.data(), offsets.data(),
+                            sizeof(first_array_offsets[0]) * 
first_array_offsets.size()) != 0)) {
+                    return Status::InvalidArgument(
+                            "in array map function, the input column size are 
not equal "
+                            "completely, nested column data rows 1st size is 
{}, {}th size is {}.",
+                            nested_array_column_rows, i + 1, 
lambda_datas[i]->size());
+                }
+            }
+        } else {
+            std::vector<size_t> previous_offsets(arguments.size(), 0);
+            for (size_t row = 0; row < count; ++row) {
+                const size_t first_row_size = first_array_offsets[row] - 
previous_offsets[0];
+                has_hidden_nested_data |= outside_null_map_data[row] != 0 && 
first_row_size > 0;
+                for (int i = 1; i < arguments.size(); ++i) {
+                    const auto& offsets =
+                            assert_cast<const 
ColumnArray::ColumnOffsets&>(*lambda_offsets[i])
+                                    .get_data();
+                    const size_t row_size = offsets[row] - previous_offsets[i];
+                    has_hidden_nested_data |= outside_null_map_data[row] != 0 
&& row_size > 0;
+                    if (outside_null_map_data[row] == 0 && first_row_size != 
row_size) {
+                        return Status::InvalidArgument(
+                                "in array map function, the input column size 
are not equal "
+                                "completely at row {}, 1st size is {}, {}th 
size is {}.",
+                                row, first_row_size, i + 1, row_size);
+                    }
+                    previous_offsets[i] = offsets[row];
+                }
+                previous_offsets[0] = first_array_offsets[row];
+            }
+        }
+
+        // NULL rows are skipped. If they retain hidden payload, rebuild only 
result offsets;
+        // the bounded execution path reads each argument through its own 
original offsets.
+        if (has_hidden_nested_data) {
+            auto res_offsets = ColumnArray::ColumnOffsets::create();
+            auto& res_offsets_data = res_offsets->get_data();
+            res_offsets_data.reserve(count);
+            size_t previous_offset = 0;
+            size_t compacted_rows = 0;
+            for (size_t row = 0; row < count; ++row) {
+                const size_t current_offset = first_array_offsets[row];
+                if (outside_null_map_data[row] == 0) {
+                    const size_t row_size = current_offset - previous_offset;
+                    compacted_rows += row_size;
+                }
+                res_offsets_data.push_back(compacted_rows);

Review Comment:
   [P1] Compacting an outer-NULL row to length 0 breaks callers that combine 
this result with another logically aligned array. In a mixed two-row block this 
result can have offsets `[0,2]` and null map `[1,0]` while the other array has 
`[2,4]`; nullable execution still exposes those physical offsets to the nested 
function. High-order `array_split`/`array_reverse_split`, 
`array_zip(array_map(...), a)`, multi-argument `array_enumerate_uniq`, and 
multi-array `_foreach` aggregates then reject the unequal or shifted offsets 
instead of producing the required NULL row. Please make the nullable 
execution/affected consumers ignore or normalize outer-NULL rows, and add mixed 
NULL/non-NULL composition regressions.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to