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