Skip to content
Merged
382 changes: 336 additions & 46 deletions datafusion/src/physical_optimizer/repartition.rs

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::{
use arrow::{array::StringBuilder, datatypes::SchemaRef, record_batch::RecordBatch};
use futures::StreamExt;

use super::expressions::PhysicalSortExpr;
use super::{stream::RecordBatchReceiverStream, Distribution, SendableRecordBatchStream};
use crate::execution::runtime_env::RuntimeEnv;
use async_trait::async_trait;
Expand Down Expand Up @@ -82,6 +83,10 @@ impl ExecutionPlan for AnalyzeExec {
Partitioning::UnknownPartitioning(1)
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to sprinkle output_ordering around was annoying -- but I think it may be worth it to try and avoid some nasty bugs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, makes sense to be explicit

None
}

fn with_new_children(
&self,
mut children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/coalesce_batches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use async_trait::async_trait;
use futures::stream::{Stream, StreamExt};
use log::debug;

use super::expressions::PhysicalSortExpr;
use super::metrics::{BaselineMetrics, MetricsSet};
use super::{metrics::ExecutionPlanMetricsSet, Statistics};

Expand Down Expand Up @@ -97,6 +98,10 @@ impl ExecutionPlan for CoalesceBatchesExec {
self.input.output_partitioning()
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/coalesce_partitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use arrow::record_batch::RecordBatch;
use arrow::{datatypes::SchemaRef, error::Result as ArrowResult};

use super::common::AbortOnDropMany;
use super::expressions::PhysicalSortExpr;
use super::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet};
use super::{RecordBatchStream, Statistics};
use crate::error::{DataFusionError, Result};
Expand Down Expand Up @@ -86,6 +87,10 @@ impl ExecutionPlan for CoalescePartitionsExec {
Partitioning::UnknownPartitioning(1)
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/cross_join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use arrow::record_batch::RecordBatch;

use futures::{Stream, TryStreamExt};

use super::expressions::PhysicalSortExpr;
use super::{
coalesce_partitions::CoalescePartitionsExec, join_utils::check_join_is_valid,
ColumnStatistics, Statistics,
Expand Down Expand Up @@ -137,6 +138,10 @@ impl ExecutionPlan for CrossJoinExec {
self.right.output_partitioning()
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

async fn execute(
&self,
partition: usize,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use arrow::array::NullArray;
use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
use arrow::record_batch::RecordBatch;

use super::expressions::PhysicalSortExpr;
use super::{common, SendableRecordBatchStream, Statistics};

use crate::execution::runtime_env::RuntimeEnv;
Expand Down Expand Up @@ -98,6 +99,10 @@ impl ExecutionPlan for EmptyExec {
Partitioning::UnknownPartitioning(1)
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
6 changes: 5 additions & 1 deletion datafusion/src/physical_plan/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::{
};
use arrow::{array::StringBuilder, datatypes::SchemaRef, record_batch::RecordBatch};

use super::SendableRecordBatchStream;
use super::{expressions::PhysicalSortExpr, SendableRecordBatchStream};
use crate::execution::runtime_env::RuntimeEnv;
use crate::physical_plan::metrics::{ExecutionPlanMetricsSet, MemTrackingMetrics};
use async_trait::async_trait;
Expand Down Expand Up @@ -89,6 +89,10 @@ impl ExecutionPlan for ExplainExec {
Partitioning::UnknownPartitioning(1)
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/file_format/avro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#[cfg(feature = "avro")]
use crate::avro_to_arrow;
use crate::error::{DataFusionError, Result};
use crate::physical_plan::expressions::PhysicalSortExpr;
use crate::physical_plan::{
DisplayFormatType, ExecutionPlan, Partitioning, SendableRecordBatchStream, Statistics,
};
Expand Down Expand Up @@ -74,6 +75,10 @@ impl ExecutionPlan for AvroExec {
Partitioning::UnknownPartitioning(self.base_config.file_groups.len())
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn children(&self) -> Vec<Arc<dyn ExecutionPlan>> {
Vec::new()
}
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/file_format/csv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//! Execution plan for reading CSV files

use crate::error::{DataFusionError, Result};
use crate::physical_plan::expressions::PhysicalSortExpr;
use crate::physical_plan::{
DisplayFormatType, ExecutionPlan, Partitioning, SendableRecordBatchStream, Statistics,
};
Expand Down Expand Up @@ -88,6 +89,10 @@ impl ExecutionPlan for CsvExec {
Partitioning::UnknownPartitioning(self.base_config.file_groups.len())
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn children(&self) -> Vec<Arc<dyn ExecutionPlan>> {
// this is a leaf node and has no children
vec![]
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/file_format/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use async_trait::async_trait;

use crate::error::{DataFusionError, Result};
use crate::execution::runtime_env::RuntimeEnv;
use crate::physical_plan::expressions::PhysicalSortExpr;
use crate::physical_plan::{
DisplayFormatType, ExecutionPlan, Partitioning, SendableRecordBatchStream, Statistics,
};
Expand Down Expand Up @@ -65,6 +66,10 @@ impl ExecutionPlan for NdJsonExec {
Partitioning::UnknownPartitioning(self.base_config.file_groups.len())
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn children(&self) -> Vec<Arc<dyn ExecutionPlan>> {
Vec::new()
}
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/file_format/parquet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use std::{any::Any, convert::TryInto};
use crate::datasource::file_format::parquet::ChunkObjectReader;
use crate::datasource::object_store::ObjectStore;
use crate::datasource::PartitionedFile;
use crate::physical_plan::expressions::PhysicalSortExpr;
use crate::{
error::{DataFusionError, Result},
logical_plan::{Column, Expr},
Expand Down Expand Up @@ -174,6 +175,10 @@ impl ExecutionPlan for ParquetExec {
Partitioning::UnknownPartitioning(self.base_config.file_groups.len())
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
10 changes: 10 additions & 0 deletions datafusion/src/physical_plan/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use super::expressions::PhysicalSortExpr;
use super::{RecordBatchStream, SendableRecordBatchStream, Statistics};
use crate::error::{DataFusionError, Result};
use crate::physical_plan::{
Expand Down Expand Up @@ -104,6 +105,15 @@ impl ExecutionPlan for FilterExec {
self.input.output_partitioning()
}

fn maintains_input_order(&self) -> bool {
// tell optimizer this operator doesn't reorder its input
true
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
self.input.output_ordering()
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
9 changes: 9 additions & 0 deletions datafusion/src/physical_plan/hash_aggregate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use crate::execution::runtime_env::RuntimeEnv;
use async_trait::async_trait;

use super::common::AbortOnDropSingle;
use super::expressions::PhysicalSortExpr;
use super::metrics::{
self, BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet, RecordOutput,
};
Expand Down Expand Up @@ -208,6 +209,10 @@ impl ExecutionPlan for HashAggregateExec {
self.input.output_partitioning()
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

async fn execute(
&self,
partition: usize,
Expand Down Expand Up @@ -1148,6 +1153,10 @@ mod tests {
Partitioning::UnknownPartitioning(1)
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
_: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/hash_join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use hashbrown::raw::RawTable;

use super::{
coalesce_partitions::CoalescePartitionsExec,
expressions::PhysicalSortExpr,
join_utils::{build_join_schema, check_join_is_valid, ColumnIndex, JoinOn, JoinSide},
};
use super::{
Expand Down Expand Up @@ -278,6 +279,10 @@ impl ExecutionPlan for HashJoinExec {
self.right.output_partitioning()
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably doesn't make sense to address this now, but order of the right side might be (fully or partially) maintained for hash joins.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To give a concrete example, if the right side of the join is sorted on field x and we use an inner join, the output is sorted on x too as rows are not reordered.

@alamb alamb Feb 8, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 makes sense.

I think that only applies for inner joins though (as some types of outer joins may stick nulls into inconvenient places 😆 )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 😂 I think right join is the other one that maintains sortedness too.

}

async fn execute(
&self,
partition: usize,
Expand Down
40 changes: 35 additions & 5 deletions datafusion/src/physical_plan/limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use arrow::datatypes::SchemaRef;
use arrow::error::Result as ArrowResult;
use arrow::record_batch::RecordBatch;

use super::expressions::PhysicalSortExpr;
use super::{
metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet},
RecordBatchStream, SendableRecordBatchStream, Statistics,
Expand Down Expand Up @@ -99,6 +100,22 @@ impl ExecutionPlan for GlobalLimitExec {
Partitioning::UnknownPartitioning(1)
}

fn relies_on_input_order(&self) -> bool {
self.input.output_ordering().is_some()
}

fn maintains_input_order(&self) -> bool {
true
}

fn benefits_from_input_partitioning(&self) -> bool {
false
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
self.input.output_ordering()
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down Expand Up @@ -232,6 +249,24 @@ impl ExecutionPlan for LocalLimitExec {
self.input.output_partitioning()
}

fn relies_on_input_order(&self) -> bool {
self.input.output_ordering().is_some()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like an optimization that really belongs in the Repartition optimizer, namely that if the children of a plan don't have a sort order, you can freely repartition them even if the parent relies_on_input_order.

}

fn benefits_from_input_partitioning(&self) -> bool {
false
}

// Local limit does not make any attempt to maintain the input
// sortedness (if there is more than one partition)
fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
if self.output_partitioning().partition_count() == 1 {
self.input.output_ordering()
} else {
None
}
}

fn with_new_children(
&self,
children: Vec<Arc<dyn ExecutionPlan>>,
Expand Down Expand Up @@ -300,11 +335,6 @@ impl ExecutionPlan for LocalLimitExec {
_ => Statistics::default(),
}
}

fn should_repartition_children(&self) -> bool {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is effectively renamed to benefits_from_input_partitioning

// No reason to repartition children as this node is just limiting each input partition.
false
}
}

/// Truncate a RecordBatch to maximum of n rows
Expand Down
5 changes: 5 additions & 0 deletions datafusion/src/physical_plan/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::any::Any;
use std::sync::Arc;
use std::task::{Context, Poll};

use super::expressions::PhysicalSortExpr;
use super::{
common, project_schema, DisplayFormatType, ExecutionPlan, Partitioning,
RecordBatchStream, SendableRecordBatchStream, Statistics,
Expand Down Expand Up @@ -77,6 +78,10 @@ impl ExecutionPlan for MemoryExec {
Partitioning::UnknownPartitioning(self.partitions.len())
}

fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}

fn with_new_children(
&self,
_: Vec<Arc<dyn ExecutionPlan>>,
Expand Down
Loading