datafusion_optimizer/
eliminate_join.rs1use crate::optimizer::ApplyOrder;
20use crate::{OptimizerConfig, OptimizerRule};
21use datafusion_common::tree_node::Transformed;
22use datafusion_common::{Result, ScalarValue};
23use datafusion_expr::JoinType::Inner;
24use datafusion_expr::{
25 logical_plan::{EmptyRelation, LogicalPlan},
26 Expr,
27};
28
29#[derive(Default, Debug)]
32pub struct EliminateJoin;
33
34impl EliminateJoin {
35 pub fn new() -> Self {
36 Self {}
37 }
38}
39
40impl OptimizerRule for EliminateJoin {
41 fn name(&self) -> &str {
42 "eliminate_join"
43 }
44
45 fn apply_order(&self) -> Option<ApplyOrder> {
46 Some(ApplyOrder::TopDown)
47 }
48
49 fn rewrite(
50 &self,
51 plan: LogicalPlan,
52 _config: &dyn OptimizerConfig,
53 ) -> Result<Transformed<LogicalPlan>> {
54 match plan {
55 LogicalPlan::Join(join) if join.join_type == Inner && join.on.is_empty() => {
56 match join.filter {
57 Some(Expr::Literal(ScalarValue::Boolean(Some(false)))) => Ok(
58 Transformed::yes(LogicalPlan::EmptyRelation(EmptyRelation {
59 produce_one_row: false,
60 schema: join.schema,
61 })),
62 ),
63 _ => Ok(Transformed::no(LogicalPlan::Join(join))),
64 }
65 }
66 _ => Ok(Transformed::no(plan)),
67 }
68 }
69
70 fn supports_rewrite(&self) -> bool {
71 true
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use crate::eliminate_join::EliminateJoin;
78 use crate::test::*;
79 use datafusion_common::Result;
80 use datafusion_expr::JoinType::Inner;
81 use datafusion_expr::{lit, logical_plan::builder::LogicalPlanBuilder, LogicalPlan};
82 use std::sync::Arc;
83
84 fn assert_optimized_plan_equal(plan: LogicalPlan, expected: &str) -> Result<()> {
85 assert_optimized_plan_eq(Arc::new(EliminateJoin::new()), plan, expected)
86 }
87
88 #[test]
89 fn join_on_false() -> Result<()> {
90 let plan = LogicalPlanBuilder::empty(false)
91 .join_on(
92 LogicalPlanBuilder::empty(false).build()?,
93 Inner,
94 Some(lit(false)),
95 )?
96 .build()?;
97
98 let expected = "EmptyRelation";
99 assert_optimized_plan_equal(plan, expected)
100 }
101}