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