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