@@ -55,12 +55,20 @@ impl RuleEliminateUnion {
5555 return Ok ( false ) ;
5656 }
5757 if child_num == 0 {
58- Ok ( matches ! (
58+ return Ok ( matches ! (
5959 s_expr. plan( ) ,
6060 RelOperator :: ConstantTableScan ( ConstantTableScan { num_rows: 0 , .. } )
61- ) )
62- } else {
61+ ) ) ;
62+ }
63+
64+ // Only pass through unary operators that preserve empty input as empty output.
65+ if matches ! (
66+ s_expr. plan( ) . rel_op( ) ,
67+ RelOp :: Filter | RelOp :: EvalScalar | RelOp :: Sort | RelOp :: Limit | RelOp :: Exchange
68+ ) {
6369 Self :: is_empty_scan ( s_expr. child ( 0 ) ?)
70+ } else {
71+ Ok ( false )
6472 }
6573 }
6674}
@@ -122,3 +130,35 @@ impl Rule for RuleEliminateUnion {
122130 & self . matchers
123131 }
124132}
133+
134+ #[ cfg( test) ]
135+ mod tests {
136+ use std:: collections:: BTreeSet ;
137+
138+ use super :: * ;
139+ use crate :: plans:: Aggregate ;
140+ use crate :: plans:: Filter ;
141+
142+ fn empty_scan_expr ( ) -> SExpr {
143+ let empty_scan =
144+ ConstantTableScan :: new_empty_scan ( DataSchemaRefExt :: create ( vec ! [ ] ) , BTreeSet :: new ( ) ) ;
145+ SExpr :: create_leaf ( Arc :: new ( RelOperator :: ConstantTableScan ( empty_scan) ) )
146+ }
147+
148+ #[ test]
149+ fn only_empty_preserving_unary_nodes_are_transparent ( ) -> Result < ( ) > {
150+ let filter = SExpr :: create_unary (
151+ Arc :: new ( RelOperator :: Filter ( Filter { predicates : vec ! [ ] } ) ) ,
152+ Arc :: new ( empty_scan_expr ( ) ) ,
153+ ) ;
154+ assert ! ( RuleEliminateUnion :: is_empty_scan( & filter) ?) ;
155+
156+ let aggregate = SExpr :: create_unary (
157+ Arc :: new ( RelOperator :: Aggregate ( Aggregate :: default ( ) ) ) ,
158+ Arc :: new ( empty_scan_expr ( ) ) ,
159+ ) ;
160+ assert ! ( !RuleEliminateUnion :: is_empty_scan( & aggregate) ?) ;
161+
162+ Ok ( ( ) )
163+ }
164+ }
0 commit comments