1use 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 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), Ok(Datum::True) => {}
86 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#[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 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 return Err(InvalidArrayError::WrongCardinality { actual, expected }.into());
201 }
202 }
203 dim = Some(actual_dims);
204 }
205 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 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 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 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 return None;
391 }
392
393 let mut final_idx = 0;
394
395 for (d, idx) in dims.into_iter().zip_eq(indices.iter()) {
396 let idx = isize::cast_from(*idx + self.offset);
398
399 let (lower, upper) = d.dimension_bounds();
400
401 if !(lower..upper + 1).contains(&idx) {
404 return None;
405 }
406
407 final_idx *= d.length;
409
410 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 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 let null_str = null_str_arg.flatten();
475 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 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 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 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 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 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#[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 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 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 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 let map: std::collections::BTreeMap<&str, _> = datums
1154 .into_iter()
1155 .filter_map(|(k, v)| k.map(|k| (k, v)))
1156 .collect();
1157
1158 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 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), 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 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 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, ®exp, 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 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 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 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 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 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 delimiter.is_empty() {
1566 if index == 0 {
1567 return Ok(string);
1568 } else {
1569 return Ok("");
1570 }
1571 }
1572
1573 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 let widest = to_str.chars().map(char::len_utf8).max().unwrap_or(0);
1636 let max_result_bytes = match widest.saturating_sub(1) {
1637 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#[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 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_idx += start_inner;
1688
1689 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 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 fn output_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType;
1743
1744 fn propagates_nulls(&self) -> bool;
1746
1747 fn introduces_nulls(&self) -> bool;
1749
1750 fn could_error(&self) -> bool {
1752 true
1753 }
1754
1755 fn is_monotone(&self) -> bool {
1757 false
1758 }
1759
1760 fn is_associative(&self) -> bool {
1762 false
1763 }
1764
1765 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
1804impl<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 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 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 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}