github-actions[bot] commented on code in PR #69646:
URL: https://github.com/apache/doris/pull/69646#discussion_r4379658688
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/TopnFilterPushDownVisitor.java:
##########
@@ -92,6 +92,11 @@ public Boolean visit(Plan plan, PushDownContext ctx) {
@Override
public Boolean visitPhysicalProject(
PhysicalProject<? extends Plan> project, PushDownContext ctx) {
+ // TopN can skip or reorder input before this project. Crossing
assert_true(k < 3), for
+ // example, could suppress errors in pruned row groups instead of
preserving evaluation.
+ if
(project.getProjects().stream().anyMatch(Expression::containsVolatileOrNoneMovableExpression))
{
Review Comment:
[P2] Fence throwing date arithmetic before pushing the TopN filter
This marker check still allows `weeks_add(d, 1)`, which implements
`DateAddSubMonotonic` rather than either checked marker, although its DATEV2
implementation throws on overflow. A reduced triggering plan is:
```text
TopN(k ASC, v ASC LIMIT 1)
Project(k, weeks_add(d, 1) AS v)
FileScan(k, d)
```
The secondary order key keeps `v` below TopN. In one Parquet split/scanner,
let physical RG0 contain `(k=100, d=9999-12-31)` and RG1 contain `(k=0,
d=2020-01-01)` for several full output batches. Previously RG0 reached the
scanner projection and raised overflow before any frontier existed. The new
ordering reads RG1 first; when its output publishes bound `k=0` before the next
group opens, the footer recheck skips RG0 and the query can succeed.
`allow_row_reordering` only inspects scan conjuncts, so it cannot see the
projection fused onto ScanNode by the translator. Extend the project fence to
throwing date-arithmetic semantics and add this secondary-key/error regression.
##########
be/src/format_v2/parquet/parquet_scan.cpp:
##########
@@ -1409,21 +1414,147 @@ std::vector<format::LocalColumnIndex>
ParquetScanScheduler::adaptive_predicate_p
return result;
}
+namespace {
+
+struct TopNOrderingColumn {
+ const VTopNPred* predicate;
+ const ParquetColumnSchema* schema;
+};
+
+std::optional<TopNOrderingColumn> find_topn_ordering_column(
+ const std::vector<std::unique_ptr<ParquetColumnSchema>>& file_schema,
+ const format::FileScanRequest& request) {
+ const size_t safe_count =
+ std::min(request.metadata_pruning_safe_conjunct_count,
request.conjuncts.size());
+ const VTopNPred* topn = nullptr;
+ for (size_t idx = 0; idx < safe_count; ++idx) {
+ const auto& root = request.conjuncts[idx]->root();
+ if (root->is_topn_filter() && root->can_evaluate_zonemap_filter()) {
+ topn = assert_cast<const VTopNPred*>(root.get());
+ break;
+ }
+ }
+ if (topn == nullptr) {
+ return std::nullopt;
+ }
+ const auto* slot = assert_cast<const VSlotRef*>(topn->get_child(0).get());
+ const auto position = std::ranges::find_if(request.local_positions,
[&](const auto& entry) {
+ return entry.second.value() == slot->column_id();
+ });
+ if (position == request.local_positions.end()) {
+ return std::nullopt;
+ }
+ const auto column = std::ranges::find_if(file_schema, [&](const auto&
schema) {
+ return schema->local_id == position->first.value();
+ });
+ if (column == file_schema.end() || (*column)->kind !=
ParquetColumnSchemaKind::PRIMITIVE ||
+ (*column)->max_repetition_level != 0 ||
+ !expr_zonemap::data_types_compatible((*column)->type,
slot->data_type())) {
+ return std::nullopt;
+ }
+ return TopNOrderingColumn {.predicate = topn, .schema = column->get()};
+}
+
+} // namespace
+
+void ParquetScanScheduler::prepare_topn_row_group_order(
+ const ParquetFileContext& file_context,
+ const std::vector<std::unique_ptr<ParquetColumnSchema>>& file_schema,
+ const format::FileScanRequest& request) {
+ _row_group_order_prepared = true;
+ const auto binding = find_topn_ordering_column(file_schema, request);
+ if (!binding.has_value()) {
+ return;
+ }
+ const auto* topn = binding->predicate;
+ const auto& schema = *binding->schema;
+ const size_t safe_count =
+ std::min(request.metadata_pruning_safe_conjunct_count,
request.conjuncts.size());
+ const auto& metadata = file_context.native_metadata->to_thrift();
+ _topn_footer_recheck = std::ranges::any_of(_scan_plan->row_groups,
[&](const auto& plan) {
+ const auto& group = metadata.row_groups[plan.row_group_id];
+ DORIS_CHECK(schema.leaf_column_id >= 0 && schema.leaf_column_id <
group.columns.size());
+ const auto& chunk = group.columns[schema.leaf_column_id];
+ return chunk.__isset.meta_data && chunk.meta_data.__isset.statistics;
+ });
+ // Files without statistics cannot benefit from repeated footer checks.
Row-ID fetches promise
+ // increasing positions, and an unsafe table expression must continue
observing file order.
+ if (!_topn_footer_recheck || request.row_ids.has_value() ||
!request.allow_row_reordering ||
Review Comment:
[P2] Include join predicates in the reordering safety fence
These request flags only describe scan conjuncts; they cannot establish
safety for expressions retained on a join above the scan. For example, this
reduced plan is still allowed by
`TopnFilterPushDownVisitor.visitPhysicalHashJoin`:
```text
TopN(l.k ASC LIMIT 1)
InnerHashJoin(l.id = r.id;
other: assert_true(l.k < 3, 'bad'))
FileScan(l.id, l.k)
small matching build r
```
`PushDownJoinOtherCondition` deliberately keeps this NoneMovable predicate
on the join, and the translator preserves it as an other-join conjunct. With
one broadcast-build/local-TopN fragment, one Parquet split/scanner, all IDs
matching, physical RG0 containing `k=100` and RG1 containing several full
batches of `k=0`, the old physical order raises `bad` before a frontier exists.
Reordering visits RG1 first; after its joined rows publish bound `0`, the new
footer check can reject RG0 before the assertion executes. The scan's reorder
flag remains true, and the new project-only visitor guard is never applicable.
Fence TopN pushdown at evaluation-sensitive join predicates and cover this
reversed-group ON-assertion case. Expanding the project-expression check in the
other finding does not fix this separate join boundary.
--
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]