mz_expr/scalar/
optimizable.rs1use std::fmt::Debug;
16use std::hash::Hash;
17
18use mz_ore::stack::RecursionLimitError;
19use serde::Serialize;
20
21use crate::scalar::columns::Columns;
22use crate::scalar::func::{BinaryFunc, UnaryFunc, VariadicFunc};
23use crate::visit::{Visit, VisitChildren};
24use crate::{MirScalarExpr, func};
25
26pub trait OptimizableExpr:
30 Columns + VisitChildren<Self> + Clone + Eq + Ord + Hash + Debug + Sized + Serialize
31{
32 fn is_literal(&self) -> bool;
34
35 fn is_literal_err(&self) -> bool;
37
38 fn contains_temporal(&self) -> bool;
40
41 fn size(&self) -> usize;
43
44 fn eager_children(&mut self) -> Option<Vec<&mut Self>>;
50
51 fn equality_column_alias(predicate: &Self, expr: &Self, threshold: usize) -> Option<Self>;
56
57 fn extract_temporal_bounds(temporal: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), String>;
61
62 fn visit_pre<F>(&self, f: &mut F) -> Result<(), RecursionLimitError>
64 where
65 F: FnMut(&Self),
66 {
67 Visit::visit_pre(self, f)
68 }
69}
70
71impl OptimizableExpr for MirScalarExpr {
72 fn is_literal(&self) -> bool {
73 self.is_literal()
74 }
75
76 fn is_literal_err(&self) -> bool {
77 self.is_literal_err()
78 }
79
80 fn contains_temporal(&self) -> bool {
81 self.contains_temporal()
82 }
83
84 fn size(&self) -> usize {
85 self.size()
86 }
87
88 fn eager_children(&mut self) -> Option<Vec<&mut Self>> {
89 if let MirScalarExpr::If { cond, .. } = self {
91 return Some(vec![cond]);
92 }
93
94 if let MirScalarExpr::CallVariadic {
98 func: VariadicFunc::Coalesce(_),
99 exprs,
100 } = self
101 {
102 return Some(exprs.iter_mut().take(1).collect());
103 }
104
105 if let Ok((_func, other_side)) = self.as_mut_temporal_filter() {
109 return Some(vec![other_side]);
110 }
111
112 None
113 }
114
115 fn equality_column_alias(predicate: &Self, expr: &Self, threshold: usize) -> Option<Self> {
116 if let MirScalarExpr::CallBinary {
117 func: BinaryFunc::Eq(_),
118 expr1,
119 expr2,
120 } = predicate
121 {
122 if let MirScalarExpr::Column(c, name) = &**expr1 {
123 if *c < threshold && &**expr2 == expr {
124 return Some(MirScalarExpr::Column(*c, name.clone()));
125 }
126 }
127 if let MirScalarExpr::Column(c, name) = &**expr2 {
128 if *c < threshold && &**expr1 == expr {
129 return Some(MirScalarExpr::Column(*c, name.clone()));
130 }
131 }
132 }
133 None
134 }
135
136 fn extract_temporal_bounds(temporal: Vec<Self>) -> Result<(Vec<Self>, Vec<Self>), String> {
137 let mut lower_bounds = Vec::new();
138 let mut upper_bounds = Vec::new();
139
140 for mut predicate in temporal.into_iter() {
141 let (func, expr2) = predicate.as_mut_temporal_filter()?;
142 let expr2 = expr2.clone();
143
144 match func {
145 BinaryFunc::Eq(_) => {
146 lower_bounds.push(expr2.clone());
147 upper_bounds
148 .push(expr2.call_unary(UnaryFunc::StepMzTimestamp(func::StepMzTimestamp)));
149 }
150 BinaryFunc::Lt(_) => {
151 upper_bounds.push(expr2.clone());
152 }
153 BinaryFunc::Lte(_) => {
154 upper_bounds
155 .push(expr2.call_unary(UnaryFunc::StepMzTimestamp(func::StepMzTimestamp)));
156 }
157 BinaryFunc::Gt(_) => {
158 lower_bounds
159 .push(expr2.call_unary(UnaryFunc::StepMzTimestamp(func::StepMzTimestamp)));
160 }
161 BinaryFunc::Gte(_) => {
162 lower_bounds.push(expr2.clone());
163 }
164 _ => {
165 return Err(format!("Unsupported binary temporal operation: {:?}", func));
166 }
167 }
168 }
169
170 Ok((lower_bounds, upper_bounds))
171 }
172
173 fn visit_pre<F>(&self, f: &mut F) -> Result<(), RecursionLimitError>
174 where
175 F: FnMut(&Self),
176 {
177 self.visit_pre(f);
178 Ok(())
179 }
180}