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


##########
be/src/format_v2/table_reader.cpp:
##########
@@ -1249,6 +1368,102 @@ Status TableReader::prepare_split(const 
SplitReadOptions& options) {
     return _parse_delete_predicates(options);
 }
 
+Status TableReader::build_physical_splits(const FileScanSplit& source_split,
+                                          std::vector<FileScanSplit>* splits, 
bool* was_split) {
+    SCOPED_TIMER(_profile.total_timer);
+    DORIS_CHECK(splits != nullptr);
+    DORIS_CHECK(was_split != nullptr);
+    splits->clear();
+    *was_split = false;
+    if (_format != FileFormat::PARQUET || _current_split_pruned ||
+        _current_split_uses_metadata_count || _current_task == nullptr) {

Review Comment:
   [P2] Keep footer-only aggregates on the planning reader. This guard only 
recognizes the FE table-level count, but after `open_reader()` the ready 
Parquet reader can also answer `COUNT(*)`, required-primitive `COUNT(col)`, and 
successful `MIN/MAX` from the complete source footer plan. Refinement closes it 
and performs R child create/map/plan/aggregate/close lifecycles; `MIN/MAX` 
additionally sends 2R synthetic rows to the upper aggregate instead of two. 
Retain the planning reader when the source-level metadata aggregate succeeds 
(falling back to refinement for level-reading COUNT or unsafe/missing MIN/MAX 
stats), and cover these multi-row-group cases.



##########
be/src/exec/scan/file_scanner_v2.cpp:
##########
@@ -585,15 +656,53 @@ Status FileScannerV2::_prepare_next_split(bool* eos) {
             // advance exactly one scan range and preserve later files in the 
same scan.
             RETURN_IF_ERROR(_table_reader->abort_split());
             COUNTER_UPDATE(_empty_file_counter, 1);
-            _state->update_num_finished_scan_range(1);
+            RETURN_IF_ERROR(_complete_current_split());
             continue;
         }
         RETURN_IF_ERROR(status);
         if (_table_reader->current_split_pruned()) {
-            _state->update_num_finished_scan_range(1);
+            RETURN_IF_ERROR(_complete_current_split());
             continue;
         }
-        COUNTER_UPDATE(_file_counter, 1);
+        _update_file_counter(_file_counter, _current_split);
+        if (_current_split.is_source_split &&
+            _should_refine_source_split(_current_range, 
_constructed_scanners)) {
+            std::vector<FileScanSplit> generated_splits;
+            bool was_split = false;
+            const auto split_status = _table_reader->build_physical_splits(
+                    _current_split, &generated_splits, &was_split);
+            const auto ignored_split_status = _classify_ignored_split_status(
+                    split_status, 
config::ignore_not_found_file_in_external_table,
+                    _should_stop || _io_ctx->should_stop);
+            if (ignored_split_status == IgnoredSplitStatus::NOT_FOUND) {
+                RETURN_IF_ERROR(_table_reader->abort_split());
+                COUNTER_UPDATE(_not_found_file_counter, 1);
+                RETURN_IF_ERROR(_complete_current_split());
+                continue;
+            }
+            if (ignored_split_status == IgnoredSplitStatus::EMPTY) {
+                RETURN_IF_ERROR(_table_reader->abort_split());
+                COUNTER_UPDATE(_empty_file_counter, 1);
+                RETURN_IF_ERROR(_complete_current_split());
+                continue;
+            }
+            RETURN_IF_ERROR(split_status);
+            if (was_split) {
+                RETURN_IF_ERROR(_table_reader->abort_split());
+                const bool has_children = !generated_splits.empty();
+                
RETURN_IF_ERROR(_retire_current_source_split(std::move(generated_splits)));

Review Comment:
   [P2] Preserve adaptive batch history across row-group children. Every 
generated child re-enters `_prepare_next_split()`, which resets the predictor 
and starts a fresh 32-row probe; the unsplit reader learned table-block bytes 
per row once and reused that cap across later row groups. A source with R 
surviving groups can now pay R scanner learning probes, and a filtered prefix 
makes each child repeat the cold 32/256/1024/... predicate ramp before its 
first width sample. Retain or seed the scanner-owned predictor for siblings 
with the same source identity (with a safe synchronized hint for other 
scanners), and cover a multi-row-group source by bounding 
`AdaptiveBatchProbeCount` per FE range.



##########
be/src/format_v2/parquet/parquet_reader.cpp:
##########
@@ -551,6 +574,97 @@ Status ParquetReader::init(RuntimeState* state) {
     return Status::OK();
 }
 
+Status ParquetReader::build_physical_splits(std::vector<PhysicalFileSplit>* 
splits,
+                                            bool* was_split) const {
+    DORIS_CHECK(splits != nullptr);
+    DORIS_CHECK(was_split != nullptr);
+    splits->clear();
+    *was_split = false;
+    if (_state == nullptr || _state->file_context.native_metadata == nullptr ||
+        _state->file_context.shared_file_context == nullptr) {
+        return Status::Uninitialized("ParquetReader is not open");
+    }
+    if (!_state->file_context.shared_file_context->has_stable_identity) {
+        // A path and size do not identify a mutable remote object. Keep the 
initialized parent
+        // reader instead of publishing children whose shared footer could 
become stale.
+        return Status::OK();
+    }
+    if (!_state->file_context.can_refine_physical_splits()) {
+        return Status::OK();
+    }
+
+    ParquetScanRange scan_range {
+            .start_offset = _file_description->range_start_offset,
+            .size = _file_description->range_size,
+            .file_size = _file_description->file_size,
+    };
+    std::vector<int> selected_row_groups;
+    if (_state->scan_plan != nullptr) {
+        selected_row_groups.reserve(_state->scan_plan->row_groups.size());
+        for (const auto& row_group_plan : _state->scan_plan->row_groups) {
+            selected_row_groups.push_back(row_group_plan.row_group_id);
+        }
+    } else {
+        RETURN_IF_ERROR(detail::select_native_row_groups_by_scan_range(
+                _state->file_context.native_metadata->to_thrift(), scan_range,
+                _state->file_context.native_metadata->row_group_first_rows(),
+                &selected_row_groups));
+    }
+    const auto& metadata = _state->file_context.native_metadata->to_thrift();
+    const auto compat = native::parquet_reader_compat(
+            metadata.__isset.created_by ? metadata.created_by : std::string 
{});
+    const size_t file_size = _state->file_context.native_file->size();
+    splits->reserve(selected_row_groups.size());
+    for (const int row_group_id : selected_row_groups) {
+        const auto& row_group = metadata.row_groups[row_group_id];
+        if (row_group.num_rows == 0) {
+            // Empty row groups are valid and the ordinary scan planner 
ignores them. Refinement
+            // must preserve that behavior before inspecting their potentially 
empty chunks.
+            continue;
+        }
+        if (row_group.columns.empty()) {
+            // A root-only schema can still carry rows for metadata COUNT(*), 
but it has no byte
+            // envelope that can identify a child. Keep the initialized source 
reader so those
+            // rows are not turned into a corruption error or discarded after 
tentative children.
+            splits->clear();
+            return Status::OK();
+        }
+        size_t group_start = std::numeric_limits<size_t>::max();
+        size_t group_end = 0;
+        for (size_t column_id = 0; column_id < row_group.columns.size(); 
++column_id) {
+            const auto& chunk = row_group.columns[column_id];
+            if (!chunk.__isset.meta_data) {
+                // Scheduling envelopes span every chunk, including columns 
the current request
+                // does not read. If an unused chunk cannot prove a safe 
envelope, retain the
+                // initialized source reader so projected-column validation 
remains authoritative.
+                splits->clear();
+                return Status::OK();
+            }
+            native::ColumnChunkRange chunk_range;
+            const auto range_status = native::compute_column_chunk_range(
+                    chunk.meta_data, file_size, compat.parquet_816_padding, 
&chunk_range);
+            if (!range_status.ok()) {
+                splits->clear();
+                return Status::OK();
+            }
+            group_start = std::min(group_start, chunk_range.offset);
+            group_end = std::max(group_end, chunk_range.offset + 
chunk_range.length);
+        }
+        if (group_end <= group_start) {
+            splits->clear();
+            return Status::OK();
+        }
+        PhysicalFileSplit child;
+        child.start_offset = cast_set<int64_t>(group_start);
+        child.size = cast_set<int64_t>(group_end - group_start);
+        child.file_context = _state->file_context.shared_file_context;
+        child.format_split_id = row_group_id;

Review Comment:
   [P2] Preserve Parquet adaptive state across row-group children. An unsplit 
scheduler keeps `_predicate_runtime_stats` and `_predicate_survival_ratio` 
across `reset_current_row_group()`, but each generated child constructs a fresh 
scheduler. Each child with eligible staged predicates therefore restarts in 
request order and repeats warmup sampling; on non-MergeRange prefetch paths it 
also warms the full cold candidate set and cannot use learned high survival to 
prefetch output chunks before its first surviving predicate batch. The source 
previously reused those decisions on later groups. Carry a synchronized 
source-level stats snapshot keyed to the predicate snapshot (or seed later 
siblings), and test both ordering and output-prefetch reuse across multiple 
children.



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