in datafusion/optimizer/src/propagate_empty_relation.rs [55:194]
fn rewrite(
&self,
plan: LogicalPlan,
_config: &dyn OptimizerConfig,
) -> Result<Transformed<LogicalPlan>> {
match plan {
LogicalPlan::EmptyRelation(_) => Ok(Transformed::no(plan)),
LogicalPlan::Projection(_)
| LogicalPlan::Filter(_)
| LogicalPlan::Window(_)
| LogicalPlan::Sort(_)
| LogicalPlan::SubqueryAlias(_)
| LogicalPlan::Repartition(_)
| LogicalPlan::Limit(_) => {
let empty = empty_child(&plan)?;
if let Some(empty_plan) = empty {
return Ok(Transformed::yes(empty_plan));
}
Ok(Transformed::no(plan))
}
LogicalPlan::Join(ref join) => {
// TODO: For Join, more join type need to be careful:
// For LeftOut/Full Join, if the right side is empty, the Join can be eliminated with a Projection with left side
// columns + right side columns replaced with null values.
// For RightOut/Full Join, if the left side is empty, the Join can be eliminated with a Projection with right side
// columns + left side columns replaced with null values.
let (left_empty, right_empty) = binary_plan_children_is_empty(&plan)?;
match join.join_type {
// For Full Join, only both sides are empty, the Join result is empty.
JoinType::Full if left_empty && right_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
JoinType::Inner if left_empty || right_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
JoinType::Left if left_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
JoinType::Right if right_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
JoinType::LeftSemi if left_empty || right_empty => Ok(
Transformed::yes(LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
})),
),
JoinType::RightSemi if left_empty || right_empty => Ok(
Transformed::yes(LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
})),
),
JoinType::LeftAnti if left_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
JoinType::LeftAnti if right_empty => {
Ok(Transformed::yes((*join.left).clone()))
}
JoinType::RightAnti if left_empty => {
Ok(Transformed::yes((*join.right).clone()))
}
JoinType::RightAnti if right_empty => Ok(Transformed::yes(
LogicalPlan::EmptyRelation(EmptyRelation {
produce_one_row: false,
schema: Arc::clone(&join.schema),
}),
)),
_ => Ok(Transformed::no(plan)),
}
}
LogicalPlan::Aggregate(ref agg) => {
if !agg.group_expr.is_empty() {
if let Some(empty_plan) = empty_child(&plan)? {
return Ok(Transformed::yes(empty_plan));
}
}
Ok(Transformed::no(LogicalPlan::Aggregate(agg.clone())))
}
LogicalPlan::Union(ref union) => {
let new_inputs = union
.inputs
.iter()
.filter(|input| match &***input {
LogicalPlan::EmptyRelation(empty) => empty.produce_one_row,
_ => true,
})
.cloned()
.collect::<Vec<_>>();
if new_inputs.len() == union.inputs.len() {
Ok(Transformed::no(plan))
} else if new_inputs.is_empty() {
Ok(Transformed::yes(LogicalPlan::EmptyRelation(
EmptyRelation {
produce_one_row: false,
schema: Arc::clone(plan.schema()),
},
)))
} else if new_inputs.len() == 1 {
let mut new_inputs = new_inputs;
let input_plan = new_inputs.pop().unwrap(); // length checked
let child = Arc::unwrap_or_clone(input_plan);
if child.schema().eq(plan.schema()) {
Ok(Transformed::yes(child))
} else {
Ok(Transformed::yes(LogicalPlan::Projection(
Projection::new_from_schema(
Arc::new(child),
Arc::clone(plan.schema()),
),
)))
}
} else {
Ok(Transformed::yes(LogicalPlan::Union(Union {
inputs: new_inputs,
schema: Arc::clone(&union.schema),
})))
}
}
_ => Ok(Transformed::no(plan)),
}
}