2020use std:: sync:: Arc ;
2121
2222use datafusion_common:: tree_node:: Transformed ;
23- use datafusion_common:: JoinType :: Inner ;
23+ use datafusion_common:: JoinType ;
2424use datafusion_common:: { internal_err, plan_err, Result } ;
2525use datafusion_expr:: logical_plan:: tree_node:: unwrap_arc;
2626use datafusion_expr:: logical_plan:: LogicalPlan ;
@@ -94,29 +94,62 @@ impl OptimizerRule for PropagateEmptyRelation {
9494 Ok ( Transformed :: no ( LogicalPlan :: CrossJoin ( join. clone ( ) ) ) )
9595 }
9696
97- LogicalPlan :: Join ( ref join) if join . join_type == Inner => {
97+ LogicalPlan :: Join ( ref join) => {
9898 // TODO: For Join, more join type need to be careful:
99- // For LeftOuter/LeftSemi/LeftAnti Join, only the left side is empty, the Join result is empty.
100- // For LeftSemi Join, if the right side is empty, the Join result is empty.
10199 // For LeftAnti Join, if the right side is empty, the Join result is left side(should exclude null ??).
102- // For RightOuter/RightSemi/RightAnti Join, only the right side is empty, the Join result is empty.
103- // For RightSemi Join, if the left side is empty, the Join result is empty.
104100 // For RightAnti Join, if the left side is empty, the Join result is right side(should exclude null ??).
105101 // For Full Join, only both sides are empty, the Join result is empty.
106102 // For LeftOut/Full Join, if the right side is empty, the Join can be eliminated with a Projection with left side
107103 // columns + right side columns replaced with null values.
108104 // For RightOut/Full Join, if the left side is empty, the Join can be eliminated with a Projection with right side
109105 // columns + left side columns replaced with null values.
110106 let ( left_empty, right_empty) = binary_plan_children_is_empty ( & plan) ?;
111- if left_empty || right_empty {
112- return Ok ( Transformed :: yes ( LogicalPlan :: EmptyRelation (
113- EmptyRelation {
107+
108+ match join. join_type {
109+ JoinType :: Inner if left_empty || right_empty => Ok ( Transformed :: yes (
110+ LogicalPlan :: EmptyRelation ( EmptyRelation {
114111 produce_one_row : false ,
115112 schema : join. schema . clone ( ) ,
116- } ,
117- ) ) ) ;
113+ } ) ,
114+ ) ) ,
115+ JoinType :: Left if left_empty => Ok ( Transformed :: yes (
116+ LogicalPlan :: EmptyRelation ( EmptyRelation {
117+ produce_one_row : false ,
118+ schema : join. schema . clone ( ) ,
119+ } ) ,
120+ ) ) ,
121+ JoinType :: Right if right_empty => Ok ( Transformed :: yes (
122+ LogicalPlan :: EmptyRelation ( EmptyRelation {
123+ produce_one_row : false ,
124+ schema : join. schema . clone ( ) ,
125+ } ) ,
126+ ) ) ,
127+ JoinType :: LeftSemi if left_empty || right_empty => Ok (
128+ Transformed :: yes ( LogicalPlan :: EmptyRelation ( EmptyRelation {
129+ produce_one_row : false ,
130+ schema : join. schema . clone ( ) ,
131+ } ) ) ,
132+ ) ,
133+ JoinType :: RightSemi if left_empty || right_empty => Ok (
134+ Transformed :: yes ( LogicalPlan :: EmptyRelation ( EmptyRelation {
135+ produce_one_row : false ,
136+ schema : join. schema . clone ( ) ,
137+ } ) ) ,
138+ ) ,
139+ JoinType :: LeftAnti if left_empty => Ok ( Transformed :: yes (
140+ LogicalPlan :: EmptyRelation ( EmptyRelation {
141+ produce_one_row : false ,
142+ schema : join. schema . clone ( ) ,
143+ } ) ,
144+ ) ) ,
145+ JoinType :: RightAnti if right_empty => Ok ( Transformed :: yes (
146+ LogicalPlan :: EmptyRelation ( EmptyRelation {
147+ produce_one_row : false ,
148+ schema : join. schema . clone ( ) ,
149+ } ) ,
150+ ) ) ,
151+ _ => Ok ( Transformed :: no ( LogicalPlan :: Join ( join. clone ( ) ) ) ) ,
118152 }
119- Ok ( Transformed :: no ( LogicalPlan :: Join ( join. clone ( ) ) ) )
120153 }
121154 LogicalPlan :: Aggregate ( ref agg) => {
122155 if !agg. group_expr . is_empty ( ) {
@@ -222,7 +255,7 @@ mod tests {
222255 use crate :: eliminate_filter:: EliminateFilter ;
223256 use crate :: eliminate_nested_union:: EliminateNestedUnion ;
224257 use crate :: test:: {
225- assert_optimized_plan_eq, assert_optimized_plan_eq_with_rules , test_table_scan,
258+ assert_optimized_plan_eq, assert_optimized_plan_with_rules , test_table_scan,
226259 test_table_scan_fields, test_table_scan_with_name,
227260 } ;
228261
@@ -232,18 +265,20 @@ mod tests {
232265 assert_optimized_plan_eq ( Arc :: new ( PropagateEmptyRelation :: new ( ) ) , plan, expected)
233266 }
234267
235- fn assert_together_optimized_plan_eq (
268+ fn assert_together_optimized_plan (
236269 plan : LogicalPlan ,
237270 expected : & str ,
271+ eq : bool ,
238272 ) -> Result < ( ) > {
239- assert_optimized_plan_eq_with_rules (
273+ assert_optimized_plan_with_rules (
240274 vec ! [
241275 Arc :: new( EliminateFilter :: new( ) ) ,
242276 Arc :: new( EliminateNestedUnion :: new( ) ) ,
243277 Arc :: new( PropagateEmptyRelation :: new( ) ) ,
244278 ] ,
245279 plan,
246280 expected,
281+ eq,
247282 )
248283 }
249284
@@ -279,7 +314,7 @@ mod tests {
279314 . build ( ) ?;
280315
281316 let expected = "EmptyRelation" ;
282- assert_together_optimized_plan_eq ( plan, expected)
317+ assert_together_optimized_plan ( plan, expected, true )
283318 }
284319
285320 #[ test]
@@ -292,7 +327,7 @@ mod tests {
292327 let plan = LogicalPlanBuilder :: from ( left) . union ( right) ?. build ( ) ?;
293328
294329 let expected = "TableScan: test" ;
295- assert_together_optimized_plan_eq ( plan, expected)
330+ assert_together_optimized_plan ( plan, expected, true )
296331 }
297332
298333 #[ test]
@@ -317,7 +352,7 @@ mod tests {
317352 let expected = "Union\
318353 \n TableScan: test1\
319354 \n TableScan: test4";
320- assert_together_optimized_plan_eq ( plan, expected)
355+ assert_together_optimized_plan ( plan, expected, true )
321356 }
322357
323358 #[ test]
@@ -342,7 +377,7 @@ mod tests {
342377 . build ( ) ?;
343378
344379 let expected = "EmptyRelation" ;
345- assert_together_optimized_plan_eq ( plan, expected)
380+ assert_together_optimized_plan ( plan, expected, true )
346381 }
347382
348383 #[ test]
@@ -369,7 +404,7 @@ mod tests {
369404 let expected = "Union\
370405 \n TableScan: test2\
371406 \n TableScan: test3";
372- assert_together_optimized_plan_eq ( plan, expected)
407+ assert_together_optimized_plan ( plan, expected, true )
373408 }
374409
375410 #[ test]
@@ -382,7 +417,7 @@ mod tests {
382417 let plan = LogicalPlanBuilder :: from ( left) . union ( right) ?. build ( ) ?;
383418
384419 let expected = "TableScan: test" ;
385- assert_together_optimized_plan_eq ( plan, expected)
420+ assert_together_optimized_plan ( plan, expected, true )
386421 }
387422
388423 #[ test]
@@ -397,7 +432,103 @@ mod tests {
397432 . build ( ) ?;
398433
399434 let expected = "EmptyRelation" ;
400- assert_together_optimized_plan_eq ( plan, expected)
435+ assert_together_optimized_plan ( plan, expected, true )
436+ }
437+
438+ fn assert_empty_left_empty_right_lp (
439+ left_empty : bool ,
440+ right_empty : bool ,
441+ join_type : JoinType ,
442+ eq : bool ,
443+ ) -> Result < ( ) > {
444+ let left_lp = if left_empty {
445+ let left_table_scan = test_table_scan ( ) ?;
446+
447+ LogicalPlanBuilder :: from ( left_table_scan)
448+ . filter ( Expr :: Literal ( ScalarValue :: Boolean ( Some ( false ) ) ) ) ?
449+ . build ( )
450+ } else {
451+ let scan = test_table_scan_with_name ( "left" ) . unwrap ( ) ;
452+ LogicalPlanBuilder :: from ( scan) . build ( )
453+ } ?;
454+
455+ let right_lp = if right_empty {
456+ let right_table_scan = test_table_scan_with_name ( "right" ) ?;
457+
458+ LogicalPlanBuilder :: from ( right_table_scan)
459+ . filter ( Expr :: Literal ( ScalarValue :: Boolean ( Some ( false ) ) ) ) ?
460+ . build ( )
461+ } else {
462+ let scan = test_table_scan_with_name ( "right" ) . unwrap ( ) ;
463+ LogicalPlanBuilder :: from ( scan) . build ( )
464+ } ?;
465+
466+ let plan = LogicalPlanBuilder :: from ( left_lp)
467+ . join_using (
468+ right_lp,
469+ join_type,
470+ vec ! [ Column :: from_name( "a" . to_string( ) ) ] ,
471+ ) ?
472+ . build ( ) ?;
473+
474+ let expected = "EmptyRelation" ;
475+ assert_together_optimized_plan ( plan, expected, eq)
476+ }
477+
478+ #[ test]
479+ fn test_join_empty_propagation_rules ( ) -> Result < ( ) > {
480+ // test left join with empty left
481+ assert_empty_left_empty_right_lp ( true , false , JoinType :: Left , true ) ?;
482+
483+ // test right join with empty right
484+ assert_empty_left_empty_right_lp ( false , true , JoinType :: Right , true ) ?;
485+
486+ // test left semi join with empty left
487+ assert_empty_left_empty_right_lp ( true , false , JoinType :: LeftSemi , true ) ?;
488+
489+ // test left semi join with empty right
490+ assert_empty_left_empty_right_lp ( false , true , JoinType :: LeftSemi , true ) ?;
491+
492+ // test right semi join with empty left
493+ assert_empty_left_empty_right_lp ( true , false , JoinType :: RightSemi , true ) ?;
494+
495+ // test right semi join with empty right
496+ assert_empty_left_empty_right_lp ( false , true , JoinType :: RightSemi , true ) ?;
497+
498+ // test left anti join empty left
499+ assert_empty_left_empty_right_lp ( true , false , JoinType :: LeftAnti , true ) ?;
500+
501+ // test right anti join empty right
502+ assert_empty_left_empty_right_lp ( false , true , JoinType :: RightAnti , true )
503+ }
504+
505+ #[ test]
506+ fn test_join_empty_propagation_rules_noop ( ) -> Result < ( ) > {
507+ // these cases should not result in an empty relation
508+
509+ // test left join with empty right
510+ assert_empty_left_empty_right_lp ( false , true , JoinType :: Left , false ) ?;
511+
512+ // test right join with empty left
513+ assert_empty_left_empty_right_lp ( true , false , JoinType :: Right , false ) ?;
514+
515+ // test left semi with non-empty left and right
516+ assert_empty_left_empty_right_lp ( false , false , JoinType :: LeftSemi , false ) ?;
517+
518+ // test right semi with non-empty left and right
519+ assert_empty_left_empty_right_lp ( false , false , JoinType :: RightSemi , false ) ?;
520+
521+ // test left anti join with non-empty left and right
522+ assert_empty_left_empty_right_lp ( false , false , JoinType :: LeftAnti , false ) ?;
523+
524+ // test left anti with non-empty left and empty right
525+ assert_empty_left_empty_right_lp ( false , true , JoinType :: LeftAnti , false ) ?;
526+
527+ // test right anti join with non-empty left and right
528+ assert_empty_left_empty_right_lp ( false , false , JoinType :: RightAnti , false ) ?;
529+
530+ // test right anti with empty left and non-empty right
531+ assert_empty_left_empty_right_lp ( true , false , JoinType :: RightAnti , false )
401532 }
402533
403534 #[ test]
@@ -430,6 +561,6 @@ mod tests {
430561 let expected = "Projection: a, b, c\
431562 \n TableScan: test";
432563
433- assert_together_optimized_plan_eq ( plan, expected)
564+ assert_together_optimized_plan ( plan, expected, true )
434565 }
435566}
0 commit comments