Skip to content

Commit

Permalink
Merge commit '1dd535451198777693ac962a21b79a680bd4b7e8' into chunchun…
Browse files Browse the repository at this point in the history
…/update-df-apr-week-1-2
  • Loading branch information
appletreeisyellow committed Apr 22, 2024
2 parents a689b9d + 1dd5354 commit 59cb935
Show file tree
Hide file tree
Showing 26 changed files with 972 additions and 655 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"datafusion/functions",
"datafusion/functions-array",
"datafusion/optimizer",
"datafusion/physical-expr-common",
"datafusion/physical-expr",
"datafusion/physical-plan",
"datafusion/proto",
Expand Down Expand Up @@ -80,6 +81,7 @@ datafusion-functions = { path = "datafusion/functions", version = "37.0.0" }
datafusion-functions-array = { path = "datafusion/functions-array", version = "37.0.0" }
datafusion-optimizer = { path = "datafusion/optimizer", version = "37.0.0", default-features = false }
datafusion-physical-expr = { path = "datafusion/physical-expr", version = "37.0.0", default-features = false }
datafusion-physical-expr-common = { path = "datafusion/physical-expr-common", version = "37.0.0", default-features = false }
datafusion-physical-plan = { path = "datafusion/physical-plan", version = "37.0.0" }
datafusion-proto = { path = "datafusion/proto", version = "37.0.0" }
datafusion-sql = { path = "datafusion/sql", version = "37.0.0" }
Expand Down
10 changes: 10 additions & 0 deletions datafusion-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 64 additions & 32 deletions datafusion/core/src/physical_optimizer/enforce_distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1327,11 +1327,6 @@ pub(crate) mod tests {
}

