1use std::borrow::Cow;
11use std::fmt;
12use std::sync::LazyLock;
13
14use chrono::{DateTime, NaiveDateTime, NaiveTime, Utc};
15use mz_expr_derive::sqlfunc;
16use mz_ore::cast::CastFrom;
17use mz_ore::result::ResultExt;
18use mz_ore::str::StrExt;
19use mz_repr::adt::char::{Char, format_str_trim};
20use mz_repr::adt::date::Date;
21use mz_repr::adt::interval::Interval;
22use mz_repr::adt::jsonb::Jsonb;
23use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
24use mz_repr::adt::pg_legacy_name::PgLegacyName;
25use mz_repr::adt::regex::Regex;
26use mz_repr::adt::system::{Oid, PgLegacyChar};
27use mz_repr::adt::timestamp::{CheckedTimestamp, TimestampPrecision};
28use mz_repr::adt::varchar::{VarChar, VarCharMaxLength};
29use mz_repr::{Datum, RowArena, SqlColumnType, SqlScalarType, strconv};
30use serde::{Deserialize, Serialize};
31use uuid::Uuid;
32
33use crate::func::{binary, regexp_match_static};
34use crate::scalar::func::{
35 EagerUnaryFunc, LazyUnaryFunc, array_create_scalar, regexp_split_to_array_re,
36};
37use crate::{Eval, EvalError, MirScalarExpr, UnaryFunc, like_pattern};
38
39#[sqlfunc(
40 sqlname = "text_to_boolean",
41 preserves_uniqueness = false,
42 inverse = to_unary!(super::CastBoolToString)
43)]
44fn cast_string_to_bool<'a>(a: &'a str) -> Result<bool, EvalError> {
45 strconv::parse_bool(a).err_into()
46}
47
48#[sqlfunc(
49 sqlname = "text_to_\"char\"",
50 preserves_uniqueness = false,
54 inverse = to_unary!(super::CastPgLegacyCharToString)
55)]
56fn cast_string_to_pg_legacy_char<'a>(a: &'a str) -> PgLegacyChar {
57 PgLegacyChar(a.as_bytes().get(0).copied().unwrap_or(0))
58}
59
60#[sqlfunc(sqlname = "text_to_name", preserves_uniqueness = false)]
61fn cast_string_to_pg_legacy_name<'a>(a: &'a str) -> PgLegacyName<String> {
62 PgLegacyName(strconv::parse_pg_legacy_name(a))
63}
64
65#[sqlfunc(
66 sqlname = "text_to_bytea",
67 preserves_uniqueness = false,
73 inverse = to_unary!(super::CastBytesToString)
74)]
75fn cast_string_to_bytes<'a>(a: &'a str) -> Result<Vec<u8>, EvalError> {
76 strconv::parse_bytes(a).err_into()
77}
78
79#[sqlfunc(
80 sqlname = "text_to_smallint",
81 preserves_uniqueness = false,
82 inverse = to_unary!(super::CastInt16ToString)
83)]
84fn cast_string_to_int16<'a>(a: &'a str) -> Result<i16, EvalError> {
85 strconv::parse_int16(a).err_into()
86}
87
88#[sqlfunc(
89 sqlname = "text_to_integer",
90 preserves_uniqueness = false,
91 inverse = to_unary!(super::CastInt32ToString)
92)]
93fn cast_string_to_int32<'a>(a: &'a str) -> Result<i32, EvalError> {
94 strconv::parse_int32(a).err_into()
95}
96
97#[sqlfunc(
98 sqlname = "text_to_bigint",
99 preserves_uniqueness = false,
100 inverse = to_unary!(super::CastInt64ToString)
101)]
102fn cast_string_to_int64<'a>(a: &'a str) -> Result<i64, EvalError> {
103 strconv::parse_int64(a).err_into()
104}
105
106#[sqlfunc(
107 sqlname = "text_to_real",
108 preserves_uniqueness = false,
109 inverse = to_unary!(super::CastFloat32ToString)
110)]
111fn cast_string_to_float32<'a>(a: &'a str) -> Result<f32, EvalError> {
112 strconv::parse_float32(a).err_into()
113}
114
115#[sqlfunc(
116 sqlname = "text_to_double",
117 preserves_uniqueness = false,
118 inverse = to_unary!(super::CastFloat64ToString)
119)]
120fn cast_string_to_float64<'a>(a: &'a str) -> Result<f64, EvalError> {
121 strconv::parse_float64(a).err_into()
122}
123
124#[sqlfunc(
125 sqlname = "text_to_oid",
126 preserves_uniqueness = false,
127 inverse = to_unary!(super::CastOidToString)
128)]
129fn cast_string_to_oid<'a>(a: &'a str) -> Result<Oid, EvalError> {
130 Ok(Oid(strconv::parse_oid(a)?))
131}
132
133#[sqlfunc(
134 sqlname = "text_to_uint2",
135 preserves_uniqueness = false,
136 inverse = to_unary!(super::CastUint16ToString)
137)]
138fn cast_string_to_uint16(a: &str) -> Result<u16, EvalError> {
139 strconv::parse_uint16(a).err_into()
140}
141
142#[sqlfunc(
143 sqlname = "text_to_uint4",
144 preserves_uniqueness = false,
145 inverse = to_unary!(super::CastUint32ToString)
146)]
147fn cast_string_to_uint32(a: &str) -> Result<u32, EvalError> {
148 strconv::parse_uint32(a).err_into()
149}
150
151#[sqlfunc(
152 sqlname = "text_to_uint8",
153 preserves_uniqueness = false,
154 inverse = to_unary!(super::CastUint64ToString)
155)]
156fn cast_string_to_uint64(a: &str) -> Result<u64, EvalError> {
157 strconv::parse_uint64(a).err_into()
158}
159
160#[sqlfunc(preserves_uniqueness = true, inverse = to_unary!(Reverse))]
161fn reverse<'a>(a: &'a str) -> String {
162 a.chars().rev().collect()
163}
164
165#[derive(
166 Ord,
167 PartialOrd,
168 Clone,
169 Debug,
170 Eq,
171 PartialEq,
172 Serialize,
173 Deserialize,
174 Hash
175)]
176pub struct CastStringToNumeric(pub Option<NumericMaxScale>);
177
178impl EagerUnaryFunc for CastStringToNumeric {
179 type Input<'a> = &'a str;
180 type Output<'a> = Result<Numeric, EvalError>;
181
182 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
183 let mut d = strconv::parse_numeric(a)?;
184 if let Some(scale) = self.0 {
185 if numeric::rescale(&mut d.0, scale.into_u8()).is_err() {
186 return Err(EvalError::NumericFieldOverflow);
187 }
188 }
189 Ok(d.into_inner())
190 }
191
192 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
193 SqlScalarType::Numeric { max_scale: self.0 }.nullable(input.nullable)
194 }
195
196 fn inverse(&self) -> Option<crate::UnaryFunc> {
197 to_unary!(super::CastNumericToString)
198 }
199}
200
201impl fmt::Display for CastStringToNumeric {
202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203 f.write_str("text_to_numeric")
204 }
205}
206
207#[sqlfunc(
208 sqlname = "text_to_date",
209 preserves_uniqueness = false,
210 inverse = to_unary!(super::CastDateToString)
211)]
212fn cast_string_to_date<'a>(a: &'a str) -> Result<Date, EvalError> {
213 strconv::parse_date(a).err_into()
214}
215
216#[sqlfunc(
217 sqlname = "text_to_time",
218 preserves_uniqueness = false,
219 inverse = to_unary!(super::CastTimeToString)
220)]
221fn cast_string_to_time<'a>(a: &'a str) -> Result<NaiveTime, EvalError> {
222 strconv::parse_time(a).err_into()
223}
224
225#[derive(
226 Ord,
227 PartialOrd,
228 Clone,
229 Debug,
230 Eq,
231 PartialEq,
232 Serialize,
233 Deserialize,
234 Hash
235)]
236pub struct CastStringToTimestamp(pub Option<TimestampPrecision>);
237
238impl EagerUnaryFunc for CastStringToTimestamp {
239 type Input<'a> = &'a str;
240 type Output<'a> = Result<CheckedTimestamp<NaiveDateTime>, EvalError>;
241
242 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
243 let out = strconv::parse_timestamp(a)?;
244 let updated = out.round_to_precision(self.0)?;
245 Ok(updated)
246 }
247
248 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
249 SqlScalarType::Timestamp { precision: self.0 }.nullable(input.nullable)
250 }
251
252 fn inverse(&self) -> Option<crate::UnaryFunc> {
253 to_unary!(super::CastTimestampToString)
254 }
255}
256
257impl fmt::Display for CastStringToTimestamp {
258 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259 f.write_str("text_to_timestamp")
260 }
261}
262
263#[sqlfunc(sqlname = "try_parse_monotonic_iso8601_timestamp")]
264fn try_parse_monotonic_iso8601_timestamp<'a>(
270 a: &'a str,
271) -> Option<CheckedTimestamp<NaiveDateTime>> {
272 let ts = mz_persist_types::timestamp::try_parse_monotonic_iso8601_timestamp(a)?;
273 let ts = CheckedTimestamp::from_timestamplike(ts)
274 .expect("monotonic_iso8601 range is a subset of CheckedTimestamp domain");
275 Some(ts)
276}
277
278#[derive(
279 Ord,
280 PartialOrd,
281 Clone,
282 Debug,
283 Eq,
284 PartialEq,
285 Serialize,
286 Deserialize,
287 Hash
288)]
289pub struct CastStringToTimestampTz(pub Option<TimestampPrecision>);
290
291impl EagerUnaryFunc for CastStringToTimestampTz {
292 type Input<'a> = &'a str;
293 type Output<'a> = Result<CheckedTimestamp<DateTime<Utc>>, EvalError>;
294
295 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
296 let out = strconv::parse_timestamptz(a)?;
297 let updated = out.round_to_precision(self.0)?;
298 Ok(updated)
299 }
300
301 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
302 SqlScalarType::TimestampTz { precision: self.0 }.nullable(input.nullable)
303 }
304
305 fn inverse(&self) -> Option<crate::UnaryFunc> {
306 to_unary!(super::CastTimestampTzToString)
307 }
308}
309
310impl fmt::Display for CastStringToTimestampTz {
311 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 f.write_str("text_to_timestamp_with_time_zone")
313 }
314}
315
316#[sqlfunc(
317 sqlname = "text_to_interval",
318 preserves_uniqueness = false,
319 inverse = to_unary!(super::CastIntervalToString)
320)]
321fn cast_string_to_interval<'a>(a: &'a str) -> Result<Interval, EvalError> {
322 strconv::parse_interval(a).err_into()
323}
324
325#[sqlfunc(
326 sqlname = "text_to_uuid",
327 preserves_uniqueness = false,
328 inverse = to_unary!(super::CastUuidToString)
329)]
330fn cast_string_to_uuid<'a>(a: &'a str) -> Result<Uuid, EvalError> {
331 strconv::parse_uuid(a).err_into()
332}
333
334#[derive(
335 Ord,
336 PartialOrd,
337 Clone,
338 Debug,
339 Eq,
340 PartialEq,
341 Serialize,
342 Deserialize,
343 Hash
344)]
345pub struct CastStringToArray<E = MirScalarExpr> {
346 pub return_ty: SqlScalarType,
348 pub cast_expr: Box<E>,
351}
352
353impl<E: Eval> LazyUnaryFunc for CastStringToArray<E> {
354 fn eval<'a>(
355 &'a self,
356 datums: &[Datum<'a>],
357 temp_storage: &'a RowArena,
358 a: &'a impl Eval,
359 ) -> Result<Datum<'a>, EvalError> {
360 let a = a.eval(datums, temp_storage)?;
361 if a.is_null() {
362 return Ok(Datum::Null);
363 }
364 let (datums, dims) = strconv::parse_array(
365 a.unwrap_str(),
366 || Datum::Null,
367 |elem_text| {
368 let elem_text = match elem_text {
369 Cow::Owned(s) => temp_storage.push_string(s),
370 Cow::Borrowed(s) => s,
371 };
372 self.cast_expr
373 .eval(&[Datum::String(elem_text)], temp_storage)
374 },
375 )?;
376
377 Ok(temp_storage.try_make_datum(|packer| packer.try_push_array(&dims, datums))?)
378 }
379
380 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
382 self.return_ty.clone().nullable(input_type.nullable)
383 }
384
385 fn propagates_nulls(&self) -> bool {
387 true
388 }
389
390 fn introduces_nulls(&self) -> bool {
392 false
393 }
394
395 fn preserves_uniqueness(&self) -> bool {
397 false
398 }
399
400 fn inverse(&self) -> Option<crate::UnaryFunc> {
401 to_unary!(super::CastArrayToString {
402 ty: self.return_ty.clone(),
403 })
404 }
405
406 fn is_monotone(&self) -> bool {
407 false
408 }
409
410 fn is_eliminable_cast(&self) -> bool {
411 false
412 }
413}
414
415impl<E> CastStringToArray<E> {
416 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
419 &'a self,
420 ) -> Result<CastStringToArray<E2>, E2::Error> {
421 Ok(CastStringToArray {
422 return_ty: self.return_ty.clone(),
423 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
424 })
425 }
426
427 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToArray<E2> {
430 CastStringToArray {
431 return_ty: self.return_ty.clone(),
432 cast_expr: Box::new(E2::from(&*self.cast_expr)),
433 }
434 }
435}
436
437impl<E> fmt::Display for CastStringToArray<E> {
438 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439 f.write_str("strtoarray")
440 }
441}
442
443#[derive(
444 Ord,
445 PartialOrd,
446 Clone,
447 Debug,
448 Eq,
449 PartialEq,
450 Serialize,
451 Deserialize,
452 Hash
453)]
454pub struct CastStringToList<E = MirScalarExpr> {
455 pub return_ty: SqlScalarType,
457 pub cast_expr: Box<E>,
460}
461
462impl<E: Eval> LazyUnaryFunc for CastStringToList<E> {
463 fn eval<'a>(
464 &'a self,
465 datums: &[Datum<'a>],
466 temp_storage: &'a RowArena,
467 a: &'a impl Eval,
468 ) -> Result<Datum<'a>, EvalError> {
469 let a = a.eval(datums, temp_storage)?;
470 if a.is_null() {
471 return Ok(Datum::Null);
472 }
473 let parsed_datums = strconv::parse_list(
474 a.unwrap_str(),
475 matches!(
476 self.return_ty.unwrap_list_element_type(),
477 SqlScalarType::List { .. }
478 ),
479 || Datum::Null,
480 |elem_text| {
481 let elem_text = match elem_text {
482 Cow::Owned(s) => temp_storage.push_string(s),
483 Cow::Borrowed(s) => s,
484 };
485 self.cast_expr
486 .eval(&[Datum::String(elem_text)], temp_storage)
487 },
488 )?;
489
490 Ok(temp_storage.make_datum(|packer| packer.push_list(parsed_datums)))
491 }
492
493 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
495 self.return_ty
496 .without_modifiers()
497 .nullable(input_type.nullable)
498 }
499
500 fn propagates_nulls(&self) -> bool {
502 true
503 }
504
505 fn introduces_nulls(&self) -> bool {
507 false
508 }
509
510 fn preserves_uniqueness(&self) -> bool {
512 false
513 }
514
515 fn inverse(&self) -> Option<crate::UnaryFunc> {
516 to_unary!(super::CastListToString {
517 ty: self.return_ty.clone(),
518 })
519 }
520
521 fn is_monotone(&self) -> bool {
522 false
523 }
524
525 fn is_eliminable_cast(&self) -> bool {
526 false
527 }
528}
529
530impl<E> CastStringToList<E> {
531 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
534 &'a self,
535 ) -> Result<CastStringToList<E2>, E2::Error> {
536 Ok(CastStringToList {
537 return_ty: self.return_ty.clone(),
538 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
539 })
540 }
541
542 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToList<E2> {
545 CastStringToList {
546 return_ty: self.return_ty.clone(),
547 cast_expr: Box::new(E2::from(&*self.cast_expr)),
548 }
549 }
550}
551
552impl<E> fmt::Display for CastStringToList<E> {
553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554 f.write_str("strtolist")
555 }
556}
557
558#[derive(
559 Ord,
560 PartialOrd,
561 Clone,
562 Debug,
563 Eq,
564 PartialEq,
565 Serialize,
566 Deserialize,
567 Hash
568)]
569pub struct CastStringToMap<E = MirScalarExpr> {
570 pub return_ty: SqlScalarType,
572 pub cast_expr: Box<E>,
575}
576
577impl<E: Eval> LazyUnaryFunc for CastStringToMap<E> {
578 fn eval<'a>(
579 &'a self,
580 datums: &[Datum<'a>],
581 temp_storage: &'a RowArena,
582 a: &'a impl Eval,
583 ) -> Result<Datum<'a>, EvalError> {
584 let a = a.eval(datums, temp_storage)?;
585 if a.is_null() {
586 return Ok(Datum::Null);
587 }
588 let parsed_map = strconv::parse_map(
589 a.unwrap_str(),
590 matches!(
591 self.return_ty.unwrap_map_value_type(),
592 SqlScalarType::Map { .. }
593 ),
594 |value_text| -> Result<Datum, EvalError> {
595 let value_text = match value_text {
596 Some(Cow::Owned(s)) => Datum::String(temp_storage.push_string(s)),
597 Some(Cow::Borrowed(s)) => Datum::String(s),
598 None => Datum::Null,
599 };
600 self.cast_expr.eval(&[value_text], temp_storage)
601 },
602 )?;
603 let mut pairs: Vec<(String, Datum)> = parsed_map.into_iter().map(|(k, v)| (k, v)).collect();
604 pairs.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2));
605 pairs.dedup_by(|(k1, _v1), (k2, _v2)| k1 == k2);
606 Ok(temp_storage.make_datum(|packer| {
607 packer.push_dict_with(|packer| {
608 for (k, v) in pairs {
609 packer.push(Datum::String(&k));
610 packer.push(v);
611 }
612 })
613 }))
614 }
615
616 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
618 self.return_ty.clone().nullable(input_type.nullable)
619 }
620
621 fn propagates_nulls(&self) -> bool {
623 true
624 }
625
626 fn introduces_nulls(&self) -> bool {
628 false
629 }
630
631 fn preserves_uniqueness(&self) -> bool {
633 false
634 }
635
636 fn inverse(&self) -> Option<crate::UnaryFunc> {
637 to_unary!(super::CastMapToString {
638 ty: self.return_ty.clone(),
639 })
640 }
641
642 fn is_monotone(&self) -> bool {
643 false
644 }
645
646 fn is_eliminable_cast(&self) -> bool {
647 false
648 }
649}
650
651impl<E> CastStringToMap<E> {
652 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
655 &'a self,
656 ) -> Result<CastStringToMap<E2>, E2::Error> {
657 Ok(CastStringToMap {
658 return_ty: self.return_ty.clone(),
659 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
660 })
661 }
662
663 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToMap<E2> {
666 CastStringToMap {
667 return_ty: self.return_ty.clone(),
668 cast_expr: Box::new(E2::from(&*self.cast_expr)),
669 }
670 }
671}
672
673impl<E> fmt::Display for CastStringToMap<E> {
674 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
675 f.write_str("strtomap")
676 }
677}
678
679#[derive(
680 Ord,
681 PartialOrd,
682 Clone,
683 Debug,
684 Eq,
685 PartialEq,
686 Serialize,
687 Deserialize,
688 Hash
689)]
690pub struct CastStringToChar {
691 pub length: Option<mz_repr::adt::char::CharLength>,
692 pub fail_on_len: bool,
693}
694
695impl EagerUnaryFunc for CastStringToChar {
696 type Input<'a> = &'a str;
697 type Output<'a> = Result<Char<String>, EvalError>;
698
699 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
700 let s = format_str_trim(a, self.length, self.fail_on_len).map_err(|_| {
701 assert!(self.fail_on_len);
702 EvalError::StringValueTooLong {
703 target_type: "character".into(),
704 length: usize::cast_from(self.length.unwrap().into_u32()),
705 }
706 })?;
707
708 Ok(Char(s))
709 }
710
711 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
712 SqlScalarType::Char {
713 length: self.length,
714 }
715 .nullable(input.nullable)
716 }
717
718 fn could_error(&self) -> bool {
719 self.fail_on_len && self.length.is_some()
720 }
721
722 fn inverse(&self) -> Option<crate::UnaryFunc> {
723 to_unary!(super::CastCharToString)
724 }
725
726 fn is_eliminable_cast(&self) -> bool {
727 false
729 }
730}
731
732impl fmt::Display for CastStringToChar {
733 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
734 match self.length {
735 Some(length) => {
736 write!(
737 f,
738 "text_to_char[len={}, fail_on_len={}]",
739 length.into_u32(),
740 self.fail_on_len
741 )
742 }
743 None => f.write_str("text_to_char[len=unbounded]"),
744 }
745 }
746}
747
748#[derive(
749 Ord,
750 PartialOrd,
751 Clone,
752 Debug,
753 Eq,
754 PartialEq,
755 Serialize,
756 Deserialize,
757 Hash
758)]
759pub struct CastStringToRange<E = MirScalarExpr> {
760 pub return_ty: SqlScalarType,
762 pub cast_expr: Box<E>,
765}
766
767impl<E: Eval> LazyUnaryFunc for CastStringToRange<E> {
768 fn eval<'a>(
769 &'a self,
770 datums: &[Datum<'a>],
771 temp_storage: &'a RowArena,
772 a: &'a impl Eval,
773 ) -> Result<Datum<'a>, EvalError> {
774 let a = a.eval(datums, temp_storage)?;
775 if a.is_null() {
776 return Ok(Datum::Null);
777 }
778 let mut range = strconv::parse_range(a.unwrap_str(), |elem_text| {
779 let elem_text = match elem_text {
780 Cow::Owned(s) => temp_storage.push_string(s),
781 Cow::Borrowed(s) => s,
782 };
783 self.cast_expr
784 .eval(&[Datum::String(elem_text)], temp_storage)
785 })?;
786
787 range.canonicalize()?;
788
789 Ok(temp_storage.make_datum(|packer| {
790 packer
791 .push_range(range)
792 .expect("must have already handled errors")
793 }))
794 }
795
796 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
798 self.return_ty
799 .without_modifiers()
800 .nullable(input_type.nullable)
801 }
802
803 fn propagates_nulls(&self) -> bool {
805 true
806 }
807
808 fn introduces_nulls(&self) -> bool {
810 false
811 }
812
813 fn preserves_uniqueness(&self) -> bool {
815 false
816 }
817
818 fn inverse(&self) -> Option<crate::UnaryFunc> {
819 to_unary!(super::CastRangeToString {
820 ty: self.return_ty.clone(),
821 })
822 }
823
824 fn is_monotone(&self) -> bool {
825 false
826 }
827
828 fn is_eliminable_cast(&self) -> bool {
829 false
830 }
831}
832
833impl<E> CastStringToRange<E> {
834 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
837 &'a self,
838 ) -> Result<CastStringToRange<E2>, E2::Error> {
839 Ok(CastStringToRange {
840 return_ty: self.return_ty.clone(),
841 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
842 })
843 }
844
845 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToRange<E2> {
848 CastStringToRange {
849 return_ty: self.return_ty.clone(),
850 cast_expr: Box::new(E2::from(&*self.cast_expr)),
851 }
852 }
853}
854
855impl<E> fmt::Display for CastStringToRange<E> {
856 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
857 f.write_str("strtorange")
858 }
859}
860
861#[derive(
862 Ord,
863 PartialOrd,
864 Clone,
865 Debug,
866 Eq,
867 PartialEq,
868 Serialize,
869 Deserialize,
870 Hash
871)]
872pub struct CastStringToVarChar {
873 pub length: Option<VarCharMaxLength>,
874 pub fail_on_len: bool,
875}
876
877impl EagerUnaryFunc for CastStringToVarChar {
878 type Input<'a> = &'a str;
879 type Output<'a> = Result<VarChar<&'a str>, EvalError>;
880
881 fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> {
882 let s =
883 mz_repr::adt::varchar::format_str(a, self.length, self.fail_on_len).map_err(|_| {
884 assert!(self.fail_on_len);
885 EvalError::StringValueTooLong {
886 target_type: "character varying".into(),
887 length: usize::cast_from(self.length.unwrap().into_u32()),
888 }
889 })?;
890
891 Ok(VarChar(s))
892 }
893
894 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
895 SqlScalarType::VarChar {
896 max_length: self.length,
897 }
898 .nullable(input.nullable)
899 }
900
901 fn could_error(&self) -> bool {
902 self.fail_on_len && self.length.is_some()
903 }
904
905 fn preserves_uniqueness(&self) -> bool {
906 self.length.is_none()
907 }
908
909 fn inverse(&self) -> Option<crate::UnaryFunc> {
910 to_unary!(super::CastVarCharToString)
911 }
912
913 fn is_eliminable_cast(&self) -> bool {
914 self.length.is_none()
915 }
916}
917
918impl fmt::Display for CastStringToVarChar {
919 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
920 match self.length {
921 Some(length) => {
922 write!(
923 f,
924 "text_to_varchar[len={}, fail_on_len={}]",
925 length.into_u32(),
926 self.fail_on_len
927 )
928 }
929 None => f.write_str("text_to_varchar[len=unbounded]"),
930 }
931 }
932}
933
934static INT2VECTOR_CAST_EXPR: LazyLock<MirScalarExpr> = LazyLock::new(|| MirScalarExpr::CallUnary {
937 func: UnaryFunc::CastStringToInt16(CastStringToInt16),
938 expr: Box::new(MirScalarExpr::column(0)),
939});
940
941#[derive(
942 Ord,
943 PartialOrd,
944 Clone,
945 Debug,
946 Eq,
947 PartialEq,
948 Serialize,
949 Deserialize,
950 Hash
951)]
952pub struct CastStringToInt2Vector;
953
954impl LazyUnaryFunc for CastStringToInt2Vector {
955 fn eval<'a>(
956 &'a self,
957 datums: &[Datum<'a>],
958 temp_storage: &'a RowArena,
959 a: &'a impl Eval,
960 ) -> Result<Datum<'a>, EvalError> {
961 let a = a.eval(datums, temp_storage)?;
962 if a.is_null() {
963 return Ok(Datum::Null);
964 }
965
966 let datums = strconv::parse_legacy_vector(a.unwrap_str(), |elem_text| {
967 let elem_text = match elem_text {
968 Cow::Owned(s) => temp_storage.push_string(s),
969 Cow::Borrowed(s) => s,
970 };
971 INT2VECTOR_CAST_EXPR.eval(&[Datum::String(elem_text)], temp_storage)
972 })?;
973 array_create_scalar(&datums, temp_storage)
974 }
975
976 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
978 SqlScalarType::Int2Vector.nullable(input_type.nullable)
979 }
980
981 fn propagates_nulls(&self) -> bool {
983 true
984 }
985
986 fn introduces_nulls(&self) -> bool {
988 false
989 }
990
991 fn preserves_uniqueness(&self) -> bool {
993 false
994 }
995
996 fn inverse(&self) -> Option<crate::UnaryFunc> {
997 to_unary!(super::CastInt2VectorToString)
998 }
999
1000 fn is_monotone(&self) -> bool {
1001 false
1002 }
1003
1004 fn is_eliminable_cast(&self) -> bool {
1005 false
1006 }
1007}
1008
1009impl fmt::Display for CastStringToInt2Vector {
1010 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1011 f.write_str("strtoint2vector")
1012 }
1013}
1014
1015#[sqlfunc(
1016 sqlname = "text_to_jsonb",
1017 preserves_uniqueness = false,
1018 inverse = to_unary!(super::CastJsonbToString)
1019)]
1020fn cast_string_to_jsonb<'a>(a: &'a str) -> Result<Jsonb, EvalError> {
1022 Ok(strconv::parse_jsonb(a)?)
1023}
1024
1025#[sqlfunc(sqlname = "btrim")]
1026fn trim_whitespace<'a>(a: &'a str) -> &'a str {
1027 a.trim_matches(' ')
1028}
1029
1030#[sqlfunc(sqlname = "ltrim")]
1031fn trim_leading_whitespace<'a>(a: &'a str) -> &'a str {
1032 a.trim_start_matches(' ')
1033}
1034
1035#[sqlfunc(sqlname = "rtrim")]
1036fn trim_trailing_whitespace<'a>(a: &'a str) -> &'a str {
1037 a.trim_end_matches(' ')
1038}
1039
1040#[sqlfunc(sqlname = "initcap")]
1041fn initcap<'a>(a: &'a str) -> String {
1042 let mut out = String::new();
1043 let mut capitalize_next = true;
1044 for ch in a.chars() {
1045 if capitalize_next {
1046 out.extend(ch.to_uppercase())
1047 } else {
1048 out.extend(ch.to_lowercase())
1049 };
1050 capitalize_next = !ch.is_alphanumeric();
1051 }
1052 out
1053}
1054
1055#[sqlfunc(sqlname = "ascii")]
1056fn ascii<'a>(a: &'a str) -> i32 {
1057 a.chars()
1058 .next()
1059 .and_then(|c| i32::try_from(u32::from(c)).ok())
1060 .unwrap_or(0)
1061}
1062
1063#[sqlfunc(sqlname = "char_length")]
1064fn char_length<'a>(a: &'a str) -> Result<i32, EvalError> {
1065 let length = a.chars().count();
1066 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
1067}
1068
1069#[sqlfunc(sqlname = "bit_length")]
1070fn bit_length_string<'a>(a: &'a str) -> Result<i32, EvalError> {
1071 let length = a.as_bytes().len() * 8;
1072 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
1073}
1074
1075#[sqlfunc(sqlname = "octet_length")]
1076fn byte_length_string<'a>(a: &'a str) -> Result<i32, EvalError> {
1077 let length = a.as_bytes().len();
1078 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
1079}
1080
1081#[sqlfunc]
1082fn upper<'a>(a: &'a str) -> String {
1083 a.to_uppercase()
1084}
1085
1086#[sqlfunc]
1087fn lower<'a>(a: &'a str) -> String {
1088 a.to_lowercase()
1089}
1090
1091#[sqlfunc]
1092fn normalize(text: &str, form_str: &str) -> Result<String, EvalError> {
1093 use unicode_normalization::UnicodeNormalization;
1094
1095 match form_str.to_uppercase().as_str() {
1096 "NFC" => Ok(text.nfc().collect()),
1097 "NFD" => Ok(text.nfd().collect()),
1098 "NFKC" => Ok(text.nfkc().collect()),
1099 "NFKD" => Ok(text.nfkd().collect()),
1100 _ => Err(EvalError::InvalidParameterValue(
1101 format!("invalid normalization form: {}", form_str).into(),
1102 )),
1103 }
1104}
1105
1106#[derive(
1107 Ord,
1108 PartialOrd,
1109 Clone,
1110 Debug,
1111 Eq,
1112 PartialEq,
1113 Serialize,
1114 Deserialize,
1115 Hash
1116)]
1117pub struct IsLikeMatch(pub like_pattern::Matcher);
1118
1119impl EagerUnaryFunc for IsLikeMatch {
1120 type Input<'a> = &'a str;
1121 type Output<'a> = bool;
1122
1123 fn call<'a>(&self, haystack: Self::Input<'a>) -> Self::Output<'a> {
1124 self.0.is_match(haystack)
1125 }
1126
1127 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
1128 SqlScalarType::Bool.nullable(input.nullable)
1129 }
1130}
1131
1132impl fmt::Display for IsLikeMatch {
1133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1134 write!(
1135 f,
1136 "{}like[{}]",
1137 if self.0.case_insensitive { "i" } else { "" },
1138 self.0.pattern.escaped()
1139 )
1140 }
1141}
1142
1143#[derive(
1144 Ord,
1145 PartialOrd,
1146 Clone,
1147 Debug,
1148 Eq,
1149 PartialEq,
1150 Serialize,
1151 Deserialize,
1152 Hash
1153)]
1154pub struct IsRegexpMatch(pub Regex);
1155
1156impl EagerUnaryFunc for IsRegexpMatch {
1157 type Input<'a> = &'a str;
1158 type Output<'a> = bool;
1159
1160 fn call<'a>(&self, haystack: Self::Input<'a>) -> Self::Output<'a> {
1161 self.0.is_match(haystack)
1162 }
1163
1164 fn output_sql_type(&self, input: SqlColumnType) -> SqlColumnType {
1165 SqlScalarType::Bool.nullable(input.nullable)
1166 }
1167}
1168
1169impl fmt::Display for IsRegexpMatch {
1170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1171 write!(
1172 f,
1173 "is_regexp_match[{}, case_insensitive={}]",
1174 self.0.pattern().escaped(),
1175 self.0.case_insensitive
1176 )
1177 }
1178}
1179
1180#[derive(
1181 Ord,
1182 PartialOrd,
1183 Clone,
1184 Debug,
1185 Eq,
1186 PartialEq,
1187 Serialize,
1188 Deserialize,
1189 Hash
1190)]
1191#[serde(rename = "RegexpMatchStatic")]
1196pub struct RegexpMatch(pub Regex);
1197
1198impl LazyUnaryFunc for RegexpMatch {
1199 fn eval<'a>(
1200 &'a self,
1201 datums: &[Datum<'a>],
1202 temp_storage: &'a RowArena,
1203 a: &'a impl Eval,
1204 ) -> Result<Datum<'a>, EvalError> {
1205 let haystack = a.eval(datums, temp_storage)?;
1206 if haystack.is_null() {
1207 return Ok(Datum::Null);
1208 }
1209 regexp_match_static(haystack, temp_storage, &self.0)
1210 }
1211
1212 fn output_sql_type(&self, _input_type: SqlColumnType) -> SqlColumnType {
1214 SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)
1215 }
1216
1217 fn propagates_nulls(&self) -> bool {
1219 true
1220 }
1221
1222 fn introduces_nulls(&self) -> bool {
1224 true
1226 }
1227
1228 fn preserves_uniqueness(&self) -> bool {
1230 false
1231 }
1232
1233 fn inverse(&self) -> Option<crate::UnaryFunc> {
1234 None
1235 }
1236
1237 fn is_monotone(&self) -> bool {
1238 false
1239 }
1240
1241 fn is_eliminable_cast(&self) -> bool {
1242 false
1243 }
1244}
1245
1246impl fmt::Display for RegexpMatch {
1247 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1248 write!(
1249 f,
1250 "regexp_match[{}, case_insensitive={}]",
1251 self.0.pattern().escaped(),
1252 self.0.case_insensitive
1253 )
1254 }
1255}
1256
1257#[derive(
1258 Ord,
1259 PartialOrd,
1260 Clone,
1261 Debug,
1262 Eq,
1263 PartialEq,
1264 Serialize,
1265 Deserialize,
1266 Hash
1267)]
1268#[serde(rename = "RegexpSplitToArrayStatic")]
1270pub struct RegexpSplitToArray(pub Regex);
1271
1272impl LazyUnaryFunc for RegexpSplitToArray {
1273 fn eval<'a>(
1274 &'a self,
1275 datums: &[Datum<'a>],
1276 temp_storage: &'a RowArena,
1277 a: &'a impl Eval,
1278 ) -> Result<Datum<'a>, EvalError> {
1279 let haystack = a.eval(datums, temp_storage)?;
1280 if haystack.is_null() {
1281 return Ok(Datum::Null);
1282 }
1283 regexp_split_to_array_re(haystack.unwrap_str(), &self.0, temp_storage)
1284 }
1285
1286 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
1288 SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(input_type.nullable)
1289 }
1290
1291 fn propagates_nulls(&self) -> bool {
1293 true
1294 }
1295
1296 fn introduces_nulls(&self) -> bool {
1298 false
1299 }
1300
1301 fn preserves_uniqueness(&self) -> bool {
1303 false
1304 }
1305
1306 fn inverse(&self) -> Option<crate::UnaryFunc> {
1307 None
1308 }
1309
1310 fn is_monotone(&self) -> bool {
1311 false
1312 }
1313
1314 fn is_eliminable_cast(&self) -> bool {
1315 false
1316 }
1317}
1318
1319impl fmt::Display for RegexpSplitToArray {
1320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1321 write!(
1322 f,
1323 "regexp_split_to_array[{}, case_insensitive={}]",
1324 self.0.pattern().escaped(),
1325 self.0.case_insensitive
1326 )
1327 }
1328}
1329
1330#[sqlfunc(sqlname = "mz_panic")]
1331fn panic<'a>(a: &'a str) -> String {
1332 print!("{}", a);
1333 panic!("{}", a)
1334}
1335
1336#[sqlfunc(sqlname = "quote_ident", preserves_uniqueness = true)]
1337fn quote_ident<'a>(a: &'a str) -> Result<String, EvalError> {
1338 let i = mz_sql_parser::ast::Ident::new(a).map_err(|err| EvalError::InvalidIdentifier {
1339 ident: a.into(),
1340 detail: Some(err.to_string().into()),
1341 })?;
1342 Ok(i.to_string())
1343}
1344
1345#[derive(
1346 Ord,
1347 PartialOrd,
1348 Clone,
1349 Debug,
1350 Eq,
1351 PartialEq,
1352 Serialize,
1353 Deserialize,
1354 Hash
1355)]
1356#[serde(rename = "RegexpReplaceStatic")]
1358pub struct RegexpReplace {
1359 pub regex: Regex,
1360 pub limit: usize,
1361}
1362
1363impl binary::EagerBinaryFunc for RegexpReplace {
1364 type Input<'a> = (&'a str, &'a str);
1365 type Output<'a> = Cow<'a, str>;
1366
1367 fn call<'a>(
1368 &self,
1369 (source, replacement): Self::Input<'a>,
1370 _temp_storage: &'a RowArena,
1371 ) -> Self::Output<'a> {
1372 self.regex.replacen(source, self.limit, replacement)
1376 }
1377
1378 fn output_sql_type(&self, input_types: &[SqlColumnType]) -> SqlColumnType {
1379 use mz_repr::AsColumnType;
1380 let output = <Self::Output<'_> as AsColumnType>::as_column_type();
1381 let propagates_nulls = binary::EagerBinaryFunc::propagates_nulls(self);
1382 let nullable = output.nullable;
1383 let input_nullable = input_types.iter().any(|t| t.nullable);
1384 output.nullable(nullable || (propagates_nulls && input_nullable))
1385 }
1386}
1387
1388impl fmt::Display for RegexpReplace {
1389 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1390 write!(
1391 f,
1392 "regexp_replace[{}, case_insensitive={}, limit={}]",
1393 self.regex.pattern().escaped(),
1394 self.regex.case_insensitive,
1395 self.limit
1396 )
1397 }
1398}