7
7
// the Business Source License, use of this software will be governed
8
8
// by the Apache License, Version 2.0.
9
9
10
- //! For a `FlatMap` whose args are all constants, turns it into `Map` if only 1 row is produced by
11
- //! the table function, or turns it into an empty constant if 0 rows are produced by the table
12
- //! function. Additionally, a `Wrap` whose width is larger than its number of arguments can be
13
- //! removed.
10
+ //! For a `FlatMap` where the table function's arguments are all constants, turns it into `Map` if
11
+ //! only 1 row is produced by the table function, or turns it into an empty constant collection if 0
12
+ //! rows are produced by the table function.
13
+ //!
14
+ //! It does an additional optimization on the `Wrap` table function: when `Wrap`'s width is larger
15
+ //! than its number of arguments, it removes the `FlatMap Wrap ...`, because such `Wrap`s would have
16
+ //! no effect.
14
17
15
18
use itertools:: Itertools ;
16
19
use mz_expr:: visit:: Visit ;
@@ -19,7 +22,7 @@ use mz_repr::{Diff, Row, RowArena};
19
22
20
23
use crate :: TransformCtx ;
21
24
22
- /// See comment at the top of the file .
25
+ /// Attempts to eliminate FlatMaps that are sure to have 0 or 1 results on each input row .
23
26
#[ derive( Debug ) ]
24
27
pub struct FlatMapElimination ;
25
28
@@ -45,48 +48,55 @@ impl crate::Transform for FlatMapElimination {
45
48
}
46
49
47
50
impl FlatMapElimination {
48
- /// See comment at the top of the file .
51
+ /// Apply `FlatMapElimination` to the root of the given `MirRelationExpr` .
49
52
pub fn action ( relation : & mut MirRelationExpr ) {
53
+ // Treat Wrap specially: we can sometimes optimize it out even when it has non-literal
54
+ // arguments.
55
+ //
56
+ // (No need to look for WithOrdinality here, as that never occurs with Wrap: users can't
57
+ // call Wrap directly; we only create calls to Wrap ourselves, and we don't use
58
+ // WithOrdinality on it.)
50
59
if let MirRelationExpr :: FlatMap { func, exprs, input } = relation {
51
- // Treat Wrap specially.
52
60
if let TableFunc :: Wrap { width, .. } = func {
53
61
if * width >= exprs. len ( ) {
54
62
* relation = input. take_dangerous ( ) . map ( std:: mem:: take ( exprs) ) ;
55
- return ;
56
63
}
57
64
}
58
- // For all other table functions, check for all arguments being literals.
59
- let mut args = vec ! [ ] ;
60
- for e in exprs {
61
- match e. as_literal ( ) {
62
- Some ( Ok ( datum) ) => args. push ( datum) ,
63
- // Give up if any arg is not a literal, or if it's a literal error.
64
- _ => return ,
65
- }
66
- }
67
- let temp_storage = RowArena :: new ( ) ;
68
- let ( first, second) = match func. eval ( & args, & temp_storage) {
69
- Ok ( mut r) => ( r. next ( ) , r. next ( ) ) ,
70
- // don't play with errors
71
- Err ( _) => return ,
72
- } ;
73
- match ( first, second) {
74
- // The table function evaluated to an empty collection.
75
- ( None , None ) => {
76
- relation. take_safely ( None ) ;
77
- }
78
- // The table function evaluated to a collection with exactly 1 row.
79
- ( Some ( ( first_row, Diff :: ONE ) ) , None ) => {
80
- let types = func. output_type ( ) . column_types ;
81
- let map_exprs = first_row
82
- . into_iter ( )
83
- . zip_eq ( types)
84
- . map ( |( d, typ) | MirScalarExpr :: Literal ( Ok ( Row :: pack_slice ( & [ d] ) ) , typ) )
85
- . collect ( ) ;
86
- * relation = input. take_dangerous ( ) . map ( map_exprs) ;
65
+ }
66
+ // For all other table functions (and Wraps that are not covered by the above), check
67
+ // whether all arguments are literals (with no errors), in which case we'll evaluate the
68
+ // table function and check how many output rows it has, and maybe turn the FlatMap into
69
+ // something simpler.
70
+ if let MirRelationExpr :: FlatMap { func, exprs, input } = relation {
71
+ if let Some ( args) = exprs
72
+ . iter ( )
73
+ . map ( |e| e. as_literal_non_error ( ) )
74
+ . collect :: < Option < Vec < _ > > > ( )
75
+ {
76
+ let temp_storage = RowArena :: new ( ) ;
77
+ let ( first, second) = match func. eval ( & args, & temp_storage) {
78
+ Ok ( mut r) => ( r. next ( ) , r. next ( ) ) ,
79
+ // don't play with errors
80
+ Err ( _) => return ,
81
+ } ;
82
+ match ( first, second) {
83
+ // The table function evaluated to an empty collection.
84
+ ( None , _) => {
85
+ relation. take_safely ( None ) ;
86
+ }
87
+ // The table function evaluated to a collection with exactly 1 row.
88
+ ( Some ( ( first_row, Diff :: ONE ) ) , None ) => {
89
+ let types = func. output_type ( ) . column_types ;
90
+ let map_exprs = first_row
91
+ . into_iter ( )
92
+ . zip_eq ( types)
93
+ . map ( |( d, typ) | MirScalarExpr :: Literal ( Ok ( Row :: pack_slice ( & [ d] ) ) , typ) )
94
+ . collect ( ) ;
95
+ * relation = input. take_dangerous ( ) . map ( map_exprs) ;
96
+ }
97
+ // The table function evaluated to a collection with more than 1 row; nothing to do.
98
+ _ => { }
87
99
}
88
- // The table function evaluated to a collection with more than 1 row; nothing to do.
89
- _ => { }
90
100
}
91
101
}
92
102
}
0 commit comments