impl SortRequiredExec {
fn new(input: Arc<dyn ExecutionPlan>) -> Self {
let expr = input.output_ordering().unwrap_or(&[]).to_vec();
Self::new_with_requirement(input, expr)
}

fn new_with_requirement(
input: Arc<dyn ExecutionPlan>,
requirement: Vec<PhysicalSortExpr>,
Expand Down Expand Up @@ -1391,10 +1386,11 @@ pub(crate) mod tests {

// model that it requires the output ordering of its input
fn required_input_ordering(&self) -> Vec<Option<Vec<PhysicalSortRequirement>>> {
vec![self
.properties()
.output_ordering()
.map(PhysicalSortRequirement::from_sort_exprs)]
if self.expr.is_empty() {
vec![None]
} else {
vec![Some(PhysicalSortRequirement::from_sort_exprs(&self.expr))]
}
}

fn with_new_children(
Expand Down Expand Up @@ -1677,10 +1673,6 @@ pub(crate) mod tests {
Arc::new(UnionExec::new(input))
}

fn sort_required_exec(input: Arc<dyn ExecutionPlan>) -> Arc<dyn ExecutionPlan> {
Arc::new(SortRequiredExec::new(input))
}

fn sort_required_exec_with_req(
input: Arc<dyn ExecutionPlan>,
sort_exprs: LexOrdering,
Expand Down Expand Up @@ -3206,8 +3198,10 @@ pub(crate) mod tests {
expr: col("c", &schema).unwrap(),
options: SortOptions::default(),
}];
let plan =
sort_required_exec(filter_exec(sort_exec(sort_key, parquet_exec(), false)));
let plan = sort_required_exec_with_req(
filter_exec(sort_exec(sort_key.clone(), parquet_exec(), false)),
sort_key,
);

let expected = &[
"SortRequiredExec: [c@2 ASC]",
Expand Down Expand Up @@ -3367,18 +3361,20 @@ pub(crate) mod tests {
// Parquet(sorted)
let schema = schema();
let sort_key = vec![PhysicalSortExpr {
expr: col("c", &schema).unwrap(),
expr: col("d", &schema).unwrap(),
options: SortOptions::default(),
}];
let plan =
sort_required_exec(filter_exec(parquet_exec_with_sort(vec![sort_key])));
let plan = sort_required_exec_with_req(
filter_exec(parquet_exec_with_sort(vec![sort_key.clone()])),
sort_key,
);

// during repartitioning ordering is preserved
let expected = &[
"SortRequiredExec: [c@2 ASC]",
"SortRequiredExec: [d@3 ASC]",
"FilterExec: c@2 = 0",
"RepartitionExec: partitioning=RoundRobinBatch(10), input_partitions=1",
"ParquetExec: file_groups={1 group: [[x]]}, projection=[a, b, c, d, e], output_ordering=[c@2 ASC]",
"ParquetExec: file_groups={1 group: [[x]]}, projection=[a, b, c, d, e], output_ordering=[d@3 ASC]",
];

assert_optimized!(expected, plan.clone(), true, true);
Expand All @@ -3403,7 +3399,10 @@ pub(crate) mod tests {
expr: col("c", &schema).unwrap(),
options: SortOptions::default(),
}];
let input1 = sort_required_exec(parquet_exec_with_sort(vec![sort_key]));
let input1 = sort_required_exec_with_req(
parquet_exec_with_sort(vec![sort_key.clone()]),
sort_key,
);
let input2 = filter_exec(parquet_exec());
let plan = union_exec(vec![input1, input2]);

Expand Down Expand Up @@ -3481,10 +3480,13 @@ pub(crate) mod tests {
("c".to_string(), "c".to_string()),
];
// sorted input
let plan = sort_required_exec(projection_exec_with_alias(
parquet_exec_multiple_sorted(vec![sort_key]),
alias,
));
let plan = sort_required_exec_with_req(
projection_exec_with_alias(
parquet_exec_multiple_sorted(vec![sort_key.clone()]),
alias,
),
sort_key,
);

let expected = &[
"SortRequiredExec: [c@2 ASC]",
Expand Down Expand Up @@ -3639,8 +3641,8 @@ pub(crate) mod tests {
options: SortOptions::default(),
}];

let plan = filter_exec(parquet_exec_multiple_sorted(vec![sort_key]));
let plan = sort_required_exec(plan);
let plan = filter_exec(parquet_exec_multiple_sorted(vec![sort_key.clone()]));
let plan = sort_required_exec_with_req(plan, sort_key);

// The groups must have only contiguous ranges of rows from the same file
// if any group has rows from multiple files, the data is no longer sorted destroyed
Expand Down Expand Up @@ -4025,9 +4027,14 @@ pub(crate) mod tests {
}];
// SortRequired
// Parquet(sorted)
let plan_parquet =
sort_required_exec(parquet_exec_with_sort(vec![sort_key.clone()]));
let plan_csv = sort_required_exec(csv_exec_with_sort(vec![sort_key]));
let plan_parquet = sort_required_exec_with_req(
parquet_exec_with_sort(vec![sort_key.clone()]),
sort_key.clone(),
);
let plan_csv = sort_required_exec_with_req(
csv_exec_with_sort(vec![sort_key.clone()]),
sort_key,
);

// no parallelization, because SortRequiredExec doesn't benefit from increased parallelism
let expected_parquet = &[
Expand Down Expand Up @@ -4150,7 +4157,7 @@ pub(crate) mod tests {
}

#[test]
fn preserve_ordering_through_repartition() -> Result<()> {
fn remove_unnecessary_spm_after_filter() -> Result<()> {
let schema = schema();
let sort_key = vec![PhysicalSortExpr {
expr: col("c", &schema).unwrap(),
Expand All @@ -4159,8 +4166,10 @@ pub(crate) mod tests {
let input = parquet_exec_multiple_sorted(vec![sort_key.clone()]);
let physical_plan = sort_preserving_merge_exec(sort_key, filter_exec(input));

// Original plan expects its output to be ordered by c@2 ASC.
// This is still satisfied since, after filter that column is constant.
let expected = &[
"SortPreservingMergeExec: [c@2 ASC]",
"CoalescePartitionsExec",
"FilterExec: c@2 = 0",
"RepartitionExec: partitioning=RoundRobinBatch(10), input_partitions=2, preserve_order=true, sort_exprs=c@2 ASC",
"ParquetExec: file_groups={2 groups: [[x], [y]]}, projection=[a, b, c, d, e], output_ordering=[c@2 ASC]",
Expand All @@ -4172,6 +4181,29 @@ pub(crate) mod tests {
Ok(())
}

#[test]
fn preserve_ordering_through_repartition() -> Result<()> {
let schema = schema();
let sort_key = vec![PhysicalSortExpr {
expr: col("d", &schema).unwrap(),
options: SortOptions::default(),
}];
let input = parquet_exec_multiple_sorted(vec![sort_key.clone()]);
let physical_plan = sort_preserving_merge_exec(sort_key, filter_exec(input));

let expected = &[
"SortPreservingMergeExec: [d@3 ASC]",
"FilterExec: c@2 = 0",
"RepartitionExec: partitioning=RoundRobinBatch(10), input_partitions=2, preserve_order=true, sort_exprs=d@3 ASC",
"ParquetExec: file_groups={2 groups: [[x], [y]]}, projection=[a, b, c, d, e], output_ordering=[d@3 ASC]",
];
// last flag sets config.optimizer.PREFER_EXISTING_SORT
assert_optimized!(expected, physical_plan.clone(), true, true);
assert_optimized!(expected, physical_plan, false, true);

Ok(())
}

#[test]
fn do_not_preserve_ordering_through_repartition() -> Result<()> {
let schema = schema();
Expand Down
38 changes: 38 additions & 0 deletions datafusion/physical-expr-common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

[package]
name = "datafusion-physical-expr-common"
description = "Common functionality of physical expression for DataFusion query engine"
keywords = ["arrow", "query", "sql"]
readme = "README.md"
version = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
authors = { workspace = true }
rust-version = { workspace = true }

[lib]
name = "datafusion_physical_expr_common"
path = "src/lib.rs"

[dependencies]
arrow = { workspace = true }
datafusion-common = { workspace = true, default-features = true }
datafusion-expr = { workspace = true }
27 changes: 27 additions & 0 deletions datafusion/physical-expr-common/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# DataFusion Core Physical Expressions

[DataFusion][df] is an extensible query execution framework, written in Rust, that uses Apache Arrow as its in-memory format.

This crate is a submodule of DataFusion that provides shared APIs for implementing
physical expressions such as `PhysicalExpr` and `PhysicalSortExpr`.

[df]: https://crates.io/crates/datafusion
102 changes: 102 additions & 0 deletions datafusion/physical-expr-common/src/aggregate/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

pub mod utils;

use std::any::Any;
use std::fmt::Debug;
use std::sync::Arc;

use crate::physical_expr::PhysicalExpr;
use crate::sort_expr::PhysicalSortExpr;

use arrow::datatypes::Field;
use datafusion_common::{not_impl_err, Result};
use datafusion_expr::{Accumulator, GroupsAccumulator};

/// An aggregate expression that:
/// * knows its resulting field
/// * knows how to create its accumulator
/// * knows its accumulator's state's field
/// * knows the expressions from whose its accumulator will receive values
///
/// Any implementation of this trait also needs to implement the
/// `PartialEq<dyn Any>` to allows comparing equality between the
/// trait objects.
pub trait AggregateExpr: Send + Sync + Debug + PartialEq<dyn Any> {
/// Returns the aggregate expression as [`Any`] so that it can be
/// downcast to a specific implementation.
fn as_any(&self) -> &dyn Any;

/// the field of the final result of this aggregation.
fn field(&self) -> Result<Field>;

/// the accumulator used to accumulate values from the expressions.
/// the accumulator expects the same number of arguments as `expressions` and must
/// return states with the same description as `state_fields`
fn create_accumulator(&self) -> Result<Box<dyn Accumulator>>;

/// the fields that encapsulate the Accumulator's state
/// the number of fields here equals the number of states that the accumulator contains
fn state_fields(&self) -> Result<Vec<Field>>;

/// expressions that are passed to the Accumulator.
/// Single-column aggregations such as `sum` return a single value, others (e.g. `cov`) return many.
fn expressions(&self) -> Vec<Arc<dyn PhysicalExpr>>;

/// Order by requirements for the aggregate function
/// By default it is `None` (there is no requirement)
/// Order-sensitive aggregators, such as `FIRST_VALUE(x ORDER BY y)` should implement this
fn order_bys(&self) -> Option<&[PhysicalSortExpr]> {
None
}

/// Human readable name such as `"MIN(c2)"`. The default
/// implementation returns placeholder text.
fn name(&self) -> &str {
"AggregateExpr: default name"
}

/// If the aggregate expression has a specialized
/// [`GroupsAccumulator`] implementation. If this returns true,
/// `[Self::create_groups_accumulator`] will be called.
fn groups_accumulator_supported(&self) -> bool {
false
}

/// Return a specialized [`GroupsAccumulator`] that manages state
/// for all groups.
///
/// For maximum performance, a [`GroupsAccumulator`] should be
/// implemented in addition to [`Accumulator`].
fn create_groups_accumulator(&self) -> Result<Box<dyn GroupsAccumulator>> {
not_impl_err!("GroupsAccumulator hasn't been implemented for {self:?} yet")
}

/// Construct an expression that calculates the aggregate in reverse.
/// Typically the "reverse" expression is itself (e.g. SUM, COUNT).
/// For aggregates that do not support calculation in reverse,
/// returns None (which is the default value).
fn reverse_expr(&self) -> Option<Arc<dyn AggregateExpr>> {
None
}

/// Creates accumulator implementation that supports retract
fn create_sliding_accumulator(&self) -> Result<Box<dyn Accumulator>> {
not_impl_err!("Retractable Accumulator hasn't been implemented for {self:?} yet")
}
}
Loading

0 comments on commit 59cb935

Please sign in to comment.