1use std::borrow::Cow;
15use std::cmp::Ordering;
16use std::convert::{TryFrom, TryInto};
17use std::str::FromStr;
18use std::{iter, str};
19
20use ::encoding::DecoderTrap;
21use ::encoding::label::encoding_from_whatwg_label;
22use aws_lc_rs::constant_time::verify_slices_are_equal;
23use aws_lc_rs::digest;
24use chrono::{DateTime, Duration, NaiveDate, NaiveDateTime, TimeZone, Timelike, Utc};
25use chrono_tz::{OffsetComponents, OffsetName, Tz};
26use dec::OrderedDecimal;
27use itertools::Itertools;
28use md5::{Digest, Md5};
29use mz_expr_derive::sqlfunc;
30use mz_ore::cast::{self, CastFrom};
31use mz_ore::fmt::FormatBuffer;
32use mz_ore::lex::LexBuf;
33use mz_ore::option::OptionExt;
34use mz_pgrepr::Type;
35use mz_pgtz::timezone::{Timezone, TimezoneSpec};
36use mz_repr::adt::array::{Array, ArrayDimension};
37use mz_repr::adt::date::Date;
38use mz_repr::adt::interval::{Interval, RoundBehavior};
39use mz_repr::adt::jsonb::JsonbRef;
40use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
41use mz_repr::adt::numeric::{self, Numeric};
42use mz_repr::adt::range::Range;
43use mz_repr::adt::regex::Regex;
44use mz_repr::adt::timestamp::{CheckedTimestamp, TimestampLike};
45use mz_repr::{
46 ArrayRustType, Datum, DatumList, DatumMap, ExcludeNull, FromDatum, InputDatumType, Row,
47 RowArena, SqlScalarType, strconv,
48};
49use mz_sql_parser::ast::display::{AstDisplay, FormatMode};
50use mz_sql_pretty::{PrettyConfig, pretty_str};
51use num::traits::CheckedNeg;
52
53use crate::scalar::func::format::DateTimeFormat;
54use crate::{EvalError, like_pattern};
55
56#[macro_use]
57mod macros;
58mod binary;
59mod encoding;
60pub(crate) mod format;
61pub(crate) mod impls;
62mod jq;
63#[cfg(feature = "func-registry")]
64pub mod registry;
65mod unary;
66mod unmaterializable;
67pub mod variadic;
68
69pub use binary::BinaryFunc;
70pub use impls::*;
71pub use jq::{Jq, JqProgram};
72pub use unary::{EagerUnaryFunc, LazyUnaryFunc, UnaryFunc};
73pub use unmaterializable::UnmaterializableFunc;
74pub use variadic::VariadicFunc;
75
76pub trait FuncName {
84 const NAME: &'static str;
85
86 #[cfg(feature = "func-registry")]
89 const SQLFUNC: Option<SqlFuncSource> = None;
90
91 #[cfg(feature = "func-registry")]
96 fn sqlfunc_input_types() -> Option<Vec<mz_repr::SqlColumnType>> {
97 None
98 }
99}
100
101#[cfg(feature = "func-registry")]
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
108pub struct SqlFuncSource {
109 pub decl: &'static str,
112 pub signature: &'static str,
114 pub body_fingerprint: u64,
116}
117
118macro_rules! func_name {
122 () => {};
123 (<$param:ident> $ty:ty => $name:literal, $($rest:tt)*) => {
124 impl<$param> FuncName for $ty {
125 const NAME: &'static str = $name;
126 }
127 func_name!($($rest)*);
128 };
129 ($ty:ty => $name:literal, $($rest:tt)*) => {
130 impl FuncName for $ty {
131 const NAME: &'static str = $name;
132 }
133 func_name!($($rest)*);
134 };
135}
136
137func_name! {
138 CaseLiteral => "case_literal",
139 <E> CastArrayToArray<E> => "cast_array_to_array",
140 <E> CastArrayToJsonb<E> => "cast_array_to_jsonb",
141 <E> CastList1ToList2<E> => "cast_list1_to_list2",
142 <E> CastListToJsonb<E> => "cast_list_to_jsonb",
143 <E> CastRecord1ToRecord2<E> => "cast_record1_to_record2",
144 <E> CastStringToArray<E> => "cast_string_to_array",
145 <E> CastStringToList<E> => "cast_string_to_list",
146 <E> CastStringToMap<E> => "cast_string_to_map",
147 <E> CastStringToRange<E> => "cast_string_to_range",
148 variadic::ErrorIfNull => "error_if_null",
149 <E> TryCast<E> => "try_cast",
150 variadic::And => "and",
151 variadic::Coalesce => "coalesce",
152 variadic::Greatest => "greatest",
153 variadic::Least => "least",
154 variadic::Or => "or",
155}
156
157pub const MAX_STRING_FUNC_RESULT_BYTES: usize = 1024 * 1024 * 100;
164
165pub fn max_string_func_result_bytes(temp_storage: &RowArena) -> usize {
175 std::cmp::min(
176 MAX_STRING_FUNC_RESULT_BYTES,
177 temp_storage.budget_remaining(),
178 )
179}
180
181pub fn check_datums_fit_budget<'a>(
193 datums: impl IntoIterator<Item = Datum<'a>>,
194 temp_storage: &RowArena,
195) -> Result<(), EvalError> {
196 let need: usize = datums
197 .into_iter()
198 .map(|d| mz_repr::datum_size(&d))
199 .fold(0, usize::saturating_add);
200 if need > temp_storage.budget_remaining() {
201 return Err(EvalError::TempStorageBudgetExceeded);
202 }
203 Ok(())
204}
205
206pub fn check_build_fits_budget(
222 n_elems: impl FnOnce() -> usize,
223 elem_size: usize,
224 temp_storage: &RowArena,
225) -> Result<(), EvalError> {
226 let budget_remaining = temp_storage.budget_remaining();
227 if budget_remaining == usize::MAX {
230 return Ok(());
231 }
232 if n_elems().saturating_mul(elem_size) > budget_remaining {
233 return Err(EvalError::TempStorageBudgetExceeded);
234 }
235 Ok(())
236}
237
238pub fn jsonb_stringify<'a>(a: Datum<'a>, temp_storage: &'a RowArena) -> Option<&'a str> {
239 match a {
240 Datum::JsonNull => None,
241 Datum::String(s) => Some(s),
242 _ => {
243 let s = cast_jsonb_to_string(JsonbRef::from_datum(a));
244 Some(temp_storage.push_string(s))
245 }
246 }
247}
248
249#[sqlfunc(
250 is_monotone = "(true, true)",
251 is_infix_op = true,
252 sqlname = "+",
253 propagates_nulls = true
254)]
255fn add_int16(a: i16, b: i16) -> Result<i16, EvalError> {
256 a.checked_add(b).ok_or(EvalError::NumericFieldOverflow)
257}
258
259#[sqlfunc(
260 is_monotone = "(true, true)",
261 is_infix_op = true,
262 sqlname = "+",
263 propagates_nulls = true
264)]
265fn add_int32(a: i32, b: i32) -> Result<i32, EvalError> {
266 a.checked_add(b).ok_or(EvalError::NumericFieldOverflow)
267}
268
269#[sqlfunc(
270 is_monotone = "(true, true)",
271 is_infix_op = true,
272 sqlname = "+",
273 propagates_nulls = true
274)]
275fn add_int64(a: i64, b: i64) -> Result<i64, EvalError> {
276 a.checked_add(b).ok_or(EvalError::NumericFieldOverflow)
277}
278
279#[sqlfunc(
280 is_monotone = "(true, true)",
281 is_infix_op = true,
282 sqlname = "+",
283 propagates_nulls = true
284)]
285fn add_uint16(a: u16, b: u16) -> Result<u16, EvalError> {
286 a.checked_add(b)
287 .ok_or_else(|| EvalError::UInt16OutOfRange(format!("{a} + {b}").into()))
288}
289
290#[sqlfunc(
291 is_monotone = "(true, true)",
292 is_infix_op = true,
293 sqlname = "+",
294 propagates_nulls = true
295)]
296fn add_uint32(a: u32, b: u32) -> Result<u32, EvalError> {
297 a.checked_add(b)
298 .ok_or_else(|| EvalError::UInt32OutOfRange(format!("{a} + {b}").into()))
299}
300
301#[sqlfunc(
302 is_monotone = "(true, true)",
303 is_infix_op = true,
304 sqlname = "+",
305 propagates_nulls = true
306)]
307fn add_uint64(a: u64, b: u64) -> Result<u64, EvalError> {
308 a.checked_add(b)
309 .ok_or_else(|| EvalError::UInt64OutOfRange(format!("{a} + {b}").into()))
310}
311
312#[sqlfunc(
313 is_monotone = "(true, true)",
314 is_infix_op = true,
315 sqlname = "+",
316 propagates_nulls = true
317)]
318fn add_float32(a: f32, b: f32) -> Result<f32, EvalError> {
319 let sum = a + b;
320 if sum.is_infinite() && !a.is_infinite() && !b.is_infinite() {
321 Err(EvalError::FloatOverflow)
322 } else {
323 Ok(sum)
324 }
325}
326
327#[sqlfunc(
328 is_monotone = "(true, true)",
329 is_infix_op = true,
330 sqlname = "+",
331 propagates_nulls = true
332)]
333fn add_float64(a: f64, b: f64) -> Result<f64, EvalError> {
334 let sum = a + b;
335 if sum.is_infinite() && !a.is_infinite() && !b.is_infinite() {
336 Err(EvalError::FloatOverflow)
337 } else {
338 Ok(sum)
339 }
340}
341
342#[sqlfunc(is_monotone = "(false, false)", is_infix_op = true, sqlname = "+")]
349fn add_timestamp_interval(
350 a: CheckedTimestamp<NaiveDateTime>,
351 b: Interval,
352) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
353 add_timestamplike_interval(a, b)
354}
355
356#[sqlfunc(is_monotone = "(false, false)", is_infix_op = true, sqlname = "+")]
357fn add_timestamp_tz_interval(
358 a: CheckedTimestamp<DateTime<Utc>>,
359 b: Interval,
360) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
361 add_timestamplike_interval(a, b)
362}
363
364fn add_timestamplike_interval<T>(
365 a: CheckedTimestamp<T>,
366 b: Interval,
367) -> Result<CheckedTimestamp<T>, EvalError>
368where
369 T: TimestampLike,
370{
371 let dt = a.date_time();
372 let dt = add_timestamp_months(&dt, b.months)?;
373 let dt = dt
374 .checked_add_signed(b.duration_as_chrono())
375 .ok_or(EvalError::TimestampOutOfRange)?;
376 Ok(CheckedTimestamp::from_timestamplike(T::from_date_time(dt))?)
377}
378
379#[sqlfunc(is_monotone = "(false, false)", is_infix_op = true, sqlname = "-")]
381fn sub_timestamp_interval(
382 a: CheckedTimestamp<NaiveDateTime>,
383 b: Interval,
384) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
385 sub_timestamplike_interval(a, b)
386}
387
388#[sqlfunc(is_monotone = "(false, false)", is_infix_op = true, sqlname = "-")]
389fn sub_timestamp_tz_interval(
390 a: CheckedTimestamp<DateTime<Utc>>,
391 b: Interval,
392) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
393 sub_timestamplike_interval(a, b)
394}
395
396fn sub_timestamplike_interval<T>(
397 a: CheckedTimestamp<T>,
398 b: Interval,
399) -> Result<CheckedTimestamp<T>, EvalError>
400where
401 T: TimestampLike,
402{
403 neg_interval_inner(b).and_then(|i| add_timestamplike_interval(a, i))
404}
405
406#[sqlfunc(is_monotone = "(true, true)", is_infix_op = true, sqlname = "+")]
407fn add_date_time(
408 date: Date,
409 time: chrono::NaiveTime,
410) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
411 let (extra_sec, nanos) = match time.nanosecond().checked_sub(1_000_000_000) {
417 Some(nanos) => (1, nanos),
418 None => (0, time.nanosecond()),
419 };
420 let dt = NaiveDate::from(date)
421 .and_hms_nano_opt(time.hour(), time.minute(), time.second(), nanos)
422 .unwrap()
423 .checked_add_signed(chrono::Duration::try_seconds(extra_sec).unwrap())
424 .ok_or(EvalError::TimestampOutOfRange)?;
425 Ok(CheckedTimestamp::from_timestamplike(dt)?)
426}
427
428#[sqlfunc(is_monotone = "(true, false)", is_infix_op = true, sqlname = "+")]
434fn add_date_interval(
435 date: Date,
436 interval: Interval,
437) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
438 let dt = NaiveDate::from(date).and_hms_opt(0, 0, 0).unwrap();
439 let dt = add_timestamp_months(&dt, interval.months)?;
440 let dt = dt
441 .checked_add_signed(interval.duration_as_chrono())
442 .ok_or(EvalError::TimestampOutOfRange)?;
443 Ok(CheckedTimestamp::from_timestamplike(dt)?)
444}
445
446#[sqlfunc(
447 is_monotone = "(false, false)",
449 is_infix_op = true,
450 sqlname = "+",
451 propagates_nulls = true
452)]
453fn add_time_interval(time: chrono::NaiveTime, interval: Interval) -> chrono::NaiveTime {
454 let (t, _) = time.overflowing_add_signed(interval.duration_as_chrono());
455 t
456}
457
458#[sqlfunc(
459 is_monotone = "(true, false)",
460 output_type = "Numeric",
461 sqlname = "round",
462 propagates_nulls = true
463)]
464fn round_numeric_binary(a: OrderedDecimal<Numeric>, mut b: i32) -> Result<Numeric, EvalError> {
465 let mut a = a.0;
466 let mut cx = numeric::cx_datum();
467 let a_scale = numeric::get_scale(&a);
468 if a.is_finite() && i64::from(b) > i64::from(a_scale) {
469 let max_remaining_scale = u32::from(numeric::NUMERIC_DATUM_MAX_PRECISION)
488 - (numeric::get_precision(&a) - a_scale);
489 b = match i32::try_from(max_remaining_scale) {
490 Ok(max_remaining_scale) => std::cmp::min(b, max_remaining_scale),
491 Err(_) => b,
492 };
493 cx.rescale(&mut a, &numeric::Numeric::from(-b));
494 } else {
495 const MAX_P_LIMIT: i32 = 1 + cast::u8_to_i32(numeric::NUMERIC_DATUM_MAX_PRECISION);
498 b = std::cmp::min(MAX_P_LIMIT, b);
499 b = std::cmp::max(-MAX_P_LIMIT, b);
500 let mut b = numeric::Numeric::from(b);
501 cx.scaleb(&mut a, &b);
503 cx.round(&mut a);
504 cx.neg(&mut b);
506 cx.scaleb(&mut a, &b);
507 }
508
509 if cx.status().overflow() {
510 Err(EvalError::FloatOverflow)
511 } else if a.is_zero() {
512 Ok(numeric::Numeric::zero())
516 } else {
517 numeric::munge_numeric(&mut a).unwrap();
518 Ok(a)
519 }
520}
521
522#[sqlfunc(sqlname = "convert_from", propagates_nulls = true)]
523fn convert_from<'a>(a: &'a [u8], b: &str) -> Result<&'a str, EvalError> {
524 let encoding_name = b.to_lowercase().replace('_', "-").into_boxed_str();
530
531 if encoding_from_whatwg_label(&encoding_name).map(|e| e.name()) != Some("utf-8") {
533 return Err(EvalError::InvalidEncodingName(encoding_name));
534 }
535
536 match str::from_utf8(a) {
537 Ok(from) if from.contains('\0') => Err(EvalError::InvalidByteSequence {
540 byte_sequence: "0x00".into(),
541 encoding_name,
542 }),
543 Ok(from) => Ok(from),
544 Err(e) => Err(EvalError::InvalidByteSequence {
545 byte_sequence: e.to_string().into(),
546 encoding_name,
547 }),
548 }
549}
550
551#[sqlfunc]
552fn encode(bytes: &[u8], format: &str) -> Result<String, EvalError> {
553 let format = encoding::lookup_format(format)?;
554 Ok(format.encode(bytes))
555}
556
557#[sqlfunc]
558fn decode(string: &str, format: &str, temp_storage: &RowArena) -> Result<Vec<u8>, EvalError> {
559 let format = encoding::lookup_format(format)?;
560 let out = format.decode(string)?;
561 if out.len() > max_string_func_result_bytes(temp_storage) {
562 Err(EvalError::LengthTooLarge)
563 } else {
564 Ok(out)
565 }
566}
567
568#[sqlfunc(sqlname = "length", propagates_nulls = true)]
569fn encoded_bytes_char_length(a: &[u8], b: &str) -> Result<i32, EvalError> {
570 let encoding_name = b.to_lowercase().replace('_', "-").into_boxed_str();
576
577 let enc = match encoding_from_whatwg_label(&encoding_name) {
578 Some(enc) => enc,
579 None => return Err(EvalError::InvalidEncodingName(encoding_name)),
580 };
581
582 let decoded_string = match enc.decode(a, DecoderTrap::Strict) {
583 Ok(s) => s,
584 Err(e) => {
585 return Err(EvalError::InvalidByteSequence {
586 byte_sequence: e.into(),
587 encoding_name,
588 });
589 }
590 };
591
592 let count = decoded_string.chars().count();
593 i32::try_from(count).map_err(|_| EvalError::Int32OutOfRange(count.to_string().into()))
594}
595
596#[allow(clippy::as_conversions)]
598pub fn add_timestamp_months<T: TimestampLike>(
599 dt: &T,
600 mut months: i32,
601) -> Result<CheckedTimestamp<T>, EvalError> {
602 if months == 0 {
603 return Ok(CheckedTimestamp::from_timestamplike(dt.clone())?);
604 }
605
606 let (mut year, mut month, mut day) = (dt.year(), dt.month0() as i32, dt.day());
607 let years = months / 12;
608 year = year
609 .checked_add(years)
610 .ok_or(EvalError::TimestampOutOfRange)?;
611
612 months %= 12;
613 if months < 0 {
615 year -= 1;
616 months += 12;
617 }
618 year += (month + months) / 12;
619 month = (month + months) % 12;
620 month += 1;
622
623 let mut new_d = chrono::NaiveDate::from_ymd_opt(year, month as u32, day);
625 while new_d.is_none() {
626 if day < 28 {
629 return Err(EvalError::TimestampOutOfRange);
630 }
631 day -= 1;
632 new_d = chrono::NaiveDate::from_ymd_opt(year, month as u32, day);
633 }
634 let new_d = new_d.unwrap();
635
636 let new_dt = new_d
641 .and_hms_nano_opt(dt.hour(), dt.minute(), dt.second(), dt.nanosecond())
642 .unwrap();
643 let new_dt = T::from_date_time(new_dt);
644 Ok(CheckedTimestamp::from_timestamplike(new_dt)?)
645}
646
647#[sqlfunc(
648 is_monotone = "(true, true)",
649 is_infix_op = true,
650 sqlname = "+",
651 propagates_nulls = true
652)]
653fn add_numeric(
654 a: OrderedDecimal<Numeric>,
655 b: OrderedDecimal<Numeric>,
656) -> Result<Numeric, EvalError> {
657 let mut cx = numeric::cx_datum();
658 let mut a = a.0;
659 cx.add(&mut a, &b.0);
660 if cx.status().overflow() {
661 Err(EvalError::FloatOverflow)
662 } else {
663 Ok(a)
664 }
665}
666
667#[sqlfunc(
668 is_monotone = "(true, true)",
669 is_infix_op = true,
670 sqlname = "+",
671 propagates_nulls = true
672)]
673fn add_interval(a: Interval, b: Interval) -> Result<Interval, EvalError> {
674 a.checked_add(&b)
675 .ok_or_else(|| EvalError::IntervalOutOfRange(format!("{a} + {b}").into()))
676}
677
678#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
679fn bit_and_int16(a: i16, b: i16) -> i16 {
680 a & b
681}
682
683#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
684fn bit_and_int32(a: i32, b: i32) -> i32 {
685 a & b
686}
687
688#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
689fn bit_and_int64(a: i64, b: i64) -> i64 {
690 a & b
691}
692
693#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
694fn bit_and_uint16(a: u16, b: u16) -> u16 {
695 a & b
696}
697
698#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
699fn bit_and_uint32(a: u32, b: u32) -> u32 {
700 a & b
701}
702
703#[sqlfunc(is_infix_op = true, sqlname = "&", propagates_nulls = true)]
704fn bit_and_uint64(a: u64, b: u64) -> u64 {
705 a & b
706}
707
708#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
709fn bit_or_int16(a: i16, b: i16) -> i16 {
710 a | b
711}
712
713#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
714fn bit_or_int32(a: i32, b: i32) -> i32 {
715 a | b
716}
717
718#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
719fn bit_or_int64(a: i64, b: i64) -> i64 {
720 a | b
721}
722
723#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
724fn bit_or_uint16(a: u16, b: u16) -> u16 {
725 a | b
726}
727
728#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
729fn bit_or_uint32(a: u32, b: u32) -> u32 {
730 a | b
731}
732
733#[sqlfunc(is_infix_op = true, sqlname = "|", propagates_nulls = true)]
734fn bit_or_uint64(a: u64, b: u64) -> u64 {
735 a | b
736}
737
738#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
739fn bit_xor_int16(a: i16, b: i16) -> i16 {
740 a ^ b
741}
742
743#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
744fn bit_xor_int32(a: i32, b: i32) -> i32 {
745 a ^ b
746}
747
748#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
749fn bit_xor_int64(a: i64, b: i64) -> i64 {
750 a ^ b
751}
752
753#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
754fn bit_xor_uint16(a: u16, b: u16) -> u16 {
755 a ^ b
756}
757
758#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
759fn bit_xor_uint32(a: u32, b: u32) -> u32 {
760 a ^ b
761}
762
763#[sqlfunc(is_infix_op = true, sqlname = "#", propagates_nulls = true)]
764fn bit_xor_uint64(a: u64, b: u64) -> u64 {
765 a ^ b
766}
767
768#[sqlfunc(is_infix_op = true, sqlname = "<<", propagates_nulls = true)]
769#[allow(clippy::as_conversions)]
771fn bit_shift_left_int16(a: i16, b: i32) -> i16 {
772 let lhs: i32 = a as i32;
776 let rhs: u32 = b as u32;
777 lhs.wrapping_shl(rhs) as i16
778}
779
780#[sqlfunc(is_infix_op = true, sqlname = "<<", propagates_nulls = true)]
781#[allow(clippy::as_conversions)]
783fn bit_shift_left_int32(lhs: i32, rhs: i32) -> i32 {
784 let rhs = rhs as u32;
785 lhs.wrapping_shl(rhs)
786}
787
788#[sqlfunc(is_infix_op = true, sqlname = "<<", propagates_nulls = true)]
789#[allow(clippy::as_conversions)]
791fn bit_shift_left_int64(lhs: i64, rhs: i32) -> i64 {
792 let rhs = rhs as u32;
793 lhs.wrapping_shl(rhs)
794}
795
796#[sqlfunc(is_infix_op = true, sqlname = "<<", propagates_nulls = true)]
797#[allow(clippy::as_conversions)]
799fn bit_shift_left_uint16(a: u16, b: u32) -> u16 {
800 let lhs: u32 = a as u32;
804 let rhs: u32 = b;
805 lhs.wrapping_shl(rhs) as u16
806}
807
808#[sqlfunc(is_infix_op = true, sqlname = "<<", propagates_nulls = true)]
809fn bit_shift_left_uint32(a: u32, b: u32) -> u32 {
810 let lhs = a;
811 let rhs = b;
812 lhs.wrapping_shl(rhs)
813}
814
815#[sqlfunc(
816 output_type = "u64",
817 is_infix_op = true,
818 sqlname = "<<",
819 propagates_nulls = true
820)]
821fn bit_shift_left_uint64(lhs: u64, rhs: u32) -> u64 {
822 lhs.wrapping_shl(rhs)
823}
824
825#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
826#[allow(clippy::as_conversions)]
828fn bit_shift_right_int16(lhs: i16, rhs: i32) -> i16 {
829 let lhs = lhs as i32;
833 let rhs = rhs as u32;
834 lhs.wrapping_shr(rhs) as i16
835}
836
837#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
838#[allow(clippy::as_conversions)]
840fn bit_shift_right_int32(lhs: i32, rhs: i32) -> i32 {
841 lhs.wrapping_shr(rhs as u32)
842}
843
844#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
845#[allow(clippy::as_conversions)]
847fn bit_shift_right_int64(lhs: i64, rhs: i32) -> i64 {
848 lhs.wrapping_shr(rhs as u32)
849}
850
851#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
852#[allow(clippy::as_conversions)]
854fn bit_shift_right_uint16(lhs: u16, rhs: u32) -> u16 {
855 let lhs = lhs as u32;
859 lhs.wrapping_shr(rhs) as u16
860}
861
862#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
863fn bit_shift_right_uint32(lhs: u32, rhs: u32) -> u32 {
864 lhs.wrapping_shr(rhs)
865}
866
867#[sqlfunc(is_infix_op = true, sqlname = ">>", propagates_nulls = true)]
868fn bit_shift_right_uint64(lhs: u64, rhs: u32) -> u64 {
869 lhs.wrapping_shr(rhs)
870}
871
872#[sqlfunc(
873 is_monotone = "(true, true)",
874 is_infix_op = true,
875 sqlname = "-",
876 propagates_nulls = true
877)]
878fn sub_int16(a: i16, b: i16) -> Result<i16, EvalError> {
879 a.checked_sub(b).ok_or(EvalError::NumericFieldOverflow)
880}
881
882#[sqlfunc(
883 is_monotone = "(true, true)",
884 is_infix_op = true,
885 sqlname = "-",
886 propagates_nulls = true
887)]
888fn sub_int32(a: i32, b: i32) -> Result<i32, EvalError> {
889 a.checked_sub(b).ok_or(EvalError::NumericFieldOverflow)
890}
891
892#[sqlfunc(
893 is_monotone = "(true, true)",
894 is_infix_op = true,
895 sqlname = "-",
896 propagates_nulls = true
897)]
898fn sub_int64(a: i64, b: i64) -> Result<i64, EvalError> {
899 a.checked_sub(b).ok_or(EvalError::NumericFieldOverflow)
900}
901
902#[sqlfunc(
903 is_monotone = "(true, true)",
904 is_infix_op = true,
905 sqlname = "-",
906 propagates_nulls = true
907)]
908fn sub_uint16(a: u16, b: u16) -> Result<u16, EvalError> {
909 a.checked_sub(b)
910 .ok_or_else(|| EvalError::UInt16OutOfRange(format!("{a} - {b}").into()))
911}
912
913#[sqlfunc(
914 is_monotone = "(true, true)",
915 is_infix_op = true,
916 sqlname = "-",
917 propagates_nulls = true
918)]
919fn sub_uint32(a: u32, b: u32) -> Result<u32, EvalError> {
920 a.checked_sub(b)
921 .ok_or_else(|| EvalError::UInt32OutOfRange(format!("{a} - {b}").into()))
922}
923
924#[sqlfunc(
925 is_monotone = "(true, true)",
926 is_infix_op = true,
927 sqlname = "-",
928 propagates_nulls = true
929)]
930fn sub_uint64(a: u64, b: u64) -> Result<u64, EvalError> {
931 a.checked_sub(b)
932 .ok_or_else(|| EvalError::UInt64OutOfRange(format!("{a} - {b}").into()))
933}
934
935#[sqlfunc(
936 is_monotone = "(true, true)",
937 is_infix_op = true,
938 sqlname = "-",
939 propagates_nulls = true
940)]
941fn sub_float32(a: f32, b: f32) -> Result<f32, EvalError> {
942 let difference = a - b;
943 if difference.is_infinite() && !a.is_infinite() && !b.is_infinite() {
944 Err(EvalError::FloatOverflow)
945 } else {
946 Ok(difference)
947 }
948}
949
950#[sqlfunc(
951 is_monotone = "(true, true)",
952 is_infix_op = true,
953 sqlname = "-",
954 propagates_nulls = true
955)]
956fn sub_float64(a: f64, b: f64) -> Result<f64, EvalError> {
957 let difference = a - b;
958 if difference.is_infinite() && !a.is_infinite() && !b.is_infinite() {
959 Err(EvalError::FloatOverflow)
960 } else {
961 Ok(difference)
962 }
963}
964
965#[sqlfunc(
966 is_monotone = "(true, true)",
967 is_infix_op = true,
968 sqlname = "-",
969 propagates_nulls = true
970)]
971fn sub_numeric(
972 a: OrderedDecimal<Numeric>,
973 b: OrderedDecimal<Numeric>,
974) -> Result<Numeric, EvalError> {
975 let mut cx = numeric::cx_datum();
976 let mut a = a.0;
977 cx.sub(&mut a, &b.0);
978 if cx.status().overflow() {
979 Err(EvalError::FloatOverflow)
980 } else {
981 Ok(a)
982 }
983}
984
985#[sqlfunc(sqlname = "age")]
1003fn age_timestamp(
1004 a: CheckedTimestamp<chrono::NaiveDateTime>,
1005 b: CheckedTimestamp<chrono::NaiveDateTime>,
1006) -> Result<Interval, EvalError> {
1007 Ok(a.age(&b)?)
1008}
1009
1010#[sqlfunc(sqlname = "age")]
1012fn age_timestamp_tz(
1013 a: CheckedTimestamp<chrono::DateTime<Utc>>,
1014 b: CheckedTimestamp<chrono::DateTime<Utc>>,
1015) -> Result<Interval, EvalError> {
1016 Ok(a.age(&b)?)
1017}
1018
1019#[sqlfunc(is_monotone = "(true, true)", is_infix_op = true, sqlname = "-")]
1020fn sub_timestamp(
1021 a: CheckedTimestamp<NaiveDateTime>,
1022 b: CheckedTimestamp<NaiveDateTime>,
1023) -> Result<Interval, EvalError> {
1024 Interval::from_chrono_duration(a - b)
1025 .map_err(|e| EvalError::IntervalOutOfRange(e.to_string().into()))
1026}
1027
1028#[sqlfunc(is_monotone = "(true, true)", is_infix_op = true, sqlname = "-")]
1029fn sub_timestamp_tz(
1030 a: CheckedTimestamp<chrono::DateTime<Utc>>,
1031 b: CheckedTimestamp<chrono::DateTime<Utc>>,
1032) -> Result<Interval, EvalError> {
1033 Interval::from_chrono_duration(a - b)
1034 .map_err(|e| EvalError::IntervalOutOfRange(e.to_string().into()))
1035}
1036
1037#[sqlfunc(
1038 is_monotone = "(true, true)",
1039 is_infix_op = true,
1040 sqlname = "-",
1041 propagates_nulls = true
1042)]
1043fn sub_date(a: Date, b: Date) -> i32 {
1044 a - b
1045}
1046
1047#[sqlfunc(is_monotone = "(true, true)", is_infix_op = true, sqlname = "-")]
1048fn sub_time(a: chrono::NaiveTime, b: chrono::NaiveTime) -> Result<Interval, EvalError> {
1049 Interval::from_chrono_duration(a - b)
1050 .map_err(|e| EvalError::IntervalOutOfRange(e.to_string().into()))
1051}
1052
1053#[sqlfunc(
1054 is_monotone = "(true, true)",
1055 output_type = "Interval",
1056 is_infix_op = true,
1057 sqlname = "-",
1058 propagates_nulls = true
1059)]
1060fn sub_interval(a: Interval, b: Interval) -> Result<Interval, EvalError> {
1061 b.checked_neg()
1062 .and_then(|b| b.checked_add(&a))
1063 .ok_or_else(|| EvalError::IntervalOutOfRange(format!("{a} - {b}").into()))
1064}
1065
1066#[sqlfunc(
1068 is_monotone = "(true, false)",
1069 is_infix_op = true,
1070 sqlname = "-",
1071 propagates_nulls = true
1072)]
1073fn sub_date_interval(
1074 date: Date,
1075 interval: Interval,
1076) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
1077 let dt = NaiveDate::from(date).and_hms_opt(0, 0, 0).unwrap();
1078 let dt = interval
1079 .months
1080 .checked_neg()
1081 .ok_or_else(|| EvalError::IntervalOutOfRange(interval.months.to_string().into()))
1082 .and_then(|months| add_timestamp_months(&dt, months))?;
1083 let dt = dt
1084 .checked_sub_signed(interval.duration_as_chrono())
1085 .ok_or(EvalError::TimestampOutOfRange)?;
1086 Ok(dt.try_into()?)
1087}
1088
1089#[sqlfunc(
1090 is_monotone = "(false, false)",
1091 is_infix_op = true,
1092 sqlname = "-",
1093 propagates_nulls = true
1094)]
1095fn sub_time_interval(time: chrono::NaiveTime, interval: Interval) -> chrono::NaiveTime {
1096 let (t, _) = time.overflowing_sub_signed(interval.duration_as_chrono());
1097 t
1098}
1099
1100#[sqlfunc(
1101 is_monotone = "(true, true)",
1102 is_infix_op = true,
1103 sqlname = "*",
1104 propagates_nulls = true
1105)]
1106fn mul_int16(a: i16, b: i16) -> Result<i16, EvalError> {
1107 a.checked_mul(b).ok_or(EvalError::NumericFieldOverflow)
1108}
1109
1110#[sqlfunc(
1111 is_monotone = "(true, true)",
1112 is_infix_op = true,
1113 sqlname = "*",
1114 propagates_nulls = true
1115)]
1116fn mul_int32(a: i32, b: i32) -> Result<i32, EvalError> {
1117 a.checked_mul(b).ok_or(EvalError::NumericFieldOverflow)
1118}
1119
1120#[sqlfunc(
1121 is_monotone = "(true, true)",
1122 is_infix_op = true,
1123 sqlname = "*",
1124 propagates_nulls = true
1125)]
1126fn mul_int64(a: i64, b: i64) -> Result<i64, EvalError> {
1127 a.checked_mul(b).ok_or(EvalError::NumericFieldOverflow)
1128}
1129
1130#[sqlfunc(
1131 is_monotone = "(true, true)",
1132 is_infix_op = true,
1133 sqlname = "*",
1134 propagates_nulls = true
1135)]
1136fn mul_uint16(a: u16, b: u16) -> Result<u16, EvalError> {
1137 a.checked_mul(b)
1138 .ok_or_else(|| EvalError::UInt16OutOfRange(format!("{a} * {b}").into()))
1139}
1140
1141#[sqlfunc(
1142 is_monotone = "(true, true)",
1143 is_infix_op = true,
1144 sqlname = "*",
1145 propagates_nulls = true
1146)]
1147fn mul_uint32(a: u32, b: u32) -> Result<u32, EvalError> {
1148 a.checked_mul(b)
1149 .ok_or_else(|| EvalError::UInt32OutOfRange(format!("{a} * {b}").into()))
1150}
1151
1152#[sqlfunc(
1153 is_monotone = "(true, true)",
1154 is_infix_op = true,
1155 sqlname = "*",
1156 propagates_nulls = true
1157)]
1158fn mul_uint64(a: u64, b: u64) -> Result<u64, EvalError> {
1159 a.checked_mul(b)
1160 .ok_or_else(|| EvalError::UInt64OutOfRange(format!("{a} * {b}").into()))
1161}
1162
1163#[sqlfunc(
1164 is_monotone = (true, true),
1165 is_infinity_monotone = false,
1166 is_infix_op = true,
1167 sqlname = "*",
1168 propagates_nulls = true
1169)]
1170fn mul_float32(a: f32, b: f32) -> Result<f32, EvalError> {
1171 let product = a * b;
1172 if product.is_infinite() && !a.is_infinite() && !b.is_infinite() {
1173 Err(EvalError::FloatOverflow)
1174 } else if product == 0.0f32 && a != 0.0f32 && b != 0.0f32 {
1175 Err(EvalError::FloatUnderflow)
1176 } else {
1177 Ok(product)
1178 }
1179}
1180
1181#[sqlfunc(
1182 is_monotone = "(true, true)",
1183 is_infinity_monotone = false,
1184 is_infix_op = true,
1185 sqlname = "*",
1186 propagates_nulls = true
1187)]
1188fn mul_float64(a: f64, b: f64) -> Result<f64, EvalError> {
1189 let product = a * b;
1190 if product.is_infinite() && !a.is_infinite() && !b.is_infinite() {
1191 Err(EvalError::FloatOverflow)
1192 } else if product == 0.0f64 && a != 0.0f64 && b != 0.0f64 {
1193 Err(EvalError::FloatUnderflow)
1194 } else {
1195 Ok(product)
1196 }
1197}
1198
1199#[sqlfunc(
1200 is_monotone = "(true, true)",
1201 is_infinity_monotone = false,
1202 is_infix_op = true,
1203 sqlname = "*",
1204 propagates_nulls = true
1205)]
1206fn mul_numeric(mut a: Numeric, b: Numeric) -> Result<Numeric, EvalError> {
1207 let mut cx = numeric::cx_datum();
1208 cx.mul(&mut a, &b);
1209 let cx_status = cx.status();
1210 if cx_status.overflow() {
1211 Err(EvalError::FloatOverflow)
1212 } else if cx_status.subnormal() {
1213 Err(EvalError::FloatUnderflow)
1214 } else {
1215 numeric::munge_numeric(&mut a).unwrap();
1216 Ok(a)
1217 }
1218}
1219
1220#[sqlfunc(
1221 is_monotone = "(false, false)",
1222 is_infix_op = true,
1223 sqlname = "*",
1224 propagates_nulls = true
1225)]
1226fn mul_interval(a: Interval, b: f64) -> Result<Interval, EvalError> {
1227 a.checked_mul(b)
1228 .ok_or_else(|| EvalError::IntervalOutOfRange(format!("{a} * {b}").into()))
1229}
1230
1231#[sqlfunc(
1232 is_monotone = "(true, false)",
1233 is_infix_op = true,
1234 sqlname = "/",
1235 propagates_nulls = true
1236)]
1237fn div_int16(a: i16, b: i16) -> Result<i16, EvalError> {
1238 if b == 0 {
1239 Err(EvalError::DivisionByZero)
1240 } else {
1241 a.checked_div(b)
1242 .ok_or_else(|| EvalError::Int16OutOfRange(format!("{a} / {b}").into()))
1243 }
1244}
1245
1246#[sqlfunc(
1247 is_monotone = "(true, false)",
1248 is_infix_op = true,
1249 sqlname = "/",
1250 propagates_nulls = true
1251)]
1252fn div_int32(a: i32, b: i32) -> Result<i32, EvalError> {
1253 if b == 0 {
1254 Err(EvalError::DivisionByZero)
1255 } else {
1256 a.checked_div(b)
1257 .ok_or_else(|| EvalError::Int32OutOfRange(format!("{a} / {b}").into()))
1258 }
1259}
1260
1261#[sqlfunc(
1262 is_monotone = "(true, false)",
1263 is_infix_op = true,
1264 sqlname = "/",
1265 propagates_nulls = true
1266)]
1267fn div_int64(a: i64, b: i64) -> Result<i64, EvalError> {
1268 if b == 0 {
1269 Err(EvalError::DivisionByZero)
1270 } else {
1271 a.checked_div(b)
1272 .ok_or_else(|| EvalError::Int64OutOfRange(format!("{a} / {b}").into()))
1273 }
1274}
1275
1276#[sqlfunc(
1277 is_monotone = "(true, false)",
1278 is_infix_op = true,
1279 sqlname = "/",
1280 propagates_nulls = true
1281)]
1282fn div_uint16(a: u16, b: u16) -> Result<u16, EvalError> {
1283 if b == 0 {
1284 Err(EvalError::DivisionByZero)
1285 } else {
1286 Ok(a / b)
1287 }
1288}
1289
1290#[sqlfunc(
1291 is_monotone = "(true, false)",
1292 is_infix_op = true,
1293 sqlname = "/",
1294 propagates_nulls = true
1295)]
1296fn div_uint32(a: u32, b: u32) -> Result<u32, EvalError> {
1297 if b == 0 {
1298 Err(EvalError::DivisionByZero)
1299 } else {
1300 Ok(a / b)
1301 }
1302}
1303
1304#[sqlfunc(
1305 is_monotone = "(true, false)",
1306 is_infix_op = true,
1307 sqlname = "/",
1308 propagates_nulls = true
1309)]
1310fn div_uint64(a: u64, b: u64) -> Result<u64, EvalError> {
1311 if b == 0 {
1312 Err(EvalError::DivisionByZero)
1313 } else {
1314 Ok(a / b)
1315 }
1316}
1317
1318#[sqlfunc(
1319 is_monotone = "(true, false)",
1320 is_infinity_monotone = false,
1321 is_infix_op = true,
1322 sqlname = "/",
1323 propagates_nulls = true
1324)]
1325fn div_float32(a: f32, b: f32) -> Result<f32, EvalError> {
1326 if b == 0.0f32 && !a.is_nan() {
1327 Err(EvalError::DivisionByZero)
1328 } else {
1329 let quotient = a / b;
1330 if quotient.is_infinite() && !a.is_infinite() {
1331 Err(EvalError::FloatOverflow)
1332 } else if quotient == 0.0f32 && a != 0.0f32 && !b.is_infinite() {
1333 Err(EvalError::FloatUnderflow)
1334 } else {
1335 Ok(quotient)
1336 }
1337 }
1338}
1339
1340#[sqlfunc(
1341 is_monotone = "(true, false)",
1342 is_infinity_monotone = false,
1343 is_infix_op = true,
1344 sqlname = "/",
1345 propagates_nulls = true
1346)]
1347fn div_float64(a: f64, b: f64) -> Result<f64, EvalError> {
1348 if b == 0.0f64 && !a.is_nan() {
1349 Err(EvalError::DivisionByZero)
1350 } else {
1351 let quotient = a / b;
1352 if quotient.is_infinite() && !a.is_infinite() {
1353 Err(EvalError::FloatOverflow)
1354 } else if quotient == 0.0f64 && a != 0.0f64 && !b.is_infinite() {
1355 Err(EvalError::FloatUnderflow)
1356 } else {
1357 Ok(quotient)
1358 }
1359 }
1360}
1361
1362#[sqlfunc(
1363 is_monotone = "(true, false)",
1364 is_infinity_monotone = false,
1365 is_infix_op = true,
1366 sqlname = "/",
1367 propagates_nulls = true
1368)]
1369fn div_numeric(mut a: Numeric, b: Numeric) -> Result<Numeric, EvalError> {
1370 let mut cx = numeric::cx_datum();
1371
1372 cx.div(&mut a, &b);
1373 let cx_status = cx.status();
1374
1375 if b.is_zero() {
1378 Err(EvalError::DivisionByZero)
1379 } else if cx_status.overflow() {
1380 Err(EvalError::FloatOverflow)
1381 } else if cx_status.subnormal() {
1382 Err(EvalError::FloatUnderflow)
1383 } else {
1384 numeric::munge_numeric(&mut a).unwrap();
1385 Ok(a)
1386 }
1387}
1388
1389#[sqlfunc(
1390 is_monotone = "(false, false)",
1391 is_infix_op = true,
1392 sqlname = "/",
1393 propagates_nulls = true
1394)]
1395fn div_interval(a: Interval, b: f64) -> Result<Interval, EvalError> {
1396 if b == 0.0 {
1397 Err(EvalError::DivisionByZero)
1398 } else {
1399 a.checked_div(b)
1400 .ok_or_else(|| EvalError::IntervalOutOfRange(format!("{a} / {b}").into()))
1401 }
1402}
1403
1404#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1405fn mod_int16(a: i16, b: i16) -> Result<i16, EvalError> {
1406 if b == 0 {
1407 Err(EvalError::DivisionByZero)
1408 } else {
1409 Ok(a.checked_rem(b).unwrap_or(0))
1410 }
1411}
1412
1413#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1414fn mod_int32(a: i32, b: i32) -> Result<i32, EvalError> {
1415 if b == 0 {
1416 Err(EvalError::DivisionByZero)
1417 } else {
1418 Ok(a.checked_rem(b).unwrap_or(0))
1419 }
1420}
1421
1422#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1423fn mod_int64(a: i64, b: i64) -> Result<i64, EvalError> {
1424 if b == 0 {
1425 Err(EvalError::DivisionByZero)
1426 } else {
1427 Ok(a.checked_rem(b).unwrap_or(0))
1428 }
1429}
1430
1431#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1432fn mod_uint16(a: u16, b: u16) -> Result<u16, EvalError> {
1433 if b == 0 {
1434 Err(EvalError::DivisionByZero)
1435 } else {
1436 Ok(a % b)
1437 }
1438}
1439
1440#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1441fn mod_uint32(a: u32, b: u32) -> Result<u32, EvalError> {
1442 if b == 0 {
1443 Err(EvalError::DivisionByZero)
1444 } else {
1445 Ok(a % b)
1446 }
1447}
1448
1449#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1450fn mod_uint64(a: u64, b: u64) -> Result<u64, EvalError> {
1451 if b == 0 {
1452 Err(EvalError::DivisionByZero)
1453 } else {
1454 Ok(a % b)
1455 }
1456}
1457
1458#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1459fn mod_float32(a: f32, b: f32) -> Result<f32, EvalError> {
1460 if b == 0.0 {
1461 Err(EvalError::DivisionByZero)
1462 } else {
1463 Ok(a % b)
1464 }
1465}
1466
1467#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1468fn mod_float64(a: f64, b: f64) -> Result<f64, EvalError> {
1469 if b == 0.0 {
1470 Err(EvalError::DivisionByZero)
1471 } else {
1472 Ok(a % b)
1473 }
1474}
1475
1476#[sqlfunc(is_infix_op = true, sqlname = "%", propagates_nulls = true)]
1477fn mod_numeric(mut a: Numeric, b: Numeric) -> Result<Numeric, EvalError> {
1478 if b.is_zero() {
1479 return Err(EvalError::DivisionByZero);
1480 }
1481 let mut cx = numeric::cx_datum();
1482 cx.rem(&mut a, &b);
1484 numeric::munge_numeric(&mut a).unwrap();
1485 Ok(a)
1486}
1487
1488fn neg_interval_inner(a: Interval) -> Result<Interval, EvalError> {
1489 a.checked_neg()
1490 .ok_or_else(|| EvalError::IntervalOutOfRange(a.to_string().into()))
1491}
1492
1493fn log_guard_numeric(val: &Numeric, function_name: &str) -> Result<(), EvalError> {
1494 if val.is_negative() {
1495 return Err(EvalError::NegativeOutOfDomain(function_name.into()));
1496 }
1497 if val.is_zero() {
1498 return Err(EvalError::ZeroOutOfDomain(function_name.into()));
1499 }
1500 Ok(())
1501}
1502
1503#[sqlfunc(sqlname = "log", propagates_nulls = true)]
1504fn log_base_numeric(mut a: Numeric, mut b: Numeric) -> Result<Numeric, EvalError> {
1505 log_guard_numeric(&a, "log")?;
1506 log_guard_numeric(&b, "log")?;
1507 let mut cx = numeric::cx_datum();
1508 cx.ln(&mut a);
1509 cx.ln(&mut b);
1510 cx.div(&mut b, &a);
1511 if a.is_zero() {
1512 Err(EvalError::DivisionByZero)
1513 } else {
1514 cx.set_precision(usize::from(numeric::NUMERIC_DATUM_MAX_PRECISION - 1))
1519 .expect("reducing precision below max always succeeds");
1520 let mut integral_check = b.clone();
1521
1522 cx.reduce(&mut integral_check);
1526
1527 let mut b = if integral_check.exponent() >= 0 {
1529 integral_check
1531 } else {
1532 b
1533 };
1534
1535 numeric::munge_numeric(&mut b).unwrap();
1536 Ok(b)
1537 }
1538}
1539
1540#[sqlfunc(propagates_nulls = true)]
1541fn power(a: f64, b: f64) -> Result<f64, EvalError> {
1542 if a == 0.0 && b.is_sign_negative() {
1543 return Err(EvalError::Undefined(
1544 "zero raised to a negative power".into(),
1545 ));
1546 }
1547 if a.is_sign_negative() && b.fract() != 0.0 {
1548 return Err(EvalError::ComplexOutOfRange("pow".into()));
1551 }
1552 let res = a.powf(b);
1553 if res.is_infinite() {
1554 return Err(EvalError::FloatOverflow);
1555 }
1556 if res == 0.0 && a != 0.0 {
1557 return Err(EvalError::FloatUnderflow);
1558 }
1559 Ok(res)
1560}
1561
1562#[sqlfunc(propagates_nulls = true)]
1563fn uuid_generate_v5(a: uuid::Uuid, b: &str) -> uuid::Uuid {
1564 uuid::Uuid::new_v5(&a, b.as_bytes())
1565}
1566
1567#[sqlfunc(output_type = "Numeric", propagates_nulls = true)]
1568fn power_numeric(mut a: Numeric, b: Numeric) -> Result<Numeric, EvalError> {
1569 if a.is_zero() {
1570 if b.is_zero() {
1571 return Ok(Numeric::from(1));
1572 }
1573 if b.is_negative() {
1574 return Err(EvalError::Undefined(
1575 "zero raised to a negative power".into(),
1576 ));
1577 }
1578 }
1579 if a.is_negative() && b.exponent() < 0 {
1580 return Err(EvalError::ComplexOutOfRange("pow".into()));
1583 }
1584 let mut cx = numeric::cx_datum();
1585 cx.pow(&mut a, &b);
1586 let cx_status = cx.status();
1587 if cx_status.overflow() || (cx_status.invalid_operation() && !b.is_negative()) {
1588 Err(EvalError::FloatOverflow)
1589 } else if cx_status.subnormal() || cx_status.invalid_operation() {
1590 Err(EvalError::FloatUnderflow)
1591 } else {
1592 numeric::munge_numeric(&mut a).unwrap();
1593 Ok(a)
1594 }
1595}
1596
1597#[sqlfunc(propagates_nulls = true)]
1598fn get_bit(bytes: &[u8], index: i32) -> Result<i32, EvalError> {
1599 let err = EvalError::IndexOutOfRange {
1600 provided: index,
1601 valid_end: i32::try_from(bytes.len().saturating_mul(8)).unwrap_or(i32::MAX) - 1,
1602 };
1603
1604 let index = usize::try_from(index).map_err(|_| err.clone())?;
1605
1606 let byte_index = index / 8;
1607 let bit_index = index % 8;
1608
1609 let i = bytes
1610 .get(byte_index)
1611 .map(|b| (*b >> bit_index) & 1)
1612 .ok_or(err)?;
1613 assert!(i == 0 || i == 1);
1614 Ok(i32::from(i))
1615}
1616
1617#[sqlfunc(propagates_nulls = true)]
1618fn get_byte(bytes: &[u8], index: i32) -> Result<i32, EvalError> {
1619 let err = EvalError::IndexOutOfRange {
1620 provided: index,
1621 valid_end: i32::try_from(bytes.len()).unwrap_or(i32::MAX) - 1,
1622 };
1623 let i: &u8 = bytes
1624 .get(usize::try_from(index).map_err(|_| err.clone())?)
1625 .ok_or(err)?;
1626 Ok(i32::from(*i))
1627}
1628
1629#[sqlfunc(sqlname = "constant_time_compare_bytes", propagates_nulls = true)]
1630pub fn constant_time_eq_bytes(a: &[u8], b: &[u8]) -> bool {
1631 verify_slices_are_equal(a, b).is_ok()
1632}
1633
1634#[sqlfunc(sqlname = "constant_time_compare_strings", propagates_nulls = true)]
1635pub fn constant_time_eq_string(a: &str, b: &str) -> bool {
1636 verify_slices_are_equal(a.as_bytes(), b.as_bytes()).is_ok()
1637}
1638
1639#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1640fn range_contains_i32<'a>(a: Range<Datum<'a>>, b: i32) -> bool {
1641 a.contains_elem(&b)
1642}
1643
1644#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1645fn range_contains_i64<'a>(a: Range<Datum<'a>>, elem: i64) -> bool {
1646 a.contains_elem(&elem)
1647}
1648
1649#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1650fn range_contains_date<'a>(a: Range<Datum<'a>>, elem: Date) -> bool {
1651 a.contains_elem(&elem)
1652}
1653
1654#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1655fn range_contains_numeric<'a>(a: Range<Datum<'a>>, elem: OrderedDecimal<Numeric>) -> bool {
1656 a.contains_elem(&elem)
1657}
1658
1659#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1660fn range_contains_timestamp<'a>(
1661 a: Range<Datum<'a>>,
1662 elem: CheckedTimestamp<NaiveDateTime>,
1663) -> bool {
1664 a.contains_elem(&elem)
1665}
1666
1667#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1668fn range_contains_timestamp_tz<'a>(
1669 a: Range<Datum<'a>>,
1670 elem: CheckedTimestamp<DateTime<Utc>>,
1671) -> bool {
1672 a.contains_elem(&elem)
1673}
1674
1675#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1676fn range_contains_i32_rev<'a>(a: Range<Datum<'a>>, b: i32) -> bool {
1677 a.contains_elem(&b)
1678}
1679
1680#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1681fn range_contains_i64_rev<'a>(a: Range<Datum<'a>>, elem: i64) -> bool {
1682 a.contains_elem(&elem)
1683}
1684
1685#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1686fn range_contains_date_rev<'a>(a: Range<Datum<'a>>, elem: Date) -> bool {
1687 a.contains_elem(&elem)
1688}
1689
1690#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1691fn range_contains_numeric_rev<'a>(a: Range<Datum<'a>>, elem: OrderedDecimal<Numeric>) -> bool {
1692 a.contains_elem(&elem)
1693}
1694
1695#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1696fn range_contains_timestamp_rev<'a>(
1697 a: Range<Datum<'a>>,
1698 elem: CheckedTimestamp<NaiveDateTime>,
1699) -> bool {
1700 a.contains_elem(&elem)
1701}
1702
1703#[sqlfunc(is_infix_op = true, sqlname = "<@", propagates_nulls = true)]
1704fn range_contains_timestamp_tz_rev<'a>(
1705 a: Range<Datum<'a>>,
1706 elem: CheckedTimestamp<DateTime<Utc>>,
1707) -> bool {
1708 a.contains_elem(&elem)
1709}
1710
1711macro_rules! range_fn {
1717 ($fn:expr, $range_fn:expr, $sqlname:expr) => {
1718 paste::paste! {
1719
1720 #[sqlfunc(
1721 output_type = "bool",
1722 is_infix_op = true,
1723 sqlname = $sqlname,
1724 propagates_nulls = true
1725 )]
1726 fn [< range_ $fn >]<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a>
1727 {
1728 if a.is_null() || b.is_null() { return Datum::Null }
1729 let l = a.unwrap_range();
1730 let r = b.unwrap_range();
1731 Datum::from(Range::<Datum<'a>>::$range_fn(&l, &r))
1732 }
1733 }
1734 };
1735}
1736
1737range_fn!(contains_range, contains_range, "@>");
1740range_fn!(contains_range_rev, contains_range, "<@");
1741range_fn!(overlaps, overlaps, "&&");
1742range_fn!(after, after, ">>");
1743range_fn!(before, before, "<<");
1744range_fn!(overleft, overleft, "&<");
1745range_fn!(overright, overright, "&>");
1746range_fn!(adjacent, adjacent, "-|-");
1747
1748#[sqlfunc(is_infix_op = true, sqlname = "+")]
1749fn range_union<T: Copy + Ord>(l: Range<T>, r: Range<T>) -> Result<Range<T>, EvalError> {
1750 Ok(l.union(&r)?)
1751}
1752
1753#[sqlfunc(is_infix_op = true, sqlname = "*")]
1754fn range_intersection<T: Copy + Ord>(l: Range<T>, r: Range<T>) -> Range<T> {
1755 l.intersection(&r)
1756}
1757
1758#[sqlfunc(
1759 output_type_expr = "input_types[0].scalar_type.without_modifiers().nullable(true)",
1760 is_infix_op = true,
1761 sqlname = "-",
1762 propagates_nulls = true
1763)]
1764fn range_difference<'a>(
1765 l: Range<Datum<'a>>,
1766 r: Range<Datum<'a>>,
1767) -> Result<Range<Datum<'a>>, EvalError> {
1768 Ok(l.difference(&r)?)
1769}
1770
1771#[sqlfunc(is_infix_op = true, sqlname = "=", negate = NotEq)]
1772fn eq<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1773 a == b
1777}
1778
1779#[sqlfunc(is_infix_op = true, sqlname = "!=", negate = Eq)]
1780fn not_eq<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1781 a != b
1782}
1783
1784#[sqlfunc(
1785 is_monotone = "(true, true)",
1786 is_infix_op = true,
1787 sqlname = "<",
1788 negate = Gte
1789)]
1790fn lt<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1791 a < b
1792}
1793
1794#[sqlfunc(
1795 is_monotone = "(true, true)",
1796 is_infix_op = true,
1797 sqlname = "<=",
1798 negate = Gt
1799)]
1800fn lte<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1801 a <= b
1802}
1803
1804#[sqlfunc(
1805 is_monotone = "(true, true)",
1806 is_infix_op = true,
1807 sqlname = ">",
1808 negate = Lte
1809)]
1810fn gt<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1811 a > b
1812}
1813
1814#[sqlfunc(
1815 is_monotone = "(true, true)",
1816 is_infix_op = true,
1817 sqlname = ">=",
1818 negate = Lt
1819)]
1820fn gte<'a>(a: ExcludeNull<Datum<'a>>, b: ExcludeNull<Datum<'a>>) -> bool {
1821 a >= b
1822}
1823
1824#[sqlfunc(sqlname = "tocharts", propagates_nulls = true)]
1825fn to_char_timestamp_format(ts: CheckedTimestamp<chrono::NaiveDateTime>, format: &str) -> String {
1826 let fmt = DateTimeFormat::compile(format);
1827 fmt.render(&*ts)
1828}
1829
1830#[sqlfunc(sqlname = "tochartstz", propagates_nulls = true)]
1831fn to_char_timestamp_tz_format(
1832 ts: CheckedTimestamp<chrono::DateTime<Utc>>,
1833 format: &str,
1834) -> String {
1835 let fmt = DateTimeFormat::compile(format);
1836 fmt.render(&*ts)
1837}
1838
1839#[sqlfunc(sqlname = "->", is_infix_op = true)]
1840fn jsonb_get_int64<'a>(a: JsonbRef<'a>, i: i64) -> Option<JsonbRef<'a>> {
1841 match a.into_datum() {
1842 Datum::List(list) => {
1843 let i = if i >= 0 {
1844 usize::cast_from(i.unsigned_abs())
1845 } else {
1846 let i = usize::cast_from(i.unsigned_abs());
1848 (list.iter().count()).wrapping_sub(i)
1849 };
1850 let v = list.iter().nth(i)?;
1851 JsonbRef::try_from_result(Ok::<_, ()>(v)).ok()
1855 }
1856 Datum::Map(_) => None,
1857 _ => {
1858 (i == 0 || i == -1).then_some(a)
1860 }
1861 }
1862}
1863
1864#[sqlfunc(sqlname = "->>", is_infix_op = true)]
1865fn jsonb_get_int64_stringify<'a>(
1866 a: JsonbRef<'a>,
1867 i: i64,
1868 temp_storage: &'a RowArena,
1869) -> Option<&'a str> {
1870 let json = jsonb_get_int64(a, i)?;
1871 jsonb_stringify(json.into_datum(), temp_storage)
1872}
1873
1874#[sqlfunc(sqlname = "->", is_infix_op = true)]
1875fn jsonb_get_string<'a>(a: JsonbRef<'a>, k: &str) -> Option<JsonbRef<'a>> {
1876 let dict = DatumMap::try_from_result(Ok::<_, ()>(a.into_datum())).ok()?;
1877 let v = dict.iter().find(|(k2, _v)| k == *k2).map(|(_k, v)| v)?;
1878 JsonbRef::try_from_result(Ok::<_, ()>(v)).ok()
1879}
1880
1881#[sqlfunc(sqlname = "->>", is_infix_op = true)]
1882fn jsonb_get_string_stringify<'a>(
1883 a: JsonbRef<'a>,
1884 k: &str,
1885 temp_storage: &'a RowArena,
1886) -> Option<&'a str> {
1887 let v = jsonb_get_string(a, k)?;
1888 jsonb_stringify(v.into_datum(), temp_storage)
1889}
1890
1891#[sqlfunc(sqlname = "#>", is_infix_op = true)]
1892fn jsonb_get_path<'a>(mut json: JsonbRef<'a>, b: Array<'a>) -> Option<JsonbRef<'a>> {
1893 let path = b.elements();
1894 for key in path.iter() {
1895 let key = match key {
1896 Datum::String(s) => s,
1897 Datum::Null => return None,
1898 _ => unreachable!("keys in jsonb_get_path known to be strings"),
1899 };
1900 let v = match json.into_datum() {
1901 Datum::Map(map) => map.iter().find(|(k, _)| key == *k).map(|(_k, v)| v),
1902 Datum::List(list) => {
1903 let i = strconv::parse_int64(key).ok()?;
1904 let i = if i >= 0 {
1905 usize::cast_from(i.unsigned_abs())
1906 } else {
1907 let i = usize::cast_from(i.unsigned_abs());
1909 (list.iter().count()).wrapping_sub(i)
1910 };
1911 list.iter().nth(i)
1912 }
1913 _ => return None,
1914 }?;
1915 json = JsonbRef::try_from_result(Ok::<_, ()>(v)).ok()?;
1916 }
1917 Some(json)
1918}
1919
1920#[sqlfunc(sqlname = "#>>", is_infix_op = true)]
1921fn jsonb_get_path_stringify<'a>(
1922 a: JsonbRef<'a>,
1923 b: Array<'a>,
1924 temp_storage: &'a RowArena,
1925) -> Option<&'a str> {
1926 let json = jsonb_get_path(a, b)?;
1927 jsonb_stringify(json.into_datum(), temp_storage)
1928}
1929
1930#[sqlfunc(is_infix_op = true, sqlname = "?")]
1931fn jsonb_contains_string<'a>(a: JsonbRef<'a>, k: &str) -> bool {
1932 match a.into_datum() {
1937 Datum::List(list) => list.iter().any(|k2| Datum::from(k) == k2),
1938 Datum::Map(dict) => dict.iter().any(|(k2, _v)| k == k2),
1939 Datum::String(string) => string == k,
1940 _ => false,
1941 }
1942}
1943
1944#[sqlfunc(is_infix_op = true, sqlname = "?", propagates_nulls = true)]
1945fn map_contains_key<'a>(map: DatumMap<'a>, k: &str) -> bool {
1947 map.iter().any(|(k2, _v)| k == k2)
1948}
1949
1950#[sqlfunc(is_infix_op = true, sqlname = "?&")]
1951fn map_contains_all_keys<'a>(map: DatumMap<'a>, keys: Array<'a>) -> bool {
1952 keys.elements()
1953 .iter()
1954 .all(|key| !key.is_null() && map.iter().any(|(k, _v)| k == key.unwrap_str()))
1955}
1956
1957#[sqlfunc(is_infix_op = true, sqlname = "?|", propagates_nulls = true)]
1958fn map_contains_any_keys<'a>(map: DatumMap<'a>, keys: Array<'a>) -> bool {
1959 keys.elements()
1960 .iter()
1961 .any(|key| !key.is_null() && map.iter().any(|(k, _v)| k == key.unwrap_str()))
1962}
1963
1964#[sqlfunc(is_infix_op = true, sqlname = "@>", propagates_nulls = true)]
1965fn map_contains_map<'a>(map_a: DatumMap<'a>, b: DatumMap<'a>) -> bool {
1966 b.iter().all(|(b_key, b_val)| {
1967 map_a
1968 .iter()
1969 .any(|(a_key, a_val)| (a_key == b_key) && (a_val == b_val))
1970 })
1971}
1972
1973#[sqlfunc(is_infix_op = true, sqlname = "->", propagates_nulls = true)]
1974fn map_get_value<'a, T: FromDatum<'a>>(a: DatumMap<'a, T>, target_key: &str) -> Option<T> {
1975 a.typed_iter()
1976 .find(|(key, _v)| target_key == *key)
1977 .map(|(_k, v)| v)
1978}
1979
1980#[sqlfunc(is_infix_op = true, sqlname = "@>")]
1981fn list_contains_list<'a>(a: ExcludeNull<DatumList<'a>>, b: ExcludeNull<DatumList<'a>>) -> bool {
1982 if b.iter().contains(&Datum::Null) {
1984 false
1985 } else {
1986 b.iter()
1987 .all(|item_b| a.iter().any(|item_a| item_a == item_b))
1988 }
1989}
1990
1991#[sqlfunc(is_infix_op = true, sqlname = "<@")]
1992fn list_contains_list_rev<'a>(
1993 a: ExcludeNull<DatumList<'a>>,
1994 b: ExcludeNull<DatumList<'a>>,
1995) -> bool {
1996 list_contains_list(b, a)
1997}
1998
1999#[sqlfunc(is_infix_op = true, sqlname = "@>")]
2001fn jsonb_contains_jsonb<'a>(a: JsonbRef<'a>, b: JsonbRef<'a>) -> bool {
2002 fn contains(a: Datum, b: Datum, at_top_level: bool) -> bool {
2004 match (a, b) {
2005 (Datum::JsonNull, Datum::JsonNull) => true,
2006 (Datum::False, Datum::False) => true,
2007 (Datum::True, Datum::True) => true,
2008 (Datum::Numeric(a), Datum::Numeric(b)) => a == b,
2009 (Datum::String(a), Datum::String(b)) => a == b,
2010 (Datum::List(a), Datum::List(b)) => b
2011 .iter()
2012 .all(|b_elem| a.iter().any(|a_elem| contains(a_elem, b_elem, false))),
2013 (Datum::Map(a), Datum::Map(b)) => b.iter().all(|(b_key, b_val)| {
2014 a.iter()
2015 .any(|(a_key, a_val)| (a_key == b_key) && contains(a_val, b_val, false))
2016 }),
2017
2018 (Datum::List(a), b) => {
2020 at_top_level && a.iter().any(|a_elem| contains(a_elem, b, false))
2021 }
2022
2023 _ => false,
2024 }
2025 }
2026 contains(a.into_datum(), b.into_datum(), true)
2027}
2028
2029#[sqlfunc(is_infix_op = true, sqlname = "||")]
2030fn jsonb_concat<'a>(
2031 a: JsonbRef<'a>,
2032 b: JsonbRef<'a>,
2033 temp_storage: &'a RowArena,
2034) -> Option<JsonbRef<'a>> {
2035 let res = match (a.into_datum(), b.into_datum()) {
2036 (Datum::Map(dict_a), Datum::Map(dict_b)) => {
2037 let mut pairs = dict_b.iter().chain(dict_a.iter()).collect::<Vec<_>>();
2038 pairs.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2));
2040 pairs.dedup_by(|(k1, _v1), (k2, _v2)| k1 == k2);
2041 temp_storage.make_datum(|packer| packer.push_dict(pairs))
2042 }
2043 (Datum::List(list_a), Datum::List(list_b)) => {
2044 let elems = list_a.iter().chain(list_b.iter());
2045 temp_storage.make_datum(|packer| packer.push_list(elems))
2046 }
2047 (Datum::List(list_a), b) => {
2048 let elems = list_a.iter().chain(Some(b));
2049 temp_storage.make_datum(|packer| packer.push_list(elems))
2050 }
2051 (a, Datum::List(list_b)) => {
2052 let elems = Some(a).into_iter().chain(list_b.iter());
2053 temp_storage.make_datum(|packer| packer.push_list(elems))
2054 }
2055 _ => return None,
2056 };
2057 Some(JsonbRef::from_datum(res))
2058}
2059
2060#[sqlfunc(is_infix_op = true, sqlname = "-", propagates_nulls = true)]
2061fn jsonb_delete_int64<'a>(
2062 a: Datum<'a>,
2063 i: i64,
2064 temp_storage: &'a RowArena,
2065) -> Option<JsonbRef<'a>> {
2066 match a {
2067 Datum::List(list) => {
2068 let i = if i >= 0 {
2069 usize::cast_from(i.unsigned_abs())
2070 } else {
2071 let i = usize::cast_from(i.unsigned_abs());
2073 (list.iter().count()).wrapping_sub(i)
2074 };
2075 let elems = list
2076 .iter()
2077 .enumerate()
2078 .filter(|(i2, _e)| i != *i2)
2079 .map(|(_, e)| e);
2080 let datum = temp_storage.make_datum(|packer| packer.push_list(elems));
2081 Some(JsonbRef::from_datum(datum))
2082 }
2083 _ => None,
2084 }
2085}
2086
2087#[sqlfunc(is_infix_op = true, sqlname = "-", propagates_nulls = true)]
2088fn jsonb_delete_string<'a>(
2089 a: Datum<'a>,
2090 k: &str,
2091 temp_storage: &'a RowArena,
2092) -> Option<JsonbRef<'a>> {
2093 let datum = match a {
2094 Datum::List(list) => {
2095 let elems = list.iter().filter(|e| Datum::from(k) != *e);
2096 temp_storage.make_datum(|packer| packer.push_list(elems))
2097 }
2098 Datum::Map(dict) => {
2099 let pairs = dict.iter().filter(|(k2, _v)| k != *k2);
2100 temp_storage.make_datum(|packer| packer.push_dict(pairs))
2101 }
2102 _ => return None,
2103 };
2104 Some(JsonbRef::from_datum(datum))
2105}
2106
2107#[sqlfunc(sqlname = "extractiv", propagates_nulls = true)]
2108fn date_part_interval_numeric(units: &str, b: Interval) -> Result<Numeric, EvalError> {
2109 match units.parse() {
2110 Ok(units) => Ok(date_part_interval_inner::<Numeric>(units, b)?),
2111 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2112 }
2113}
2114
2115#[sqlfunc(sqlname = "date_partiv", propagates_nulls = true)]
2116fn date_part_interval_f64(units: &str, b: Interval) -> Result<f64, EvalError> {
2117 match units.parse() {
2118 Ok(units) => Ok(date_part_interval_inner::<f64>(units, b)?),
2119 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2120 }
2121}
2122
2123#[sqlfunc(sqlname = "extractt", propagates_nulls = true)]
2124fn date_part_time_numeric(units: &str, b: chrono::NaiveTime) -> Result<Numeric, EvalError> {
2125 match units.parse() {
2126 Ok(units) => Ok(date_part_time_inner::<Numeric>(units, b)?),
2127 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2128 }
2129}
2130
2131#[sqlfunc(sqlname = "date_partt", propagates_nulls = true)]
2132fn date_part_time_f64(units: &str, b: chrono::NaiveTime) -> Result<f64, EvalError> {
2133 match units.parse() {
2134 Ok(units) => Ok(date_part_time_inner::<f64>(units, b)?),
2135 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2136 }
2137}
2138
2139#[sqlfunc(sqlname = "extractts", propagates_nulls = true)]
2140fn date_part_timestamp_timestamp_numeric(
2141 units: &str,
2142 ts: CheckedTimestamp<NaiveDateTime>,
2143) -> Result<Numeric, EvalError> {
2144 match units.parse() {
2145 Ok(units) => Ok(date_part_timestamp_inner::<_, Numeric>(units, &*ts)?),
2146 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2147 }
2148}
2149
2150#[sqlfunc(sqlname = "extracttstz", propagates_nulls = true)]
2151fn date_part_timestamp_timestamp_tz_numeric(
2152 units: &str,
2153 ts: CheckedTimestamp<DateTime<Utc>>,
2154) -> Result<Numeric, EvalError> {
2155 match units.parse() {
2156 Ok(units) => Ok(date_part_timestamp_inner::<_, Numeric>(units, &*ts)?),
2157 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2158 }
2159}
2160
2161#[sqlfunc(sqlname = "date_partts", propagates_nulls = true)]
2162fn date_part_timestamp_timestamp_f64(
2163 units: &str,
2164 ts: CheckedTimestamp<NaiveDateTime>,
2165) -> Result<f64, EvalError> {
2166 match units.parse() {
2167 Ok(units) => date_part_timestamp_inner(units, &*ts),
2168 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2169 }
2170}
2171
2172#[sqlfunc(sqlname = "date_parttstz", propagates_nulls = true)]
2173fn date_part_timestamp_timestamp_tz_f64(
2174 units: &str,
2175 ts: CheckedTimestamp<DateTime<Utc>>,
2176) -> Result<f64, EvalError> {
2177 match units.parse() {
2178 Ok(units) => date_part_timestamp_inner(units, &*ts),
2179 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2180 }
2181}
2182
2183#[sqlfunc(sqlname = "extractd", propagates_nulls = true)]
2184fn extract_date_units(units: &str, b: Date) -> Result<Numeric, EvalError> {
2185 match units.parse() {
2186 Ok(units) => Ok(extract_date_inner(units, b.into())?),
2187 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2188 }
2189}
2190
2191pub fn date_bin<T>(
2192 stride: Interval,
2193 source: CheckedTimestamp<T>,
2194 origin: CheckedTimestamp<T>,
2195) -> Result<CheckedTimestamp<T>, EvalError>
2196where
2197 T: TimestampLike,
2198{
2199 if stride.months != 0 {
2200 return Err(EvalError::DateBinOutOfRange(
2201 "timestamps cannot be binned into intervals containing months or years".into(),
2202 ));
2203 }
2204
2205 let stride_ns = match stride.duration_as_chrono().num_nanoseconds() {
2206 Some(ns) if ns <= 0 => Err(EvalError::DateBinOutOfRange(
2207 "stride must be greater than zero".into(),
2208 )),
2209 Some(ns) => Ok(ns),
2210 None => Err(EvalError::DateBinOutOfRange(
2211 format!("stride cannot exceed {}/{} nanoseconds", i64::MAX, i64::MIN,).into(),
2212 )),
2213 }?;
2214
2215 let sub_stride = origin > source;
2219
2220 let tm_diff = (source - origin.clone()).num_nanoseconds().ok_or_else(|| {
2221 EvalError::DateBinOutOfRange(
2222 "source and origin must not differ more than 2^63 nanoseconds".into(),
2223 )
2224 })?;
2225
2226 let remainder = tm_diff % stride_ns;
2227 let mut tm_delta = tm_diff - remainder;
2228
2229 if sub_stride && remainder != 0 {
2230 tm_delta = tm_delta.checked_sub(stride_ns).ok_or_else(|| {
2231 EvalError::DateBinOutOfRange(
2232 "source and origin must not differ more than 2^63 nanoseconds".into(),
2233 )
2234 })?;
2235 }
2236
2237 let res = origin
2238 .checked_add_signed(Duration::nanoseconds(tm_delta))
2239 .ok_or(EvalError::TimestampOutOfRange)?;
2240 Ok(CheckedTimestamp::from_timestamplike(res)?)
2241}
2242
2243#[sqlfunc(is_monotone = "(false, true)", sqlname = "bin_unix_epoch_timestamp")]
2249fn date_bin_timestamp(
2250 stride: Interval,
2251 source: CheckedTimestamp<NaiveDateTime>,
2252) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
2253 let origin =
2254 CheckedTimestamp::from_timestamplike(DateTime::from_timestamp(0, 0).unwrap().naive_utc())
2255 .expect("must fit");
2256 date_bin(stride, source, origin)
2257}
2258
2259#[sqlfunc(is_monotone = "(false, true)", sqlname = "bin_unix_epoch_timestamptz")]
2261fn date_bin_timestamp_tz(
2262 stride: Interval,
2263 source: CheckedTimestamp<DateTime<Utc>>,
2264) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
2265 let origin = CheckedTimestamp::from_timestamplike(DateTime::from_timestamp(0, 0).unwrap())
2266 .expect("must fit");
2267 date_bin(stride, source, origin)
2268}
2269
2270#[sqlfunc(sqlname = "date_truncts", propagates_nulls = true)]
2271fn date_trunc_units_timestamp(
2272 units: &str,
2273 ts: CheckedTimestamp<NaiveDateTime>,
2274) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
2275 match units.parse() {
2276 Ok(units) => Ok(date_trunc_inner(units, &*ts)?.try_into()?),
2277 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2278 }
2279}
2280
2281#[sqlfunc(sqlname = "date_trunctstz", propagates_nulls = true)]
2282fn date_trunc_units_timestamp_tz(
2283 units: &str,
2284 ts: CheckedTimestamp<DateTime<Utc>>,
2285) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
2286 match units.parse() {
2287 Ok(units) => Ok(date_trunc_inner(units, &*ts)?.try_into()?),
2288 Err(_) => Err(EvalError::UnknownUnits(units.into())),
2289 }
2290}
2291
2292#[sqlfunc(sqlname = "date_trunciv", propagates_nulls = true)]
2293fn date_trunc_interval(units: &str, mut interval: Interval) -> Result<Interval, EvalError> {
2294 let dtf = units
2295 .parse()
2296 .map_err(|_| EvalError::UnknownUnits(units.into()))?;
2297
2298 interval
2299 .truncate_low_fields(dtf, Some(0), RoundBehavior::Truncate)
2300 .expect(
2301 "truncate_low_fields should not fail with max_precision 0 and RoundBehavior::Truncate",
2302 );
2303 Ok(interval)
2304}
2305
2306pub(crate) fn parse_timezone(tz: &str, spec: TimezoneSpec) -> Result<Timezone, EvalError> {
2311 Timezone::parse(tz, spec).map_err(|_| EvalError::InvalidTimezone(tz.into()))
2312}
2313
2314#[sqlfunc(sqlname = "timezoneit")]
2318fn timezone_interval_time_binary(
2319 interval: Interval,
2320 time: chrono::NaiveTime,
2321) -> Result<chrono::NaiveTime, EvalError> {
2322 if interval.months != 0 {
2323 Err(EvalError::InvalidTimezoneInterval)
2324 } else {
2325 Ok(time.overflowing_add_signed(interval.duration_as_chrono()).0)
2326 }
2327}
2328
2329#[sqlfunc(sqlname = "timezoneits")]
2333fn timezone_interval_timestamp_binary(
2334 interval: Interval,
2335 ts: CheckedTimestamp<NaiveDateTime>,
2336) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
2337 if interval.months != 0 {
2338 Err(EvalError::InvalidTimezoneInterval)
2339 } else {
2340 match ts.checked_sub_signed(interval.duration_as_chrono()) {
2341 Some(sub) => Ok(DateTime::from_naive_utc_and_offset(sub, Utc).try_into()?),
2342 None => Err(EvalError::TimestampOutOfRange),
2343 }
2344 }
2345}
2346
2347#[sqlfunc(sqlname = "timezoneitstz")]
2351fn timezone_interval_timestamp_tz_binary(
2352 interval: Interval,
2353 tstz: CheckedTimestamp<DateTime<Utc>>,
2354) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
2355 if interval.months != 0 {
2356 return Err(EvalError::InvalidTimezoneInterval);
2357 }
2358 match tstz
2359 .naive_utc()
2360 .checked_add_signed(interval.duration_as_chrono())
2361 {
2362 Some(dt) => Ok(dt.try_into()?),
2363 None => Err(EvalError::TimestampOutOfRange),
2364 }
2365}
2366
2367#[sqlfunc(
2368 output_type_expr = r#"SqlScalarType::Record {
2369 fields: [
2370 ("abbrev".into(), SqlScalarType::String.nullable(false)),
2371 ("base_utc_offset".into(), SqlScalarType::Interval.nullable(false)),
2372 ("dst_offset".into(), SqlScalarType::Interval.nullable(false)),
2373 ].into(),
2374 custom_id: None,
2375 }.nullable(true)"#,
2376 propagates_nulls = true
2377)]
2378fn timezone_offset<'a>(
2379 tz_str: &str,
2380 b: CheckedTimestamp<chrono::DateTime<Utc>>,
2381 temp_storage: &'a RowArena,
2382) -> Result<DatumList<'a>, EvalError> {
2383 let tz = match Tz::from_str_insensitive(tz_str) {
2384 Ok(tz) => tz,
2385 Err(_) => return Err(EvalError::InvalidIanaTimezoneId(tz_str.into())),
2386 };
2387 let offset = tz.offset_from_utc_datetime(&b.naive_utc());
2388 let abbrev = match offset.abbreviation() {
2392 Some(abbrev) => abbrev.to_string(),
2393 None => {
2394 const SECONDS_PER_MINUTE: i64 = 60;
2395 const MINUTES_PER_HOUR: i64 = 60;
2396 let secs = (offset.base_utc_offset() + offset.dst_offset()).num_seconds();
2397 let sign = if secs < 0 { '-' } else { '+' };
2398 let (mins, s) = (
2399 secs.abs() / SECONDS_PER_MINUTE,
2400 secs.abs() % SECONDS_PER_MINUTE,
2401 );
2402 let (h, m) = (mins / MINUTES_PER_HOUR, mins % MINUTES_PER_HOUR);
2403 if s != 0 {
2404 format!("{sign}{h:02}{m:02}{s:02}")
2411 } else if m != 0 {
2412 format!("{sign}{h:02}{m:02}")
2414 } else {
2415 format!("{sign}{h:02}")
2417 }
2418 }
2419 };
2420 Ok(temp_storage
2421 .make_datum(|packer| {
2422 packer.push_list_with(|packer| {
2423 packer.push(Datum::from(abbrev.as_str()));
2424 packer.push(Datum::from(offset.base_utc_offset()));
2425 packer.push(Datum::from(offset.dst_offset()));
2426 });
2427 })
2428 .unwrap_list())
2429}
2430
2431#[sqlfunc(
2434 sqlname = "mz_aclitem_contains_privilege",
2435 output_type = "bool",
2436 propagates_nulls = true
2437)]
2438fn mz_acl_item_contains_privilege(
2439 mz_acl_item: MzAclItem,
2440 privileges: &str,
2441) -> Result<bool, EvalError> {
2442 let acl_mode = AclMode::parse_multiple_privileges(privileges)
2443 .map_err(|e: anyhow::Error| EvalError::InvalidPrivileges(e.to_string().into()))?;
2444 let contains = !mz_acl_item.acl_mode.intersection(acl_mode).is_empty();
2445 Ok(contains)
2446}
2447
2448#[sqlfunc]
2449fn parse_ident<'a>(ident: &'a str, strict: bool) -> Result<ArrayRustType<Cow<'a, str>>, EvalError> {
2451 fn is_ident_start(c: char) -> bool {
2452 matches!(c, 'A'..='Z' | 'a'..='z' | '_' | '\u{80}'..=char::MAX)
2453 }
2454
2455 fn is_ident_cont(c: char) -> bool {
2456 matches!(c, '0'..='9' | '$') || is_ident_start(c)
2457 }
2458
2459 let mut elems = vec![];
2460 let buf = &mut LexBuf::new(ident);
2461
2462 let mut after_dot = false;
2463
2464 buf.take_while(|ch| ch.is_ascii_whitespace());
2465
2466 loop {
2467 let mut missing_ident = true;
2468
2469 let c = buf.next();
2470
2471 if c == Some('"') {
2472 let s = buf.take_while(|ch| !matches!(ch, '"'));
2473
2474 if buf.next() != Some('"') {
2475 return Err(EvalError::InvalidIdentifier {
2476 ident: ident.into(),
2477 detail: Some("String has unclosed double quotes.".into()),
2478 });
2479 }
2480 elems.push(Cow::Borrowed(s));
2481 missing_ident = false;
2482 } else if c.map(is_ident_start).unwrap_or(false) {
2483 buf.prev();
2484 let s = buf.take_while(is_ident_cont);
2485 elems.push(Cow::Owned(s.to_ascii_lowercase()));
2486 missing_ident = false;
2487 }
2488
2489 if missing_ident {
2490 if c == Some('.') {
2491 return Err(EvalError::InvalidIdentifier {
2492 ident: ident.into(),
2493 detail: Some("No valid identifier before \".\".".into()),
2494 });
2495 } else if after_dot {
2496 return Err(EvalError::InvalidIdentifier {
2497 ident: ident.into(),
2498 detail: Some("No valid identifier after \".\".".into()),
2499 });
2500 } else {
2501 return Err(EvalError::InvalidIdentifier {
2502 ident: ident.into(),
2503 detail: None,
2504 });
2505 }
2506 }
2507
2508 buf.take_while(|ch| ch.is_ascii_whitespace());
2509
2510 match buf.next() {
2511 Some('.') => {
2512 after_dot = true;
2513
2514 buf.take_while(|ch| ch.is_ascii_whitespace());
2515 }
2516 Some(_) if strict => {
2517 return Err(EvalError::InvalidIdentifier {
2518 ident: ident.into(),
2519 detail: None,
2520 });
2521 }
2522 _ => break,
2523 }
2524 }
2525
2526 Ok(elems.into())
2527}
2528
2529fn regexp_split_to_array_re<'a>(
2530 text: &str,
2531 regexp: &Regex,
2532 temp_storage: &'a RowArena,
2533) -> Result<Array<'a>, EvalError> {
2534 check_build_fits_budget(
2537 || mz_regexp::regexp_split_to_array_count(text, regexp),
2538 std::mem::size_of::<&str>(),
2539 temp_storage,
2540 )?;
2541 let found = mz_regexp::regexp_split_to_array(text, regexp);
2542 check_datums_fit_budget(found.iter().copied().map(Datum::String), temp_storage)?;
2545 let mut row = Row::default();
2546 let mut packer = row.packer();
2547 packer.try_push_array(
2548 &[ArrayDimension {
2549 lower_bound: 1,
2550 length: found.len(),
2551 }],
2552 found.into_iter().map(Datum::String),
2553 )?;
2554 Ok(temp_storage.push_unary_row(row).unwrap_array())
2555}
2556
2557#[sqlfunc(propagates_nulls = true)]
2559fn pretty_sql<'a>(sql: &str, width: i32, temp_storage: &'a RowArena) -> Result<&'a str, EvalError> {
2560 let width =
2561 usize::try_from(width).map_err(|_| EvalError::PrettyError("invalid width".into()))?;
2562 let pretty = pretty_str(
2563 sql,
2564 PrettyConfig {
2565 width,
2566 format_mode: FormatMode::Simple,
2567 },
2568 )
2569 .map_err(|e| EvalError::PrettyError(e.to_string().into()))?;
2570 let pretty = temp_storage.push_string(pretty);
2571 Ok(pretty)
2572}
2573
2574#[sqlfunc]
2576fn redact_sql(sql: &str) -> Result<String, EvalError> {
2577 let stmts = mz_sql_parser::parser::parse_statements(sql)
2578 .map_err(|e| EvalError::RedactError(e.to_string().into()))?;
2579 match stmts.len() {
2580 1 => Ok(stmts[0].ast.to_ast_string_redacted()),
2581 n => Err(EvalError::RedactError(
2582 format!("expected a single statement, found {n}").into(),
2583 )),
2584 }
2585}
2586
2587#[sqlfunc(propagates_nulls = true)]
2588fn starts_with(a: &str, b: &str) -> bool {
2589 a.starts_with(b)
2590}
2591
2592#[sqlfunc(
2593 sqlname = "||",
2594 is_infix_op = true,
2595 propagates_nulls = true,
2596 is_monotone = (false, true),
2603)]
2604fn text_concat_binary(a: &str, b: &str, temp_storage: &RowArena) -> Result<String, EvalError> {
2605 if a.len() + b.len() > max_string_func_result_bytes(temp_storage) {
2606 return Err(EvalError::LengthTooLarge);
2607 }
2608 let mut buf = String::with_capacity(a.len() + b.len());
2609 buf.push_str(a);
2610 buf.push_str(b);
2611 Ok(buf)
2612}
2613
2614#[sqlfunc(propagates_nulls = true)]
2615fn like_escape<'a>(
2616 pattern: &str,
2617 b: &str,
2618 temp_storage: &'a RowArena,
2619) -> Result<&'a str, EvalError> {
2620 let escape = like_pattern::EscapeBehavior::from_str(b)?;
2621 let normalized = like_pattern::normalize_pattern(pattern, escape)?;
2622 Ok(temp_storage.push_string(normalized))
2623}
2624
2625#[sqlfunc(is_infix_op = true, sqlname = "like")]
2626fn is_like_match_case_sensitive(haystack: &str, pattern: &str) -> Result<bool, EvalError> {
2627 like_pattern::compile(pattern, false).map(|needle| needle.is_match(haystack))
2628}
2629
2630#[sqlfunc(is_infix_op = true, sqlname = "ilike")]
2631fn is_like_match_case_insensitive(haystack: &str, pattern: &str) -> Result<bool, EvalError> {
2632 like_pattern::compile(pattern, true).map(|needle| needle.is_match(haystack))
2633}
2634
2635#[sqlfunc(is_infix_op = true, sqlname = "~")]
2636fn is_regexp_match_case_sensitive(haystack: &str, needle: &str) -> Result<bool, EvalError> {
2637 let regex = build_regex(needle, "")?;
2638 Ok(regex.is_match(haystack))
2639}
2640
2641#[sqlfunc(is_infix_op = true, sqlname = "~*")]
2642fn is_regexp_match_case_insensitive(haystack: &str, needle: &str) -> Result<bool, EvalError> {
2643 let regex = build_regex(needle, "i")?;
2644 Ok(regex.is_match(haystack))
2645}
2646
2647fn regexp_match_static<'a>(
2648 haystack: &'a str,
2649 temp_storage: &'a RowArena,
2650 needle: ®ex::Regex,
2651) -> Result<Option<Array<'a>>, EvalError> {
2652 let mut row = Row::default();
2653 let mut packer = row.packer();
2654 if needle.captures_len() > 1 {
2655 match needle.captures(haystack) {
2660 None => packer.push(Datum::Null),
2661 Some(captures) => packer.try_push_array(
2662 &[ArrayDimension {
2663 lower_bound: 1,
2664 length: captures.len() - 1,
2665 }],
2666 captures.iter().skip(1).map(|mtch| match mtch {
2668 None => Datum::Null,
2669 Some(mtch) => Datum::String(mtch.as_str()),
2670 }),
2671 )?,
2672 }
2673 } else {
2674 match needle.find(haystack) {
2677 None => packer.push(Datum::Null),
2678 Some(mtch) => packer.try_push_array(
2679 &[ArrayDimension {
2680 lower_bound: 1,
2681 length: 1,
2682 }],
2683 iter::once(Datum::String(mtch.as_str())),
2684 )?,
2685 };
2686 };
2687 let datum = temp_storage.push_unary_row(row);
2688 Ok((!datum.is_null()).then(|| datum.unwrap_array()))
2689}
2690
2691pub(crate) fn regexp_replace_parse_flags(flags: &str) -> (usize, Cow<'_, str>) {
2694 let (limit, flags) = if flags.contains('g') {
2697 let flags = flags.replace('g', "");
2698 (0, Cow::Owned(flags))
2699 } else {
2700 (1, Cow::Borrowed(flags))
2701 };
2702 (limit, flags)
2703}
2704
2705pub fn build_regex(needle: &str, flags: &str) -> Result<Regex, EvalError> {
2706 let mut case_insensitive = false;
2707 for f in flags.chars() {
2709 match f {
2710 'i' => {
2711 case_insensitive = true;
2712 }
2713 'c' => {
2714 case_insensitive = false;
2715 }
2716 _ => return Err(EvalError::InvalidRegexFlag(f)),
2717 }
2718 }
2719 Ok(Regex::new(needle, case_insensitive)?)
2720}
2721
2722#[sqlfunc(sqlname = "repeat")]
2723fn repeat_string(string: &str, count: i32, temp_storage: &RowArena) -> Result<String, EvalError> {
2724 let len = usize::try_from(count).unwrap_or(0);
2725 if len.saturating_mul(string.len()) > max_string_func_result_bytes(temp_storage) {
2726 return Err(EvalError::LengthTooLarge);
2727 }
2728 Ok(string.repeat(len))
2729}
2730
2731fn array_create_scalar<'a>(
2738 datums: &[Datum<'a>],
2739 temp_storage: &'a RowArena,
2740) -> Result<Array<'a>, EvalError> {
2741 let mut dims = &[ArrayDimension {
2742 lower_bound: 1,
2743 length: datums.len(),
2744 }][..];
2745 if datums.is_empty() {
2746 dims = &[];
2750 }
2751 check_datums_fit_budget(datums.iter().copied(), temp_storage)?;
2752 let datum = temp_storage.try_make_datum(|packer| packer.try_push_array(dims, datums))?;
2753 Ok(datum.unwrap_array())
2754}
2755
2756fn stringify_datum<'a, B>(
2757 buf: &mut B,
2758 d: Datum<'a>,
2759 ty: &SqlScalarType,
2760) -> Result<strconv::Nestable, EvalError>
2761where
2762 B: FormatBuffer,
2763{
2764 use SqlScalarType::*;
2765 match &ty {
2766 AclItem => Ok(strconv::format_acl_item(buf, d.unwrap_acl_item())),
2767 Bool => Ok(strconv::format_bool(buf, d.unwrap_bool())),
2768 Int16 => Ok(strconv::format_int16(buf, d.unwrap_int16())),
2769 Int32 => Ok(strconv::format_int32(buf, d.unwrap_int32())),
2770 Int64 => Ok(strconv::format_int64(buf, d.unwrap_int64())),
2771 UInt16 => Ok(strconv::format_uint16(buf, d.unwrap_uint16())),
2772 UInt32 | Oid | RegClass | RegProc | RegType => {
2773 Ok(strconv::format_uint32(buf, d.unwrap_uint32()))
2774 }
2775 UInt64 => Ok(strconv::format_uint64(buf, d.unwrap_uint64())),
2776 Float32 => Ok(strconv::format_float32(buf, d.unwrap_float32())),
2777 Float64 => Ok(strconv::format_float64(buf, d.unwrap_float64())),
2778 Numeric { .. } => Ok(strconv::format_numeric(buf, &d.unwrap_numeric())),
2779 Date => Ok(strconv::format_date(buf, d.unwrap_date())),
2780 Time => Ok(strconv::format_time(buf, d.unwrap_time())),
2781 Timestamp { .. } => Ok(strconv::format_timestamp(buf, &d.unwrap_timestamp())),
2782 TimestampTz { .. } => Ok(strconv::format_timestamptz(buf, &d.unwrap_timestamptz())),
2783 Interval => Ok(strconv::format_interval(buf, d.unwrap_interval())),
2784 Bytes => Ok(strconv::format_bytes(buf, d.unwrap_bytes())),
2785 String | VarChar { .. } | PgLegacyName => Ok(strconv::format_string(buf, d.unwrap_str())),
2786 Char { length } => Ok(strconv::format_string(
2787 buf,
2788 &mz_repr::adt::char::format_str_pad(d.unwrap_str(), *length),
2789 )),
2790 PgLegacyChar => {
2791 format_pg_legacy_char(buf, d.unwrap_uint8())?;
2792 Ok(strconv::Nestable::MayNeedEscaping)
2793 }
2794 Jsonb => Ok(strconv::format_jsonb(buf, JsonbRef::from_datum(d))),
2795 Uuid => Ok(strconv::format_uuid(buf, d.unwrap_uuid())),
2796 Record { fields, .. } => {
2797 let mut fields = fields.iter();
2798 strconv::format_record(buf, d.unwrap_list(), |buf, d| {
2799 let (_name, ty) = fields.next().unwrap();
2800 if d.is_null() {
2801 Ok(buf.write_null())
2802 } else {
2803 stringify_datum(buf.nonnull_buffer(), d, &ty.scalar_type)
2804 }
2805 })
2806 }
2807 Array(elem_type) => strconv::format_array(
2808 buf,
2809 &d.unwrap_array().dims().into_iter().collect::<Vec<_>>(),
2810 d.unwrap_array().elements(),
2811 |buf, d| {
2812 if d.is_null() {
2813 Ok(buf.write_null())
2814 } else {
2815 stringify_datum(buf.nonnull_buffer(), d, elem_type)
2816 }
2817 },
2818 ),
2819 List { element_type, .. } => strconv::format_list(buf, d.unwrap_list(), |buf, d| {
2820 if d.is_null() {
2821 Ok(buf.write_null())
2822 } else {
2823 stringify_datum(buf.nonnull_buffer(), d, element_type)
2824 }
2825 }),
2826 Map { value_type, .. } => strconv::format_map(buf, &d.unwrap_map(), |buf, d| {
2827 if d.is_null() {
2828 Ok(buf.write_null())
2829 } else {
2830 stringify_datum(buf.nonnull_buffer(), d, value_type)
2831 }
2832 }),
2833 Int2Vector => strconv::format_legacy_vector(buf, d.unwrap_array().elements(), |buf, d| {
2834 stringify_datum(buf.nonnull_buffer(), d, &SqlScalarType::Int16)
2835 }),
2836 MzTimestamp { .. } => Ok(strconv::format_mz_timestamp(buf, d.unwrap_mz_timestamp())),
2837 Range { element_type } => strconv::format_range(buf, &d.unwrap_range(), |buf, d| match d {
2838 Some(d) => stringify_datum(buf.nonnull_buffer(), *d, element_type),
2839 None => Ok::<_, EvalError>(buf.write_null()),
2840 }),
2841 MzAclItem => Ok(strconv::format_mz_acl_item(buf, d.unwrap_mz_acl_item())),
2842 }
2843}
2844
2845#[sqlfunc]
2846fn position(substring: &str, string: &str) -> Result<i32, EvalError> {
2847 let char_index = string.find(substring);
2848
2849 if let Some(char_index) = char_index {
2850 let string_prefix = &string[0..char_index];
2852
2853 let num_prefix_chars = string_prefix.chars().count();
2854 let num_prefix_chars = i32::try_from(num_prefix_chars)
2855 .map_err(|_| EvalError::Int32OutOfRange(num_prefix_chars.to_string().into()))?;
2856
2857 Ok(num_prefix_chars + 1)
2858 } else {
2859 Ok(0)
2860 }
2861}
2862
2863#[sqlfunc]
2864fn strpos(string: &str, substring: &str) -> Result<i32, EvalError> {
2865 position(substring, string)
2866}
2867
2868#[sqlfunc(
2869 propagates_nulls = true,
2870 is_monotone = (false, false)
2873)]
2874fn left<'a>(string: &'a str, b: i32) -> Result<&'a str, EvalError> {
2875 let n = i64::from(b);
2876
2877 let mut byte_indices = string.char_indices().map(|(i, _)| i);
2878
2879 let end_in_bytes = match n.cmp(&0) {
2880 Ordering::Equal => 0,
2881 Ordering::Greater => {
2882 let n = usize::try_from(n).map_err(|_| {
2883 EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n).into())
2884 })?;
2885 byte_indices.nth(n).unwrap_or(string.len())
2887 }
2888 Ordering::Less => {
2889 let n = usize::try_from(n.abs() - 1).map_err(|_| {
2890 EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n).into())
2891 })?;
2892 byte_indices.rev().nth(n).unwrap_or(0)
2893 }
2894 };
2895
2896 Ok(&string[..end_in_bytes])
2897}
2898
2899#[sqlfunc(propagates_nulls = true)]
2900fn right<'a>(string: &'a str, n: i32) -> Result<&'a str, EvalError> {
2901 let mut byte_indices = string.char_indices().map(|(i, _)| i);
2902
2903 let start_in_bytes = if n == 0 {
2904 string.len()
2905 } else if n > 0 {
2906 let n = usize::try_from(n - 1).map_err(|_| {
2907 EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n).into())
2908 })?;
2909 byte_indices.rev().nth(n).unwrap_or(0)
2911 } else if n == i32::MIN {
2912 0
2914 } else {
2915 let n = n.abs();
2916 let n = usize::try_from(n).map_err(|_| {
2917 EvalError::InvalidParameterValue(format!("invalid parameter n: {:?}", n).into())
2918 })?;
2919 byte_indices.nth(n).unwrap_or(string.len())
2920 };
2921
2922 Ok(&string[start_in_bytes..])
2923}
2924
2925#[sqlfunc(sqlname = "btrim", propagates_nulls = true)]
2926fn trim<'a>(a: &'a str, trim_chars: &str) -> &'a str {
2927 a.trim_matches(|c| trim_chars.contains(c))
2928}
2929
2930#[sqlfunc(sqlname = "ltrim", propagates_nulls = true)]
2931fn trim_leading<'a>(a: &'a str, trim_chars: &str) -> &'a str {
2932 a.trim_start_matches(|c| trim_chars.contains(c))
2933}
2934
2935#[sqlfunc(sqlname = "rtrim", propagates_nulls = true)]
2936fn trim_trailing<'a>(a: &'a str, trim_chars: &str) -> &'a str {
2937 a.trim_end_matches(|c| trim_chars.contains(c))
2938}
2939
2940#[sqlfunc(sqlname = "array_length", propagates_nulls = true)]
2941fn array_length<'a>(a: Array<'a>, b: i64) -> Result<Option<i32>, EvalError> {
2942 let i = match usize::try_from(b) {
2943 Ok(0) | Err(_) => return Ok(None),
2944 Ok(n) => n - 1,
2945 };
2946 Ok(match a.dims().into_iter().nth(i) {
2947 None => None,
2948 Some(dim) => Some(
2949 dim.length
2950 .try_into()
2951 .map_err(|_| EvalError::Int32OutOfRange(dim.length.to_string().into()))?,
2952 ),
2953 })
2954}
2955
2956#[sqlfunc(is_infix_op = true)]
2957#[allow(clippy::as_conversions)]
2959fn array_lower<'a>(a: Array<'a>, i: i64) -> Result<Option<i32>, EvalError> {
2960 if i < 1 {
2961 return Ok(None);
2962 }
2963 a.dims()
2964 .into_iter()
2965 .nth(i as usize - 1)
2966 .map(|dim| {
2967 let (lower, _upper) = dim.dimension_bounds();
2968 lower
2969 .try_into()
2970 .map_err(|_| EvalError::Int32OutOfRange(lower.to_string().into()))
2971 })
2972 .transpose()
2973}
2974
2975#[sqlfunc(
2976 output_type_expr = "input_types[0].scalar_type.without_modifiers().nullable(true)",
2977 sqlname = "array_remove",
2978 propagates_nulls = false
2979)]
2980fn array_remove<'a>(
2981 arr: Array<'a>,
2982 b: Datum<'a>,
2983 temp_storage: &'a RowArena,
2984) -> Result<Array<'a>, EvalError> {
2985 if arr.dims().len() == 0 {
2987 return Ok(arr);
2988 }
2989
2990 if arr.dims().len() > 1 {
2992 return Err(EvalError::MultidimensionalArrayRemovalNotSupported);
2993 }
2994
2995 let mut dims = arr.dims().into_iter().collect::<Vec<_>>();
2996 check_build_fits_budget(
2999 || dims[0].length,
3000 std::mem::size_of::<Datum<'a>>(),
3001 temp_storage,
3002 )?;
3003
3004 let elems: Vec<_> = arr.elements().iter().filter(|v| v != &b).collect();
3005 dims[0] = ArrayDimension {
3007 lower_bound: 1,
3008 length: elems.len(),
3009 };
3010
3011 let datum = temp_storage.try_make_datum(|packer| packer.try_push_array(&dims, elems))?;
3012 Ok(datum.unwrap_array())
3013}
3014
3015#[sqlfunc(is_infix_op = true)]
3016#[allow(clippy::as_conversions)]
3018fn array_upper<'a>(a: Array<'a>, i: i64) -> Result<Option<i32>, EvalError> {
3019 if i < 1 {
3020 return Ok(None);
3021 }
3022 a.dims()
3023 .into_iter()
3024 .nth(i as usize - 1)
3025 .map(|dim| {
3026 let (_lower, upper) = dim.dimension_bounds();
3027 upper
3028 .try_into()
3029 .map_err(|_| EvalError::Int32OutOfRange(upper.to_string().into()))
3030 })
3031 .transpose()
3032}
3033
3034#[sqlfunc(
3035 is_infix_op = true,
3036 sqlname = "array_contains",
3037 propagates_nulls = true
3038)]
3039fn array_contains<'a>(a: Datum<'a>, array: Array<'a>) -> bool {
3040 array.elements().iter().any(|e| e == a)
3041}
3042
3043#[sqlfunc(is_infix_op = true, sqlname = "@>")]
3044fn array_contains_array<'a>(a: Array<'a>, b: Array<'a>) -> bool {
3045 let a = a.elements();
3046 let b = b.elements();
3047
3048 if b.iter().contains(&Datum::Null) {
3050 false
3051 } else {
3052 b.iter()
3053 .all(|item_b| a.iter().any(|item_a| item_a == item_b))
3054 }
3055}
3056
3057#[sqlfunc(is_infix_op = true, sqlname = "<@")]
3058fn array_contains_array_rev<'a>(a: Array<'a>, b: Array<'a>) -> bool {
3059 array_contains_array(b, a)
3060}
3061
3062#[sqlfunc(
3063 output_type_expr = "input_types[0].scalar_type.without_modifiers().nullable(true)",
3064 is_infix_op = true,
3065 sqlname = "||",
3066 propagates_nulls = false,
3067 introduces_nulls = false
3068)]
3069fn array_array_concat<'a>(
3070 a: Option<Array<'a>>,
3071 b: Option<Array<'a>>,
3072 temp_storage: &'a RowArena,
3073) -> Result<Option<Array<'a>>, EvalError> {
3074 let Some(a_array) = a else {
3075 return Ok(b);
3076 };
3077 let Some(b_array) = b else {
3078 return Ok(a);
3079 };
3080
3081 let a_dims: Vec<ArrayDimension> = a_array.dims().into_iter().collect();
3082 let b_dims: Vec<ArrayDimension> = b_array.dims().into_iter().collect();
3083
3084 let a_ndims = a_dims.len();
3085 let b_ndims = b_dims.len();
3086
3087 if a_ndims == 0 {
3090 return Ok(b);
3091 } else if b_ndims == 0 {
3092 return Ok(a);
3093 }
3094
3095 #[allow(clippy::as_conversions)]
3106 if (a_ndims as isize - b_ndims as isize).abs() > 1 {
3107 return Err(EvalError::IncompatibleArrayDimensions {
3108 dims: Some((a_ndims, b_ndims)),
3109 });
3110 }
3111
3112 let mut dims;
3113
3114 match a_ndims.cmp(&b_ndims) {
3119 Ordering::Equal => {
3123 if &a_dims[1..] != &b_dims[1..] {
3124 return Err(EvalError::IncompatibleArrayDimensions { dims: None });
3125 }
3126 dims = vec![ArrayDimension {
3127 lower_bound: a_dims[0].lower_bound,
3128 length: a_dims[0].length + b_dims[0].length,
3129 }];
3130 dims.extend(&a_dims[1..]);
3131 }
3132 Ordering::Less => {
3136 if &a_dims[..] != &b_dims[1..] {
3137 return Err(EvalError::IncompatibleArrayDimensions { dims: None });
3138 }
3139 dims = vec![ArrayDimension {
3140 lower_bound: b_dims[0].lower_bound,
3141 length: b_dims[0].length + 1,
3145 }];
3146 dims.extend(a_dims);
3147 }
3148 Ordering::Greater => {
3152 if &a_dims[1..] != &b_dims[..] {
3153 return Err(EvalError::IncompatibleArrayDimensions { dims: None });
3154 }
3155 dims = vec![ArrayDimension {
3156 lower_bound: a_dims[0].lower_bound,
3157 length: a_dims[0].length + 1,
3161 }];
3162 dims.extend(b_dims);
3163 }
3164 }
3165
3166 let elems = a_array.elements().iter().chain(b_array.elements().iter());
3167
3168 let datum = temp_storage.try_make_datum(|packer| packer.try_push_array(&dims, elems))?;
3169 Ok(Some(datum.unwrap_array()))
3170}
3171
3172#[sqlfunc(
3173 is_infix_op = true,
3174 sqlname = "||",
3175 propagates_nulls = false,
3176 introduces_nulls = false
3177)]
3178fn list_list_concat<'a, T: FromDatum<'a>>(
3179 a: Option<DatumList<'a, T>>,
3180 b: Option<DatumList<'a, T>>,
3181 temp_storage: &'a RowArena,
3182) -> Option<DatumList<'a, T>> {
3183 let Some(a) = a else {
3184 return b;
3185 };
3186 let Some(b) = b else {
3187 return Some(a);
3188 };
3189
3190 Some(temp_storage.make_datum_list(a.typed_iter().chain(b.typed_iter())))
3191}
3192
3193#[sqlfunc(is_infix_op = true, sqlname = "||", propagates_nulls = false)]
3194fn list_element_concat<'a, T: FromDatum<'a>>(
3195 a: Option<DatumList<'a, T>>,
3196 b: T,
3197 temp_storage: &'a RowArena,
3198) -> DatumList<'a, T> {
3199 let a_elems = a.into_iter().flat_map(|a| a.typed_iter());
3200 temp_storage.make_datum_list(a_elems.chain(std::iter::once(b)))
3201}
3202
3203#[sqlfunc(is_infix_op = true, sqlname = "||", propagates_nulls = false)]
3205fn element_list_concat<'a, T: FromDatum<'a>>(
3206 a: T,
3207 b: Option<DatumList<'a, T>>,
3208 temp_storage: &'a RowArena,
3209) -> DatumList<'a, T> {
3210 let b_elems = b.into_iter().flat_map(|b| b.typed_iter());
3211 temp_storage.make_datum_list(std::iter::once(a).chain(b_elems))
3212}
3213
3214#[sqlfunc(sqlname = "list_remove")]
3215fn list_remove<'a, T: FromDatum<'a>>(
3216 a: DatumList<'a, T>,
3217 b: T,
3218 temp_storage: &'a RowArena,
3219) -> DatumList<'a, T> {
3220 temp_storage.make_datum_list(a.typed_iter().filter(|elem| *elem != b))
3221}
3222
3223#[sqlfunc(sqlname = "digest")]
3224fn digest_string(to_digest: &str, digest_fn: &str) -> Result<Vec<u8>, EvalError> {
3225 digest_inner(to_digest.as_bytes(), digest_fn)
3226}
3227
3228#[sqlfunc(sqlname = "digest")]
3229fn digest_bytes(to_digest: &[u8], digest_fn: &str) -> Result<Vec<u8>, EvalError> {
3230 digest_inner(to_digest, digest_fn)
3231}
3232
3233fn digest_inner(bytes: &[u8], digest_fn: &str) -> Result<Vec<u8>, EvalError> {
3234 match digest_fn {
3235 "md5" => Ok(Md5::digest(bytes).to_vec()),
3236 "sha1" => Ok(digest::digest(&digest::SHA1_FOR_LEGACY_USE_ONLY, bytes)
3237 .as_ref()
3238 .to_vec()),
3239 "sha224" => Ok(digest::digest(&digest::SHA224, bytes).as_ref().to_vec()),
3240 "sha256" => Ok(digest::digest(&digest::SHA256, bytes).as_ref().to_vec()),
3241 "sha384" => Ok(digest::digest(&digest::SHA384, bytes).as_ref().to_vec()),
3242 "sha512" => Ok(digest::digest(&digest::SHA512, bytes).as_ref().to_vec()),
3243 other => Err(EvalError::InvalidHashAlgorithm(other.into())),
3244 }
3245}
3246
3247#[sqlfunc]
3248fn mz_render_typmod(oid: u32, typmod: i32) -> String {
3249 match Type::from_oid_and_typmod(oid, typmod) {
3250 Ok(typ) => typ.constraint().display_or("").to_string(),
3251 Err(_) if typmod >= 0 => format!("({typmod})"),
3254 Err(_) => "".into(),
3255 }
3256}
3257
3258#[cfg(test)]
3259mod test {
3260 use mz_repr::PropDatum;
3261 use proptest::prelude::*;
3262
3263 use super::*;
3264 use crate::{Eval, MirScalarExpr};
3265
3266 #[mz_ore::test]
3267 fn variant_names_unique() {
3268 fn assert_unique(enum_name: &str, names: impl Iterator<Item = &'static str>) {
3272 let mut seen = std::collections::BTreeSet::new();
3273 for name in names {
3274 assert!(seen.insert(name), "duplicate {enum_name} name: {name}");
3275 }
3276 }
3277 assert_unique("UnaryFunc", UnaryFunc::variant_names());
3278 assert_unique("BinaryFunc", BinaryFunc::variant_names());
3279 assert_unique("VariadicFunc", VariadicFunc::variant_names());
3280 }
3281
3282 #[mz_ore::test]
3283 fn add_interval_months() {
3284 let dt = ym(2000, 1);
3285
3286 assert_eq!(add_timestamp_months(&*dt, 0).unwrap(), dt);
3287 assert_eq!(add_timestamp_months(&*dt, 1).unwrap(), ym(2000, 2));
3288 assert_eq!(add_timestamp_months(&*dt, 12).unwrap(), ym(2001, 1));
3289 assert_eq!(add_timestamp_months(&*dt, 13).unwrap(), ym(2001, 2));
3290 assert_eq!(add_timestamp_months(&*dt, 24).unwrap(), ym(2002, 1));
3291 assert_eq!(add_timestamp_months(&*dt, 30).unwrap(), ym(2002, 7));
3292
3293 assert_eq!(add_timestamp_months(&*dt, -1).unwrap(), ym(1999, 12));
3295 assert_eq!(add_timestamp_months(&*dt, -12).unwrap(), ym(1999, 1));
3296 assert_eq!(add_timestamp_months(&*dt, -13).unwrap(), ym(1998, 12));
3297 assert_eq!(add_timestamp_months(&*dt, -24).unwrap(), ym(1998, 1));
3298 assert_eq!(add_timestamp_months(&*dt, -30).unwrap(), ym(1997, 7));
3299
3300 let dt = ym(1999, 12);
3302 assert_eq!(add_timestamp_months(&*dt, 1).unwrap(), ym(2000, 1));
3303 let end_of_month_dt = NaiveDate::from_ymd_opt(1999, 12, 31)
3304 .unwrap()
3305 .and_hms_opt(9, 9, 9)
3306 .unwrap();
3307 assert_eq!(
3308 add_timestamp_months(&end_of_month_dt, 2).unwrap(),
3310 NaiveDate::from_ymd_opt(2000, 2, 29)
3311 .unwrap()
3312 .and_hms_opt(9, 9, 9)
3313 .unwrap()
3314 .try_into()
3315 .unwrap(),
3316 );
3317 assert_eq!(
3318 add_timestamp_months(&end_of_month_dt, 14).unwrap(),
3320 NaiveDate::from_ymd_opt(2001, 2, 28)
3321 .unwrap()
3322 .and_hms_opt(9, 9, 9)
3323 .unwrap()
3324 .try_into()
3325 .unwrap(),
3326 );
3327 }
3328
3329 fn ym(year: i32, month: u32) -> CheckedTimestamp<NaiveDateTime> {
3330 NaiveDate::from_ymd_opt(year, month, 1)
3331 .unwrap()
3332 .and_hms_opt(9, 9, 9)
3333 .unwrap()
3334 .try_into()
3335 .unwrap()
3336 }
3337
3338 #[mz_ore::test]
3339 fn array_lower_upper_respect_lower_bound() {
3340 use mz_repr::adt::array::ArrayDimension;
3341 use mz_repr::{Datum, RowArena};
3342
3343 let arena = RowArena::new();
3344
3345 let bounds = |lower_bound: isize, length: usize| {
3348 let dims = [ArrayDimension {
3349 lower_bound,
3350 length,
3351 }];
3352 let elems = vec![Datum::Int32(0); length];
3353 let datum = arena.make_datum(|packer| packer.try_push_array(&dims, elems).unwrap());
3354 let arr = match datum {
3355 Datum::Array(arr) => arr,
3356 other => panic!("expected array, got {other:?}"),
3357 };
3358 (array_lower(arr, 1).unwrap(), array_upper(arr, 1).unwrap())
3359 };
3360
3361 assert_eq!(bounds(1, 3), (Some(1), Some(3)));
3363 assert_eq!(bounds(5, 3), (Some(5), Some(7)));
3365 assert_eq!(bounds(-3, 3), (Some(-3), Some(-1)));
3367
3368 let dims = [ArrayDimension {
3370 lower_bound: 5,
3371 length: 3,
3372 }];
3373 let elems = vec![Datum::Int32(0); 3];
3374 let datum = arena.make_datum(|packer| packer.try_push_array(&dims, elems).unwrap());
3375 let arr = match datum {
3376 Datum::Array(arr) => arr,
3377 other => panic!("expected array, got {other:?}"),
3378 };
3379 assert_eq!(array_lower(arr, 0).unwrap(), None);
3380 assert_eq!(array_upper(arr, 0).unwrap(), None);
3381 assert_eq!(array_lower(arr, 2).unwrap(), None);
3382 assert_eq!(array_upper(arr, 2).unwrap(), None);
3383 }
3384
3385 #[mz_ore::test]
3386 #[cfg_attr(miri, ignore)] fn test_is_monotone() {
3388 use proptest::prelude::*;
3389
3390 fn assert_monotone<'a, const N: usize>(
3393 expr: &MirScalarExpr,
3394 arena: &'a RowArena,
3395 datums: &[[Datum<'a>; N]],
3396 ) {
3397 let Ok(results) = datums
3399 .iter()
3400 .map(|args| expr.eval(args.as_slice(), arena))
3401 .collect::<Result<Vec<_>, _>>()
3402 else {
3403 return;
3404 };
3405
3406 let forward = results.iter().tuple_windows().all(|(a, b)| a <= b);
3407 let reverse = results.iter().tuple_windows().all(|(a, b)| a >= b);
3408 assert!(
3409 forward || reverse,
3410 "expected {expr} to be monotone, but passing {datums:?} returned {results:?}"
3411 );
3412 }
3413
3414 fn proptest_binary<'a>(
3415 func: BinaryFunc,
3416 arena: &'a RowArena,
3417 left: impl Strategy<Value = PropDatum>,
3418 right: impl Strategy<Value = PropDatum>,
3419 ) {
3420 let (left_monotone, right_monotone) = func.is_monotone();
3421 let expr = MirScalarExpr::CallBinary {
3422 func,
3423 expr1: Box::new(MirScalarExpr::column(0)),
3424 expr2: Box::new(MirScalarExpr::column(1)),
3425 };
3426 proptest!(|(
3427 mut left in proptest::array::uniform3(left),
3428 mut right in proptest::array::uniform3(right),
3429 )| {
3430 left.sort();
3431 right.sort();
3432 if left_monotone {
3433 for r in &right {
3434 let args: Vec<[_; 2]> = left
3435 .iter()
3436 .map(|l| [Datum::from(l), Datum::from(r)])
3437 .collect();
3438 assert_monotone(&expr, arena, &args);
3439 }
3440 }
3441 if right_monotone {
3442 for l in &left {
3443 let args: Vec<[_; 2]> = right
3444 .iter()
3445 .map(|r| [Datum::from(l), Datum::from(r)])
3446 .collect();
3447 assert_monotone(&expr, arena, &args);
3448 }
3449 }
3450 });
3451 }
3452
3453 let interesting_strs: Vec<_> = SqlScalarType::String.interesting_datums().collect();
3454 let str_datums = proptest::strategy::Union::new([
3455 proptest::string::string_regex("[A-Z]{0,10}")
3456 .expect("valid regex")
3457 .prop_map(|s| PropDatum::String(s.to_string()))
3458 .boxed(),
3459 (0..interesting_strs.len())
3460 .prop_map(move |i| {
3461 let Datum::String(val) = interesting_strs[i] else {
3462 unreachable!("interesting strings has non-strings")
3463 };
3464 PropDatum::String(val.to_string())
3465 })
3466 .boxed(),
3467 ]);
3468
3469 let interesting_i32s: Vec<Datum<'static>> =
3470 SqlScalarType::Int32.interesting_datums().collect();
3471 let i32_datums = proptest::strategy::Union::new([
3472 any::<i32>().prop_map(PropDatum::Int32).boxed(),
3473 (0..interesting_i32s.len())
3474 .prop_map(move |i| {
3475 let Datum::Int32(val) = interesting_i32s[i] else {
3476 unreachable!("interesting int32 has non-i32s")
3477 };
3478 PropDatum::Int32(val)
3479 })
3480 .boxed(),
3481 (-10i32..10).prop_map(PropDatum::Int32).boxed(),
3482 ]);
3483
3484 let arena = RowArena::new();
3485
3486 proptest_binary(
3490 BinaryFunc::AddInt32(AddInt32),
3491 &arena,
3492 &i32_datums,
3493 &i32_datums,
3494 );
3495 proptest_binary(SubInt32.into(), &arena, &i32_datums, &i32_datums);
3496 proptest_binary(MulInt32.into(), &arena, &i32_datums, &i32_datums);
3497 proptest_binary(DivInt32.into(), &arena, &i32_datums, &i32_datums);
3498 proptest_binary(TextConcatBinary.into(), &arena, &str_datums, &str_datums);
3499 proptest_binary(Left.into(), &arena, &str_datums, &i32_datums);
3500 }
3501}