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


##########
be/src/exprs/function/array/function_array_map.h:
##########
@@ -191,6 +198,70 @@ struct ArrayMapImpl {
         return true;
     }
 
+    static bool _execute_nested_array(ColumnArrayMutableData& dst,
+                                      const ColumnArrayExecutionDatas& datas,
+                                      const std::vector<bool>& col_const, 
size_t start_row,
+                                      size_t end_row) {
+        for (const auto& data : datas) {
+            if (!is_column<ColumnArray>(*data.nested_col)) {
+                return false;
+            }
+        }
+
+        size_t result_offset = 0;
+        phmap::flat_hash_set<StringRef, StringRefHash> seen;

Review Comment:
   Please use Doris's allocator-aware containers for this new cardinality-sized 
state. `phmap::flat_hash_set` and `std::vector` use their default allocators 
here, while `doris::flat_hash_set` and `DorisVector` route allocations through 
the thread query tracker. A legal one-million-element row needs about 16 MB for 
the `StringRef` vector alone, plus the hash table, none of which is charged to 
the query or checked against its memory limit. This is separate from the Arena 
payload; switch both containers to tracked allocators.



##########
be/src/exprs/function/array/function_array_map.h:
##########
@@ -167,7 +168,13 @@ struct ArrayMapImpl {
                           std::vector<bool>& col_const, size_t start_row, 
size_t end_row) {
         ColumnArrayMutableData dst =
                 create_mutable_data(datas[0].nested_col.get(), 
datas[0].nested_nullmap_data);
-        if (_execute_internal<ALL_COLUMNS_SIMPLE>(dst, datas, col_const, 
start_row, end_row)) {
+      
+        bool executed =
+                _execute_internal<ALL_COLUMNS_SIMPLE>(dst, datas, col_const, 
start_row, end_row);
+        if constexpr (operation == MapOperation::UNION) {
+            executed = executed || _execute_nested_array(dst, datas, 
col_const, start_row, end_row);

Review Comment:
   This new success path invalidates the existing p0 test at 
`regression-test/suites/doc/sql-manual/sql-function/test_array_function.groovy:406-409`,
 which still requires `ARRAY<ARRAY<INT>>` to be rejected. With the FE exemption 
in this PR, that query reaches this branch and no longer throws, so the suite 
will fail. If support is intended, update the PR contract, replace that 
negative case with an auto-generated positive oracle, and add the issue's 
empty-left reproduction plus nullable/duplicate/variadic cases.



##########
be/src/exprs/function/array/function_array_map.h:
##########
@@ -191,6 +198,70 @@ struct ArrayMapImpl {
         return true;
     }
 
+    static bool _execute_nested_array(ColumnArrayMutableData& dst,
+                                      const ColumnArrayExecutionDatas& datas,
+                                      const std::vector<bool>& col_const, 
size_t start_row,
+                                      size_t end_row) {
+        for (const auto& data : datas) {
+            if (!is_column<ColumnArray>(*data.nested_col)) {
+                return false;
+            }
+        }
+
+        size_t result_offset = 0;
+        phmap::flat_hash_set<StringRef, StringRefHash> seen;
+        std::vector<StringRef> distinct_elements;
+        Arena arena;
+
+        for (size_t row = start_row; row < end_row; ++row) {
+            seen.clear();
+            distinct_elements.clear();
+            arena.clear();
+
+            bool has_null = false;
+            for (size_t arg_idx = 0; arg_idx < datas.size(); ++arg_idx) {
+                const auto& data = datas[arg_idx];
+                const size_t input_row = index_check_const(row, 
col_const[arg_idx]);
+                const size_t begin = (*data.offsets_ptr)[input_row - 1];
+                const size_t end = (*data.offsets_ptr)[input_row];
+
+                for (size_t off = begin; off < end; ++off) {
+                    if (data.nested_nullmap_data && 
data.nested_nullmap_data[off]) {
+                        has_null = true;
+                        continue;
+                    }
+
+                    const char* serialized_begin = nullptr;
+                    StringRef key = 
data.nested_col->serialize_value_into_arena(off, arena,

Review Comment:
   Each occurrence is serialized before deduplication, but a failed 
`seen.emplace` leaves that allocation in the Arena until the next row. 
Duplicate-heavy input such as 1,000 copies of the same large inner array 
therefore retains serialized bytes proportional to all occurrences even though 
the result has one value, adding input-sized avoidable peak memory. 
Reclaim/reuse the last serialization on duplicates (or hash/compare row 
references) and cover this with a duplicate-heavy test.



##########
be/src/exprs/function/array/function_array_map.h:
##########
@@ -167,7 +168,13 @@ struct ArrayMapImpl {
                           std::vector<bool>& col_const, size_t start_row, 
size_t end_row) {
         ColumnArrayMutableData dst =
                 create_mutable_data(datas[0].nested_col.get(), 
datas[0].nested_nullmap_data);
-        if (_execute_internal<ALL_COLUMNS_SIMPLE>(dst, datas, col_const, 
start_row, end_row)) {
+      

Review Comment:
   Please remove the spaces from this otherwise blank line and run the required 
BE formatter. The authoritative patch adds a six-space-only line here; 
clang-format v16 removes it, so `build-support/check-format.sh` reports a diff 
and fails this file.



##########
be/src/exprs/function/array/function_array_map.h:
##########
@@ -191,6 +198,70 @@ struct ArrayMapImpl {
         return true;
     }
 
+    static bool _execute_nested_array(ColumnArrayMutableData& dst,
+                                      const ColumnArrayExecutionDatas& datas,
+                                      const std::vector<bool>& col_const, 
size_t start_row,
+                                      size_t end_row) {
+        for (const auto& data : datas) {
+            if (!is_column<ColumnArray>(*data.nested_col)) {
+                return false;
+            }
+        }
+
+        size_t result_offset = 0;
+        phmap::flat_hash_set<StringRef, StringRefHash> seen;
+        std::vector<StringRef> distinct_elements;
+        Arena arena;
+
+        for (size_t row = start_row; row < end_row; ++row) {
+            seen.clear();
+            distinct_elements.clear();
+            arena.clear();
+
+            bool has_null = false;
+            for (size_t arg_idx = 0; arg_idx < datas.size(); ++arg_idx) {
+                const auto& data = datas[arg_idx];
+                const size_t input_row = index_check_const(row, 
col_const[arg_idx]);
+                const size_t begin = (*data.offsets_ptr)[input_row - 1];
+                const size_t end = (*data.offsets_ptr)[input_row];
+
+                for (size_t off = begin; off < end; ++off) {
+                    if (data.nested_nullmap_data && 
data.nested_nullmap_data[off]) {
+                        has_null = true;
+                        continue;
+                    }
+
+                    const char* serialized_begin = nullptr;
+                    StringRef key = 
data.nested_col->serialize_value_into_arena(off, arena,
+                                                                               
 serialized_begin);
+
+                    if (seen.emplace(key).second) {

Review Comment:
   Please do not use raw serialized bytes as the equality key. 
`array_union([[CAST('0.0' AS DOUBLE)]], [[CAST('-0.0' AS DOUBLE)]])` reaches 
this path; `+0.0` and `-0.0` are equal under Doris/scalar-union and 
`ColumnArray::compare_at` semantics, but `ColumnVector::serialize_impl` 
preserves the sign bit and `StringRef` compares with `memcmp`, so both inner 
arrays are emitted. Use value-semantic hashing/equality (also canonicalizing 
NaNs), and add focused regressions.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java:
##########
@@ -66,13 +68,24 @@ public ArrayUnion withChildren(List<Expression> children) {
         return new ArrayUnion(getFunctionParams(children));
     }
 
+
     @Override
     public void checkLegalityBeforeTypeCoercion() {
-        DataType argType = getArgument(0).getDataType();
-        if (argType.isArrayType() && (((ArrayType) 
argType).getItemType().isComplexType()
-                    || ((ArrayType) argType).getItemType().isVariantType()
-                    || ((ArrayType) argType).getItemType().isJsonType())) {
-            throw new AnalysisException("array_union does not support types: " 
+ argType.toSql());
+        for (Expression arg : getArguments()) {
+            DataType argType = arg.getDataType();
+            if (!argType.isArrayType()) {
+                continue;
+            }
+
+            DataType itemType = ((ArrayType) argType).getItemType();
+            boolean unsupported = (itemType.isComplexType() && 
!itemType.isArrayType())

Review Comment:
   Please validate nested array leaves recursively here. For a non-null VARIANT 
expression `v`, `array_repeat(v, 1)` produces `ARRAY<VARIANT>` and wrapping it 
again produces `ARRAY<ARRAY<VARIANT>>`; this immediate-array exemption accepts 
that argument. BE then serializes the inner `ColumnArray`, reaches 
`ColumnVariant::serialize_size_at`/`serialize_impl` (not implemented), and 
throws `NOT_IMPLEMENTED` instead of the FE error this guard is meant to 
provide. Recurse through nested `ArrayType`s and reject unsupported leaves, 
with a regression for the nested `array_repeat` trigger.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java:
##########
@@ -38,21 +38,23 @@
  * ScalarFunction 'array_union'. This class is generated by GenerateFunction.
  */
 public class ArrayUnion extends ScalarFunction implements 
ExplicitlyCastableSignature,
-        BinaryExpression, PropagateNullable {
+    BinaryExpression, PropagateNullable {

Review Comment:
   Please restore the generated-function continuation indentation. Nereids 
Checkstyle requires `lineWrappingIndentation=8`, but this continuation (and the 
signature/condition continuations below) was shifted to four spaces; the two 
blank lines before `@Override` also violate the Nereids `EmptyLineSeparator` 
rule. FE Maven validation will fail before reaching the functional tests.



-- 
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