Skip to main content

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::borrow::Cow;
17use std::cmp;
18use std::fmt;
19
20use aws_lc_rs::hmac as aws_hmac;
21use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
22use fallible_iterator::FallibleIterator;
23use hmac::{Hmac, Mac};
24use itertools::Itertools;
25use md5::Md5;
26use mz_expr_derive::sqlfunc;
27use mz_ore::cast::{CastFrom, ReinterpretCast};
28use mz_pgtz::timezone::TimezoneSpec;
29use mz_repr::ReprColumnType;
30use mz_repr::adt::array::{Array, ArrayDimension, ArrayDimensions, InvalidArrayError};
31use mz_repr::adt::mz_acl_item::{AclItem, AclMode, MzAclItem};
32use mz_repr::adt::range::{InvalidRangeError, Range, RangeBound, parse_range_bound_flags};
33use mz_repr::adt::system::Oid;
34use mz_repr::adt::timestamp::CheckedTimestamp;
35use mz_repr::role_id::RoleId;
36use mz_repr::{
37    ColumnName, Datum, DatumList, FromDatum, InputDatumType, OptionalArg, OutputDatumType, Row,
38    RowArena, SqlColumnType, SqlScalarType, Variadic,
39};
40use serde::{Deserialize, Serialize};
41
42use crate::func::{
43    CaseLiteral, array_create_scalar, build_regex, check_build_fits_budget,
44    check_datums_fit_budget, date_bin, max_string_func_result_bytes, parse_timezone,
45    regexp_match_static, regexp_replace_parse_flags, regexp_split_to_array_re, stringify_datum,
46    timezone_time,
47};
48use crate::{Eval, EvalError, MirScalarExpr};
49use mz_repr::adt::date::Date;
50use mz_repr::adt::interval::Interval;
51use mz_repr::adt::jsonb::JsonbRef;
52
53#[derive(
54    Ord,
55    PartialOrd,
56    Clone,
57    Debug,
58    Eq,
59    PartialEq,
60    Serialize,
61    Deserialize,
62    Hash
63)]
64pub struct And;
65
66impl fmt::Display for And {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        f.write_str("AND")
69    }
70}
71
72impl LazyVariadicFunc for And {
73    fn eval<'a>(
74        &'a self,
75        datums: &[Datum<'a>],
76        temp_storage: &'a RowArena,
77        exprs: &'a [impl Eval],
78    ) -> Result<Datum<'a>, EvalError> {
79        // If any is false, then return false. Else, if any is null, then return null. Else, return true.
80        let mut null = false;
81        let mut err = None;
82        for expr in exprs {
83            match expr.eval(datums, temp_storage) {
84                Ok(Datum::False) => return Ok(Datum::False), // short-circuit
85                Ok(Datum::True) => {}
86                // No return in these two cases, because we might still see a false
87                Ok(Datum::Null) => null = true,
88                Err(this_err) => err = std::cmp::max(err.take(), Some(this_err)),
89                _ => unreachable!(),
90            }
91        }
92        match (err, null) {
93            (Some(err), _) => Err(err),
94            (None, true) => Ok(Datum::Null),
95            (None, false) => Ok(Datum::True),
96        }
97    }
98
99    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
100        let in_nullable = input_types.iter().any(|t| t.nullable);
101        SqlScalarType::Bool.nullable(in_nullable)
102    }
103
104    fn propagates_nulls(&self) -> bool {
105        false
106    }
107
108    fn introduces_nulls(&self) -> bool {
109        false
110    }
111
112    fn could_error(&self) -> bool {
113        false
114    }
115
116    fn is_monotone(&self) -> bool {
117        true
118    }
119
120    fn is_associative(&self) -> bool {
121        true
122    }
123
124    fn is_infix_op(&self) -> bool {
125        true
126    }
127}
128
129#[derive(
130    Ord,
131    PartialOrd,
132    Clone,
133    Debug,
134    Eq,
135    PartialEq,
136    Serialize,
137    Deserialize,
138    Hash
139)]
140pub struct ArrayCreate {
141    pub elem_type: SqlScalarType,
142}
143
144/// Constructs a new multidimensional array out of an arbitrary number of
145/// lower-dimensional arrays.
146///
147/// For example, if given three 1D arrays of length 2, this function will
148/// construct a 2D array with dimensions 3x2.
149///
150/// The input datums in `datums` must all be arrays of the same dimensions.
151/// (The arrays must also be of the same element type, but that is checked by
152/// the SQL type system, rather than checked here at runtime.)
153///
154/// If all input arrays are zero-dimensional arrays, then the output is a zero-
155/// dimensional array. Otherwise, the lower bound of the additional dimension is
156/// one and the length of the new dimension is equal to `datums.len()`.
157///
158/// Null elements are allowed and considered to be zero-dimensional arrays.
159#[sqlfunc(
160    ArrayCreate,
161    output_type_expr = "match &self.elem_type { SqlScalarType::Array(_) => self.elem_type.clone().nullable(false), _ => SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false) }",
162    introduces_nulls = false
163)]
164fn array_create<'a>(
165    &self,
166    datums: Variadic<Datum<'a>>,
167    temp_storage: &'a RowArena,
168) -> Result<Datum<'a>, EvalError> {
169    match &self.elem_type {
170        SqlScalarType::Array(_) => array_create_multidim(&datums, temp_storage),
171        _ => array_create_scalar(&datums, temp_storage),
172    }
173}
174fn array_create_multidim<'a>(
175    datums: &[Datum<'a>],
176    temp_storage: &'a RowArena,
177) -> Result<Datum<'a>, EvalError> {
178    let mut dim: Option<ArrayDimensions> = None;
179    for datum in datums {
180        let actual_dims = match datum {
181            Datum::Null => ArrayDimensions::default(),
182            Datum::Array(arr) => arr.dims(),
183            d => panic!("unexpected datum {d}"),
184        };
185        if let Some(expected) = &dim {
186            if actual_dims.ndims() != expected.ndims() {
187                let actual = actual_dims.ndims().into();
188                let expected = expected.ndims().into();
189                // All input arrays must have the same dimensionality.
190                return Err(InvalidArrayError::WrongCardinality { actual, expected }.into());
191            }
192            if let Some((e, a)) = expected
193                .into_iter()
194                .zip_eq(actual_dims)
195                .find(|(e, a)| e != a)
196            {
197                let actual = a.length;
198                let expected = e.length;
199                // All input arrays must have the same dimensionality.
200                return Err(InvalidArrayError::WrongCardinality { actual, expected }.into());
201            }
202        }
203        dim = Some(actual_dims);
204    }
205    // Per PostgreSQL, if all input arrays are zero dimensional, so is the output.
206    if dim.as_ref().map_or(true, ArrayDimensions::is_empty) {
207        return Ok(temp_storage.try_make_datum(|packer| packer.try_push_array(&[], &[]))?);
208    }
209
210    let mut dims = vec![ArrayDimension {
211        lower_bound: 1,
212        length: datums.len(),
213    }];
214    if let Some(d) = datums.first() {
215        dims.extend(d.unwrap_array().dims());
216    };
217    check_datums_fit_budget(datums.iter().copied(), temp_storage)?;
218    let elements = datums
219        .iter()
220        .flat_map(|d| d.unwrap_array().elements().iter());
221    let datum =
222        temp_storage.try_make_datum(move |packer| packer.try_push_array(&dims, elements))?;
223    Ok(datum)
224}
225
226#[derive(
227    Ord,
228    PartialOrd,
229    Clone,
230    Debug,
231    Eq,
232    PartialEq,
233    Serialize,
234    Deserialize,
235    Hash
236)]
237pub struct ArrayFill {
238    pub elem_type: SqlScalarType,
239}
240
241#[sqlfunc(
242    ArrayFill,
243    output_type_expr = "SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)",
244    introduces_nulls = false
245)]
246fn array_fill<'a>(
247    &self,
248    fill: Datum<'a>,
249    dims: Option<Array<'a>>,
250    lower_bounds: OptionalArg<Option<Array<'a>>>,
251    temp_storage: &'a RowArena,
252) -> Result<Datum<'a>, EvalError> {
253    const MAX_SIZE: usize = (1 << 28) - 1;
254    const NULL_ARR_ERR: &str = "dimension array or low bound array";
255    const NULL_ELEM_ERR: &str = "dimension values";
256
257    if matches!(fill, Datum::Array(_)) {
258        return Err(EvalError::Unsupported {
259            feature: "array_fill with arrays".into(),
260            discussion_no: None,
261        });
262    }
263
264    let Some(arr) = dims else {
265        return Err(EvalError::MustNotBeNull(NULL_ARR_ERR.into()));
266    };
267
268    // The dimensions array must be one-dimensional.
269    if arr.dims().ndims() > 1 {
270        return Err(EvalError::ArrayFillWrongArraySubscripts);
271    }
272
273    let dimensions = arr
274        .elements()
275        .iter()
276        .map(|d| match d {
277            Datum::Null => Err(EvalError::MustNotBeNull(NULL_ELEM_ERR.into())),
278            d => Ok(usize::cast_from(u32::reinterpret_cast(d.unwrap_int32()))),
279        })
280        .collect::<Result<Vec<_>, _>>()?;
281
282    let lower_bounds = match *lower_bounds {
283        Some(d) => {
284            let Some(arr) = d else {
285                return Err(EvalError::MustNotBeNull(NULL_ARR_ERR.into()));
286            };
287
288            // The lower bounds array must be one-dimensional.
289            if arr.dims().ndims() > 1 {
290                return Err(EvalError::ArrayFillWrongArraySubscripts);
291            }
292
293            arr.elements()
294                .iter()
295                .map(|l| match l {
296                    Datum::Null => Err(EvalError::MustNotBeNull(NULL_ELEM_ERR.into())),
297                    l => Ok(isize::cast_from(l.unwrap_int32())),
298                })
299                .collect::<Result<Vec<_>, _>>()?
300        }
301        None => {
302            vec![1isize; dimensions.len()]
303        }
304    };
305
306    if lower_bounds.len() != dimensions.len() {
307        return Err(EvalError::ArrayFillWrongArraySubscripts);
308    }
309
310    let fill_count: usize = dimensions
311        .iter()
312        .cloned()
313        .map(Some)
314        .reduce(|a, b| match (a, b) {
315            (Some(a), Some(b)) => a.checked_mul(b),
316            _ => None,
317        })
318        .flatten()
319        .ok_or(EvalError::MaxArraySizeExceeded(MAX_SIZE))?;
320
321    if matches!(
322        mz_repr::datum_size(&fill).checked_mul(fill_count),
323        None | Some(MAX_SIZE..)
324    ) {
325        return Err(EvalError::MaxArraySizeExceeded(MAX_SIZE));
326    }
327
328    // The packed array lands in `temp_storage`, and building it first allocates a transient
329    // `Vec<Datum>` of `fill_count` elements. Unlike the string amplifiers, `array_fill` sizes its
330    // result from a parameter rather than its input, so without this check a single call could
331    // spike far past a budgeted arena before the evaluator's post-call check ever runs. Refuse both
332    // allocations up front instead. Without a budget `budget_remaining` is `usize::MAX` and both
333    // comparisons fold away, so this is free in a dataflow.
334    let budget_remaining = temp_storage.budget_remaining();
335    let packed_size = mz_repr::datum_size(&fill).saturating_mul(fill_count);
336    let build_size = fill_count.saturating_mul(std::mem::size_of::<Datum<'a>>());
337    if packed_size > budget_remaining || build_size > budget_remaining {
338        return Err(EvalError::TempStorageBudgetExceeded);
339    }
340
341    let array_dimensions = if fill_count == 0 {
342        vec![ArrayDimension {
343            lower_bound: 1,
344            length: 0,
345        }]
346    } else {
347        dimensions
348            .into_iter()
349            .zip_eq(lower_bounds)
350            .map(|(length, lower_bound)| ArrayDimension {
351                lower_bound,
352                length,
353            })
354            .collect()
355    };
356
357    Ok(temp_storage.try_make_datum(|packer| {
358        packer.try_push_array(&array_dimensions, vec![fill; fill_count])
359    })?)
360}
361
362#[derive(
363    Ord,
364    PartialOrd,
365    Clone,
366    Debug,
367    Eq,
368    PartialEq,
369    Serialize,
370    Deserialize,
371    Hash
372)]
373pub struct ArrayIndex {
374    pub offset: i64,
375}
376#[sqlfunc(ArrayIndex, sqlname = "array_index", introduces_nulls = true)]
377fn array_index<'a, T: FromDatum<'a>>(
378    &self,
379    array: Array<'a, T>,
380    indices: Variadic<i64>,
381) -> Option<T> {
382    mz_ore::soft_assert_no_log!(
383        self.offset == 0 || self.offset == 1,
384        "offset must be either 0 or 1"
385    );
386
387    let dims = array.dims();
388    if dims.len() != indices.len() {
389        // You missed the datums "layer"
390        return None;
391    }
392
393    let mut final_idx = 0;
394
395    for (d, idx) in dims.into_iter().zip_eq(indices.iter()) {
396        // Lower bound is written in terms of 1-based indexing, which offset accounts for.
397        let idx = isize::cast_from(*idx + self.offset);
398
399        let (lower, upper) = d.dimension_bounds();
400
401        // This index missed all of the data at this layer. The dimension bounds are inclusive,
402        // while range checks are exclusive, so adjust.
403        if !(lower..upper + 1).contains(&idx) {
404            return None;
405        }
406
407        // We discover how many indices our last index represents physically.
408        final_idx *= d.length;
409
410        // Because both index and lower bound are handled in 1-based indexing, taking their
411        // difference moves us back into 0-based indexing. Similarly, if the lower bound is
412        // negative, subtracting a negative value >= to itself ensures its non-negativity.
413        final_idx += usize::try_from(idx - d.lower_bound)
414            .expect("previous bounds check ensures physical index is at least 0");
415    }
416
417    array.elements().typed_iter().nth(final_idx)
418}
419
420#[sqlfunc]
421fn array_position<'a>(
422    array: Array<'a>,
423    search: Datum<'a>,
424    initial_pos: OptionalArg<Option<i32>>,
425) -> Result<Option<i32>, EvalError> {
426    if array.dims().len() > 1 {
427        return Err(EvalError::MultiDimensionalArraySearch);
428    }
429
430    if search == Datum::Null {
431        return Ok(None);
432    }
433
434    let skip = match initial_pos.0 {
435        None => 0,
436        Some(None) => return Err(EvalError::MustNotBeNull("initial position".into())),
437        Some(Some(o)) => usize::try_from(o).unwrap_or(0).saturating_sub(1),
438    };
439
440    let Some(r) = array.elements().iter().skip(skip).position(|d| d == search) else {
441        return Ok(None);
442    };
443
444    // Adjust count for the amount we skipped, plus 1 for adjusting to PG indexing scheme.
445    let p = i32::try_from(r + skip + 1).expect("fewer than i32::MAX elements in array");
446    Ok(Some(p))
447}
448
449#[derive(
450    Ord,
451    PartialOrd,
452    Clone,
453    Debug,
454    Eq,
455    PartialEq,
456    Serialize,
457    Deserialize,
458    Hash
459)]
460pub struct ArrayToString {
461    pub elem_type: SqlScalarType,
462}
463
464#[sqlfunc]
465fn array_to_string<'a>(
466    &self,
467    array: Array<'a>,
468    delimiter: &str,
469    null_str_arg: OptionalArg<Option<&str>>,
470    temp_storage: &RowArena,
471) -> Result<String, EvalError> {
472    // `flatten` treats absent arguments (`None`) the same as explicit NULL
473    // (`Some(None)`), both becoming `None`.
474    let null_str = null_str_arg.flatten();
475    // A delimiter is repeated once per element, so the array size doesn't bound the result. We
476    // check as it grows rather than up front, since an element's stringified length isn't known
477    // without stringifying it. That bounds the peak at the ceiling plus one element.
478    let max_result_bytes = max_string_func_result_bytes(temp_storage);
479    let mut out = String::new();
480    for elem in array.elements().iter() {
481        if elem.is_null() {
482            if let Some(null_str) = null_str {
483                out.push_str(null_str);
484                out.push_str(delimiter);
485            }
486        } else {
487            stringify_datum(&mut out, elem, &self.elem_type)?;
488            out.push_str(delimiter);
489        }
490        if out.len() > max_result_bytes {
491            return Err(EvalError::LengthTooLarge);
492        }
493    }
494    if out.len() > 0 {
495        // Lop off last delimiter only if string is not empty
496        out.truncate(out.len() - delimiter.len());
497    }
498    Ok(out)
499}
500
501#[derive(
502    Ord,
503    PartialOrd,
504    Clone,
505    Debug,
506    Eq,
507    PartialEq,
508    Serialize,
509    Deserialize,
510    Hash
511)]
512pub struct Coalesce;
513
514impl fmt::Display for Coalesce {
515    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516        f.write_str("coalesce")
517    }
518}
519
520impl LazyVariadicFunc for Coalesce {
521    fn eval<'a>(
522        &'a self,
523        datums: &[Datum<'a>],
524        temp_storage: &'a RowArena,
525        exprs: &'a [impl Eval],
526    ) -> Result<Datum<'a>, EvalError> {
527        for e in exprs {
528            let d = e.eval(datums, temp_storage)?;
529            if !d.is_null() {
530                return Ok(d);
531            }
532        }
533        Ok(Datum::Null)
534    }
535
536    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
537        // Note that the parser doesn't allow empty argument lists for variadic functions
538        // that use the standard function call syntax (ArrayCreate and co. are different
539        // because of the special syntax for calling them).
540        let nullable = input_types.iter().all(|typ| typ.nullable);
541        SqlColumnType::union_many(input_types).nullable(nullable)
542    }
543
544    fn propagates_nulls(&self) -> bool {
545        false
546    }
547
548    fn introduces_nulls(&self) -> bool {
549        false
550    }
551
552    fn could_error(&self) -> bool {
553        false
554    }
555
556    fn is_monotone(&self) -> bool {
557        true
558    }
559
560    fn is_associative(&self) -> bool {
561        true
562    }
563}
564
565#[derive(
566    Ord,
567    PartialOrd,
568    Clone,
569    Debug,
570    Eq,
571    PartialEq,
572    Serialize,
573    Deserialize,
574    Hash
575)]
576pub struct RangeCreate {
577    pub elem_type: SqlScalarType,
578}
579
580impl fmt::Display for RangeCreate {
581    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582        f.write_str(match &self.elem_type {
583            SqlScalarType::Int32 => "int4range",
584            SqlScalarType::Int64 => "int8range",
585            SqlScalarType::Date => "daterange",
586            SqlScalarType::Numeric { .. } => "numrange",
587            SqlScalarType::Timestamp { .. } => "tsrange",
588            SqlScalarType::TimestampTz { .. } => "tstzrange",
589            _ => unreachable!(),
590        })
591    }
592}
593
594impl EagerVariadicFunc for RangeCreate {
595    type Input<'a> = (Datum<'a>, Datum<'a>, Datum<'a>);
596    type Output<'a> = Result<Datum<'a>, EvalError>;
597
598    fn call<'a>(
599        &self,
600        (lower, upper, flags_datum): Self::Input<'a>,
601        temp_storage: &'a RowArena,
602    ) -> Self::Output<'a> {
603        let flags = match flags_datum {
604            Datum::Null => {
605                return Err(EvalError::InvalidRange(
606                    InvalidRangeError::NullRangeBoundFlags,
607                ));
608            }
609            o => o.unwrap_str(),
610        };
611
612        let (lower_inclusive, upper_inclusive) = parse_range_bound_flags(flags)?;
613
614        let mut range = Range::new(Some((
615            RangeBound::new(lower, lower_inclusive),
616            RangeBound::new(upper, upper_inclusive),
617        )));
618
619        range.canonicalize()?;
620
621        Ok(temp_storage.make_datum(|row| {
622            row.push_range(range).expect("errors already handled");
623        }))
624    }
625
626    fn output_type(&self, _input_types: &[SqlColumnType]) -> SqlColumnType {
627        SqlScalarType::Range {
628            element_type: Box::new(self.elem_type.clone()),
629        }
630        .nullable(false)
631    }
632
633    fn introduces_nulls(&self) -> bool {
634        false
635    }
636}
637
638#[sqlfunc(sqlname = "datediff")]
639fn date_diff_date(unit_str: &str, a: Date, b: Date) -> Result<i64, EvalError> {
640    let unit = unit_str
641        .parse()
642        .map_err(|_| EvalError::InvalidDatePart(unit_str.into()))?;
643
644    // Convert the Date into a timestamp so we can calculate age.
645    let a_ts = CheckedTimestamp::try_from(NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap())?;
646    let b_ts = CheckedTimestamp::try_from(NaiveDate::from(b).and_hms_opt(0, 0, 0).unwrap())?;
647    let diff = b_ts.diff_as(&a_ts, unit)?;
648    Ok(diff)
649}
650
651#[sqlfunc(sqlname = "datediff")]
652fn date_diff_time(unit_str: &str, a: NaiveTime, b: NaiveTime) -> Result<i64, EvalError> {
653    let unit = unit_str
654        .parse()
655        .map_err(|_| EvalError::InvalidDatePart(unit_str.into()))?;
656
657    // Convert the Time into a timestamp so we can calculate age.
658    let a_ts =
659        CheckedTimestamp::try_from(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_time(a))?;
660    let b_ts =
661        CheckedTimestamp::try_from(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_time(b))?;
662    let diff = b_ts.diff_as(&a_ts, unit)?;
663    Ok(diff)
664}
665
666#[sqlfunc(sqlname = "datediff")]
667fn date_diff_timestamp(
668    unit: &str,
669    a: CheckedTimestamp<NaiveDateTime>,
670    b: CheckedTimestamp<NaiveDateTime>,
671) -> Result<i64, EvalError> {
672    let unit = unit
673        .parse()
674        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
675
676    let diff = b.diff_as(&a, unit)?;
677    Ok(diff)
678}
679
680#[sqlfunc(sqlname = "datediff")]
681fn date_diff_timestamp_tz(
682    unit: &str,
683    a: CheckedTimestamp<DateTime<Utc>>,
684    b: CheckedTimestamp<DateTime<Utc>>,
685) -> Result<i64, EvalError> {
686    let unit = unit
687        .parse()
688        .map_err(|_| EvalError::InvalidDatePart(unit.into()))?;
689
690    let diff = b.diff_as(&a, unit)?;
691    Ok(diff)
692}
693
694#[derive(
695    Ord,
696    PartialOrd,
697    Clone,
698    Debug,
699    Eq,
700    PartialEq,
701    Serialize,
702    Deserialize,
703    Hash
704)]
705pub struct ErrorIfNull;
706
707impl fmt::Display for ErrorIfNull {
708    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
709        f.write_str("error_if_null")
710    }
711}
712
713impl LazyVariadicFunc for ErrorIfNull {
714    fn eval<'a>(
715        &'a self,
716        datums: &[Datum<'a>],
717        temp_storage: &'a RowArena,
718        exprs: &'a [impl Eval],
719    ) -> Result<Datum<'a>, EvalError> {
720        let first = exprs[0].eval(datums, temp_storage)?;
721        match first {
722            Datum::Null => {
723                let err_msg = match exprs[1].eval(datums, temp_storage)? {
724                    Datum::Null => {
725                        return Err(EvalError::Internal(
726                            "unexpected NULL in error side of error_if_null".into(),
727                        ));
728                    }
729                    o => o.unwrap_str(),
730                };
731                Err(EvalError::IfNullError(err_msg.into()))
732            }
733            _ => Ok(first),
734        }
735    }
736
737    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
738        input_types[0].scalar_type.clone().nullable(false)
739    }
740
741    fn propagates_nulls(&self) -> bool {
742        false
743    }
744
745    fn introduces_nulls(&self) -> bool {
746        false
747    }
748}
749
750#[derive(
751    Ord,
752    PartialOrd,
753    Clone,
754    Debug,
755    Eq,
756    PartialEq,
757    Serialize,
758    Deserialize,
759    Hash
760)]
761pub struct Greatest;
762
763impl fmt::Display for Greatest {
764    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
765        f.write_str("greatest")
766    }
767}
768
769impl LazyVariadicFunc for Greatest {
770    fn eval<'a>(
771        &'a self,
772        datums: &[Datum<'a>],
773        temp_storage: &'a RowArena,
774        exprs: &'a [impl Eval],
775    ) -> Result<Datum<'a>, EvalError> {
776        let datums = fallible_iterator::convert(exprs.iter().map(|e| e.eval(datums, temp_storage)));
777        Ok(datums
778            .filter(|d| Ok(!d.is_null()))
779            .max()?
780            .unwrap_or(Datum::Null))
781    }
782
783    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
784        SqlColumnType::union_many(input_types)
785    }
786
787    fn propagates_nulls(&self) -> bool {
788        false
789    }
790
791    fn introduces_nulls(&self) -> bool {
792        false
793    }
794
795    fn could_error(&self) -> bool {
796        false
797    }
798
799    fn is_monotone(&self) -> bool {
800        true
801    }
802
803    fn is_associative(&self) -> bool {
804        true
805    }
806}
807
808#[sqlfunc(sqlname = "hmac")]
809fn hmac_string(to_digest: &str, key: &str, typ: &str) -> Result<Vec<u8>, EvalError> {
810    let to_digest = to_digest.as_bytes();
811    let key = key.as_bytes();
812    hmac_inner(to_digest, key, typ)
813}
814
815#[sqlfunc(sqlname = "hmac")]
816fn hmac_bytes(to_digest: &[u8], key: &[u8], typ: &str) -> Result<Vec<u8>, EvalError> {
817    hmac_inner(to_digest, key, typ)
818}
819
820pub fn hmac_inner(to_digest: &[u8], key: &[u8], typ: &str) -> Result<Vec<u8>, EvalError> {
821    match typ {
822        "md5" => {
823            let mut mac = Hmac::<Md5>::new_from_slice(key).expect("HMAC accepts any key size");
824            mac.update(to_digest);
825            Ok(mac.finalize().into_bytes().to_vec())
826        }
827        "sha1" => {
828            let k = aws_hmac::Key::new(aws_hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY, key);
829            Ok(aws_hmac::sign(&k, to_digest).as_ref().to_vec())
830        }
831        "sha224" => {
832            let k = aws_hmac::Key::new(aws_hmac::HMAC_SHA224, key);
833            Ok(aws_hmac::sign(&k, to_digest).as_ref().to_vec())
834        }
835        "sha256" => {
836            let k = aws_hmac::Key::new(aws_hmac::HMAC_SHA256, key);
837            Ok(aws_hmac::sign(&k, to_digest).as_ref().to_vec())
838        }
839        "sha384" => {
840            let k = aws_hmac::Key::new(aws_hmac::HMAC_SHA384, key);
841            Ok(aws_hmac::sign(&k, to_digest).as_ref().to_vec())
842        }
843        "sha512" => {
844            let k = aws_hmac::Key::new(aws_hmac::HMAC_SHA512, key);
845            Ok(aws_hmac::sign(&k, to_digest).as_ref().to_vec())
846        }
847        other => Err(EvalError::InvalidHashAlgorithm(other.into())),
848    }
849}
850
851#[sqlfunc]
852fn jsonb_build_array<'a>(
853    datums: Variadic<Datum<'a>>,
854    temp_storage: &'a RowArena,
855) -> Result<JsonbRef<'a>, EvalError> {
856    check_datums_fit_budget(datums.iter().copied(), temp_storage)?;
857    let datum = temp_storage.make_datum(|packer| {
858        packer.push_list(datums.into_iter().map(|d| match d {
859            Datum::Null => Datum::JsonNull,
860            d => d,
861        }))
862    });
863    Ok(JsonbRef::from_datum(datum))
864}
865
866#[sqlfunc]
867fn jsonb_build_object<'a>(
868    mut kvs: Variadic<(Datum<'a>, Datum<'a>)>,
869    temp_storage: &'a RowArena,
870) -> Result<JsonbRef<'a>, EvalError> {
871    kvs.0.sort_by(|kv1, kv2| kv1.0.cmp(&kv2.0));
872    kvs.0.dedup_by(|kv1, kv2| kv1.0 == kv2.0);
873    // Checked after the dedup, so duplicate keys are counted once, as they are packed.
874    check_datums_fit_budget(kvs.0.iter().flat_map(|(k, v)| [*k, *v]), temp_storage)?;
875    let datum = temp_storage.try_make_datum(|packer| {
876        packer.push_dict_with(|packer| {
877            for (k, v) in kvs {
878                if k.is_null() {
879                    return Err(EvalError::KeyCannotBeNull);
880                }
881                let v = match v {
882                    Datum::Null => Datum::JsonNull,
883                    d => d,
884                };
885                packer.push(k);
886                packer.push(v);
887            }
888            Ok(())
889        })
890    })?;
891    Ok(JsonbRef::from_datum(datum))
892}
893
894#[derive(
895    Ord,
896    PartialOrd,
897    Clone,
898    Debug,
899    Eq,
900    PartialEq,
901    Serialize,
902    Deserialize,
903    Hash
904)]
905pub struct Least;
906
907impl fmt::Display for Least {
908    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
909        f.write_str("least")
910    }
911}
912
913impl LazyVariadicFunc for Least {
914    fn eval<'a>(
915        &'a self,
916        datums: &[Datum<'a>],
917        temp_storage: &'a RowArena,
918        exprs: &'a [impl Eval],
919    ) -> Result<Datum<'a>, EvalError> {
920        let datums = fallible_iterator::convert(exprs.iter().map(|e| e.eval(datums, temp_storage)));
921        Ok(datums
922            .filter(|d| Ok(!d.is_null()))
923            .min()?
924            .unwrap_or(Datum::Null))
925    }
926
927    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
928        SqlColumnType::union_many(input_types)
929    }
930
931    fn propagates_nulls(&self) -> bool {
932        false
933    }
934
935    fn introduces_nulls(&self) -> bool {
936        false
937    }
938
939    fn could_error(&self) -> bool {
940        false
941    }
942
943    fn is_monotone(&self) -> bool {
944        true
945    }
946
947    fn is_associative(&self) -> bool {
948        true
949    }
950}
951
952#[derive(
953    Ord,
954    PartialOrd,
955    Clone,
956    Debug,
957    Eq,
958    PartialEq,
959    Serialize,
960    Deserialize,
961    Hash
962)]
963pub struct ListCreate {
964    pub elem_type: SqlScalarType,
965}
966
967#[sqlfunc(
968    output_type_expr = "SqlScalarType::List { element_type: Box::new(self.elem_type.clone()), custom_id: None }.nullable(false)",
969    introduces_nulls = false
970)]
971fn list_create<'a>(
972    &self,
973    datums: Variadic<Datum<'a>>,
974    temp_storage: &'a RowArena,
975) -> Result<Datum<'a>, EvalError> {
976    check_datums_fit_budget(datums.iter().copied(), temp_storage)?;
977    Ok(temp_storage.make_datum(|packer| packer.push_list(datums)))
978}
979
980#[derive(
981    Ord,
982    PartialOrd,
983    Clone,
984    Debug,
985    Eq,
986    PartialEq,
987    Serialize,
988    Deserialize,
989    Hash
990)]
991pub struct RecordCreate {
992    pub field_names: Vec<ColumnName>,
993}
994
995#[sqlfunc(
996    output_type_expr = "SqlScalarType::Record { fields: self.field_names.clone().into_iter().zip_eq(input_types.iter().cloned()).collect(), custom_id: None }.nullable(false)",
997    introduces_nulls = false
998)]
999fn record_create<'a>(
1000    &self,
1001    datums: Variadic<Datum<'a>>,
1002    temp_storage: &'a RowArena,
1003) -> Result<Datum<'a>, EvalError> {
1004    check_datums_fit_budget(datums.iter().copied(), temp_storage)?;
1005    Ok(temp_storage.make_datum(|packer| packer.push_list(datums.iter().copied())))
1006}
1007
1008#[sqlfunc(
1009    output_type_expr = "input_types[0].scalar_type.unwrap_list_nth_layer_type(input_types.len() - 1).clone().nullable(true)",
1010    introduces_nulls = true
1011)]
1012// TODO(benesch): remove potentially dangerous usage of `as`.
1013#[allow(clippy::as_conversions)]
1014fn list_index<'a>(buf: DatumList<'a>, indices: Variadic<i64>) -> Datum<'a> {
1015    let mut buf = Datum::List(buf);
1016    for i in indices {
1017        if buf.is_null() {
1018            break;
1019        }
1020        if i < 1 {
1021            return Datum::Null;
1022        }
1023
1024        buf = match buf.unwrap_list().iter().nth(i as usize - 1) {
1025            Some(datum) => datum,
1026            None => return Datum::Null,
1027        }
1028    }
1029    buf
1030}
1031
1032#[sqlfunc(sqlname = "makeaclitem")]
1033fn make_acl_item(
1034    grantee_oid: u32,
1035    grantor_oid: u32,
1036    privileges: &str,
1037    is_grantable: bool,
1038) -> Result<AclItem, EvalError> {
1039    let grantee = Oid(grantee_oid);
1040    let grantor = Oid(grantor_oid);
1041    let acl_mode = AclMode::parse_multiple_privileges(privileges)
1042        .map_err(|e: anyhow::Error| EvalError::InvalidPrivileges(e.to_string().into()))?;
1043    if is_grantable {
1044        return Err(EvalError::Unsupported {
1045            feature: "GRANT OPTION".into(),
1046            discussion_no: None,
1047        });
1048    }
1049
1050    Ok(AclItem {
1051        grantee,
1052        grantor,
1053        acl_mode,
1054    })
1055}
1056
1057#[sqlfunc(sqlname = "make_mz_aclitem")]
1058fn make_mz_acl_item(
1059    grantee_str: &str,
1060    grantor_str: &str,
1061    privileges: &str,
1062) -> Result<MzAclItem, EvalError> {
1063    let grantee: RoleId = grantee_str
1064        .parse()
1065        .map_err(|e: anyhow::Error| EvalError::InvalidRoleId(e.to_string().into()))?;
1066    let grantor: RoleId = grantor_str
1067        .parse()
1068        .map_err(|e: anyhow::Error| EvalError::InvalidRoleId(e.to_string().into()))?;
1069    if grantor == RoleId::Public {
1070        return Err(EvalError::InvalidRoleId(
1071            "mz_aclitem grantor cannot be PUBLIC role".into(),
1072        ));
1073    }
1074    let acl_mode = AclMode::parse_multiple_privileges(privileges)
1075        .map_err(|e: anyhow::Error| EvalError::InvalidPrivileges(e.to_string().into()))?;
1076
1077    Ok(MzAclItem {
1078        grantee,
1079        grantor,
1080        acl_mode,
1081    })
1082}
1083
1084#[sqlfunc(sqlname = "makets")]
1085#[allow(clippy::as_conversions)]
1086fn make_timestamp(
1087    year: i64,
1088    month: i64,
1089    day: i64,
1090    hour: i64,
1091    minute: i64,
1092    second_float: f64,
1093) -> Result<Option<CheckedTimestamp<NaiveDateTime>>, EvalError> {
1094    // NOTE: This function never returns `Ok(None)`. The `Option` stays so
1095    // that the derived `introduces_nulls` (and with it the nullability of
1096    // existing objects planned with this function) does not change.
1097
1098    // Negative years are BC and there is no year 0. Chrono's astronomical
1099    // numbering has 1 BC as year 0, so BC years shift by one.
1100    if year == 0 {
1101        return Err(EvalError::DateOutOfRange);
1102    }
1103    let year = if year < 0 { year + 1 } else { year };
1104    let year: i32 = year.try_into().map_err(|_| EvalError::DateOutOfRange)?;
1105    let month: u32 = month.try_into().map_err(|_| EvalError::DateOutOfRange)?;
1106    let day: u32 = day.try_into().map_err(|_| EvalError::DateOutOfRange)?;
1107    let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(EvalError::DateOutOfRange)?;
1108    // Allow exactly 24:00:00 (rolls over to midnight of the next day) and
1109    // 60 seconds (rolls over into the next minute).
1110    if !(0..=24).contains(&hour)
1111        || !(0..=59).contains(&minute)
1112        || !(0.0..=60.0).contains(&second_float)
1113        || (hour == 24 && (minute > 0 || second_float > 0.0))
1114    {
1115        return Err(EvalError::TimestampOutOfRange);
1116    }
1117    // The cast is exact: second_float is in [0, 60].
1118    let micros = (second_float * 1_000_000.0).round() as i64;
1119    let time = chrono::Duration::microseconds((hour * 60 + minute) * 60 * 1_000_000 + micros);
1120    let timestamp = date
1121        .and_hms_opt(0, 0, 0)
1122        .unwrap()
1123        .checked_add_signed(time)
1124        .ok_or(EvalError::TimestampOutOfRange)?;
1125    Ok(Some(timestamp.try_into()?))
1126}
1127
1128#[derive(
1129    Ord,
1130    PartialOrd,
1131    Clone,
1132    Debug,
1133    Eq,
1134    PartialEq,
1135    Serialize,
1136    Deserialize,
1137    Hash
1138)]
1139pub struct MapBuild {
1140    pub value_type: SqlScalarType,
1141}
1142
1143#[sqlfunc(
1144    output_type_expr = "SqlScalarType::Map { value_type: Box::new(self.value_type.clone()), custom_id: None }.nullable(false)",
1145    introduces_nulls = false
1146)]
1147fn map_build<'a>(
1148    &self,
1149    datums: Variadic<(Option<&str>, Datum<'a>)>,
1150    temp_storage: &'a RowArena,
1151) -> Result<Datum<'a>, EvalError> {
1152    // Collect into a `BTreeMap` to provide the same semantics as it.
1153    let map: std::collections::BTreeMap<&str, _> = datums
1154        .into_iter()
1155        .filter_map(|(k, v)| k.map(|k| (k, v)))
1156        .collect();
1157
1158    // Checked after the collect, so duplicate keys are counted once, as they are packed. The
1159    // `BTreeMap` transient needs no pre-check: it holds one entry per argument, a count the query
1160    // text fixes, not the input.
1161    check_datums_fit_budget(
1162        map.iter().flat_map(|(k, v)| [Datum::String(k), *v]),
1163        temp_storage,
1164    )?;
1165    Ok(temp_storage.make_datum(|packer| packer.push_dict(map)))
1166}
1167
1168#[derive(
1169    Ord,
1170    PartialOrd,
1171    Clone,
1172    Debug,
1173    Eq,
1174    PartialEq,
1175    Serialize,
1176    Deserialize,
1177    Hash
1178)]
1179pub struct Or;
1180
1181impl fmt::Display for Or {
1182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1183        f.write_str("OR")
1184    }
1185}
1186
1187impl LazyVariadicFunc for Or {
1188    fn eval<'a>(
1189        &'a self,
1190        datums: &[Datum<'a>],
1191        temp_storage: &'a RowArena,
1192        exprs: &'a [impl Eval],
1193    ) -> Result<Datum<'a>, EvalError> {
1194        // If any is true, then return true. Else, if any is null, then return null. Else, return false.
1195        let mut null = false;
1196        let mut err = None;
1197        for expr in exprs {
1198            match expr.eval(datums, temp_storage) {
1199                Ok(Datum::False) => {}
1200                Ok(Datum::True) => return Ok(Datum::True), // short-circuit
1201                // No return in these two cases, because we might still see a true
1202                Ok(Datum::Null) => null = true,
1203                Err(this_err) => err = std::cmp::max(err.take(), Some(this_err)),
1204                _ => unreachable!(),
1205            }
1206        }
1207        match (err, null) {
1208            (Some(err), _) => Err(err),
1209            (None, true) => Ok(Datum::Null),
1210            (None, false) => Ok(Datum::False),
1211        }
1212    }
1213
1214    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
1215        let in_nullable = input_types.iter().any(|t| t.nullable);
1216        SqlScalarType::Bool.nullable(in_nullable)
1217    }
1218
1219    fn propagates_nulls(&self) -> bool {
1220        false
1221    }
1222
1223    fn introduces_nulls(&self) -> bool {
1224        false
1225    }
1226
1227    fn could_error(&self) -> bool {
1228        false
1229    }
1230
1231    fn is_monotone(&self) -> bool {
1232        true
1233    }
1234
1235    fn is_associative(&self) -> bool {
1236        true
1237    }
1238
1239    fn is_infix_op(&self) -> bool {
1240        true
1241    }
1242}
1243
1244#[sqlfunc(sqlname = "lpad")]
1245fn pad_leading(
1246    string: &str,
1247    raw_len: i32,
1248    pad: OptionalArg<&str>,
1249    temp_storage: &RowArena,
1250) -> Result<String, EvalError> {
1251    let len = match usize::try_from(raw_len) {
1252        Ok(len) => len,
1253        Err(_) => {
1254            return Err(EvalError::InvalidParameterValue(
1255                "length must be nonnegative".into(),
1256            ));
1257        }
1258    };
1259    let pad_string = pad.unwrap_or(" ");
1260
1261    // `len` is a character count but the ceiling is in bytes, so a multi-byte pad character writes
1262    // several bytes per character the check allowed. Bound the bytes instead. The result is the kept
1263    // prefix of `string` plus `len - string_chars` padding characters, each at most as wide as the
1264    // widest character in `pad_string`. Truncation (`len <= string_chars`) writes at most
1265    // `string.len()`, which the same expression covers.
1266    let widest_pad = pad_string.chars().map(char::len_utf8).max().unwrap_or(0);
1267    let pad_chars = len.saturating_sub(string.chars().count());
1268    let max_result_bytes = string
1269        .len()
1270        .saturating_add(pad_chars.saturating_mul(widest_pad));
1271    if max_result_bytes > max_string_func_result_bytes(temp_storage) {
1272        return Err(EvalError::LengthTooLarge);
1273    }
1274
1275    let (end_char, end_char_byte_offset) = string
1276        .chars()
1277        .take(len)
1278        .fold((0, 0), |acc, char| (acc.0 + 1, acc.1 + char.len_utf8()));
1279
1280    // NOTE: reserve from the bytes actually written, not from `len`, which is a character count.
1281    // Reserving `len` is too little for a multi-byte pad, and unbounded for an empty one, which
1282    // writes nothing and so has no result size for the ceiling above to refuse.
1283    let truncating = len == end_char;
1284    let mut buf = String::with_capacity(if truncating {
1285        end_char_byte_offset
1286    } else {
1287        max_result_bytes
1288    });
1289    if truncating {
1290        buf.push_str(&string[0..end_char_byte_offset]);
1291    } else {
1292        buf.extend(pad_string.chars().cycle().take(len - end_char));
1293        buf.push_str(string);
1294    }
1295
1296    Ok(buf)
1297}
1298
1299#[sqlfunc(
1300    output_type_expr = "SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)",
1301    introduces_nulls = true
1302)]
1303fn regexp_match<'a>(
1304    haystack: &'a str,
1305    needle: &str,
1306    flags: OptionalArg<&str>,
1307    temp_storage: &'a RowArena,
1308) -> Result<Datum<'a>, EvalError> {
1309    let flags = flags.unwrap_or("");
1310    let needle = build_regex(needle, flags)?;
1311    regexp_match_static(Datum::String(haystack), temp_storage, &needle)
1312}
1313
1314#[sqlfunc(
1315    output_type_expr = "SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)",
1316    introduces_nulls = false
1317)]
1318fn regexp_split_to_array<'a>(
1319    text: &str,
1320    regexp_str: &str,
1321    flags: OptionalArg<&str>,
1322    temp_storage: &'a RowArena,
1323) -> Result<Datum<'a>, EvalError> {
1324    let flags = flags.unwrap_or("");
1325    let regexp = build_regex(regexp_str, flags)?;
1326    regexp_split_to_array_re(text, &regexp, temp_storage)
1327}
1328
1329#[sqlfunc]
1330fn regexp_replace<'a>(
1331    source: &'a str,
1332    pattern: &str,
1333    replacement: &str,
1334    flags_opt: OptionalArg<&str>,
1335    temp_storage: &RowArena,
1336) -> Result<Cow<'a, str>, EvalError> {
1337    let flags = flags_opt.0.unwrap_or("");
1338    let (limit, flags) = regexp_replace_parse_flags(flags);
1339    let regexp = build_regex(pattern, &flags)?;
1340
1341    // The result is the unreplaced parts of `source` plus one replacement per match. A `$ref` in the
1342    // replacement expands to captured text, which is at most the match, and matches are disjoint, so
1343    // all the expansions together cost at most one `source` per `$`.
1344    let dollars = replacement.matches('$').count();
1345    let max_result_bytes = |matches: usize| {
1346        let expansions = match matches {
1347            0 => 0,
1348            _ => dollars.saturating_mul(source.len()),
1349        };
1350        source
1351            .len()
1352            .saturating_add(matches.saturating_mul(replacement.len()))
1353            .saturating_add(expansions)
1354    };
1355    // A `limit` of 0 means replace every match, and a pattern that matches the empty string matches
1356    // at every position. Assume that first, and only pay for counting the real matches when the
1357    // assumption is over the ceiling. Same compromise as `replace`.
1358    let most_matches = match limit {
1359        0 => source.len().saturating_add(1),
1360        limit => limit,
1361    };
1362    let ceiling = max_string_func_result_bytes(temp_storage);
1363    if max_result_bytes(most_matches) > ceiling {
1364        let matches = regexp.find_iter(source).take(most_matches).count();
1365        if max_result_bytes(matches) > ceiling {
1366            return Err(EvalError::LengthTooLarge);
1367        }
1368    }
1369
1370    Ok(regexp.replacen(source, limit, replacement))
1371}
1372
1373#[sqlfunc]
1374fn replace(text: &str, from: &str, to: &str, temp_storage: &RowArena) -> Result<String, EvalError> {
1375    // As a compromise to avoid always nearly duplicating the work of replace by doing size estimation,
1376    // we first check if it's possible for the fully replaced string to exceed the limit by assuming that
1377    // every possible substring is replaced.
1378    //
1379    // If that estimate exceeds the limit, we then do a more precise (and expensive) estimate by counting
1380    // the actual number of replacements that would occur, and using that to calculate the final size.
1381    let max_result_bytes = max_string_func_result_bytes(temp_storage);
1382    let possible_size = text.len() * to.len();
1383    if possible_size > max_result_bytes {
1384        let replacement_count = text.matches(from).count();
1385        let estimated_size = text.len() + replacement_count * (to.len().saturating_sub(from.len()));
1386        if estimated_size > max_result_bytes {
1387            return Err(EvalError::LengthTooLarge);
1388        }
1389    }
1390
1391    Ok(text.replace(from, to))
1392}
1393
1394#[sqlfunc(
1395    output_type_expr = "SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)",
1396    introduces_nulls = false,
1397    propagates_nulls = false
1398)]
1399fn string_to_array<'a>(
1400    string: &'a str,
1401    delimiter: Option<&'a str>,
1402    null_string: OptionalArg<Option<&'a str>>,
1403    temp_storage: &'a RowArena,
1404) -> Result<Datum<'a>, EvalError> {
1405    if string.is_empty() {
1406        let mut row = Row::default();
1407        let mut packer = row.packer();
1408        packer.try_push_array(&[], std::iter::empty::<Datum>())?;
1409
1410        return Ok(temp_storage.push_unary_row(row));
1411    }
1412
1413    let Some(delimiter) = delimiter else {
1414        let split_all_chars_delimiter = "";
1415        return string_to_array_impl(
1416            string,
1417            split_all_chars_delimiter,
1418            null_string.flatten(),
1419            temp_storage,
1420        );
1421    };
1422
1423    if delimiter.is_empty() {
1424        let mut row = Row::default();
1425        let mut packer = row.packer();
1426        let dims = &[ArrayDimension {
1427            lower_bound: 1,
1428            length: 1,
1429        }];
1430        match null_string.flatten() {
1431            Some(null_string) if null_string == string => {
1432                packer.try_push_array(dims, std::iter::once(Datum::Null))?;
1433            }
1434            _ => {
1435                packer.try_push_array(dims, vec![string].into_iter().map(Datum::String))?;
1436            }
1437        }
1438        Ok(temp_storage.push_unary_row(row))
1439    } else {
1440        string_to_array_impl(string, delimiter, null_string.flatten(), temp_storage)
1441    }
1442}
1443
1444fn string_to_array_impl<'a>(
1445    string: &str,
1446    delimiter: &str,
1447    null_string: Option<&'a str>,
1448    temp_storage: &'a RowArena,
1449) -> Result<Datum<'a>, EvalError> {
1450    // Bound the transient `Vec<&str>` before it is collected. The count walks the same iterator the
1451    // collect does, so the two agree.
1452    check_build_fits_budget(
1453        || {
1454            if delimiter.is_empty() {
1455                string.split(delimiter).filter(|s| !s.is_empty()).count()
1456            } else {
1457                string.split(delimiter).count()
1458            }
1459        },
1460        std::mem::size_of::<&str>(),
1461        temp_storage,
1462    )?;
1463
1464    let mut row = Row::default();
1465    let mut packer = row.packer();
1466
1467    let result = string.split(delimiter);
1468    let found: Vec<&str> = if delimiter.is_empty() {
1469        result.filter(|s| !s.is_empty()).collect()
1470    } else {
1471        result.collect()
1472    };
1473    let array_dimensions = [ArrayDimension {
1474        lower_bound: 1,
1475        length: found.len(),
1476    }];
1477
1478    // Splitting amplifies: each chunk is packed with its own tag and length, so a delimiter that
1479    // splits per character costs several times the input. `Datum::Null` packs smaller than the
1480    // chunk it replaces, so treating every chunk as a string is an upper bound.
1481    check_datums_fit_budget(found.iter().copied().map(Datum::String), temp_storage)?;
1482
1483    if let Some(null_string) = null_string {
1484        let found_datums = found.into_iter().map(|chunk| {
1485            if chunk.eq(null_string) {
1486                Datum::Null
1487            } else {
1488                Datum::String(chunk)
1489            }
1490        });
1491
1492        packer.try_push_array(&array_dimensions, found_datums)?;
1493    } else {
1494        packer.try_push_array(&array_dimensions, found.into_iter().map(Datum::String))?;
1495    }
1496
1497    Ok(temp_storage.push_unary_row(row))
1498}
1499
1500#[sqlfunc]
1501fn substr<'a>(s: &'a str, start: i32, length: OptionalArg<i32>) -> Result<&'a str, EvalError> {
1502    let raw_start_idx = i64::from(start) - 1;
1503    let start_idx = match usize::try_from(cmp::max(raw_start_idx, 0)) {
1504        Ok(i) => i,
1505        Err(_) => {
1506            return Err(EvalError::InvalidParameterValue(
1507                format!(
1508                    "substring starting index ({}) exceeds min/max position",
1509                    raw_start_idx
1510                )
1511                .into(),
1512            ));
1513        }
1514    };
1515
1516    let mut char_indices = s.char_indices();
1517    let get_str_index = |(index, _char)| index;
1518
1519    let str_len = s.len();
1520    let start_char_idx = char_indices.nth(start_idx).map_or(str_len, get_str_index);
1521
1522    if let OptionalArg(Some(len)) = length {
1523        let end_idx = match i64::from(len) {
1524            e if e < 0 => {
1525                return Err(EvalError::InvalidParameterValue(
1526                    "negative substring length not allowed".into(),
1527                ));
1528            }
1529            e if e == 0 || e + raw_start_idx < 1 => return Ok(""),
1530            e => {
1531                let e = cmp::min(raw_start_idx + e - 1, e - 1);
1532                match usize::try_from(e) {
1533                    Ok(i) => i,
1534                    Err(_) => {
1535                        return Err(EvalError::InvalidParameterValue(
1536                            format!("substring length ({}) exceeds max position", e).into(),
1537                        ));
1538                    }
1539                }
1540            }
1541        };
1542
1543        let end_char_idx = char_indices.nth(end_idx).map_or(str_len, get_str_index);
1544
1545        Ok(&s[start_char_idx..end_char_idx])
1546    } else {
1547        Ok(&s[start_char_idx..])
1548    }
1549}
1550
1551#[sqlfunc(sqlname = "split_string")]
1552fn split_part<'a>(string: &'a str, delimiter: &str, field: i32) -> Result<&'a str, EvalError> {
1553    let index = match usize::try_from(i64::from(field) - 1) {
1554        Ok(index) => index,
1555        Err(_) => {
1556            return Err(EvalError::InvalidParameterValue(
1557                "field position must be greater than zero".into(),
1558            ));
1559        }
1560    };
1561
1562    // If the provided delimiter is the empty string,
1563    // PostgreSQL does not break the string into individual
1564    // characters. Instead, it generates the following parts: [string].
1565    if delimiter.is_empty() {
1566        if index == 0 {
1567            return Ok(string);
1568        } else {
1569            return Ok("");
1570        }
1571    }
1572
1573    // If provided index is greater than the number of split parts,
1574    // return an empty string.
1575    Ok(string.split(delimiter).nth(index).unwrap_or(""))
1576}
1577
1578#[sqlfunc(is_associative = true)]
1579fn concat(strs: Variadic<Option<&str>>, temp_storage: &RowArena) -> Result<String, EvalError> {
1580    let max_result_bytes = max_string_func_result_bytes(temp_storage);
1581    let mut total_size = 0;
1582    for s in &strs {
1583        if let Some(s) = s {
1584            total_size += s.len();
1585            if total_size > max_result_bytes {
1586                return Err(EvalError::LengthTooLarge);
1587            }
1588        }
1589    }
1590    let mut buf = String::with_capacity(total_size);
1591    for s in strs {
1592        if let Some(s) = s {
1593            buf.push_str(s);
1594        }
1595    }
1596    Ok(buf)
1597}
1598
1599#[sqlfunc]
1600fn concat_ws(
1601    ws: &str,
1602    rest: Variadic<Option<&str>>,
1603    temp_storage: &RowArena,
1604) -> Result<String, EvalError> {
1605    let max_result_bytes = max_string_func_result_bytes(temp_storage);
1606    let mut total_size = 0;
1607    for s in &rest {
1608        if let Some(s) = s {
1609            total_size += s.len();
1610            total_size += ws.len();
1611            if total_size > max_result_bytes {
1612                return Err(EvalError::LengthTooLarge);
1613            }
1614        }
1615    }
1616
1617    let buf = Itertools::join(&mut rest.into_iter().filter_map(|s| s), ws);
1618
1619    Ok(buf)
1620}
1621
1622#[sqlfunc]
1623fn translate(
1624    string: &str,
1625    from_str: &str,
1626    to_str: &str,
1627    temp_storage: &RowArena,
1628) -> Result<String, EvalError> {
1629    // Translating to a wider character amplifies: each byte of an ASCII input can become four.
1630    // Every output character is either the input character or a `to` character, so it costs at most
1631    // `max(len_utf8(c), widest)` bytes, and at most `len_utf8(c) + widest - 1` since a character is
1632    // at least one byte. Summed, that's the input's byte length plus one `widest - 1` per character.
1633    // Tight when every character widens, exactly the input length when `to` is ASCII, so we refuse
1634    // only calls that really would exceed the ceiling.
1635    let widest = to_str.chars().map(char::len_utf8).max().unwrap_or(0);
1636    let max_result_bytes = match widest.saturating_sub(1) {
1637        // An ASCII `to` widens nothing, so the result fits the input and counting characters isn't
1638        // worth a second pass.
1639        0 => string.len(),
1640        widening => string
1641            .len()
1642            .saturating_add(string.chars().count().saturating_mul(widening)),
1643    };
1644    if max_result_bytes > max_string_func_result_bytes(temp_storage) {
1645        return Err(EvalError::LengthTooLarge);
1646    }
1647
1648    let from = from_str.chars().collect::<Vec<_>>();
1649    let to = to_str.chars().collect::<Vec<_>>();
1650
1651    Ok(string
1652        .chars()
1653        .filter_map(|c| match from.iter().position(|f| f == &c) {
1654            Some(idx) => to.get(idx).copied(),
1655            None => Some(c),
1656        })
1657        .collect())
1658}
1659
1660#[sqlfunc(
1661    output_type_expr = "input_types[0].scalar_type.clone().nullable(false)",
1662    introduces_nulls = false
1663)]
1664// TODO(benesch): remove potentially dangerous usage of `as`.
1665#[allow(clippy::as_conversions)]
1666fn list_slice_linear<'a>(
1667    list: DatumList<'a>,
1668    first: (i64, i64),
1669    remainder: Variadic<(i64, i64)>,
1670    temp_storage: &'a RowArena,
1671) -> Datum<'a> {
1672    let mut start_idx = 0;
1673    let mut total_length = usize::MAX;
1674
1675    for (start, end) in std::iter::once(first).chain(remainder) {
1676        let start = std::cmp::max(start, 1);
1677
1678        // Result should be empty list.
1679        if start > end {
1680            start_idx = 0;
1681            total_length = 0;
1682            break;
1683        }
1684
1685        let start_inner = start as usize - 1;
1686        // Start index only moves to geq positions.
1687        start_idx += start_inner;
1688
1689        // Length index only moves to leq positions
1690        let length_inner = (end - start) as usize + 1;
1691        total_length = std::cmp::min(length_inner, total_length - start_inner);
1692    }
1693
1694    let iter = list.iter().skip(start_idx).take(total_length);
1695
1696    temp_storage.make_datum(|row| {
1697        row.push_list_with(|row| {
1698            // if iter is empty, will get the appropriate empty list.
1699            for d in iter {
1700                row.push(d);
1701            }
1702        });
1703    })
1704}
1705
1706#[sqlfunc(sqlname = "timestamp_bin")]
1707fn date_bin_timestamp(
1708    stride: Interval,
1709    source: CheckedTimestamp<NaiveDateTime>,
1710    origin: CheckedTimestamp<NaiveDateTime>,
1711) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
1712    date_bin(stride, source, origin)
1713}
1714
1715#[sqlfunc(sqlname = "timestamptz_bin")]
1716fn date_bin_timestamp_tz(
1717    stride: Interval,
1718    source: CheckedTimestamp<DateTime<Utc>>,
1719    origin: CheckedTimestamp<DateTime<Utc>>,
1720) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
1721    date_bin(stride, source, origin)
1722}
1723
1724#[sqlfunc(sqlname = "timezonet")]
1725fn timezone_time_variadic(
1726    tz_str: &str,
1727    time: NaiveTime,
1728    wall_time: CheckedTimestamp<DateTime<Utc>>,
1729) -> Result<NaiveTime, EvalError> {
1730    parse_timezone(tz_str, TimezoneSpec::Posix)
1731        .map(|tz| timezone_time(tz, time, &wall_time.naive_utc()))
1732}
1733pub(crate) trait LazyVariadicFunc: fmt::Display {
1734    fn eval<'a>(
1735        &'a self,
1736        datums: &[Datum<'a>],
1737        temp_storage: &'a RowArena,
1738        exprs: &'a [impl Eval],
1739    ) -> Result<Datum<'a>, EvalError>;
1740
1741    /// The output SqlColumnType of this function.
1742    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType;
1743
1744    /// Whether this function will produce NULL on NULL input.
1745    fn propagates_nulls(&self) -> bool;
1746
1747    /// Whether this function will produce NULL on non-NULL input.
1748    fn introduces_nulls(&self) -> bool;
1749
1750    /// Whether this function might error on non-error input.
1751    fn could_error(&self) -> bool {
1752        true
1753    }
1754
1755    /// Returns true if the function is monotone.
1756    fn is_monotone(&self) -> bool {
1757        false
1758    }
1759
1760    /// Returns true if the function is associative.
1761    fn is_associative(&self) -> bool {
1762        false
1763    }
1764
1765    /// Returns true if the function is an infix operator.
1766    fn is_infix_op(&self) -> bool {
1767        false
1768    }
1769}
1770
1771pub(crate) trait EagerVariadicFunc: fmt::Display {
1772    type Input<'a>: InputDatumType<'a, EvalError>;
1773    type Output<'a>: OutputDatumType<'a, EvalError>;
1774
1775    fn call<'a>(&self, input: Self::Input<'a>, temp_storage: &'a RowArena) -> Self::Output<'a>;
1776
1777    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType;
1778
1779    fn propagates_nulls(&self) -> bool {
1780        !Self::Input::nullable()
1781    }
1782
1783    fn introduces_nulls(&self) -> bool {
1784        Self::Output::nullable()
1785    }
1786
1787    fn could_error(&self) -> bool {
1788        Self::Output::fallible()
1789    }
1790
1791    fn is_monotone(&self) -> bool {
1792        false
1793    }
1794
1795    fn is_associative(&self) -> bool {
1796        false
1797    }
1798
1799    fn is_infix_op(&self) -> bool {
1800        false
1801    }
1802}
1803
1804/// Blanket `LazyVariadicFunc` impl for each eager type, bridging
1805/// expression evaluation and null propagation via `InputDatumType::try_from_iter`.
1806impl<T: EagerVariadicFunc> LazyVariadicFunc for T {
1807    fn eval<'a>(
1808        &'a self,
1809        datums: &[Datum<'a>],
1810        temp_storage: &'a RowArena,
1811        exprs: &'a [impl Eval],
1812    ) -> Result<Datum<'a>, EvalError> {
1813        let mut datums = exprs.iter().map(|e| e.eval(datums, temp_storage));
1814        match T::Input::try_from_iter(&mut datums) {
1815            Ok(input) => self.call(input, temp_storage).into_result(temp_storage),
1816            Err(Ok(None)) => Err(EvalError::Internal("missing parameter".into())),
1817            Err(Ok(Some(datum))) if datum.is_null() => Ok(datum),
1818            Err(Ok(Some(_datum))) => {
1819                // datum is _not_ NULL
1820                Err(EvalError::Internal("invalid input type".into()))
1821            }
1822            Err(Err(res)) => Err(res),
1823        }
1824    }
1825
1826    fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
1827        self.output_type(input_types)
1828    }
1829
1830    fn propagates_nulls(&self) -> bool {
1831        self.propagates_nulls()
1832    }
1833
1834    fn introduces_nulls(&self) -> bool {
1835        self.introduces_nulls()
1836    }
1837
1838    fn could_error(&self) -> bool {
1839        self.could_error()
1840    }
1841
1842    fn is_monotone(&self) -> bool {
1843        self.is_monotone()
1844    }
1845
1846    fn is_associative(&self) -> bool {
1847        self.is_associative()
1848    }
1849
1850    fn is_infix_op(&self) -> bool {
1851        self.is_infix_op()
1852    }
1853}
1854
1855derive_variadic! {
1856    Coalesce(Coalesce),
1857    Greatest(Greatest),
1858    Least(Least),
1859    Concat(Concat),
1860    ConcatWs(ConcatWs),
1861    MakeTimestamp(MakeTimestamp),
1862    PadLeading(PadLeading),
1863    Substr(Substr),
1864    Replace(Replace),
1865    JsonbBuildArray(JsonbBuildArray),
1866    JsonbBuildObject(JsonbBuildObject),
1867    MapBuild(MapBuild),
1868    ArrayCreate(ArrayCreate),
1869    ArrayToString(ArrayToString),
1870    ArrayIndex(ArrayIndex),
1871    ListCreate(ListCreate),
1872    RecordCreate(RecordCreate),
1873    ListIndex(ListIndex),
1874    ListSliceLinear(ListSliceLinear),
1875    SplitPart(SplitPart),
1876    RegexpMatch(RegexpMatch),
1877    HmacString(HmacString),
1878    HmacBytes(HmacBytes),
1879    ErrorIfNull(ErrorIfNull),
1880    DateBinTimestamp(DateBinTimestamp),
1881    DateBinTimestampTz(DateBinTimestampTz),
1882    DateDiffTimestamp(DateDiffTimestamp),
1883    DateDiffTimestampTz(DateDiffTimestampTz),
1884    DateDiffDate(DateDiffDate),
1885    DateDiffTime(DateDiffTime),
1886    And(And),
1887    Or(Or),
1888    RangeCreate(RangeCreate),
1889    MakeAclItem(MakeAclItem),
1890    MakeMzAclItem(MakeMzAclItem),
1891    Translate(Translate),
1892    ArrayPosition(ArrayPosition),
1893    ArrayFill(ArrayFill),
1894    StringToArray(StringToArray),
1895    TimezoneTimeVariadic(TimezoneTimeVariadic),
1896    RegexpSplitToArray(RegexpSplitToArray),
1897    RegexpReplace(RegexpReplace),
1898    CaseLiteral(CaseLiteral),
1899}
1900
1901impl VariadicFunc {
1902    pub fn switch_and_or(&self) -> Self {
1903        match self {
1904            VariadicFunc::And(_) => Or.into(),
1905            VariadicFunc::Or(_) => And.into(),
1906            _ => unreachable!(),
1907        }
1908    }
1909
1910    /// Gives the unit (u) of OR or AND, such that `u AND/OR x == x`.
1911    /// Note that a 0-arg AND/OR evaluates to unit_of_and_or.
1912    pub fn unit_of_and_or(&self) -> MirScalarExpr {
1913        match self {
1914            VariadicFunc::And(_) => MirScalarExpr::literal_true(),
1915            VariadicFunc::Or(_) => MirScalarExpr::literal_false(),
1916            _ => unreachable!(),
1917        }
1918    }
1919
1920    /// Gives the zero (z) of OR or AND, such that `z AND/OR x == z`.
1921    pub fn zero_of_and_or(&self) -> MirScalarExpr {
1922        match self {
1923            VariadicFunc::And(_) => MirScalarExpr::literal_false(),
1924            VariadicFunc::Or(_) => MirScalarExpr::literal_true(),
1925            _ => unreachable!(),
1926        }
1927    }
1928}