mz_expr/scalar/func/
variadic.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9//
10// Portions of this file are derived from the PostgreSQL project. The original
11// source code is subject to the terms of the PostgreSQL license, a copy of
12// which can be found in the LICENSE file at the root of this repository.
13
14//! Variadic functions.
15
16use std::cmp;
17
18use chrono::NaiveDate;
19use fallible_iterator::FallibleIterator;
20use hmac::{Hmac, Mac};
21use itertools::Itertools;
22use md5::Md5;
23use mz_lowertest::MzReflect;
24use mz_ore::cast::{CastFrom, ReinterpretCast};
25use mz_pgtz::timezone::TimezoneSpec;
26use mz_repr::adt::array::ArrayDimension;
27use mz_repr::adt::mz_acl_item::{AclItem, AclMode, MzAclItem};
28use mz_repr::adt::range::{InvalidRangeError, Range, RangeBound, parse_range_bound_flags};
29use mz_repr::adt::system::Oid;
30use mz_repr::adt::timestamp::CheckedTimestamp;
31use mz_repr::role_id::RoleId;
32use mz_repr::{ColumnName, Datum, Row, RowArena, SqlColumnType, SqlScalarType};
33use serde::{Deserialize, Serialize};
34use sha1::Sha1;
35use sha2::{Sha224, Sha256, Sha384, Sha512};
36
37use crate::func::{
38    MAX_STRING_BYTES, array_create_scalar, build_regex, date_bin, parse_timezone,
39    regexp_match_static, regexp_replace_parse_flags, regexp_replace_static,
40    regexp_split_to_array_re, stringify_datum, timezone_time,
41};
42use crate::{EvalError, MirScalarExpr};
43
44pub fn and<'a>(
45    datums: &[Datum<'a>],
46    temp_storage: &'a RowArena,
47    exprs: &'a [MirScalarExpr],
48) -> Result<Datum<'a>, EvalError> {
49    // If any is false, then return false. Else, if any is null, then return null. Else, return true.
50    let mut null = false;
51    let mut err = None;
52    for expr in exprs {
53        match expr.eval(datums, temp_storage) {
54            Ok(Datum::False) => return Ok(Datum::False), // short-circuit
55            Ok(Datum::True) => {}
56            // No return in these two cases, because we might still see a false
57            Ok(Datum::Null) => null = true,
58            Err(this_err) => err = std::cmp::max(err.take(), Some(this_err)),
59            _ => unreachable!(),
60        }
61    }
62    match (err, null) {
63        (Some(err), _) => Err(err),
64        (None, true) => Ok(Datum::Null),
65        (None, false) => Ok(Datum::True),
66    }
67}
68
69/// Constructs a new multidimensional array out of an arbitrary number of
70/// lower-dimensional arrays.
71///
72/// For example, if given three 1D arrays of length 2, this function will
73/// construct a 2D array with dimensions 3x2.
74///
75/// The input datums in `datums` must all be arrays of the same dimensions.
76/// (The arrays must also be of the same element type, but that is checked by
77/// the SQL type system, rather than checked here at runtime.)
78///
79/// If all input arrays are zero-dimensional arrays, then the output is a zero-
80/// dimensional array. Otherwise the lower bound of the additional dimension is
81/// one and the length of the new dimension is equal to `datums.len()`.
82fn array_create_multidim<'a>(
83    datums: &[Datum<'a>],
84    temp_storage: &'a RowArena,
85) -> Result<Datum<'a>, EvalError> {
86    // Per PostgreSQL, if all input arrays are zero dimensional, so is the
87    // output.
88    if datums.iter().all(|d| d.unwrap_array().dims().is_empty()) {
89        let dims = &[];
90        let datums = &[];
91        let datum = temp_storage.try_make_datum(|packer| packer.try_push_array(dims, datums))?;
92        return Ok(datum);
93    }
94
95    let mut dims = vec![ArrayDimension {
96        lower_bound: 1,
97        length: datums.len(),
98    }];
99    if let Some(d) = datums.first() {
100        dims.extend(d.unwrap_array().dims());
101    };
102    let elements = datums
103        .iter()
104        .flat_map(|d| d.unwrap_array().elements().iter());
105    let datum =
106        temp_storage.try_make_datum(move |packer| packer.try_push_array(&dims, elements))?;
107    Ok(datum)
108}
109
110fn array_fill<'a>(
111    datums: &[Datum<'a>],
112    temp_storage: &'a RowArena,
113) -> Result<Datum<'a>, EvalError> {
114    const MAX_SIZE: usize = 1 << 28 - 1;
115    const NULL_ARR_ERR: &str = "dimension array or low bound array";
116    const NULL_ELEM_ERR: &str = "dimension values";
117
118    let fill = datums[0];
119    if matches!(fill, Datum::Array(_)) {
120        return Err(EvalError::Unsupported {
121            feature: "array_fill with arrays".into(),
122            discussion_no: None,
123        });
124    }
125
126    let arr = match datums[1] {
127        Datum::Null => return Err(EvalError::MustNotBeNull(NULL_ARR_ERR.into())),
128        o => o.unwrap_array(),
129    };
130
131    let dimensions = arr
132        .elements()
133        .iter()
134        .map(|d| match d {
135            Datum::Null => Err(EvalError::MustNotBeNull(NULL_ELEM_ERR.into())),
136            d => Ok(usize::cast_from(u32::reinterpret_cast(d.unwrap_int32()))),
137        })
138        .collect::<Result<Vec<_>, _>>()?;
139
140    let lower_bounds = match datums.get(2) {
141        Some(d) => {
142            let arr = match d {
143                Datum::Null => return Err(EvalError::MustNotBeNull(NULL_ARR_ERR.into())),
144                o => o.unwrap_array(),
145            };
146
147            arr.elements()
148                .iter()
149                .map(|l| match l {
150                    Datum::Null => Err(EvalError::MustNotBeNull(NULL_ELEM_ERR.into())),
151                    l => Ok(isize::cast_from(l.unwrap_int32())),
152                })
153                .collect::<Result<Vec<_>, _>>()?
154        }
155        None => {
156            vec![1isize; dimensions.len()]
157        }
158    };
159
160    if lower_bounds.len() != dimensions.len() {
161        return Err(EvalError::ArrayFillWrongArraySubscripts);
162    }
163
164    let fill_count: usize = dimensions
165        .iter()
166        .cloned()
167        .map(Some)
168        .reduce(|a, b| match (a, b) {
169            (Some(a), Some(b)) => a.checked_mul(b),
170            _ => None,
171        })
172        .flatten()
173        .ok_or(EvalError::MaxArraySizeExceeded(MAX_SIZE))?;
174
175    if matches!(
176        mz_repr::datum_size(&fill).checked_mul(fill_count),
177        None | Some(MAX_SIZE..)
178    ) {
179        return Err(EvalError::MaxArraySizeExceeded(MAX_SIZE));
180    }
181
182    let array_dimensions = if fill_count == 0 {
183        vec![ArrayDimension {
184            lower_bound: 1,
185            length: 0,
186        }]
187    } else {
188        dimensions
189            .into_iter()
190            .zip_eq(lower_bounds)
191            .map(|(length, lower_bound)| ArrayDimension {
192                lower_bound,
193                length,
194            })
195            .collect()
196    };
197
198    Ok(temp_storage.try_make_datum(|packer| {
199        packer.try_push_array(&array_dimensions, vec![fill; fill_count])
200    })?)
201}
202
203fn array_index<'a>(datums: &[Datum<'a>], offset: i64) -> Datum<'a> {
204    mz_ore::soft_assert_no_log!(offset == 0 || offset == 1, "offset must be either 0 or 1");
205
206    let array = datums[0].unwrap_array();
207    let dims = array.dims();
208    if dims.len() != datums.len() - 1 {
209        // You missed the datums "layer"
210        return Datum::Null;
211    }
212
213    let mut final_idx = 0;
214
215    for (d, idx) in dims.into_iter().zip_eq(datums[1..].iter()) {
216        // Lower bound is written in terms of 1-based indexing, which offset accounts for.
217        let idx = isize::cast_from(idx.unwrap_int64() + offset);
218
219        let (lower, upper) = d.dimension_bounds();
220
221        // This index missed all of the data at this layer. The dimension bounds are inclusive,
222        // while range checks are exclusive, so adjust.
223        if !(lower..upper + 1).contains(&idx) {
224            return Datum::Null;
225        }
226
227        // We discover how many indices our last index represents physically.
228        final_idx *= d.length;
229
230        // Because both index and lower bound are handled in 1-based indexing, taking their
231        // difference moves us back into 0-based indexing. Similarly, if the lower bound is
232        // negative, subtracting a negative value >= to itself ensures its non-negativity.
233        final_idx += usize::try_from(idx - d.lower_bound)
234            .expect("previous bounds check ensures phsical index is at least 0");
235    }
236
237    array
238        .elements()
239        .iter()
240        .nth(final_idx)
241        .unwrap_or(Datum::Null)
242}
243
244fn array_position<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
245    let array = match datums[0] {
246        Datum::Null => return Ok(Datum::Null),
247        o => o.unwrap_array(),
248    };
249
250    if array.dims().len() > 1 {
251        return Err(EvalError::MultiDimensionalArraySearch);
252    }
253
254    let search = datums[1];
255    if search == Datum::Null {
256        return Ok(Datum::Null);
257    }
258
259    let skip: usize = match datums.get(2) {
260        Some(Datum::Null) => return Err(EvalError::MustNotBeNull("initial position".into())),
261        None => 0,
262        Some(o) => usize::try_from(o.unwrap_int32())
263            .unwrap_or(0)
264            .saturating_sub(1),
265    };
266
267    let r = array.elements().iter().skip(skip).position(|d| d == search);
268
269    Ok(Datum::from(r.map(|p| {
270        // Adjust count for the amount we skipped, plus 1 for adjustng to PG indexing scheme.
271        i32::try_from(p + skip + 1).expect("fewer than i32::MAX elements in array")
272    })))
273}
274
275fn array_to_string<'a>(
276    datums: &[Datum<'a>],
277    elem_type: &SqlScalarType,
278    temp_storage: &'a RowArena,
279) -> Result<Datum<'a>, EvalError> {
280    if datums[0].is_null() || datums[1].is_null() {
281        return Ok(Datum::Null);
282    }
283    let array = datums[0].unwrap_array();
284    let delimiter = datums[1].unwrap_str();
285    let null_str = match datums.get(2) {
286        None | Some(Datum::Null) => None,
287        Some(d) => Some(d.unwrap_str()),
288    };
289
290    let mut out = String::new();
291    for elem in array.elements().iter() {
292        if elem.is_null() {
293            if let Some(null_str) = null_str {
294                out.push_str(null_str);
295                out.push_str(delimiter);
296            }
297        } else {
298            stringify_datum(&mut out, elem, elem_type)?;
299            out.push_str(delimiter);
300        }
301    }
302    if out.len() > 0 {
303        // Lop off last delimiter only if string is not empty
304        out.truncate(out.len() - delimiter.len());
305    }
306    Ok(Datum::String(temp_storage.push_string(out)))
307}
308
309fn coalesce<'a>(
310    datums: &[Datum<'a>],
311    temp_storage: &'a RowArena,
312    exprs: &'a [MirScalarExpr],
313) -> Result<Datum<'a>, EvalError> {
314    for e in exprs {
315        let d = e.eval(datums, temp_storage)?;
316        if !d.is_null() {
317            return Ok(d);
318        }
319    }
320    Ok(Datum::Null)
321}
322
323fn create_range<'a>(
324    datums: &[Datum<'a>],
325    temp_storage: &'a RowArena,
326) -> Result<Datum<'a>, EvalError> {
327    let flags = match datums[2] {
328        Datum::Null => {
329            return Err(EvalError::InvalidRange(
330                InvalidRangeError::NullRangeBoundFlags,
331            ));
332        }
333        o => o.unwrap_str(),
334    };
335
336    let (lower_inclusive, upper_inclusive) = parse_range_bound_flags(flags)?;
337
338    let mut range = Range::new(Some((
339        RangeBound::new(datums[0], lower_inclusive),
340        RangeBound::new(datums[1], upper_inclusive),
341    )));
342
343    range.canonicalize()?;
344
345    Ok(temp_storage.make_datum(|row| {
346        row.push_range(range).expect("errors already handled");
347    }))
348}
349
350fn date_diff_date<'a>(unit: Datum, a: Datum, b: Datum) -> Result<Datum<'a>, EvalError> {
351    let unit = unit.unwrap_str();
352    let unit = unit
353        .parse()
354        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
355
356    let a = a.unwrap_date();
357    let b = b.unwrap_date();
358
359    // Convert the Date into a timestamp so we can calculate age.
360    let a_ts = CheckedTimestamp::try_from(NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap())?;
361    let b_ts = CheckedTimestamp::try_from(NaiveDate::from(b).and_hms_opt(0, 0, 0).unwrap())?;
362    let diff = b_ts.diff_as(&a_ts, unit)?;
363
364    Ok(Datum::Int64(diff))
365}
366
367fn date_diff_time<'a>(unit: Datum, a: Datum, b: Datum) -> Result<Datum<'a>, EvalError> {
368    let unit = unit.unwrap_str();
369    let unit = unit
370        .parse()
371        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
372
373    let a = a.unwrap_time();
374    let b = b.unwrap_time();
375
376    // Convert the Time into a timestamp so we can calculate age.
377    let a_ts =
378        CheckedTimestamp::try_from(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_time(a))?;
379    let b_ts =
380        CheckedTimestamp::try_from(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_time(b))?;
381    let diff = b_ts.diff_as(&a_ts, unit)?;
382
383    Ok(Datum::Int64(diff))
384}
385
386fn date_diff_timestamp<'a>(unit: Datum, a: Datum, b: Datum) -> Result<Datum<'a>, EvalError> {
387    let unit = unit.unwrap_str();
388    let unit = unit
389        .parse()
390        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
391
392    let a = a.unwrap_timestamp();
393    let b = b.unwrap_timestamp();
394    let diff = b.diff_as(&a, unit)?;
395
396    Ok(Datum::Int64(diff))
397}
398
399fn date_diff_timestamptz<'a>(unit: Datum, a: Datum, b: Datum) -> Result<Datum<'a>, EvalError> {
400    let unit = unit.unwrap_str();
401    let unit = unit
402        .parse()
403        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
404
405    let a = a.unwrap_timestamptz();
406    let b = b.unwrap_timestamptz();
407    let diff = b.diff_as(&a, unit)?;
408
409    Ok(Datum::Int64(diff))
410}
411
412fn error_if_null<'a>(
413    datums: &[Datum<'a>],
414    temp_storage: &'a RowArena,
415    exprs: &'a [MirScalarExpr],
416) -> Result<Datum<'a>, EvalError> {
417    let first = exprs[0].eval(datums, temp_storage)?;
418    match first {
419        Datum::Null => {
420            let err_msg = match exprs[1].eval(datums, temp_storage)? {
421                Datum::Null => {
422                    return Err(EvalError::Internal(
423                        "unexpected NULL in error side of error_if_null".into(),
424                    ));
425                }
426                o => o.unwrap_str(),
427            };
428            Err(EvalError::IfNullError(err_msg.into()))
429        }
430        _ => Ok(first),
431    }
432}
433
434fn greatest<'a>(
435    datums: &[Datum<'a>],
436    temp_storage: &'a RowArena,
437    exprs: &'a [MirScalarExpr],
438) -> Result<Datum<'a>, EvalError> {
439    let datums = fallible_iterator::convert(exprs.iter().map(|e| e.eval(datums, temp_storage)));
440    Ok(datums
441        .filter(|d| Ok(!d.is_null()))
442        .max()?
443        .unwrap_or(Datum::Null))
444}
445
446pub fn hmac_string<'a>(
447    datums: &[Datum<'a>],
448    temp_storage: &'a RowArena,
449) -> Result<Datum<'a>, EvalError> {
450    let to_digest = datums[0].unwrap_str().as_bytes();
451    let key = datums[1].unwrap_str().as_bytes();
452    let typ = datums[2].unwrap_str();
453    hmac_inner(to_digest, key, typ, temp_storage)
454}
455
456pub fn hmac_bytes<'a>(
457    datums: &[Datum<'a>],
458    temp_storage: &'a RowArena,
459) -> Result<Datum<'a>, EvalError> {
460    let to_digest = datums[0].unwrap_bytes();
461    let key = datums[1].unwrap_bytes();
462    let typ = datums[2].unwrap_str();
463    hmac_inner(to_digest, key, typ, temp_storage)
464}
465
466pub fn hmac_inner<'a>(
467    to_digest: &[u8],
468    key: &[u8],
469    typ: &str,
470    temp_storage: &'a RowArena,
471) -> Result<Datum<'a>, EvalError> {
472    let bytes = match typ {
473        "md5" => {
474            let mut mac = Hmac::<Md5>::new_from_slice(key).expect("HMAC accepts any key size");
475            mac.update(to_digest);
476            mac.finalize().into_bytes().to_vec()
477        }
478        "sha1" => {
479            let mut mac = Hmac::<Sha1>::new_from_slice(key).expect("HMAC accepts any key size");
480            mac.update(to_digest);
481            mac.finalize().into_bytes().to_vec()
482        }
483        "sha224" => {
484            let mut mac = Hmac::<Sha224>::new_from_slice(key).expect("HMAC accepts any key size");
485            mac.update(to_digest);
486            mac.finalize().into_bytes().to_vec()
487        }
488        "sha256" => {
489            let mut mac = Hmac::<Sha256>::new_from_slice(key).expect("HMAC accepts any key size");
490            mac.update(to_digest);
491            mac.finalize().into_bytes().to_vec()
492        }
493        "sha384" => {
494            let mut mac = Hmac::<Sha384>::new_from_slice(key).expect("HMAC accepts any key size");
495            mac.update(to_digest);
496            mac.finalize().into_bytes().to_vec()
497        }
498        "sha512" => {
499            let mut mac = Hmac::<Sha512>::new_from_slice(key).expect("HMAC accepts any key size");
500            mac.update(to_digest);
501            mac.finalize().into_bytes().to_vec()
502        }
503        other => return Err(EvalError::InvalidHashAlgorithm(other.into())),
504    };
505    Ok(Datum::Bytes(temp_storage.push_bytes(bytes)))
506}
507
508fn jsonb_build_array<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
509    temp_storage.make_datum(|packer| {
510        packer.push_list(datums.into_iter().map(|d| match d {
511            Datum::Null => Datum::JsonNull,
512            d => *d,
513        }))
514    })
515}
516
517fn jsonb_build_object<'a>(
518    datums: &[Datum<'a>],
519    temp_storage: &'a RowArena,
520) -> Result<Datum<'a>, EvalError> {
521    let mut kvs = datums.chunks(2).collect::<Vec<_>>();
522    kvs.sort_by(|kv1, kv2| kv1[0].cmp(&kv2[0]));
523    kvs.dedup_by(|kv1, kv2| kv1[0] == kv2[0]);
524    temp_storage.try_make_datum(|packer| {
525        packer.push_dict_with(|packer| {
526            for kv in kvs {
527                let k = kv[0];
528                if k.is_null() {
529                    return Err(EvalError::KeyCannotBeNull);
530                };
531                let v = match kv[1] {
532                    Datum::Null => Datum::JsonNull,
533                    d => d,
534                };
535                packer.push(k);
536                packer.push(v);
537            }
538            Ok(())
539        })
540    })
541}
542
543fn least<'a>(
544    datums: &[Datum<'a>],
545    temp_storage: &'a RowArena,
546    exprs: &'a [MirScalarExpr],
547) -> Result<Datum<'a>, EvalError> {
548    let datums = fallible_iterator::convert(exprs.iter().map(|e| e.eval(datums, temp_storage)));
549    Ok(datums
550        .filter(|d| Ok(!d.is_null()))
551        .min()?
552        .unwrap_or(Datum::Null))
553}
554
555fn list_create<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
556    temp_storage.make_datum(|packer| packer.push_list(datums))
557}
558
559// TODO(benesch): remove potentially dangerous usage of `as`.
560#[allow(clippy::as_conversions)]
561fn list_index<'a>(datums: &[Datum<'a>]) -> Datum<'a> {
562    let mut buf = datums[0];
563
564    for i in datums[1..].iter() {
565        if buf.is_null() {
566            break;
567        }
568
569        let i = i.unwrap_int64();
570        if i < 1 {
571            return Datum::Null;
572        }
573
574        buf = buf
575            .unwrap_list()
576            .iter()
577            .nth(i as usize - 1)
578            .unwrap_or(Datum::Null);
579    }
580    buf
581}
582
583fn make_acl_item<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
584    let grantee = Oid(datums[0].unwrap_uint32());
585    let grantor = Oid(datums[1].unwrap_uint32());
586    let privileges = datums[2].unwrap_str();
587    let acl_mode = AclMode::parse_multiple_privileges(privileges)
588        .map_err(|e: anyhow::Error| EvalError::InvalidPrivileges(e.to_string().into()))?;
589    let is_grantable = datums[3].unwrap_bool();
590    if is_grantable {
591        return Err(EvalError::Unsupported {
592            feature: "GRANT OPTION".into(),
593            discussion_no: None,
594        });
595    }
596
597    Ok(Datum::AclItem(AclItem {
598        grantee,
599        grantor,
600        acl_mode,
601    }))
602}
603
604fn make_mz_acl_item<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
605    let grantee: RoleId = datums[0]
606        .unwrap_str()
607        .parse()
608        .map_err(|e: anyhow::Error| EvalError::InvalidRoleId(e.to_string().into()))?;
609    let grantor: RoleId = datums[1]
610        .unwrap_str()
611        .parse()
612        .map_err(|e: anyhow::Error| EvalError::InvalidRoleId(e.to_string().into()))?;
613    if grantor == RoleId::Public {
614        return Err(EvalError::InvalidRoleId(
615            "mz_aclitem grantor cannot be PUBLIC role".into(),
616        ));
617    }
618    let privileges = datums[2].unwrap_str();
619    let acl_mode = AclMode::parse_multiple_privileges(privileges)
620        .map_err(|e: anyhow::Error| EvalError::InvalidPrivileges(e.to_string().into()))?;
621
622    Ok(Datum::MzAclItem(MzAclItem {
623        grantee,
624        grantor,
625        acl_mode,
626    }))
627}
628
629// TODO(benesch): remove potentially dangerous usage of `as`.
630#[allow(clippy::as_conversions)]
631fn make_timestamp<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
632    let year: i32 = match datums[0].unwrap_int64().try_into() {
633        Ok(year) => year,
634        Err(_) => return Ok(Datum::Null),
635    };
636    let month: u32 = match datums[1].unwrap_int64().try_into() {
637        Ok(month) => month,
638        Err(_) => return Ok(Datum::Null),
639    };
640    let day: u32 = match datums[2].unwrap_int64().try_into() {
641        Ok(day) => day,
642        Err(_) => return Ok(Datum::Null),
643    };
644    let hour: u32 = match datums[3].unwrap_int64().try_into() {
645        Ok(day) => day,
646        Err(_) => return Ok(Datum::Null),
647    };
648    let minute: u32 = match datums[4].unwrap_int64().try_into() {
649        Ok(day) => day,
650        Err(_) => return Ok(Datum::Null),
651    };
652    let second_float = datums[5].unwrap_float64();
653    let second = second_float as u32;
654    let micros = ((second_float - second as f64) * 1_000_000.0) as u32;
655    let date = match NaiveDate::from_ymd_opt(year, month, day) {
656        Some(date) => date,
657        None => return Ok(Datum::Null),
658    };
659    let timestamp = match date.and_hms_micro_opt(hour, minute, second, micros) {
660        Some(timestamp) => timestamp,
661        None => return Ok(Datum::Null),
662    };
663    Ok(timestamp.try_into()?)
664}
665
666fn map_build<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
667    // Collect into a `BTreeMap` to provide the same semantics as it.
668    let map: std::collections::BTreeMap<&str, _> = datums
669        .into_iter()
670        .tuples()
671        .filter_map(|(k, v)| {
672            if k.is_null() {
673                None
674            } else {
675                Some((k.unwrap_str(), v))
676            }
677        })
678        .collect();
679
680    temp_storage.make_datum(|packer| packer.push_dict(map))
681}
682
683pub fn or<'a>(
684    datums: &[Datum<'a>],
685    temp_storage: &'a RowArena,
686    exprs: &'a [MirScalarExpr],
687) -> Result<Datum<'a>, EvalError> {
688    // If any is true, then return true. Else, if any is null, then return null. Else, return false.
689    let mut null = false;
690    let mut err = None;
691    for expr in exprs {
692        match expr.eval(datums, temp_storage) {
693            Ok(Datum::False) => {}
694            Ok(Datum::True) => return Ok(Datum::True), // short-circuit
695            // No return in these two cases, because we might still see a true
696            Ok(Datum::Null) => null = true,
697            Err(this_err) => err = std::cmp::max(err.take(), Some(this_err)),
698            _ => unreachable!(),
699        }
700    }
701    match (err, null) {
702        (Some(err), _) => Err(err),
703        (None, true) => Ok(Datum::Null),
704        (None, false) => Ok(Datum::False),
705    }
706}
707
708fn pad_leading<'a>(
709    datums: &[Datum<'a>],
710    temp_storage: &'a RowArena,
711) -> Result<Datum<'a>, EvalError> {
712    let string = datums[0].unwrap_str();
713
714    let len = match usize::try_from(datums[1].unwrap_int32()) {
715        Ok(len) => len,
716        Err(_) => {
717            return Err(EvalError::InvalidParameterValue(
718                "length must be nonnegative".into(),
719            ));
720        }
721    };
722    if len > MAX_STRING_BYTES {
723        return Err(EvalError::LengthTooLarge);
724    }
725
726    let pad_string = if datums.len() == 3 {
727        datums[2].unwrap_str()
728    } else {
729        " "
730    };
731
732    let (end_char, end_char_byte_offset) = string
733        .chars()
734        .take(len)
735        .fold((0, 0), |acc, char| (acc.0 + 1, acc.1 + char.len_utf8()));
736
737    let mut buf = String::with_capacity(len);
738    if len == end_char {
739        buf.push_str(&string[0..end_char_byte_offset]);
740    } else {
741        buf.extend(pad_string.chars().cycle().take(len - end_char));
742        buf.push_str(string);
743    }
744
745    Ok(Datum::String(temp_storage.push_string(buf)))
746}
747
748fn regexp_match_dynamic<'a>(
749    datums: &[Datum<'a>],
750    temp_storage: &'a RowArena,
751) -> Result<Datum<'a>, EvalError> {
752    let haystack = datums[0];
753    let needle = datums[1].unwrap_str();
754    let flags = match datums.get(2) {
755        Some(d) => d.unwrap_str(),
756        None => "",
757    };
758    let needle = build_regex(needle, flags)?;
759    regexp_match_static(haystack, temp_storage, &needle)
760}
761
762fn regexp_split_to_array<'a>(
763    text: Datum<'a>,
764    regexp: Datum<'a>,
765    flags: Datum<'a>,
766    temp_storage: &'a RowArena,
767) -> Result<Datum<'a>, EvalError> {
768    let text = text.unwrap_str();
769    let regexp = regexp.unwrap_str();
770    let flags = flags.unwrap_str();
771    let regexp = build_regex(regexp, flags)?;
772    regexp_split_to_array_re(text, &regexp, temp_storage)
773}
774
775fn regexp_replace_dynamic<'a>(
776    datums: &[Datum<'a>],
777    temp_storage: &'a RowArena,
778) -> Result<Datum<'a>, EvalError> {
779    let source = datums[0];
780    let pattern = datums[1];
781    let replacement = datums[2];
782    let flags = match datums.get(3) {
783        Some(d) => d.unwrap_str(),
784        None => "",
785    };
786    let (limit, flags) = regexp_replace_parse_flags(flags);
787    let regexp = build_regex(pattern.unwrap_str(), &flags)?;
788    regexp_replace_static(source, replacement, &regexp, limit, temp_storage)
789}
790
791fn replace<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
792    Datum::String(
793        temp_storage.push_string(
794            datums[0]
795                .unwrap_str()
796                .replace(datums[1].unwrap_str(), datums[2].unwrap_str()),
797        ),
798    )
799}
800
801fn string_to_array<'a>(
802    string_datum: Datum<'a>,
803    delimiter: Datum<'a>,
804    null_string: Datum<'a>,
805    temp_storage: &'a RowArena,
806) -> Result<Datum<'a>, EvalError> {
807    if string_datum.is_null() {
808        return Ok(Datum::Null);
809    }
810
811    let string = string_datum.unwrap_str();
812
813    if string.is_empty() {
814        let mut row = Row::default();
815        let mut packer = row.packer();
816        packer.try_push_array(&[], std::iter::empty::<Datum>())?;
817
818        return Ok(temp_storage.push_unary_row(row));
819    }
820
821    if delimiter.is_null() {
822        let split_all_chars_delimiter = "";
823        return string_to_array_impl(string, split_all_chars_delimiter, null_string, temp_storage);
824    }
825
826    let delimiter = delimiter.unwrap_str();
827
828    if delimiter.is_empty() {
829        let mut row = Row::default();
830        let mut packer = row.packer();
831        packer.try_push_array(
832            &[ArrayDimension {
833                lower_bound: 1,
834                length: 1,
835            }],
836            vec![string].into_iter().map(Datum::String),
837        )?;
838
839        Ok(temp_storage.push_unary_row(row))
840    } else {
841        string_to_array_impl(string, delimiter, null_string, temp_storage)
842    }
843}
844
845fn string_to_array_impl<'a>(
846    string: &str,
847    delimiter: &str,
848    null_string: Datum<'a>,
849    temp_storage: &'a RowArena,
850) -> Result<Datum<'a>, EvalError> {
851    let mut row = Row::default();
852    let mut packer = row.packer();
853
854    let result = string.split(delimiter);
855    let found: Vec<&str> = if delimiter.is_empty() {
856        result.filter(|s| !s.is_empty()).collect()
857    } else {
858        result.collect()
859    };
860    let array_dimensions = [ArrayDimension {
861        lower_bound: 1,
862        length: found.len(),
863    }];
864
865    if null_string.is_null() {
866        packer.try_push_array(&array_dimensions, found.into_iter().map(Datum::String))?;
867    } else {
868        let null_string = null_string.unwrap_str();
869        let found_datums = found.into_iter().map(|chunk| {
870            if chunk.eq(null_string) {
871                Datum::Null
872            } else {
873                Datum::String(chunk)
874            }
875        });
876
877        packer.try_push_array(&array_dimensions, found_datums)?;
878    }
879
880    Ok(temp_storage.push_unary_row(row))
881}
882
883fn substr<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
884    let s: &'a str = datums[0].unwrap_str();
885
886    let raw_start_idx = i64::from(datums[1].unwrap_int32()) - 1;
887    let start_idx = match usize::try_from(cmp::max(raw_start_idx, 0)) {
888        Ok(i) => i,
889        Err(_) => {
890            return Err(EvalError::InvalidParameterValue(
891                format!(
892                    "substring starting index ({}) exceeds min/max position",
893                    raw_start_idx
894                )
895                .into(),
896            ));
897        }
898    };
899
900    let mut char_indices = s.char_indices();
901    let get_str_index = |(index, _char)| index;
902
903    let str_len = s.len();
904    let start_char_idx = char_indices.nth(start_idx).map_or(str_len, get_str_index);
905
906    if datums.len() == 3 {
907        let end_idx = match i64::from(datums[2].unwrap_int32()) {
908            e if e < 0 => {
909                return Err(EvalError::InvalidParameterValue(
910                    "negative substring length not allowed".into(),
911                ));
912            }
913            e if e == 0 || e + raw_start_idx < 1 => return Ok(Datum::String("")),
914            e => {
915                let e = cmp::min(raw_start_idx + e - 1, e - 1);
916                match usize::try_from(e) {
917                    Ok(i) => i,
918                    Err(_) => {
919                        return Err(EvalError::InvalidParameterValue(
920                            format!("substring length ({}) exceeds max position", e).into(),
921                        ));
922                    }
923                }
924            }
925        };
926
927        let end_char_idx = char_indices.nth(end_idx).map_or(str_len, get_str_index);
928
929        Ok(Datum::String(&s[start_char_idx..end_char_idx]))
930    } else {
931        Ok(Datum::String(&s[start_char_idx..]))
932    }
933}
934
935fn split_part<'a>(datums: &[Datum<'a>]) -> Result<Datum<'a>, EvalError> {
936    let string = datums[0].unwrap_str();
937    let delimiter = datums[1].unwrap_str();
938
939    // Provided index value begins at 1, not 0.
940    let index = match usize::try_from(i64::from(datums[2].unwrap_int32()) - 1) {
941        Ok(index) => index,
942        Err(_) => {
943            return Err(EvalError::InvalidParameterValue(
944                "field position must be greater than zero".into(),
945            ));
946        }
947    };
948
949    // If the provided delimiter is the empty string,
950    // PostgreSQL does not break the string into individual
951    // characters. Instead, it generates the following parts: [string].
952    if delimiter.is_empty() {
953        if index == 0 {
954            return Ok(datums[0]);
955        } else {
956            return Ok(Datum::String(""));
957        }
958    }
959
960    // If provided index is greater than the number of split parts,
961    // return an empty string.
962    Ok(Datum::String(
963        string.split(delimiter).nth(index).unwrap_or(""),
964    ))
965}
966
967fn text_concat_variadic<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
968    let mut buf = String::new();
969    for d in datums {
970        if !d.is_null() {
971            buf.push_str(d.unwrap_str());
972        }
973    }
974    Datum::String(temp_storage.push_string(buf))
975}
976
977fn text_concat_ws<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
978    let ws = match datums[0] {
979        Datum::Null => return Datum::Null,
980        d => d.unwrap_str(),
981    };
982
983    let buf = Itertools::join(
984        &mut datums[1..].iter().filter_map(|d| match d {
985            Datum::Null => None,
986            d => Some(d.unwrap_str()),
987        }),
988        ws,
989    );
990
991    Datum::String(temp_storage.push_string(buf))
992}
993
994fn translate<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
995    let string = datums[0].unwrap_str();
996    let from = datums[1].unwrap_str().chars().collect::<Vec<_>>();
997    let to = datums[2].unwrap_str().chars().collect::<Vec<_>>();
998
999    Datum::String(
1000        temp_storage.push_string(
1001            string
1002                .chars()
1003                .filter_map(|c| match from.iter().position(|f| f == &c) {
1004                    Some(idx) => to.get(idx).copied(),
1005                    None => Some(c),
1006                })
1007                .collect(),
1008        ),
1009    )
1010}
1011
1012// TODO ///
1013
1014// TODO(benesch): remove potentially dangerous usage of `as`.
1015#[allow(clippy::as_conversions)]
1016fn list_slice_linear<'a>(datums: &[Datum<'a>], temp_storage: &'a RowArena) -> Datum<'a> {
1017    assert_eq!(
1018        datums.len() % 2,
1019        1,
1020        "expr::scalar::func::list_slice expects an odd number of arguments; 1 for list + 2 \
1021        for each start-end pair"
1022    );
1023    assert!(
1024        datums.len() > 2,
1025        "expr::scalar::func::list_slice expects at least 3 arguments; 1 for list + at least \
1026        one start-end pair"
1027    );
1028
1029    let mut start_idx = 0;
1030    let mut total_length = usize::MAX;
1031
1032    for (start, end) in datums[1..].iter().tuples::<(_, _)>() {
1033        let start = std::cmp::max(start.unwrap_int64(), 1);
1034        let end = end.unwrap_int64();
1035
1036        // Result should be empty list.
1037        if start > end {
1038            start_idx = 0;
1039            total_length = 0;
1040            break;
1041        }
1042
1043        let start_inner = start as usize - 1;
1044        // Start index only moves to geq positions.
1045        start_idx += start_inner;
1046
1047        // Length index only moves to leq positions
1048        let length_inner = (end - start) as usize + 1;
1049        total_length = std::cmp::min(length_inner, total_length - start_inner);
1050    }
1051
1052    let iter = datums[0]
1053        .unwrap_list()
1054        .iter()
1055        .skip(start_idx)
1056        .take(total_length);
1057
1058    temp_storage.make_datum(|row| {
1059        row.push_list_with(|row| {
1060            // if iter is empty, will get the appropriate empty list.
1061            for d in iter {
1062                row.push(d);
1063            }
1064        });
1065    })
1066}
1067
1068#[derive(Ord, PartialOrd, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, MzReflect)]
1069pub enum VariadicFunc {
1070    Coalesce,
1071    Greatest,
1072    Least,
1073    Concat,
1074    ConcatWs,
1075    MakeTimestamp,
1076    PadLeading,
1077    Substr,
1078    Replace,
1079    JsonbBuildArray,
1080    JsonbBuildObject,
1081    MapBuild {
1082        value_type: SqlScalarType,
1083    },
1084    ArrayCreate {
1085        // We need to know the element type to type empty arrays.
1086        elem_type: SqlScalarType,
1087    },
1088    ArrayToString {
1089        elem_type: SqlScalarType,
1090    },
1091    ArrayIndex {
1092        // Adjusts the index by offset depending on whether being called on an array or an
1093        // Int2Vector.
1094        offset: i64,
1095    },
1096    ListCreate {
1097        // We need to know the element type to type empty lists.
1098        elem_type: SqlScalarType,
1099    },
1100    RecordCreate {
1101        field_names: Vec<ColumnName>,
1102    },
1103    ListIndex,
1104    ListSliceLinear,
1105    SplitPart,
1106    RegexpMatch,
1107    HmacString,
1108    HmacBytes,
1109    ErrorIfNull,
1110    DateBinTimestamp,
1111    DateBinTimestampTz,
1112    DateDiffTimestamp,
1113    DateDiffTimestampTz,
1114    DateDiffDate,
1115    DateDiffTime,
1116    And,
1117    Or,
1118    RangeCreate {
1119        elem_type: SqlScalarType,
1120    },
1121    MakeAclItem,
1122    MakeMzAclItem,
1123    Translate,
1124    ArrayPosition,
1125    ArrayFill {
1126        elem_type: SqlScalarType,
1127    },
1128    StringToArray,
1129    TimezoneTime,
1130    RegexpSplitToArray,
1131    RegexpReplace,
1132}
1133
1134impl VariadicFunc {
1135    pub fn eval<'a>(
1136        &'a self,
1137        datums: &[Datum<'a>],
1138        temp_storage: &'a RowArena,
1139        exprs: &'a [MirScalarExpr],
1140    ) -> Result<Datum<'a>, EvalError> {
1141        // Evaluate all non-eager functions directly
1142        match self {
1143            VariadicFunc::Coalesce => return coalesce(datums, temp_storage, exprs),
1144            VariadicFunc::Greatest => return greatest(datums, temp_storage, exprs),
1145            VariadicFunc::And => return and(datums, temp_storage, exprs),
1146            VariadicFunc::Or => return or(datums, temp_storage, exprs),
1147            VariadicFunc::ErrorIfNull => return error_if_null(datums, temp_storage, exprs),
1148            VariadicFunc::Least => return least(datums, temp_storage, exprs),
1149            _ => {}
1150        };
1151
1152        // Compute parameters to eager functions
1153        let ds = exprs
1154            .iter()
1155            .map(|e| e.eval(datums, temp_storage))
1156            .collect::<Result<Vec<_>, _>>()?;
1157        // Check NULL propagation
1158        if self.propagates_nulls() && ds.iter().any(|d| d.is_null()) {
1159            return Ok(Datum::Null);
1160        }
1161
1162        // Evaluate eager functions
1163        match self {
1164            VariadicFunc::Coalesce
1165            | VariadicFunc::Greatest
1166            | VariadicFunc::And
1167            | VariadicFunc::Or
1168            | VariadicFunc::ErrorIfNull
1169            | VariadicFunc::Least => unreachable!(),
1170            VariadicFunc::Concat => Ok(text_concat_variadic(&ds, temp_storage)),
1171            VariadicFunc::ConcatWs => Ok(text_concat_ws(&ds, temp_storage)),
1172            VariadicFunc::MakeTimestamp => make_timestamp(&ds),
1173            VariadicFunc::PadLeading => pad_leading(&ds, temp_storage),
1174            VariadicFunc::Substr => substr(&ds),
1175            VariadicFunc::Replace => Ok(replace(&ds, temp_storage)),
1176            VariadicFunc::Translate => Ok(translate(&ds, temp_storage)),
1177            VariadicFunc::JsonbBuildArray => Ok(jsonb_build_array(&ds, temp_storage)),
1178            VariadicFunc::JsonbBuildObject => jsonb_build_object(&ds, temp_storage),
1179            VariadicFunc::MapBuild { .. } => Ok(map_build(&ds, temp_storage)),
1180            VariadicFunc::ArrayCreate {
1181                elem_type: SqlScalarType::Array(_),
1182            } => array_create_multidim(&ds, temp_storage),
1183            VariadicFunc::ArrayCreate { .. } => array_create_scalar(&ds, temp_storage),
1184            VariadicFunc::ArrayToString { elem_type } => {
1185                array_to_string(&ds, elem_type, temp_storage)
1186            }
1187            VariadicFunc::ArrayIndex { offset } => Ok(array_index(&ds, *offset)),
1188
1189            VariadicFunc::ListCreate { .. } | VariadicFunc::RecordCreate { .. } => {
1190                Ok(list_create(&ds, temp_storage))
1191            }
1192            VariadicFunc::ListIndex => Ok(list_index(&ds)),
1193            VariadicFunc::ListSliceLinear => Ok(list_slice_linear(&ds, temp_storage)),
1194            VariadicFunc::SplitPart => split_part(&ds),
1195            VariadicFunc::RegexpMatch => regexp_match_dynamic(&ds, temp_storage),
1196            VariadicFunc::HmacString => hmac_string(&ds, temp_storage),
1197            VariadicFunc::HmacBytes => hmac_bytes(&ds, temp_storage),
1198            VariadicFunc::DateBinTimestamp => date_bin(
1199                ds[0].unwrap_interval(),
1200                ds[1].unwrap_timestamp(),
1201                ds[2].unwrap_timestamp(),
1202            ),
1203            VariadicFunc::DateBinTimestampTz => date_bin(
1204                ds[0].unwrap_interval(),
1205                ds[1].unwrap_timestamptz(),
1206                ds[2].unwrap_timestamptz(),
1207            ),
1208            VariadicFunc::DateDiffTimestamp => date_diff_timestamp(ds[0], ds[1], ds[2]),
1209            VariadicFunc::DateDiffTimestampTz => date_diff_timestamptz(ds[0], ds[1], ds[2]),
1210            VariadicFunc::DateDiffDate => date_diff_date(ds[0], ds[1], ds[2]),
1211            VariadicFunc::DateDiffTime => date_diff_time(ds[0], ds[1], ds[2]),
1212            VariadicFunc::RangeCreate { .. } => create_range(&ds, temp_storage),
1213            VariadicFunc::MakeAclItem => make_acl_item(&ds),
1214            VariadicFunc::MakeMzAclItem => make_mz_acl_item(&ds),
1215            VariadicFunc::ArrayPosition => array_position(&ds),
1216            VariadicFunc::ArrayFill { .. } => array_fill(&ds, temp_storage),
1217            VariadicFunc::TimezoneTime => parse_timezone(ds[0].unwrap_str(), TimezoneSpec::Posix)
1218                .map(|tz| {
1219                    timezone_time(
1220                        tz,
1221                        ds[1].unwrap_time(),
1222                        &ds[2].unwrap_timestamptz().naive_utc(),
1223                    )
1224                    .into()
1225                }),
1226            VariadicFunc::RegexpSplitToArray => {
1227                let flags = if ds.len() == 2 {
1228                    Datum::String("")
1229                } else {
1230                    ds[2]
1231                };
1232                regexp_split_to_array(ds[0], ds[1], flags, temp_storage)
1233            }
1234            VariadicFunc::RegexpReplace => regexp_replace_dynamic(&ds, temp_storage),
1235            VariadicFunc::StringToArray => {
1236                let null_string = if ds.len() == 2 { Datum::Null } else { ds[2] };
1237
1238                string_to_array(ds[0], ds[1], null_string, temp_storage)
1239            }
1240        }
1241    }
1242
1243    pub fn is_associative(&self) -> bool {
1244        match self {
1245            VariadicFunc::Coalesce
1246            | VariadicFunc::Greatest
1247            | VariadicFunc::Least
1248            | VariadicFunc::Concat
1249            | VariadicFunc::And
1250            | VariadicFunc::Or => true,
1251
1252            VariadicFunc::MakeTimestamp
1253            | VariadicFunc::PadLeading
1254            | VariadicFunc::ConcatWs
1255            | VariadicFunc::Substr
1256            | VariadicFunc::Replace
1257            | VariadicFunc::Translate
1258            | VariadicFunc::JsonbBuildArray
1259            | VariadicFunc::JsonbBuildObject
1260            | VariadicFunc::MapBuild { value_type: _ }
1261            | VariadicFunc::ArrayCreate { elem_type: _ }
1262            | VariadicFunc::ArrayToString { elem_type: _ }
1263            | VariadicFunc::ArrayIndex { offset: _ }
1264            | VariadicFunc::ListCreate { elem_type: _ }
1265            | VariadicFunc::RecordCreate { field_names: _ }
1266            | VariadicFunc::ListIndex
1267            | VariadicFunc::ListSliceLinear
1268            | VariadicFunc::SplitPart
1269            | VariadicFunc::RegexpMatch
1270            | VariadicFunc::HmacString
1271            | VariadicFunc::HmacBytes
1272            | VariadicFunc::ErrorIfNull
1273            | VariadicFunc::DateBinTimestamp
1274            | VariadicFunc::DateBinTimestampTz
1275            | VariadicFunc::DateDiffTimestamp
1276            | VariadicFunc::DateDiffTimestampTz
1277            | VariadicFunc::DateDiffDate
1278            | VariadicFunc::DateDiffTime
1279            | VariadicFunc::RangeCreate { .. }
1280            | VariadicFunc::MakeAclItem
1281            | VariadicFunc::MakeMzAclItem
1282            | VariadicFunc::ArrayPosition
1283            | VariadicFunc::ArrayFill { .. }
1284            | VariadicFunc::TimezoneTime
1285            | VariadicFunc::RegexpSplitToArray
1286            | VariadicFunc::StringToArray
1287            | VariadicFunc::RegexpReplace => false,
1288        }
1289    }
1290
1291    pub fn output_type(&self, input_types: Vec<SqlColumnType>) -> SqlColumnType {
1292        use VariadicFunc::*;
1293        let in_nullable = input_types.iter().any(|t| t.nullable);
1294        match self {
1295            Greatest | Least => input_types
1296                .into_iter()
1297                .reduce(|l, r| l.union(&r).unwrap())
1298                .unwrap(),
1299            Coalesce => {
1300                // Note that the parser doesn't allow empty argument lists for variadic functions
1301                // that use the standard function call syntax (ArrayCreate and co. are different
1302                // because of the special syntax for calling them).
1303                let nullable = input_types.iter().all(|typ| typ.nullable);
1304                input_types
1305                    .into_iter()
1306                    .reduce(|l, r| l.union(&r).unwrap())
1307                    .unwrap()
1308                    .nullable(nullable)
1309            }
1310            Concat | ConcatWs => SqlScalarType::String.nullable(in_nullable),
1311            MakeTimestamp => SqlScalarType::Timestamp { precision: None }.nullable(true),
1312            PadLeading => SqlScalarType::String.nullable(in_nullable),
1313            Substr => SqlScalarType::String.nullable(in_nullable),
1314            Replace => SqlScalarType::String.nullable(in_nullable),
1315            Translate => SqlScalarType::String.nullable(in_nullable),
1316            JsonbBuildArray | JsonbBuildObject => SqlScalarType::Jsonb.nullable(true),
1317            MapBuild { value_type } => SqlScalarType::Map {
1318                value_type: Box::new(value_type.clone()),
1319                custom_id: None,
1320            }
1321            .nullable(true),
1322            ArrayCreate { elem_type } => {
1323                debug_assert!(
1324                    input_types.iter().all(|t| t.scalar_type.base_eq(elem_type)),
1325                    "Args to ArrayCreate should have types that are compatible with the elem_type"
1326                );
1327                match elem_type {
1328                    SqlScalarType::Array(_) => elem_type.clone().nullable(false),
1329                    _ => SqlScalarType::Array(Box::new(elem_type.clone())).nullable(false),
1330                }
1331            }
1332            ArrayToString { .. } => SqlScalarType::String.nullable(in_nullable),
1333            ArrayIndex { .. } => input_types[0]
1334                .scalar_type
1335                .unwrap_array_element_type()
1336                .clone()
1337                .nullable(true),
1338            ListCreate { elem_type } => {
1339                // commented out to work around
1340                // https://github.com/MaterializeInc/database-issues/issues/2730
1341                // soft_assert!(
1342                //     input_types.iter().all(|t| t.scalar_type.base_eq(elem_type)),
1343                //     "{}", format!("Args to ListCreate should have types that are compatible with the elem_type.\nArgs:{:#?}\nelem_type:{:#?}", input_types, elem_type)
1344                // );
1345                SqlScalarType::List {
1346                    element_type: Box::new(elem_type.clone()),
1347                    custom_id: None,
1348                }
1349                .nullable(false)
1350            }
1351            ListIndex => input_types[0]
1352                .scalar_type
1353                .unwrap_list_nth_layer_type(input_types.len() - 1)
1354                .clone()
1355                .nullable(true),
1356            ListSliceLinear { .. } => input_types[0].scalar_type.clone().nullable(in_nullable),
1357            RecordCreate { field_names } => SqlScalarType::Record {
1358                fields: field_names
1359                    .clone()
1360                    .into_iter()
1361                    .zip_eq(input_types)
1362                    .collect(),
1363                custom_id: None,
1364            }
1365            .nullable(false),
1366            SplitPart => SqlScalarType::String.nullable(in_nullable),
1367            RegexpMatch => SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
1368            HmacString | HmacBytes => SqlScalarType::Bytes.nullable(in_nullable),
1369            ErrorIfNull => input_types[0].scalar_type.clone().nullable(false),
1370            DateBinTimestamp => SqlScalarType::Timestamp { precision: None }.nullable(in_nullable),
1371            DateBinTimestampTz => {
1372                SqlScalarType::TimestampTz { precision: None }.nullable(in_nullable)
1373            }
1374            DateDiffTimestamp => SqlScalarType::Int64.nullable(in_nullable),
1375            DateDiffTimestampTz => SqlScalarType::Int64.nullable(in_nullable),
1376            DateDiffDate => SqlScalarType::Int64.nullable(in_nullable),
1377            DateDiffTime => SqlScalarType::Int64.nullable(in_nullable),
1378            And | Or => SqlScalarType::Bool.nullable(in_nullable),
1379            RangeCreate { elem_type } => SqlScalarType::Range {
1380                element_type: Box::new(elem_type.clone()),
1381            }
1382            .nullable(false),
1383            MakeAclItem => SqlScalarType::AclItem.nullable(true),
1384            MakeMzAclItem => SqlScalarType::MzAclItem.nullable(true),
1385            ArrayPosition => SqlScalarType::Int32.nullable(true),
1386            ArrayFill { elem_type } => {
1387                SqlScalarType::Array(Box::new(elem_type.clone())).nullable(false)
1388            }
1389            TimezoneTime => SqlScalarType::Time.nullable(in_nullable),
1390            RegexpSplitToArray => {
1391                SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(in_nullable)
1392            }
1393            RegexpReplace => SqlScalarType::String.nullable(in_nullable),
1394            StringToArray => SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true),
1395        }
1396    }
1397
1398    /// Whether the function output is NULL if any of its inputs are NULL.
1399    ///
1400    /// NB: if any input is NULL the output will be returned as NULL without
1401    /// calling the function.
1402    pub fn propagates_nulls(&self) -> bool {
1403        // NOTE: The following is a list of the variadic functions
1404        // that **DO NOT** propagate nulls.
1405        !matches!(
1406            self,
1407            VariadicFunc::And
1408                | VariadicFunc::Or
1409                | VariadicFunc::Coalesce
1410                | VariadicFunc::Greatest
1411                | VariadicFunc::Least
1412                | VariadicFunc::Concat
1413                | VariadicFunc::ConcatWs
1414                | VariadicFunc::JsonbBuildArray
1415                | VariadicFunc::JsonbBuildObject
1416                | VariadicFunc::MapBuild { .. }
1417                | VariadicFunc::ListCreate { .. }
1418                | VariadicFunc::RecordCreate { .. }
1419                | VariadicFunc::ArrayCreate { .. }
1420                | VariadicFunc::ArrayToString { .. }
1421                | VariadicFunc::ErrorIfNull
1422                | VariadicFunc::RangeCreate { .. }
1423                | VariadicFunc::ArrayPosition
1424                | VariadicFunc::ArrayFill { .. }
1425                | VariadicFunc::StringToArray
1426        )
1427    }
1428
1429    /// Whether the function might return NULL even if none of its inputs are
1430    /// NULL.
1431    ///
1432    /// This is presently conservative, and may indicate that a function
1433    /// introduces nulls even when it does not.
1434    pub fn introduces_nulls(&self) -> bool {
1435        use VariadicFunc::*;
1436        match self {
1437            Concat
1438            | ConcatWs
1439            | PadLeading
1440            | Substr
1441            | Replace
1442            | Translate
1443            | JsonbBuildArray
1444            | JsonbBuildObject
1445            | MapBuild { .. }
1446            | ArrayCreate { .. }
1447            | ArrayToString { .. }
1448            | ListCreate { .. }
1449            | RecordCreate { .. }
1450            | ListSliceLinear
1451            | SplitPart
1452            | HmacString
1453            | HmacBytes
1454            | ErrorIfNull
1455            | DateBinTimestamp
1456            | DateBinTimestampTz
1457            | DateDiffTimestamp
1458            | DateDiffTimestampTz
1459            | DateDiffDate
1460            | DateDiffTime
1461            | RangeCreate { .. }
1462            | And
1463            | Or
1464            | MakeAclItem
1465            | MakeMzAclItem
1466            | ArrayPosition
1467            | ArrayFill { .. }
1468            | TimezoneTime
1469            | RegexpSplitToArray
1470            | RegexpReplace => false,
1471            Coalesce
1472            | Greatest
1473            | Least
1474            | MakeTimestamp
1475            | ArrayIndex { .. }
1476            | StringToArray
1477            | ListIndex
1478            | RegexpMatch => true,
1479        }
1480    }
1481
1482    pub fn switch_and_or(&self) -> Self {
1483        match self {
1484            VariadicFunc::And => VariadicFunc::Or,
1485            VariadicFunc::Or => VariadicFunc::And,
1486            _ => unreachable!(),
1487        }
1488    }
1489
1490    pub fn is_infix_op(&self) -> bool {
1491        use VariadicFunc::*;
1492        matches!(self, And | Or)
1493    }
1494
1495    /// Gives the unit (u) of OR or AND, such that `u AND/OR x == x`.
1496    /// Note that a 0-arg AND/OR evaluates to unit_of_and_or.
1497    pub fn unit_of_and_or(&self) -> MirScalarExpr {
1498        match self {
1499            VariadicFunc::And => MirScalarExpr::literal_true(),
1500            VariadicFunc::Or => MirScalarExpr::literal_false(),
1501            _ => unreachable!(),
1502        }
1503    }
1504
1505    /// Gives the zero (z) of OR or AND, such that `z AND/OR x == z`.
1506    pub fn zero_of_and_or(&self) -> MirScalarExpr {
1507        match self {
1508            VariadicFunc::And => MirScalarExpr::literal_false(),
1509            VariadicFunc::Or => MirScalarExpr::literal_true(),
1510            _ => unreachable!(),
1511        }
1512    }
1513
1514    /// Returns true if the function could introduce an error on non-error inputs.
1515    pub fn could_error(&self) -> bool {
1516        match self {
1517            VariadicFunc::And | VariadicFunc::Or => false,
1518            VariadicFunc::Coalesce => false,
1519            VariadicFunc::Greatest | VariadicFunc::Least => false,
1520            VariadicFunc::Concat | VariadicFunc::ConcatWs => false,
1521            VariadicFunc::Replace => false,
1522            VariadicFunc::Translate => false,
1523            VariadicFunc::ArrayIndex { .. } => false,
1524            VariadicFunc::ListCreate { .. } | VariadicFunc::RecordCreate { .. } => false,
1525            // All other cases are unknown
1526            _ => true,
1527        }
1528    }
1529
1530    /// Returns true if the function is monotone. (Non-strict; either increasing or decreasing.)
1531    /// Monotone functions map ranges to ranges: ie. given a range of possible inputs, we can
1532    /// determine the range of possible outputs just by mapping the endpoints.
1533    ///
1534    /// This describes the *pointwise* behaviour of the function:
1535    /// ie. if more than one argument is provided, this describes the behaviour of
1536    /// any specific argument as the others are held constant. (For example, `COALESCE(a, b)` is
1537    /// monotone in `a` because for any particular value of `b`, increasing `a` will never
1538    /// cause the result to decrease.)
1539    ///
1540    /// This property describes the behaviour of the function over ranges where the function is defined:
1541    /// ie. the arguments and the result are non-error datums.
1542    pub fn is_monotone(&self) -> bool {
1543        match self {
1544            VariadicFunc::Coalesce
1545            | VariadicFunc::Greatest
1546            | VariadicFunc::Least
1547            | VariadicFunc::And
1548            | VariadicFunc::Or => true,
1549            VariadicFunc::Concat
1550            | VariadicFunc::ConcatWs
1551            | VariadicFunc::MakeTimestamp
1552            | VariadicFunc::PadLeading
1553            | VariadicFunc::Substr
1554            | VariadicFunc::Replace
1555            | VariadicFunc::JsonbBuildArray
1556            | VariadicFunc::JsonbBuildObject
1557            | VariadicFunc::MapBuild { .. }
1558            | VariadicFunc::ArrayCreate { .. }
1559            | VariadicFunc::ArrayToString { .. }
1560            | VariadicFunc::ArrayIndex { .. }
1561            | VariadicFunc::ListCreate { .. }
1562            | VariadicFunc::RecordCreate { .. }
1563            | VariadicFunc::ListIndex
1564            | VariadicFunc::ListSliceLinear
1565            | VariadicFunc::SplitPart
1566            | VariadicFunc::RegexpMatch
1567            | VariadicFunc::HmacString
1568            | VariadicFunc::HmacBytes
1569            | VariadicFunc::ErrorIfNull
1570            | VariadicFunc::DateBinTimestamp
1571            | VariadicFunc::DateBinTimestampTz
1572            | VariadicFunc::RangeCreate { .. }
1573            | VariadicFunc::MakeAclItem
1574            | VariadicFunc::MakeMzAclItem
1575            | VariadicFunc::Translate
1576            | VariadicFunc::ArrayPosition
1577            | VariadicFunc::ArrayFill { .. }
1578            | VariadicFunc::DateDiffTimestamp
1579            | VariadicFunc::DateDiffTimestampTz
1580            | VariadicFunc::DateDiffDate
1581            | VariadicFunc::DateDiffTime
1582            | VariadicFunc::TimezoneTime
1583            | VariadicFunc::RegexpSplitToArray
1584            | VariadicFunc::StringToArray
1585            | VariadicFunc::RegexpReplace => false,
1586        }
1587    }
1588}
1589
1590impl std::fmt::Display for VariadicFunc {
1591    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1592        match self {
1593            VariadicFunc::Coalesce => f.write_str("coalesce"),
1594            VariadicFunc::Greatest => f.write_str("greatest"),
1595            VariadicFunc::Least => f.write_str("least"),
1596            VariadicFunc::Concat => f.write_str("concat"),
1597            VariadicFunc::ConcatWs => f.write_str("concat_ws"),
1598            VariadicFunc::MakeTimestamp => f.write_str("makets"),
1599            VariadicFunc::PadLeading => f.write_str("lpad"),
1600            VariadicFunc::Substr => f.write_str("substr"),
1601            VariadicFunc::Replace => f.write_str("replace"),
1602            VariadicFunc::Translate => f.write_str("translate"),
1603            VariadicFunc::JsonbBuildArray => f.write_str("jsonb_build_array"),
1604            VariadicFunc::JsonbBuildObject => f.write_str("jsonb_build_object"),
1605            VariadicFunc::MapBuild { .. } => f.write_str("map_build"),
1606            VariadicFunc::ArrayCreate { .. } => f.write_str("array_create"),
1607            VariadicFunc::ArrayToString { .. } => f.write_str("array_to_string"),
1608            VariadicFunc::ArrayIndex { .. } => f.write_str("array_index"),
1609            VariadicFunc::ListCreate { .. } => f.write_str("list_create"),
1610            VariadicFunc::RecordCreate { .. } => f.write_str("record_create"),
1611            VariadicFunc::ListIndex => f.write_str("list_index"),
1612            VariadicFunc::ListSliceLinear => f.write_str("list_slice_linear"),
1613            VariadicFunc::SplitPart => f.write_str("split_string"),
1614            VariadicFunc::RegexpMatch => f.write_str("regexp_match"),
1615            VariadicFunc::HmacString | VariadicFunc::HmacBytes => f.write_str("hmac"),
1616            VariadicFunc::ErrorIfNull => f.write_str("error_if_null"),
1617            VariadicFunc::DateBinTimestamp => f.write_str("timestamp_bin"),
1618            VariadicFunc::DateBinTimestampTz => f.write_str("timestamptz_bin"),
1619            VariadicFunc::DateDiffTimestamp
1620            | VariadicFunc::DateDiffTimestampTz
1621            | VariadicFunc::DateDiffDate
1622            | VariadicFunc::DateDiffTime => f.write_str("datediff"),
1623            VariadicFunc::And => f.write_str("AND"),
1624            VariadicFunc::Or => f.write_str("OR"),
1625            VariadicFunc::RangeCreate {
1626                elem_type: element_type,
1627            } => f.write_str(match element_type {
1628                SqlScalarType::Int32 => "int4range",
1629                SqlScalarType::Int64 => "int8range",
1630                SqlScalarType::Date => "daterange",
1631                SqlScalarType::Numeric { .. } => "numrange",
1632                SqlScalarType::Timestamp { .. } => "tsrange",
1633                SqlScalarType::TimestampTz { .. } => "tstzrange",
1634                _ => unreachable!(),
1635            }),
1636            VariadicFunc::MakeAclItem => f.write_str("makeaclitem"),
1637            VariadicFunc::MakeMzAclItem => f.write_str("make_mz_aclitem"),
1638            VariadicFunc::ArrayPosition => f.write_str("array_position"),
1639            VariadicFunc::ArrayFill { .. } => f.write_str("array_fill"),
1640            VariadicFunc::TimezoneTime => f.write_str("timezonet"),
1641            VariadicFunc::RegexpSplitToArray => f.write_str("regexp_split_to_array"),
1642            VariadicFunc::RegexpReplace => f.write_str("regexp_replace"),
1643            VariadicFunc::StringToArray => f.write_str("string_to_array"),
1644        }
1645    }
1646}