16
16
// under the License.
17
17
18
18
//! [`PropagateEmptyRelation`] eliminates nodes fed by `EmptyRelation`
19
- use datafusion_common:: { plan_err, Result } ;
20
- use datafusion_expr:: logical_plan:: LogicalPlan ;
21
- use datafusion_expr:: { EmptyRelation , JoinType , Projection , Union } ;
19
+
22
20
use std:: sync:: Arc ;
23
21
22
+ use datafusion_common:: tree_node:: Transformed ;
23
+ use datafusion_common:: JoinType :: Inner ;
24
+ use datafusion_common:: { internal_err, plan_err, Result } ;
25
+ use datafusion_expr:: logical_plan:: tree_node:: unwrap_arc;
26
+ use datafusion_expr:: logical_plan:: LogicalPlan ;
27
+ use datafusion_expr:: { EmptyRelation , Projection , Union } ;
28
+
24
29
use crate :: optimizer:: ApplyOrder ;
25
30
use crate :: { OptimizerConfig , OptimizerRule } ;
26
31
@@ -38,32 +43,58 @@ impl PropagateEmptyRelation {
38
43
impl OptimizerRule for PropagateEmptyRelation {
39
44
fn try_optimize (
40
45
& self ,
41
- plan : & LogicalPlan ,
46
+ _plan : & LogicalPlan ,
42
47
_config : & dyn OptimizerConfig ,
43
48
) -> Result < Option < LogicalPlan > > {
49
+ internal_err ! ( "Should have called PropagateEmptyRelation::rewrite" )
50
+ }
51
+
52
+ fn name ( & self ) -> & str {
53
+ "propagate_empty_relation"
54
+ }
55
+
56
+ fn apply_order ( & self ) -> Option < ApplyOrder > {
57
+ Some ( ApplyOrder :: BottomUp )
58
+ }
59
+
60
+ fn supports_rewrite ( & self ) -> bool {
61
+ true
62
+ }
63
+
64
+ fn rewrite (
65
+ & self ,
66
+ plan : LogicalPlan ,
67
+ _config : & dyn OptimizerConfig ,
68
+ ) -> Result < Transformed < LogicalPlan > > {
44
69
match plan {
45
- LogicalPlan :: EmptyRelation ( _) => { }
70
+ LogicalPlan :: EmptyRelation ( _) => Ok ( Transformed :: no ( plan ) ) ,
46
71
LogicalPlan :: Projection ( _)
47
72
| LogicalPlan :: Filter ( _)
48
73
| LogicalPlan :: Window ( _)
49
74
| LogicalPlan :: Sort ( _)
50
75
| LogicalPlan :: SubqueryAlias ( _)
51
76
| LogicalPlan :: Repartition ( _)
52
77
| LogicalPlan :: Limit ( _) => {
53
- if let Some ( empty) = empty_child ( plan) ? {
54
- return Ok ( Some ( empty) ) ;
78
+ let empty = empty_child ( & plan) ?;
79
+ if let Some ( empty_plan) = empty {
80
+ return Ok ( Transformed :: yes ( empty_plan) ) ;
55
81
}
82
+ Ok ( Transformed :: no ( plan) )
56
83
}
57
- LogicalPlan :: CrossJoin ( _ ) => {
58
- let ( left_empty, right_empty) = binary_plan_children_is_empty ( plan) ?;
84
+ LogicalPlan :: CrossJoin ( ref join ) => {
85
+ let ( left_empty, right_empty) = binary_plan_children_is_empty ( & plan) ?;
59
86
if left_empty || right_empty {
60
- return Ok ( Some ( LogicalPlan :: EmptyRelation ( EmptyRelation {
61
- produce_one_row : false ,
62
- schema : plan. schema ( ) . clone ( ) ,
63
- } ) ) ) ;
87
+ return Ok ( Transformed :: yes ( LogicalPlan :: EmptyRelation (
88
+ EmptyRelation {
89
+ produce_one_row : false ,
90
+ schema : plan. schema ( ) . clone ( ) ,
91
+ } ,
92
+ ) ) ) ;
64
93
}
94
+ Ok ( Transformed :: no ( LogicalPlan :: CrossJoin ( join. clone ( ) ) ) )
65
95
}
66
- LogicalPlan :: Join ( join) => {
96
+
97
+ LogicalPlan :: Join ( ref join) if join. join_type == Inner => {
67
98
// TODO: For Join, more join type need to be careful:
68
99
// For LeftOuter/LeftSemi/LeftAnti Join, only the left side is empty, the Join result is empty.
69
100
// For LeftSemi Join, if the right side is empty, the Join result is empty.
@@ -76,17 +107,26 @@ impl OptimizerRule for PropagateEmptyRelation {
76
107
// columns + right side columns replaced with null values.
77
108
// For RightOut/Full Join, if the left side is empty, the Join can be eliminated with a Projection with right side
78
109
// columns + left side columns replaced with null values.
79
- if join . join_type == JoinType :: Inner {
80
- let ( left_empty, right_empty) = binary_plan_children_is_empty ( plan ) ? ;
81
- if left_empty || right_empty {
82
- return Ok ( Some ( LogicalPlan :: EmptyRelation ( EmptyRelation {
110
+ 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 {
83
114
produce_one_row : false ,
84
- schema : plan. schema ( ) . clone ( ) ,
85
- } ) ) ) ;
115
+ schema : join. schema . clone ( ) ,
116
+ } ,
117
+ ) ) ) ;
118
+ }
119
+ Ok ( Transformed :: no ( LogicalPlan :: Join ( join. clone ( ) ) ) )
120
+ }
121
+ LogicalPlan :: Aggregate ( ref agg) => {
122
+ if !agg. group_expr . is_empty ( ) {
123
+ if let Some ( empty_plan) = empty_child ( & plan) ? {
124
+ return Ok ( Transformed :: yes ( empty_plan) ) ;
86
125
}
87
126
}
127
+ Ok ( Transformed :: no ( LogicalPlan :: Aggregate ( agg. clone ( ) ) ) )
88
128
}
89
- LogicalPlan :: Union ( union) => {
129
+ LogicalPlan :: Union ( ref union) => {
90
130
let new_inputs = union
91
131
. inputs
92
132
. iter ( )
@@ -98,49 +138,36 @@ impl OptimizerRule for PropagateEmptyRelation {
98
138
. collect :: < Vec < _ > > ( ) ;
99
139
100
140
if new_inputs. len ( ) == union. inputs . len ( ) {
101
- return Ok ( None ) ;
141
+ Ok ( Transformed :: no ( plan ) )
102
142
} else if new_inputs. is_empty ( ) {
103
- return Ok ( Some ( LogicalPlan :: EmptyRelation ( EmptyRelation {
104
- produce_one_row : false ,
105
- schema : plan. schema ( ) . clone ( ) ,
106
- } ) ) ) ;
143
+ Ok ( Transformed :: yes ( LogicalPlan :: EmptyRelation (
144
+ EmptyRelation {
145
+ produce_one_row : false ,
146
+ schema : plan. schema ( ) . clone ( ) ,
147
+ } ,
148
+ ) ) )
107
149
} else if new_inputs. len ( ) == 1 {
108
- let child = ( * new_inputs[ 0 ] ) . clone ( ) ;
150
+ let child = unwrap_arc ( new_inputs[ 0 ] . clone ( ) ) ;
109
151
if child. schema ( ) . eq ( plan. schema ( ) ) {
110
- return Ok ( Some ( child) ) ;
152
+ Ok ( Transformed :: yes ( child) )
111
153
} else {
112
- return Ok ( Some ( LogicalPlan :: Projection (
154
+ Ok ( Transformed :: yes ( LogicalPlan :: Projection (
113
155
Projection :: new_from_schema (
114
156
Arc :: new ( child) ,
115
157
plan. schema ( ) . clone ( ) ,
116
158
) ,
117
- ) ) ) ;
159
+ ) ) )
118
160
}
119
161
} else {
120
- return Ok ( Some ( LogicalPlan :: Union ( Union {
162
+ Ok ( Transformed :: yes ( LogicalPlan :: Union ( Union {
121
163
inputs : new_inputs,
122
164
schema : union. schema . clone ( ) ,
123
- } ) ) ) ;
124
- }
125
- }
126
- LogicalPlan :: Aggregate ( agg) => {
127
- if !agg. group_expr . is_empty ( ) {
128
- if let Some ( empty) = empty_child ( plan) ? {
129
- return Ok ( Some ( empty) ) ;
130
- }
165
+ } ) ) )
131
166
}
132
167
}
133
- _ => { }
134
- }
135
- Ok ( None )
136
- }
137
168
138
- fn name ( & self ) -> & str {
139
- "propagate_empty_relation"
140
- }
141
-
142
- fn apply_order ( & self ) -> Option < ApplyOrder > {
143
- Some ( ApplyOrder :: BottomUp )
169
+ _ => Ok ( Transformed :: no ( plan) ) ,
170
+ }
144
171
}
145
172
}
146
173
@@ -182,18 +209,22 @@ fn empty_child(plan: &LogicalPlan) -> Result<Option<LogicalPlan>> {
182
209
183
210
#[ cfg( test) ]
184
211
mod tests {
212
+ use std:: sync:: Arc ;
213
+
214
+ use arrow:: datatypes:: { DataType , Field , Schema } ;
215
+
216
+ use datafusion_common:: { Column , DFSchema , JoinType , ScalarValue } ;
217
+ use datafusion_expr:: logical_plan:: table_scan;
218
+ use datafusion_expr:: {
219
+ binary_expr, col, lit, logical_plan:: builder:: LogicalPlanBuilder , Expr , Operator ,
220
+ } ;
221
+
185
222
use crate :: eliminate_filter:: EliminateFilter ;
186
223
use crate :: eliminate_nested_union:: EliminateNestedUnion ;
187
224
use crate :: test:: {
188
225
assert_optimized_plan_eq, assert_optimized_plan_eq_with_rules, test_table_scan,
189
226
test_table_scan_fields, test_table_scan_with_name,
190
227
} ;
191
- use arrow:: datatypes:: { DataType , Field , Schema } ;
192
- use datafusion_common:: { Column , DFSchema , ScalarValue } ;
193
- use datafusion_expr:: logical_plan:: table_scan;
194
- use datafusion_expr:: {
195
- binary_expr, col, lit, logical_plan:: builder:: LogicalPlanBuilder , Expr , Operator ,
196
- } ;
197
228
198
229
use super :: * ;
199
230
0 commit comments