20
20
use std:: sync:: Arc ;
21
21
22
22
use datafusion_common:: tree_node:: Transformed ;
23
- use datafusion_common:: JoinType :: Inner ;
23
+ use datafusion_common:: JoinType ;
24
24
use datafusion_common:: { internal_err, plan_err, Result } ;
25
25
use datafusion_expr:: logical_plan:: tree_node:: unwrap_arc;
26
26
use datafusion_expr:: logical_plan:: LogicalPlan ;
@@ -94,29 +94,62 @@ impl OptimizerRule for PropagateEmptyRelation {
94
94
Ok ( Transformed :: no ( LogicalPlan :: CrossJoin ( join. clone ( ) ) ) )
95
95
}
96
96
97
- LogicalPlan :: Join ( ref join) if join . join_type == Inner => {
97
+ LogicalPlan :: Join ( ref join) => {
98
98
// 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.
101
99
// 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.
104
100
// For RightAnti Join, if the left side is empty, the Join result is right side(should exclude null ??).
105
101
// For Full Join, only both sides are empty, the Join result is empty.
106
102
// For LeftOut/Full Join, if the right side is empty, the Join can be eliminated with a Projection with left side
107
103
// columns + right side columns replaced with null values.
108
104
// For RightOut/Full Join, if the left side is empty, the Join can be eliminated with a Projection with right side
109
105
// columns + left side columns replaced with null values.
110
106
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 {
114
111
produce_one_row : false ,
115
112
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 ( ) ) ) ) ,
118
152
}
119
- Ok ( Transformed :: no ( LogicalPlan :: Join ( join. clone ( ) ) ) )
120
153
}
121
154
LogicalPlan :: Aggregate ( ref agg) => {
122
155
if !agg. group_expr . is_empty ( ) {
@@ -222,7 +255,7 @@ mod tests {
222
255
use crate :: eliminate_filter:: EliminateFilter ;
223
256
use crate :: eliminate_nested_union:: EliminateNestedUnion ;
224
257
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,
226
259
test_table_scan_fields, test_table_scan_with_name,
227
260
} ;
228
261
@@ -232,18 +265,20 @@ mod tests {
232
265
assert_optimized_plan_eq ( Arc :: new ( PropagateEmptyRelation :: new ( ) ) , plan, expected)
233
266
}
234
267
235
- fn assert_together_optimized_plan_eq (
268
+ fn assert_together_optimized_plan (
236
269
plan : LogicalPlan ,
237
270
expected : & str ,
271
+ eq : bool ,
238
272
) -> Result < ( ) > {
239
- assert_optimized_plan_eq_with_rules (
273
+ assert_optimized_plan_with_rules (
240
274
vec ! [
241
275
Arc :: new( EliminateFilter :: new( ) ) ,
242
276
Arc :: new( EliminateNestedUnion :: new( ) ) ,
243
277
Arc :: new( PropagateEmptyRelation :: new( ) ) ,
244
278
] ,
245
279
plan,
246
280
expected,
281
+ eq,
247
282
)
248
283
}
249
284
@@ -279,7 +314,7 @@ mod tests {
279
314
. build ( ) ?;
280
315
281
316
let expected = "EmptyRelation" ;
282
- assert_together_optimized_plan_eq ( plan, expected)
317
+ assert_together_optimized_plan ( plan, expected, true )
283
318
}
284
319
285
320
#[ test]
@@ -292,7 +327,7 @@ mod tests {
292
327
let plan = LogicalPlanBuilder :: from ( left) . union ( right) ?. build ( ) ?;
293
328
294
329
let expected = "TableScan: test" ;
295
- assert_together_optimized_plan_eq ( plan, expected)
330
+ assert_together_optimized_plan ( plan, expected, true )
296
331
}
297
332
298
333
#[ test]
@@ -317,7 +352,7 @@ mod tests {
317
352
let expected = "Union\
318
353
\n TableScan: test1\
319
354
\n TableScan: test4";
320
- assert_together_optimized_plan_eq ( plan, expected)
355
+ assert_together_optimized_plan ( plan, expected, true )
321
356
}
322
357
323
358
#[ test]
@@ -342,7 +377,7 @@ mod tests {
342
377
. build ( ) ?;
343
378
344
379
let expected = "EmptyRelation" ;
345
- assert_together_optimized_plan_eq ( plan, expected)
380
+ assert_together_optimized_plan ( plan, expected, true )
346
381
}
347
382
348
383
#[ test]
@@ -369,7 +404,7 @@ mod tests {
369
404
let expected = "Union\
370
405
\n TableScan: test2\
371
406
\n TableScan: test3";
372
- assert_together_optimized_plan_eq ( plan, expected)
407
+ assert_together_optimized_plan ( plan, expected, true )
373
408
}
374
409
375
410
#[ test]
@@ -382,7 +417,7 @@ mod tests {
382
417
let plan = LogicalPlanBuilder :: from ( left) . union ( right) ?. build ( ) ?;
383
418
384
419
let expected = "TableScan: test" ;
385
- assert_together_optimized_plan_eq ( plan, expected)
420
+ assert_together_optimized_plan ( plan, expected, true )
386
421
}
387
422
388
423
#[ test]
@@ -397,7 +432,103 @@ mod tests {
397
432
. build ( ) ?;
398
433
399
434
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 )
401
532
}
402
533
403
534
#[ test]
@@ -430,6 +561,6 @@ mod tests {
430
561
let expected = "Projection: a, b, c\
431
562
\n TableScan: test";
432
563
433
- assert_together_optimized_plan_eq ( plan, expected)
564
+ assert_together_optimized_plan ( plan, expected, true )
434
565
}
435
566
}
0 commit comments