1use std::collections::BTreeSet;
13use std::mem;
14
15use mz_ore::collections::CollectionExt;
16use mz_pgtz::timezone::TimezoneSpec;
17use mz_repr::{Datum, ReprColumnType, ReprScalarType, RowArena, SqlScalarType};
18
19use crate::scalar::func::variadic::{Coalesce, ListCreate, ListIndex};
20use crate::scalar::func::{
21 self, BinaryFunc, UnaryFunc, VariadicFunc, parse_timezone, regexp_replace_parse_flags,
22};
23use crate::{Eval, MirScalarExpr};
24
25pub(super) fn reduce_call_variadic(
26 e: &mut MirScalarExpr,
27 column_types: &[ReprColumnType],
28 temp_storage: &RowArena,
29) {
30 e.flatten_associative();
33
34 let MirScalarExpr::CallVariadic { func, exprs } = e else {
35 unreachable!("`flatten_associative` shouldn't change node type");
36 };
37
38 if *func == Coalesce.into() {
41 simplify_coalesce(e, column_types);
42 return;
43 }
44
45 if exprs.iter().all(|x| x.is_literal()) {
47 *e = MirScalarExpr::literal(e.eval(&[], temp_storage), e.typ(column_types).scalar_type);
48 return;
49 }
50 if func.propagates_nulls() && exprs.iter().any(|x| x.is_literal_null()) {
51 *e = MirScalarExpr::literal_null(e.typ(column_types).scalar_type);
52 return;
53 }
54 if let Some(err) = exprs.iter().find_map(|x| x.as_literal_err()) {
55 *e = MirScalarExpr::literal(Err(err.clone()), e.typ(column_types).scalar_type);
56 return;
57 }
58
59 match func {
62 VariadicFunc::Greatest(_) | VariadicFunc::Least(_) => {
63 reduce_greatest_least(e, column_types);
64 }
65 VariadicFunc::Substr(_)
66 if exprs.len() == 2 && matches!(exprs[1].as_literal(), Some(Ok(Datum::Int32(1)))) =>
67 {
68 *e = exprs.swap_remove(0);
71 }
72 VariadicFunc::RegexpMatch(_)
73 if exprs[1].is_literal() && exprs.get(2).map_or(true, |e| e.is_literal()) =>
74 {
75 let needle = exprs[1].as_literal_str().unwrap();
76 let flags = if exprs.len() == 3 {
77 exprs[2].as_literal_str().unwrap()
78 } else {
79 ""
80 };
81 *e = match func::build_regex(needle, flags) {
82 Ok(regex) => mem::take(exprs)
83 .into_first()
84 .call_unary(UnaryFunc::RegexpMatch(func::RegexpMatch(regex))),
85 Err(err) => MirScalarExpr::literal(Err(err), e.typ(column_types).scalar_type),
86 };
87 }
88 VariadicFunc::RegexpReplace(_)
89 if exprs[1].is_literal() && exprs.get(3).map_or(true, |e| e.is_literal()) =>
90 {
91 let pattern = exprs[1].as_literal_str().unwrap();
92 let flags = exprs
93 .get(3)
94 .map_or("", |expr| expr.as_literal_str().unwrap());
95 let (limit, flags) = regexp_replace_parse_flags(flags);
96
97 *e = match func::build_regex(pattern, &flags) {
103 Ok(regex) => {
104 let mut exprs = mem::take(exprs);
105 let replacement = exprs.swap_remove(2);
106 let source = exprs.swap_remove(0);
107 source.call_binary(
108 replacement,
109 BinaryFunc::from(func::RegexpReplace { regex, limit }),
110 )
111 }
112 Err(err) => {
113 let mut exprs = mem::take(exprs);
114 let replacement = exprs.swap_remove(2);
115 let source = exprs.swap_remove(0);
116 let scalar_type = e.typ(column_types).scalar_type;
117 source
119 .call_is_null()
120 .or(replacement.call_is_null())
121 .if_then_else(
122 MirScalarExpr::literal_null(scalar_type.clone()),
123 MirScalarExpr::literal(Err(err), scalar_type),
124 )
125 }
126 };
127 }
128 VariadicFunc::RegexpSplitToArray(_)
129 if exprs[1].is_literal() && exprs.get(2).map_or(true, |e| e.is_literal()) =>
130 {
131 let needle = exprs[1].as_literal_str().unwrap();
132 let flags = if exprs.len() == 3 {
133 exprs[2].as_literal_str().unwrap()
134 } else {
135 ""
136 };
137 *e = match func::build_regex(needle, flags) {
138 Ok(regex) => {
139 mem::take(exprs)
140 .into_first()
141 .call_unary(UnaryFunc::RegexpSplitToArray(func::RegexpSplitToArray(
142 regex,
143 )))
144 }
145 Err(err) => MirScalarExpr::literal(Err(err), e.typ(column_types).scalar_type),
146 };
147 }
148 VariadicFunc::ListIndex(_) if is_list_create_call(&exprs[0]) => {
149 let ind_exprs = exprs.split_off(1);
152 let top_list_create = exprs.swap_remove(0);
153 *e = reduce_list_create_list_index_literal(top_list_create, ind_exprs);
154 }
155 VariadicFunc::And(_) | VariadicFunc::Or(_) => {
156 e.undistribute_and_or();
158 e.reduce_and_canonicalize_and_or();
159 }
160 VariadicFunc::TimezoneTimeVariadic(_)
161 if exprs[0].is_literal() && exprs[2].is_literal_ok() =>
162 {
163 let tz = exprs[0].as_literal_str().unwrap();
164 *e = match parse_timezone(tz, TimezoneSpec::Posix) {
165 Ok(tz) => MirScalarExpr::CallUnary {
166 func: UnaryFunc::TimezoneTime(func::TimezoneTime {
167 tz,
168 wall_time: exprs[2]
169 .as_literal()
170 .unwrap()
171 .unwrap()
172 .unwrap_timestamptz()
173 .naive_utc(),
174 }),
175 expr: Box::new(exprs[1].take()),
176 },
177 Err(err) => MirScalarExpr::literal(Err(err), e.typ(column_types).scalar_type),
178 };
179 }
180 _ => {}
181 }
182}
183
184fn reduce_greatest_least(e: &mut MirScalarExpr, column_types: &[ReprColumnType]) {
198 let typ = e.typ(column_types).scalar_type;
199 let MirScalarExpr::CallVariadic { exprs, .. } = e else {
200 unreachable!()
201 };
202 let mut seen = BTreeSet::new();
203 exprs.retain(|x| seen.insert(x.clone()));
204 exprs.retain(|x| !x.is_literal_null());
205 match exprs.len() {
206 0 => *e = MirScalarExpr::literal_null(typ),
207 1 => *e = exprs.swap_remove(0),
208 _ => {}
209 }
210}
211
212fn simplify_coalesce(e: &mut MirScalarExpr, column_types: &[ReprColumnType]) {
220 let MirScalarExpr::CallVariadic { exprs, .. } = e else {
221 unreachable!()
222 };
223
224 if exprs.iter().all(|x| x.is_literal_null()) {
228 *e = MirScalarExpr::literal_null(e.typ(column_types).scalar_type);
229 return;
230 }
231
232 exprs.retain(|x| !x.is_literal_null());
234
235 if let Some(i) = exprs
240 .iter()
241 .position(|x| x.is_literal() || !x.typ(column_types).nullable)
242 {
243 exprs.truncate(i + 1);
244 }
245
246 let mut seen = BTreeSet::new();
248 exprs.retain(|x| seen.insert(x.clone()));
249
250 if exprs.len() == 1 {
251 *e = exprs[0].take();
253 }
254}
255
256fn is_list_create_call(expr: &MirScalarExpr) -> bool {
257 matches!(
258 expr,
259 MirScalarExpr::CallVariadic {
260 func: VariadicFunc::ListCreate(..),
261 ..
262 }
263 )
264}
265
266fn list_create_type(list_create: &MirScalarExpr) -> ReprScalarType {
267 if let MirScalarExpr::CallVariadic {
268 func: VariadicFunc::ListCreate(ListCreate { elem_type: typ }),
269 ..
270 } = list_create
271 {
272 ReprScalarType::from(typ)
273 } else {
274 unreachable!()
275 }
276}
277
278fn reduce_list_create_list_index_literal(
293 mut list_create_to_reduce: MirScalarExpr,
294 mut index_exprs: Vec<MirScalarExpr>,
295) -> MirScalarExpr {
296 let mut list_create_mut_refs = vec![&mut list_create_to_reduce];
308 let mut earlier_list_create_types: Vec<&mut SqlScalarType> = vec![];
309 let mut i = 0;
310 while i < index_exprs.len()
311 && list_create_mut_refs
312 .iter()
313 .all(|lc| is_list_create_call(lc))
314 {
315 if index_exprs[i].is_literal_ok() {
316 let removed_index = index_exprs.remove(i);
318 let index_i64 = match removed_index.as_literal().unwrap().unwrap() {
319 Datum::Int64(sql_index_i64) => sql_index_i64 - 1,
320 _ => unreachable!(), };
322 for list_create in &mut list_create_mut_refs {
325 let list_create_args = match list_create {
326 MirScalarExpr::CallVariadic {
327 func: VariadicFunc::ListCreate(ListCreate { elem_type: _ }),
328 exprs,
329 } => exprs,
330 _ => unreachable!(), };
332 if index_i64 >= 0 && index_i64 < list_create_args.len().try_into().unwrap() {
334 let index: usize = index_i64.try_into().unwrap();
335 **list_create = list_create_args.swap_remove(index);
336 } else {
337 let typ = list_create_type(list_create);
338 **list_create = MirScalarExpr::literal_null(typ);
339 }
340 }
341 for t in earlier_list_create_types.iter_mut() {
343 if let SqlScalarType::List {
344 element_type,
345 custom_id: _,
346 } = t
347 {
348 **t = *element_type.clone();
349 let mut u = &mut **t;
352 while let SqlScalarType::List {
353 element_type,
354 custom_id,
355 } = u
356 {
357 *custom_id = None;
358 u = &mut **element_type;
359 }
360 } else {
361 unreachable!("already matched below");
362 }
363 }
364 } else {
365 list_create_mut_refs = list_create_mut_refs
369 .into_iter()
370 .flat_map(|list_create| match list_create {
371 MirScalarExpr::CallVariadic {
372 func: VariadicFunc::ListCreate(ListCreate { elem_type }),
373 exprs: list_create_args,
374 } => {
375 earlier_list_create_types.push(elem_type);
376 list_create_args
377 }
378 _ => unreachable!(),
380 })
381 .collect();
382 i += 1;
383 }
384 }
385 if index_exprs.is_empty() {
388 assert_eq!(list_create_mut_refs.len(), 1);
389 list_create_to_reduce
390 } else {
391 MirScalarExpr::call_variadic(
392 ListIndex,
393 std::iter::once(list_create_to_reduce)
394 .chain(index_exprs)
395 .collect(),
396 )
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use mz_repr::{Datum, ReprScalarType};
403
404 use crate::MirScalarExpr;
405 use crate::scalar::func::variadic::{Greatest, Least, Substr};
406
407 #[mz_ore::test]
408 fn greatest_least_null_operand_drop() {
409 let types = [
410 ReprScalarType::Int32.nullable(true),
411 ReprScalarType::Int32.nullable(true),
412 ];
413 let null = || MirScalarExpr::literal_null(ReprScalarType::Int32);
414 let col = MirScalarExpr::column;
415
416 let mut e = MirScalarExpr::call_variadic(Greatest, vec![col(0), null()]);
418 e.reduce(&types);
419 assert_eq!(e, col(0));
420
421 let mut e = MirScalarExpr::call_variadic(Least, vec![col(0), null(), col(1)]);
422 e.reduce(&types);
423 assert_eq!(e, MirScalarExpr::call_variadic(Least, vec![col(0), col(1)]));
424
425 let mut e = MirScalarExpr::call_variadic(Greatest, vec![col(0), col(0)]);
429 e.reduce(&types);
430 assert_eq!(e, col(0));
431
432 let mut e = MirScalarExpr::call_variadic(Least, vec![col(1), col(0), col(1)]);
433 e.reduce(&types);
434 assert_eq!(e, MirScalarExpr::call_variadic(Least, vec![col(1), col(0)]));
435
436 let mut e = MirScalarExpr::call_variadic(Greatest, vec![col(0), null(), col(0)]);
438 e.reduce(&types);
439 assert_eq!(e, col(0));
440
441 let mut e = MirScalarExpr::call_variadic(Greatest, vec![null(), null()]);
443 e.reduce(&types);
444 assert!(e.is_literal_null());
445 }
446
447 #[mz_ore::test]
448 fn substr_from_one() {
449 let types = [ReprScalarType::String.nullable(true)];
450 let col = || MirScalarExpr::column(0);
451 let lit = |v| MirScalarExpr::literal_ok(Datum::Int32(v), ReprScalarType::Int32);
452
453 let mut e = MirScalarExpr::call_variadic(Substr, vec![col(), lit(1)]);
455 e.reduce(&types);
456 assert_eq!(e, col());
457
458 let mut e = MirScalarExpr::call_variadic(Substr, vec![col(), lit(1), lit(5)]);
460 e.reduce(&types);
461 assert_ne!(e, col());
462 }
463}