HappenLee commented on code in PR #67817:
URL: https://github.com/apache/doris/pull/67817#discussion_r4059743419


##########
be/src/storage/segment/column_reader.cpp:
##########
@@ -2432,11 +2438,187 @@ Status ArrayFileColumnIterator::read_by_rowids(const 
rowid_t* rowids, const size
 
     _recovery_from_place_holder_column(dst);
 
+    if (count == 0) {
+        return Status::OK();
+    }
+
+    // A null-only consumer needs one nested row per null marker, but no 
lengths or item data.
+    if (read_null_map_only()) {
+        DORIS_CHECK(is_column_nullable(*dst));
+        auto& nullable_column = assert_cast<ColumnNullable&>(*dst);
+        if (_null_iterator) {
+            auto null_map_ptr = nullable_column.get_null_map_column_ptr();
+            MutableColumnPtr null_map_column = std::move(null_map_ptr);
+            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_column));
+        } else {
+            // A nullable schema can read an old non-nullable segment, which 
has no null stream.
+            nullable_column.get_null_map_column_ptr()->insert_many_vals(0, 
count);
+        }
+        auto& column_array = assert_cast<ColumnArray&, 
TypeCheckOnRelease::DISABLE>(
+                nullable_column.get_nested_column());
+        column_array.insert_many_defaults(count);
+        return Status::OK();
+    }
+
+    auto& column_array = assert_cast<ColumnArray&, 
TypeCheckOnRelease::DISABLE>(
+            is_column_nullable(*dst) ? 
static_cast<ColumnNullable&>(*dst).get_nested_column()
+                                     : *dst);
+    // The parent reader can stay active solely for lazy children. This flag 
controls writing
+    // parent metadata to dst, not reading source offsets to locate those 
children on disk.
+    const bool read_meta_columns = need_to_read_meta_columns();
+
+    if (_array_reader->is_nullable()) {
+        if (UNLIKELY(!is_column_nullable(*dst))) {
+            return Status::InternalError(
+                    "unexpected non-nullable destination column for nullable 
array reader");
+        }
+        auto& nullable_column = static_cast<ColumnNullable&>(*dst);
+        if (read_meta_columns) {
+            MutableColumnPtr null_map_column = 
nullable_column.get_null_map_column_ptr();
+            RETURN_IF_ERROR(_null_iterator->read_by_rowids(rowids, count, 
null_map_column));
+        } else {
+            DORIS_CHECK(nullable_column.get_null_map_column().size() == count);
+        }
+    } else if (read_meta_columns && is_column_nullable(*dst)) {
+        
static_cast<ColumnNullable&>(*dst).get_null_map_column_ptr()->insert_many_vals(0,
 count);
+    }
+
+    // Array row r spans [offset[r], offset[r + 1]) in the source item stream. 
offset_rowids
+    // identifies entries in the offset stream, not item ordinals. Read both 
endpoints in one
+    // ordered pass to avoid revisiting pages for the ends; adjacent rows 
share an endpoint:
+    // rowids [1, 2, 8] need offset entries [1, 2, 3, 8, 9].
+    DorisVector<rowid_t> offset_rowids;
+    offset_rowids.reserve(count * 2);
     for (size_t i = 0; i < count; ++i) {
-        // TODO(cambyszju): now read array one by one, need optimize later
-        RETURN_IF_ERROR(seek_to_ordinal(rowids[i]));
+        offset_rowids.push_back(rowids[i]);
+        const auto next_rowid = static_cast<uint64_t>(rowids[i]) + 1;
+        if (next_rowid < _array_reader->num_rows() &&
+            (i + 1 == count || next_rowid != rowids[i + 1])) {
+            offset_rowids.push_back(static_cast<rowid_t>(next_rowid));
+        }
+    }
+    MutableColumnPtr source_offsets_column = ColumnOffset64::create();
+    source_offsets_column->reserve(offset_rowids.size() + 1);

Review Comment:
   **Performance validation**
   
   Could we add a focused benchmark comparing the original per-row 
implementation, a simpler implementation that batches consecutive parent row 
IDs, and this implementation? The new path reduces repeated positioning, but 
also introduces O(N) temporary row IDs and offsets plus their allocation cost. 
Please include single-row/small batches, highly sparse selections, and 
workloads without empty arrays, alongside the contiguous and empty-array cases. 
These would help establish both the benefit of batching metadata and the 
additional benefit of coalescing item spans across unselected empty arrays.
   
   The new tests verify fewer iterator calls, but do not establish the 
latency/CPU or memory trade-off. Measuring these against the same baseline 
would help justify the added complexity and check for regressions in cases with 
little opportunity to merge ranges.



##########
be/test/storage/segment/column_reader_test.cpp:
##########
@@ -450,6 +474,204 @@ TEST_F(ColumnReaderTest, 
NullMapOnlyReadBySparseRowidsAcrossPages) {
     EXPECT_EQ(2, nullable_col.get_nested_column().size());
 }
 
+TEST_F(ColumnReaderTest, ArrayReadByRowidsMatchesSequentialReadAcrossPages) {

Review Comment:
   **Additional coverage for nested arrays and schema evolution**
   
   The real-file test covers nullable ARRAY<nullable INT>, including the 
predicate/filter/lazy sequence. Could we extend coverage to ARRAY<ARRAY<...>> 
and ARRAY<STRUCT<...>>, and add a case that reads an old non-nullable ARRAY 
segment into a nullable destination after schema evolution?
   
   For the nested cases, please exercise predicate materialization, row 
filtering, and lazy filling of the remaining children, and compare the final 
result with a full sequential read. This should verify that 
already-materialized offsets/null maps remain unchanged and that child data 
stays aligned across multiple selected ranges. A SQL regression for nested lazy 
materialization would also verify that planner access paths reach the intended 
reader phases.



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