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