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


##########
be/src/format/arrow/arrow_array_normalizer.cpp:
##########
@@ -48,6 +51,55 @@ std::shared_ptr<arrow::DataType> target_type_for(const 
arrow::DataType& type) {
     }
 }
 
+template <typename ViewArray, typename ListBuilder, typename ListArray>
+arrow::Result<std::shared_ptr<arrow::Array>> canonicalize_list_view(const 
ViewArray& source,
+                                                                    
arrow::MemoryPool* pool) {
+    // Imported C stream arrays are not guaranteed to have validated ranges; 
copying an invalid
+    // range before this check could read beyond the child array.
+    auto validation = source.ValidateFull();
+    if (!validation.ok()) {
+        return validation;
+    }
+
+    auto child_builder_result = arrow::MakeBuilder(source.value_type(), pool);
+    if (!child_builder_result.ok()) {
+        return child_builder_result.status();
+    }
+    std::shared_ptr<arrow::ArrayBuilder> 
child_builder(child_builder_result.MoveValueUnsafe());
+    ListBuilder builder(pool, child_builder);
+    auto reserve_status = builder.Reserve(source.length());
+    if (!reserve_status.ok()) {
+        return reserve_status;
+    }
+
+    arrow::ArraySpan values(*source.values()->data());
+    for (int64_t i = 0; i < source.length(); ++i) {
+        if (source.IsNull(i)) {
+            auto append_status = builder.AppendNull();
+            if (!append_status.ok()) {
+                return append_status;
+            }
+            continue;
+        }
+        auto append_status = builder.Append();
+        if (!append_status.ok()) {
+            return append_status;
+        }
+        append_status = child_builder->AppendArraySlice(values, 
source.value_offset(i),

Review Comment:
   [P1] Check aggregate lengths before copying into builders
   
   A valid `LargeListView<Null>` can reach signed overflow here without 
allocating a huge buffer: use a Null child of length `INT64_MAX` and two shared 
ranges of length `INT64_MAX - 1`. `ValidateFull()` accepts both ranges 
independently, but Arrow 24's `NullBuilder::AppendArraySlice` performs 
unchecked `length_ += length`, so the second append overflows before 
`LargeListBuilder` checks capacity. Checking only this source's top-level total 
is not enough either: an outer `large_list_view<large_list_view<null>>` can 
have total 2 while the nested builder overflows on the same ranges during this 
call. Please compute checked non-null logical totals before any append at every 
copied view level, or reject nested view children before building, and add a 
no-allocation regression test.



##########
be/src/format/arrow/arrow_array_normalizer.cpp:
##########
@@ -48,6 +51,55 @@ std::shared_ptr<arrow::DataType> target_type_for(const 
arrow::DataType& type) {
     }
 }
 
+template <typename ViewArray, typename ListBuilder, typename ListArray>
+arrow::Result<std::shared_ptr<arrow::Array>> canonicalize_list_view(const 
ViewArray& source,
+                                                                    
arrow::MemoryPool* pool) {
+    // Imported C stream arrays are not guaranteed to have validated ranges; 
copying an invalid
+    // range before this check could read beyond the child array.
+    auto validation = source.ValidateFull();
+    if (!validation.ok()) {
+        return validation;
+    }
+
+    auto child_builder_result = arrow::MakeBuilder(source.value_type(), pool);
+    if (!child_builder_result.ok()) {
+        return child_builder_result.status();
+    }
+    std::shared_ptr<arrow::ArrayBuilder> 
child_builder(child_builder_result.MoveValueUnsafe());
+    ListBuilder builder(pool, child_builder);
+    auto reserve_status = builder.Reserve(source.length());

Review Comment:
   [P1] Reserve the expanded child before the append loop
   
   Only the parent slots are reserved, so the child grows geometrically while 
shared ListView ranges are expanded. For example, 1,025 rows viewing the same 
100,000 Int32 values produce a roughly 391 MiB value buffer, but Arrow's final 
doubling reallocates it from about 391 MiB to 781 MiB; Doris charges the new 
allocation before releasing the old one, transiently accounting roughly 1.17 
GiB for that buffer alone. An exact reserve keeps the canonical and Doris value 
buffers near 782 MiB combined, leaving roughly 391 MiB less required headroom 
(other common query allocations aside). After validation, please compute a 
checked non-null logical total and reserve it on `child_builder` before this 
loop.



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