Repository navigation
refactor: Make AggregateExec state modeling and public updates safe (part 1: distinct limit) - #25564
Conversation
| use std::sync::Arc; | ||
|
|
||
| use datafusion_physical_plan::aggregates::{AggregateExec, LimitOptions}; | ||
| use datafusion_physical_plan::aggregates::AggregateExec; |
| use arrow_schema::FieldRef; | ||
| use datafusion_common::stats::Precision; | ||
| use datafusion_common::tree_node::TreeNodeRecursion; | ||
| use datafusion_common::tree_node::{Transformed, TreeNodeRecursion}; |
There was a problem hiding this comment.
This file includes structural change 2.
Remaning diffs are all mechanical access method change
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #25564 +/- ##
==========================================
- Coverage 82.42% 82.42% -0.01%
==========================================
Files 1139 1139
Lines 435410 435467 +57
Branches 435410 435467 +57
==========================================
+ Hits 358884 358922 +38
- Misses 54822 54838 +16
- Partials 21704 21707 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
alamb
left a comment
There was a problem hiding this comment.
Thanks @2010YOUY01 this makes sense to me. I had a few concerns about some extra cloning and filter reset, but the basic pattern looks great to me. I like where this is heading
FYI @rluvaton @Rachelint and @jayzhan211 who I think are also working in this area
| let new_aggr = aggr.with_new_limit_options(Some(LimitOptions::new(limit))); | ||
|
|
||
| Some(Arc::new(new_aggr)) | ||
| let new_aggr = aggr.clone().try_optimize_distinct_soft_limit(limit).ok()?; |
There was a problem hiding this comment.
The pattern of ignoring Errs I think is bad in DataFusion because each Err contains an allocated string (so this is inefficient).
Perhaps we could just change try_optimize_distinct_soft_limit to return an Option rather than Result 🤔
Also, it would be great to avoid the need to clone the input(as this is a deep clone, not just a an arc clone)
There was a problem hiding this comment.
Optioninstead ofResult: I agree, updated in 2cf27d8- Avoiding deep clone: It seems impossible due to the optimizer interface restriction?
| rewrite_applicable = false; | ||
| Ok(Transformed::no(plan)) | ||
| }; | ||
| let child = child.to_owned().transform_down(closure).data().ok()?; |
There was a problem hiding this comment.
though here is an example of discarding an error in existing code too 😬
| /// | ||
| /// The remaining streams consume all input. | ||
| limit_options: Option<LimitOptions>, | ||
| kind: AggregateKind, |
| mut self, | ||
| limit: usize, | ||
| ) -> Result<Transformed<Self>> { | ||
| if limit == 0 |
There was a problem hiding this comment.
This seems to always return "Ok" -- why is it returning Result?
| // An accumulator rewrite cannot inherit DISTINCT's early stop. | ||
| new.kind = AggregateKind::General { | ||
| group_by: Arc::clone(group_by), | ||
| filter_expr: vec![None; aggr_expr.len()].into(), |
There was a problem hiding this comment.
Why are the filter expressions reset?
There was a problem hiding this comment.
The previous AggregateKind is DistinctLimit, which is for the query pattern select distinct k from t limit 10 after it gets optimized into an aggregate, so it must have no aggregate functions or filters.
This transformation converts DistinctLimit -> General, so there is no filter to init.
However, the deeper question seems to be: is it safe? I'm not 100% sure. Before this refactor, this conversion happened implicitly and was easy to miss. With the typed state, it now shows up obviously.
I'll add more comment for it.
There was a problem hiding this comment.
However, the deeper question seems to be: is it safe?
Agreed
With the typed state, it now shows up obviously.
That is a neat result
|
Thanks @2010YOUY01 this is great! |
… (part 1: distinct limit) (apache#25564) ## Which issue does this PR close? <!-- We generally require a GitHub issue to be filed for all bug fixes and enhancements and this helps us generate change logs for our releases. You can link an issue to this PR using the GitHub syntax. For example `Closes apache#123` indicates that this PR will close issue apache#123. --> - Part of apache#25563 ## Rationale for this change <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. Please explain the problem you are trying to solve in terms of the user-visible behavior, rather than the implementation. For example, "The code in `foo.rs` doesn't handle nulls" is a symptom of the implementation. "COUNT(DISTINCT) returns wrong results when the column contains nulls" is the user-visible problem. --> See issue for the idea, this PR is the step 1 in the issue. For backgrounds for distinct limit optimization (this PR targets), see https://github.com/apache/datafusion/blob/1e09a2a3d76c03f469e80bed712a6bada45e77c8/datafusion/physical-plan/src/aggregates/hash_stream.rs#L77 ## What changes are included in this PR? <!-- There is no need to duplicate the description in the issue here, but it is sometimes worth providing a summary of the individual changes in this PR. --> 1. Refactor AggregateExec with enum state 2. Add safe API for this optimization, and use it in the optimizer. (unsafe one will be able to be deleted after step2, now it's reused by other path) I'll mark the structural changes (only 2 files) Most diff is mechanical changes like `aggregate_exec.aggr_expr` -> `aggregate_exec.aggr_expr()`, caused by the refactor. ## What is the testing strategy for this PR? <!-- We typically require tests for all PRs in order to: 1. Prevent the code from being accidentally broken by subsequent changes 4. Serve as another way to document the expected behavior of the code Briefly describe how this PR is tested, and point to the specific tests you added. For example: 'This new feature is covered by the `sqllogictest` cases added in `foo.slt`'. If this PR does not add tests, explain why. For example, if the change is already covered by existing tests, please mention it. You should also check the `codecov` bot reply on this PR to confirm the changed code is exercised. --> Existing tests. ## Are there any user-facing changes? <!-- If there are user-facing changes then we may require documentation to be updated before approving the PR. If there are any breaking changes to public APIs, please add the `api change` label. --> no
Which issue does this PR close?
AggregateExecstate modeling and public updates safe #25563Rationale for this change
See issue for the idea, this PR is the step 1 in the issue.
For backgrounds for distinct limit optimization (this PR targets), see
datafusion/datafusion/physical-plan/src/aggregates/hash_stream.rs
Line 77 in 1e09a2a
What changes are included in this PR?
(unsafe one will be able to be deleted after step2, now it's reused by other path)
I'll mark the structural changes (only 2 files)
Most diff is mechanical changes like
aggregate_exec.aggr_expr->aggregate_exec.aggr_expr(), caused by the refactor.What is the testing strategy for this PR?
Existing tests.
Are there any user-facing changes?
no