xudong963 commented on code in PR #14207:
URL: https://github.com/apache/datafusion/pull/14207#discussion_r1939527050
##########
datafusion/physical-optimizer/src/enforce_distribution.rs:
##########
@@ -986,18 +993,24 @@ fn add_spm_on_top(input: DistributionContext) ->
DistributionContext {
/// ```
fn remove_dist_changing_operators(
Review Comment:
The root of the bug is that if there is a **SortPreservingMergeExec**
operator and it has the fetch, the method will remove it directly, and miss the
fetch.
IIUC, **SortPreservingMergeExec** only will be added after `EnforceSorting`,
so if we want to reproduce the bug, `EnforceDistribution` needs to run after
`EnforceSorting`, the default physical optimizer has the order of rules:
`EnforceDistribution` -> `EnforceSorting`, so it won't trigger the bug(fetch is
missed), but if we run `EnforceDistribution` again after `EnforceSorting`, the
bug will surface.
What the PR is doing is that it will remain the fetch of
`SortPreservingMergeExec` and add it back if necessary, as the comments of the
method said: `If they are necessary, they will be added in subsequent stages.`
##########
datafusion/core/tests/physical_optimizer/enforce_distribution.rs:
##########
@@ -3172,3 +3181,78 @@ fn optimize_away_unnecessary_repartition2() ->
Result<()> {
Ok(())
}
+
+#[tokio::test]
+async fn apply_enforce_distribution_multiple_times() -> Result<()> {
Review Comment:
make sense
##########
datafusion/physical-optimizer/src/enforce_distribution.rs:
##########
@@ -1362,6 +1383,21 @@ pub fn ensure_distribution(
plan.with_new_children(children_plans)?
};
+ // If `fetch` was not consumed, it means that there was
`SortPreservingMergeExec` with fetch before
+ // It was removed by `remove_dist_changing_operators`
+ // and we need to add it back.
+ if fetch.is_some() {
+ plan = Arc::new(
+ SortPreservingMergeExec::new(
+ plan.output_ordering()
+ .unwrap_or(&LexOrdering::default())
+ .clone(),
+ plan,
+ )
+ .with_fetch(fetch.take()),
+ )
Review Comment:
If use the `GlobalLimitExec`, we also need to maintain the **skip**, I think
it will make code complexity(Maybe need to add the `skip()` method to
`ExecutionPlan`), so I directly follow the comment of
`remove_dist_changing_operators`: `If they are necessary, they will be added in
subsequent stages.`.
##########
datafusion/physical-optimizer/src/enforce_distribution.rs:
##########
@@ -1020,23 +1033,26 @@ fn remove_dist_changing_operators(
/// ```
fn replace_order_preserving_variants(
mut context: DistributionContext,
-) -> Result<DistributionContext> {
- context.children = context
- .children
- .into_iter()
- .map(|child| {
- if child.data {
- replace_order_preserving_variants(child)
- } else {
- Ok(child)
- }
- })
- .collect::<Result<Vec<_>>>()?;
+) -> Result<(DistributionContext, Option<usize>)> {
+ let mut children = vec![];
+ let mut fetch = None;
+ for child in context.children.into_iter() {
Review Comment:
make sense, will give a try
--
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]