@@ -62,39 +62,18 @@ impl Map {
62
62
/// This method extracts all expression AST nodes as individual expressions,
63
63
/// and builds others out of column references to them, avoiding any duplication.
64
64
/// Once complete, expressions which are referred to only once are re-inlined.
65
+ ///
66
+ /// Some care is taken to ensure that `if` expressions only memoize expresions
67
+ /// they are certain to evaluate.
65
68
fn memoize_and_reuse (
66
69
exprs : & mut [ ScalarExpr ] ,
67
70
input_arity : usize ,
68
71
) -> ( Vec < ScalarExpr > , Vec < usize > ) {
69
72
let mut projection = ( 0 ..input_arity) . collect :: < Vec < _ > > ( ) ;
70
73
let mut scalars = Vec :: new ( ) ;
71
74
for expr in exprs. iter_mut ( ) {
72
- expr. visit_mut ( & mut |node| {
73
- match node {
74
- ScalarExpr :: Column ( index) => {
75
- // Column references need to be rewritten, but do not
76
- // need to be memoized.
77
- * index = projection[ * index] ;
78
- }
79
- ScalarExpr :: Literal ( _, _) => {
80
- // Literals do not need to be memoized.
81
- }
82
- _ => {
83
- if let Some ( position) = scalars. iter ( ) . position ( |e| e == node) {
84
- // Any complex expression that already exists as a prior column can
85
- // be replaced by a reference to that column.
86
- * node = ScalarExpr :: Column ( input_arity + position) ;
87
- } else {
88
- // A complex expression that does not exist should be memoized, and
89
- // replaced by a reference to the column.
90
- scalars. push ( std:: mem:: replace (
91
- node,
92
- ScalarExpr :: Column ( input_arity + scalars. len ( ) ) ,
93
- ) ) ;
94
- }
95
- }
96
- }
97
- } ) ;
75
+ // Carefully memoize expressions that will certainly be evaluated.
76
+ memoize ( expr, & mut scalars, & mut projection, input_arity) ;
98
77
99
78
// At this point `expr` should be a column reference or a literal. It may not have
100
79
// been added to `scalars` and we should do so if it is a literal to make sure we
@@ -115,6 +94,53 @@ fn memoize_and_reuse(
115
94
( scalars, projection)
116
95
}
117
96
97
+ /// Visit and memoize expression nodes.
98
+ ///
99
+ /// Importantly, we should not memoize expressions that may not be excluded by virtue of
100
+ /// being guarded by `if` expressions.
101
+ fn memoize (
102
+ expr : & mut ScalarExpr ,
103
+ scalars : & mut Vec < ScalarExpr > ,
104
+ projection : & mut Vec < usize > ,
105
+ input_arity : usize ,
106
+ ) {
107
+ match expr {
108
+ ScalarExpr :: Column ( index) => {
109
+ // Column references need to be rewritten, but do not need to be memoized.
110
+ * index = projection[ * index] ;
111
+ }
112
+ ScalarExpr :: Literal ( _, _) => {
113
+ // Literals do not need to be memoized.
114
+ }
115
+ _ => {
116
+ // We should not eagerly memoize `if` branches that might not be taken.
117
+ // TODO: Memoize expressions in the intersection of `then` and `els`.
118
+ if let ScalarExpr :: If {
119
+ cond,
120
+ then : _,
121
+ els : _,
122
+ } = expr
123
+ {
124
+ memoize ( cond, scalars, projection, input_arity) ;
125
+ } else {
126
+ expr. visit1_mut ( |e| memoize ( e, scalars, projection, input_arity) ) ;
127
+ }
128
+ if let Some ( position) = scalars. iter ( ) . position ( |e| e == expr) {
129
+ // Any complex expression that already exists as a prior column can
130
+ // be replaced by a reference to that column.
131
+ * expr = ScalarExpr :: Column ( input_arity + position) ;
132
+ } else {
133
+ // A complex expression that does not exist should be memoized, and
134
+ // replaced by a reference to the column.
135
+ scalars. push ( std:: mem:: replace (
136
+ expr,
137
+ ScalarExpr :: Column ( input_arity + scalars. len ( ) ) ,
138
+ ) ) ;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
118
144
/// Replaces column references that occur once with the referenced expression.
119
145
///
120
146
/// This method in-lines expressions that are only referenced once, and then
0 commit comments