Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Cargo.lock

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

23 changes: 22 additions & 1 deletion datafusion/core/src/physical_planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,10 +802,17 @@ impl DefaultPhysicalPlanner {
));
}
}
return internal_err!(
debug!(
"Physical input schema should be the same as the one converted from logical input schema. Differences: {}",
differences.iter().map(|s| format!("\n\t- {s}")).join("")
);

//influx: temporarily remove error and only log so that we can find a
//reproducer in production
// return internal_err!("Physical input schema should be the same as the one converted from logical input schema. Differences: {}", differences
// .iter()
// .map(|s| format!("\n\t- {s}"))
// .join(""));
}

let groups = self.create_grouping_physical_expr(
Expand Down Expand Up @@ -4207,6 +4214,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_metadata() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4227,6 +4236,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_field_count() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4247,6 +4258,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_field_name() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4268,6 +4281,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_field_type() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4286,6 +4301,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_field_nullability() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4304,6 +4321,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_field_metadata() {
let logical_schema =
Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
Expand All @@ -4324,6 +4343,8 @@ digraph {
}

#[tokio::test]
// Ignored due to disabling the physical schema check skip.
#[ignore]
async fn test_aggregate_schema_mismatch_multiple() {
let logical_schema = Arc::new(Schema::new(vec![
Field::new("c1", DataType::Int32, false),
Expand Down
40 changes: 36 additions & 4 deletions datafusion/physical-expr/src/equivalence/properties/union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,43 @@ fn calculate_union_binary(
})
.collect::<Vec<_>>();

// TEMP HACK WORKAROUND
// Revert code from https://github.com/apache/datafusion/pull/12562
// Context: https://github.com/apache/datafusion/issues/13748
// Context: https://github.com/influxdata/influxdb_iox/issues/13038

// Next, calculate valid orderings for the union by searching for prefixes
// in both sides.
let mut orderings = UnionEquivalentOrderingBuilder::new();
orderings.add_satisfied_orderings(&lhs, &rhs)?;
orderings.add_satisfied_orderings(&rhs, &lhs)?;
let orderings = orderings.build();
let mut orderings = vec![];
for ordering in lhs.normalized_oeq_class().into_iter() {
let mut ordering: Vec<PhysicalSortExpr> = ordering.into();

// Progressively shorten the ordering to search for a satisfied prefix:
while !rhs.ordering_satisfy(ordering.clone())? {
ordering.pop();
}
// There is a non-trivial satisfied prefix, add it as a valid ordering:
if !ordering.is_empty() {
orderings.push(ordering);
}
}

for ordering in rhs.normalized_oeq_class().into_iter() {
let mut ordering: Vec<PhysicalSortExpr> = ordering.into();

// Progressively shorten the ordering to search for a satisfied prefix:
while !lhs.ordering_satisfy(ordering.clone())? {
ordering.pop();
}
// There is a non-trivial satisfied prefix, add it as a valid ordering:
if !ordering.is_empty() {
orderings.push(ordering);
}
}
let mut eq_properties = EquivalenceProperties::new(lhs.schema);
eq_properties.add_constants(constants)?;
eq_properties.add_orderings(orderings);

Ok(eq_properties)
}

Expand Down Expand Up @@ -122,6 +149,7 @@ struct UnionEquivalentOrderingBuilder {
orderings: Vec<LexOrdering>,
}

#[expect(unused)]
impl UnionEquivalentOrderingBuilder {
fn new() -> Self {
Self { orderings: vec![] }
Expand Down Expand Up @@ -504,6 +532,7 @@ mod tests {
}

#[test]
#[ignore = "InfluxData patch: chore: skip order calculation / exponential planning"]
fn test_union_equivalence_properties_constants_fill_gaps() -> Result<()> {
let schema = create_test_schema().unwrap();
UnionEquivalenceTest::new(&schema)
Expand Down Expand Up @@ -579,6 +608,7 @@ mod tests {
}

#[test]
#[ignore = "InfluxData patch: chore: skip order calculation / exponential planning"]
fn test_union_equivalence_properties_constants_fill_gaps_non_symmetric() -> Result<()>
{
let schema = create_test_schema().unwrap();
Expand Down Expand Up @@ -607,6 +637,7 @@ mod tests {
}

#[test]
#[ignore = "InfluxData patch: chore: skip order calculation / exponential planning"]
fn test_union_equivalence_properties_constants_gap_fill_symmetric() -> Result<()> {
let schema = create_test_schema().unwrap();
UnionEquivalenceTest::new(&schema)
Expand Down Expand Up @@ -658,6 +689,7 @@ mod tests {
}

#[test]
#[ignore = "InfluxData patch: chore: skip order calculation / exponential planning"]
fn test_union_equivalence_properties_constants_middle_desc() -> Result<()> {
let schema = create_test_schema().unwrap();
UnionEquivalenceTest::new(&schema)
Expand Down
74 changes: 74 additions & 0 deletions datafusion/physical-optimizer/src/enforce_sorting/sort_pushdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use datafusion_physical_expr_common::sort_expr::{
LexOrdering, LexRequirement, OrderingRequirements, PhysicalSortExpr,
PhysicalSortRequirement,
};
use datafusion_physical_plan::aggregates::AggregateExec;
use datafusion_physical_plan::execution_plan::CardinalityEffect;
use datafusion_physical_plan::filter::FilterExec;
use datafusion_physical_plan::joins::utils::{
Expand Down Expand Up @@ -353,6 +354,8 @@ fn pushdown_requirement_to_children(
Ok(None)
}
}
} else if let Some(aggregate_exec) = plan.as_any().downcast_ref::<AggregateExec>() {
handle_aggregate_pushdown(aggregate_exec, parent_required)
} else if maintains_input_order.is_empty()
|| !maintains_input_order.iter().any(|o| *o)
|| plan.as_any().is::<RepartitionExec>()
Expand Down Expand Up @@ -388,6 +391,77 @@ fn pushdown_requirement_to_children(
// TODO: Add support for Projection push down
}

/// Try to push sorting through [`AggregateExec`]
///
/// `AggregateExec` only preserves the input order of its group by columns
/// (not aggregates in general, which are formed from arbitrary expressions over
/// input)
///
/// Thus function rewrites the parent required ordering in terms of the
/// aggregate input if possible. This rewritten requirement represents the
/// ordering of the `AggregateExec`'s **input** that would also satisfy the
/// **parent** ordering.
///
/// If no such mapping is possible (e.g. because the sort references aggregate
/// columns), returns None.
fn handle_aggregate_pushdown(
aggregate_exec: &AggregateExec,
parent_required: OrderingRequirements,
) -> Result<Option<Vec<Option<OrderingRequirements>>>> {
if !aggregate_exec
.maintains_input_order()
.into_iter()
.any(|o| o)
{
return Ok(None);
}

let group_expr = aggregate_exec.group_expr();
// GROUPING SETS introduce additional output columns and NULL substitutions;
// skip pushdown until we can map those cases safely.
if group_expr.has_grouping_set() {
return Ok(None);
}

let group_input_exprs = group_expr.input_exprs();
let parent_requirement = parent_required.into_single();
let mut child_requirement = Vec::with_capacity(parent_requirement.len());

for req in parent_requirement {
// Sort above AggregateExec should reference its output columns. Map each
// output group-by column to its original input expression.
let Some(column) = req.expr.as_any().downcast_ref::<Column>() else {
return Ok(None);
};
if column.index() >= group_input_exprs.len() {
// AggregateExec does not produce output that is sorted on aggregate
// columns so those can not be pushed through.
return Ok(None);
}
child_requirement.push(PhysicalSortRequirement::new(
Arc::clone(&group_input_exprs[column.index()]),
req.options,
));
}

let Some(child_requirement) = LexRequirement::new(child_requirement) else {
return Ok(None);
};

// Keep sort above aggregate unless input ordering already satisfies the
// mapped requirement.
if aggregate_exec
.input()
.equivalence_properties()
.ordering_satisfy_requirement(child_requirement.iter().cloned())?
{
let child_requirements = OrderingRequirements::new(child_requirement);
Ok(Some(vec![Some(child_requirements)]))
} else {
Ok(None)
}
}

/// Return true if pushing the sort requirements through a node would violate
/// the input sorting requirements for the plan
fn pushdown_would_violate_requirements(
Expand Down
10 changes: 10 additions & 0 deletions datafusion/physical-optimizer/src/sanity_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use datafusion_common::tree_node::{Transformed, TransformedResult, TreeNode};
use datafusion_physical_expr::intervals::utils::{check_support, is_datatype_supported};
use datafusion_physical_plan::execution_plan::{Boundedness, EmissionType};
use datafusion_physical_plan::joins::SymmetricHashJoinExec;
use datafusion_physical_plan::sorts::sort::SortExec;
use datafusion_physical_plan::union::UnionExec;
use datafusion_physical_plan::{ExecutionPlanProperties, get_plan_string};

use crate::PhysicalOptimizerRule;
Expand Down Expand Up @@ -136,6 +138,14 @@ pub fn check_plan_sanity(
plan.required_input_ordering(),
plan.required_input_distribution(),
) {
// TEMP HACK WORKAROUND https://github.com/apache/datafusion/issues/11492
if child.as_any().downcast_ref::<UnionExec>().is_some() {
continue;
}
if child.as_any().downcast_ref::<SortExec>().is_some() {
continue;
}

let child_eq_props = child.equivalence_properties();
if let Some(sort_req) = sort_req {
let sort_req = sort_req.into_single();
Expand Down
9 changes: 5 additions & 4 deletions datafusion/physical-plan/src/joins/cross_join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use super::utils::{
OnceAsync, OnceFut, StatefulStreamResult, adjust_right_output_partitioning,
reorder_output_after_swap,
};
use crate::coop::cooperative;
use crate::execution_plan::{EmissionType, boundedness_from_children};
use crate::metrics::{ExecutionPlanMetricsSet, MetricsSet};
use crate::projection::{
Expand Down Expand Up @@ -332,7 +333,7 @@ impl ExecutionPlan for CrossJoinExec {
})?;

if enforce_batch_size_in_joins {
Ok(Box::pin(CrossJoinStream {
Ok(Box::pin(cooperative(CrossJoinStream {
schema: Arc::clone(&self.schema),
left_fut,
right: stream,
Expand All @@ -341,9 +342,9 @@ impl ExecutionPlan for CrossJoinExec {
state: CrossJoinStreamState::WaitBuildSide,
left_data: RecordBatch::new_empty(self.left().schema()),
batch_transformer: BatchSplitter::new(batch_size),
}))
})))
} else {
Ok(Box::pin(CrossJoinStream {
Ok(Box::pin(cooperative(CrossJoinStream {
schema: Arc::clone(&self.schema),
left_fut,
right: stream,
Expand All @@ -352,7 +353,7 @@ impl ExecutionPlan for CrossJoinExec {
state: CrossJoinStreamState::WaitBuildSide,
left_data: RecordBatch::new_empty(self.left().schema()),
batch_transformer: NoopBatchTransformer::new(),
}))
})))
}
}

Expand Down
Loading