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::array::Array;
20use mz_repr::adt::char::{Char, format_str_trim};
21use mz_repr::adt::date::Date;
22use mz_repr::adt::interval::Interval;
23use mz_repr::adt::jsonb::Jsonb;
24use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale};
25use mz_repr::adt::pg_legacy_name::PgLegacyName;
26use mz_repr::adt::regex::Regex;
27use mz_repr::adt::system::{Oid, PgLegacyChar};
28use mz_repr::adt::timestamp::{CheckedTimestamp, TimestampPrecision};
29use mz_repr::adt::varchar::{VarChar, VarCharMaxLength};
30use mz_repr::{Datum, Int2Vector, RowArena, SqlColumnType, SqlScalarType, strconv};
31use serde::{Deserialize, Serialize};
32use uuid::Uuid;
33
34use crate::func::regexp_match_static;
35use crate::scalar::func::{LazyUnaryFunc, array_create_scalar, regexp_split_to_array_re};
36use crate::{Eval, EvalError, MirScalarExpr, UnaryFunc, like_pattern};
37
38#[sqlfunc(
39 sqlname = "text_to_boolean",
40 preserves_uniqueness = false,
41 inverse = super::CastBoolToString
42)]
43fn cast_string_to_bool<'a>(a: &'a str) -> Result<bool, EvalError> {
44 strconv::parse_bool(a).err_into()
45}
46
47#[sqlfunc(
48 sqlname = "text_to_\"char\"",
49 preserves_uniqueness = false,
53 inverse = super::CastPgLegacyCharToString
54)]
55fn cast_string_to_pg_legacy_char<'a>(a: &'a str) -> PgLegacyChar {
56 PgLegacyChar(a.as_bytes().get(0).copied().unwrap_or(0))
57}
58
59#[sqlfunc(sqlname = "text_to_name", preserves_uniqueness = false)]
60fn cast_string_to_pg_legacy_name<'a>(a: &'a str) -> PgLegacyName<String> {
61 PgLegacyName(strconv::parse_pg_legacy_name(a))
62}
63
64#[sqlfunc(
65 sqlname = "text_to_bytea",
66 preserves_uniqueness = false,
72 inverse = super::CastBytesToString
73)]
74fn cast_string_to_bytes<'a>(a: &'a str) -> Result<Vec<u8>, EvalError> {
75 strconv::parse_bytes(a).err_into()
76}
77
78#[sqlfunc(
79 sqlname = "text_to_smallint",
80 preserves_uniqueness = false,
81 inverse = super::CastInt16ToString
82)]
83fn cast_string_to_int16<'a>(a: &'a str) -> Result<i16, EvalError> {
84 strconv::parse_int16(a).err_into()
85}
86
87#[sqlfunc(
88 sqlname = "text_to_integer",
89 preserves_uniqueness = false,
90 inverse = super::CastInt32ToString
91)]
92fn cast_string_to_int32<'a>(a: &'a str) -> Result<i32, EvalError> {
93 strconv::parse_int32(a).err_into()
94}
95
96#[sqlfunc(
97 sqlname = "text_to_bigint",
98 preserves_uniqueness = false,
99 inverse = super::CastInt64ToString
100)]
101fn cast_string_to_int64<'a>(a: &'a str) -> Result<i64, EvalError> {
102 strconv::parse_int64(a).err_into()
103}
104
105#[sqlfunc(
106 sqlname = "text_to_real",
107 preserves_uniqueness = false,
108 inverse = super::CastFloat32ToString
109)]
110fn cast_string_to_float32<'a>(a: &'a str) -> Result<f32, EvalError> {
111 strconv::parse_float32(a).err_into()
112}
113
114#[sqlfunc(
115 sqlname = "text_to_double",
116 preserves_uniqueness = false,
117 inverse = super::CastFloat64ToString
118)]
119fn cast_string_to_float64<'a>(a: &'a str) -> Result<f64, EvalError> {
120 strconv::parse_float64(a).err_into()
121}
122
123#[sqlfunc(
124 sqlname = "text_to_oid",
125 preserves_uniqueness = false,
126 inverse = super::CastOidToString
127)]
128fn cast_string_to_oid<'a>(a: &'a str) -> Result<Oid, EvalError> {
129 Ok(Oid(strconv::parse_oid(a)?))
130}
131
132#[sqlfunc(
133 sqlname = "text_to_uint2",
134 preserves_uniqueness = false,
135 inverse = super::CastUint16ToString
136)]
137fn cast_string_to_uint16(a: &str) -> Result<u16, EvalError> {
138 strconv::parse_uint16(a).err_into()
139}
140
141#[sqlfunc(
142 sqlname = "text_to_uint4",
143 preserves_uniqueness = false,
144 inverse = super::CastUint32ToString
145)]
146fn cast_string_to_uint32(a: &str) -> Result<u32, EvalError> {
147 strconv::parse_uint32(a).err_into()
148}
149
150#[sqlfunc(
151 sqlname = "text_to_uint8",
152 preserves_uniqueness = false,
153 inverse = super::CastUint64ToString
154)]
155fn cast_string_to_uint64(a: &str) -> Result<u64, EvalError> {
156 strconv::parse_uint64(a).err_into()
157}
158
159#[sqlfunc(preserves_uniqueness = true, inverse = Reverse)]
160fn reverse<'a>(a: &'a str) -> String {
161 a.chars().rev().collect()
162}
163
164#[derive(
165 Ord,
166 PartialOrd,
167 Clone,
168 Debug,
169 Eq,
170 PartialEq,
171 Serialize,
172 Deserialize,
173 Hash
174)]
175pub struct CastStringToNumeric(pub Option<NumericMaxScale>);
176
177#[sqlfunc(
178 CastStringToNumeric,
179 sqlname = "text_to_numeric",
180 output_type_expr = SqlScalarType::Numeric { max_scale: self.0 }.nullable(input_type.nullable),
181 inverse = super::CastNumericToString
182)]
183fn cast_string_to_numeric<'a>(&self, a: &'a str) -> Result<Numeric, EvalError> {
184 let mut d = strconv::parse_numeric(a)?;
185 if let Some(scale) = self.0 {
186 if numeric::rescale(&mut d.0, scale.into_u8()).is_err() {
187 return Err(EvalError::NumericFieldOverflow);
188 }
189 }
190 Ok(d.into_inner())
191}
192
193#[sqlfunc(
194 sqlname = "text_to_date",
195 preserves_uniqueness = false,
196 inverse = super::CastDateToString
197)]
198fn cast_string_to_date<'a>(a: &'a str) -> Result<Date, EvalError> {
199 strconv::parse_date(a).err_into()
200}
201
202#[sqlfunc(
203 sqlname = "text_to_time",
204 preserves_uniqueness = false,
205 inverse = super::CastTimeToString
206)]
207fn cast_string_to_time<'a>(a: &'a str) -> Result<NaiveTime, EvalError> {
208 strconv::parse_time(a).err_into()
209}
210
211#[derive(
212 Ord,
213 PartialOrd,
214 Clone,
215 Debug,
216 Eq,
217 PartialEq,
218 Serialize,
219 Deserialize,
220 Hash
221)]
222pub struct CastStringToTimestamp(pub Option<TimestampPrecision>);
223
224#[sqlfunc(
225 CastStringToTimestamp,
226 sqlname = "text_to_timestamp",
227 output_type_expr = SqlScalarType::Timestamp { precision: self.0 }.nullable(input_type.nullable),
228 inverse = super::CastTimestampToString
229)]
230fn cast_string_to_timestamp<'a>(
231 &self,
232 a: &'a str,
233) -> Result<CheckedTimestamp<NaiveDateTime>, EvalError> {
234 let out = strconv::parse_timestamp(a)?;
235 let updated = out.round_to_precision(self.0)?;
236 Ok(updated)
237}
238
239#[sqlfunc(sqlname = "try_parse_monotonic_iso8601_timestamp")]
240fn try_parse_monotonic_iso8601_timestamp<'a>(
246 a: &'a str,
247) -> Option<CheckedTimestamp<NaiveDateTime>> {
248 let ts = mz_persist_types::timestamp::try_parse_monotonic_iso8601_timestamp(a)?;
249 let ts = CheckedTimestamp::from_timestamplike(ts)
250 .expect("monotonic_iso8601 range is a subset of CheckedTimestamp domain");
251 Some(ts)
252}
253
254#[derive(
255 Ord,
256 PartialOrd,
257 Clone,
258 Debug,
259 Eq,
260 PartialEq,
261 Serialize,
262 Deserialize,
263 Hash
264)]
265pub struct CastStringToTimestampTz(pub Option<TimestampPrecision>);
266
267#[sqlfunc(
268 CastStringToTimestampTz,
269 sqlname = "text_to_timestamp_with_time_zone",
270 output_type_expr = SqlScalarType::TimestampTz { precision: self.0 }
271 .nullable(input_type.nullable),
272 inverse = super::CastTimestampTzToString
273)]
274fn cast_string_to_timestamp_tz<'a>(
275 &self,
276 a: &'a str,
277) -> Result<CheckedTimestamp<DateTime<Utc>>, EvalError> {
278 let out = strconv::parse_timestamptz(a)?;
279 let updated = out.round_to_precision(self.0)?;
280 Ok(updated)
281}
282
283#[sqlfunc(
284 sqlname = "text_to_interval",
285 preserves_uniqueness = false,
286 inverse = super::CastIntervalToString
287)]
288fn cast_string_to_interval<'a>(a: &'a str) -> Result<Interval, EvalError> {
289 strconv::parse_interval(a).err_into()
290}
291
292#[sqlfunc(
293 sqlname = "text_to_uuid",
294 preserves_uniqueness = false,
295 inverse = super::CastUuidToString
296)]
297fn cast_string_to_uuid<'a>(a: &'a str) -> Result<Uuid, EvalError> {
298 strconv::parse_uuid(a).err_into()
299}
300
301#[derive(
302 Ord,
303 PartialOrd,
304 Clone,
305 Debug,
306 Eq,
307 PartialEq,
308 Serialize,
309 Deserialize,
310 Hash
311)]
312pub struct CastStringToArray<E = MirScalarExpr> {
313 pub return_ty: SqlScalarType,
315 pub cast_expr: Box<E>,
318}
319
320impl<E: Eval> LazyUnaryFunc for CastStringToArray<E> {
321 fn eval<'a>(
322 &'a self,
323 datums: &[Datum<'a>],
324 temp_storage: &'a RowArena,
325 a: &'a impl Eval,
326 ) -> Result<Datum<'a>, EvalError> {
327 let a = a.eval(datums, temp_storage)?;
328 if a.is_null() {
329 return Ok(Datum::Null);
330 }
331 let (datums, dims) = strconv::parse_array(
332 a.unwrap_str(),
333 || Datum::Null,
334 |elem_text| {
335 let elem_text = match elem_text {
336 Cow::Owned(s) => temp_storage.push_string(s),
337 Cow::Borrowed(s) => s,
338 };
339 self.cast_expr
340 .eval(&[Datum::String(elem_text)], temp_storage)
341 },
342 )?;
343
344 Ok(temp_storage.try_make_datum(|packer| packer.try_push_array(&dims, datums))?)
345 }
346
347 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
349 self.return_ty.clone().nullable(input_type.nullable)
350 }
351
352 fn propagates_nulls(&self) -> bool {
354 true
355 }
356
357 fn introduces_nulls(&self) -> bool {
359 false
360 }
361
362 fn preserves_uniqueness(&self) -> bool {
364 false
365 }
366
367 fn inverse(&self) -> Option<crate::UnaryFunc> {
368 to_unary!(super::CastArrayToString {
369 ty: self.return_ty.clone(),
370 })
371 }
372
373 fn is_monotone(&self) -> bool {
374 false
375 }
376
377 fn is_eliminable_cast(&self) -> bool {
378 false
379 }
380}
381
382impl<E> CastStringToArray<E> {
383 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
386 &'a self,
387 ) -> Result<CastStringToArray<E2>, E2::Error> {
388 Ok(CastStringToArray {
389 return_ty: self.return_ty.clone(),
390 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
391 })
392 }
393
394 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToArray<E2> {
397 CastStringToArray {
398 return_ty: self.return_ty.clone(),
399 cast_expr: Box::new(E2::from(&*self.cast_expr)),
400 }
401 }
402}
403
404impl<E> fmt::Display for CastStringToArray<E> {
405 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406 f.write_str("strtoarray")
407 }
408}
409
410#[derive(
411 Ord,
412 PartialOrd,
413 Clone,
414 Debug,
415 Eq,
416 PartialEq,
417 Serialize,
418 Deserialize,
419 Hash
420)]
421pub struct CastStringToList<E = MirScalarExpr> {
422 pub return_ty: SqlScalarType,
424 pub cast_expr: Box<E>,
427}
428
429impl<E: Eval> LazyUnaryFunc for CastStringToList<E> {
430 fn eval<'a>(
431 &'a self,
432 datums: &[Datum<'a>],
433 temp_storage: &'a RowArena,
434 a: &'a impl Eval,
435 ) -> Result<Datum<'a>, EvalError> {
436 let a = a.eval(datums, temp_storage)?;
437 if a.is_null() {
438 return Ok(Datum::Null);
439 }
440 let parsed_datums = strconv::parse_list(
441 a.unwrap_str(),
442 matches!(
443 self.return_ty.unwrap_list_element_type(),
444 SqlScalarType::List { .. }
445 ),
446 || Datum::Null,
447 |elem_text| {
448 let elem_text = match elem_text {
449 Cow::Owned(s) => temp_storage.push_string(s),
450 Cow::Borrowed(s) => s,
451 };
452 self.cast_expr
453 .eval(&[Datum::String(elem_text)], temp_storage)
454 },
455 )?;
456
457 Ok(temp_storage.make_datum(|packer| packer.push_list(parsed_datums)))
458 }
459
460 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
462 self.return_ty
463 .without_modifiers()
464 .nullable(input_type.nullable)
465 }
466
467 fn propagates_nulls(&self) -> bool {
469 true
470 }
471
472 fn introduces_nulls(&self) -> bool {
474 false
475 }
476
477 fn preserves_uniqueness(&self) -> bool {
479 false
480 }
481
482 fn inverse(&self) -> Option<crate::UnaryFunc> {
483 to_unary!(super::CastListToString {
484 ty: self.return_ty.clone(),
485 })
486 }
487
488 fn is_monotone(&self) -> bool {
489 false
490 }
491
492 fn is_eliminable_cast(&self) -> bool {
493 false
494 }
495}
496
497impl<E> CastStringToList<E> {
498 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
501 &'a self,
502 ) -> Result<CastStringToList<E2>, E2::Error> {
503 Ok(CastStringToList {
504 return_ty: self.return_ty.clone(),
505 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
506 })
507 }
508
509 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToList<E2> {
512 CastStringToList {
513 return_ty: self.return_ty.clone(),
514 cast_expr: Box::new(E2::from(&*self.cast_expr)),
515 }
516 }
517}
518
519impl<E> fmt::Display for CastStringToList<E> {
520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 f.write_str("strtolist")
522 }
523}
524
525#[derive(
526 Ord,
527 PartialOrd,
528 Clone,
529 Debug,
530 Eq,
531 PartialEq,
532 Serialize,
533 Deserialize,
534 Hash
535)]
536pub struct CastStringToMap<E = MirScalarExpr> {
537 pub return_ty: SqlScalarType,
539 pub cast_expr: Box<E>,
542}
543
544impl<E: Eval> LazyUnaryFunc for CastStringToMap<E> {
545 fn eval<'a>(
546 &'a self,
547 datums: &[Datum<'a>],
548 temp_storage: &'a RowArena,
549 a: &'a impl Eval,
550 ) -> Result<Datum<'a>, EvalError> {
551 let a = a.eval(datums, temp_storage)?;
552 if a.is_null() {
553 return Ok(Datum::Null);
554 }
555 let parsed_map = strconv::parse_map(
556 a.unwrap_str(),
557 matches!(
558 self.return_ty.unwrap_map_value_type(),
559 SqlScalarType::Map { .. }
560 ),
561 |value_text| -> Result<Datum, EvalError> {
562 let value_text = match value_text {
563 Some(Cow::Owned(s)) => Datum::String(temp_storage.push_string(s)),
564 Some(Cow::Borrowed(s)) => Datum::String(s),
565 None => Datum::Null,
566 };
567 self.cast_expr.eval(&[value_text], temp_storage)
568 },
569 )?;
570 let mut pairs: Vec<(String, Datum)> = parsed_map.into_iter().map(|(k, v)| (k, v)).collect();
571 pairs.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2));
572 pairs.dedup_by(|(k1, _v1), (k2, _v2)| k1 == k2);
573 Ok(temp_storage.make_datum(|packer| {
574 packer.push_dict_with(|packer| {
575 for (k, v) in pairs {
576 packer.push(Datum::String(&k));
577 packer.push(v);
578 }
579 })
580 }))
581 }
582
583 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
585 self.return_ty.clone().nullable(input_type.nullable)
586 }
587
588 fn propagates_nulls(&self) -> bool {
590 true
591 }
592
593 fn introduces_nulls(&self) -> bool {
595 false
596 }
597
598 fn preserves_uniqueness(&self) -> bool {
600 false
601 }
602
603 fn inverse(&self) -> Option<crate::UnaryFunc> {
604 to_unary!(super::CastMapToString {
605 ty: self.return_ty.clone(),
606 })
607 }
608
609 fn is_monotone(&self) -> bool {
610 false
611 }
612
613 fn is_eliminable_cast(&self) -> bool {
614 false
615 }
616}
617
618impl<E> CastStringToMap<E> {
619 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
622 &'a self,
623 ) -> Result<CastStringToMap<E2>, E2::Error> {
624 Ok(CastStringToMap {
625 return_ty: self.return_ty.clone(),
626 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
627 })
628 }
629
630 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToMap<E2> {
633 CastStringToMap {
634 return_ty: self.return_ty.clone(),
635 cast_expr: Box::new(E2::from(&*self.cast_expr)),
636 }
637 }
638}
639
640impl<E> fmt::Display for CastStringToMap<E> {
641 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
642 f.write_str("strtomap")
643 }
644}
645
646#[derive(
647 Ord,
648 PartialOrd,
649 Clone,
650 Debug,
651 Eq,
652 PartialEq,
653 Serialize,
654 Deserialize,
655 Hash
656)]
657pub struct CastStringToChar {
658 pub length: Option<mz_repr::adt::char::CharLength>,
659 pub fail_on_len: bool,
660}
661
662#[sqlfunc(
664 CastStringToChar,
665 skip_display = true,
666 output_type_expr = SqlScalarType::Char { length: self.length }.nullable(input_type.nullable),
667 could_error = self.fail_on_len && self.length.is_some(),
668 inverse = super::CastCharToString
669)]
670fn cast_string_to_char<'a>(&self, a: &'a str) -> Result<Char<String>, EvalError> {
671 let s = format_str_trim(a, self.length, self.fail_on_len).map_err(|_| {
672 assert!(self.fail_on_len);
673 EvalError::StringValueTooLong {
674 target_type: "character".into(),
675 length: usize::cast_from(self.length.unwrap().into_u32()),
676 }
677 })?;
678
679 Ok(Char(s))
680}
681
682impl fmt::Display for CastStringToChar {
683 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
684 match self.length {
685 Some(length) => {
686 write!(
687 f,
688 "text_to_char[len={}, fail_on_len={}]",
689 length.into_u32(),
690 self.fail_on_len
691 )
692 }
693 None => f.write_str("text_to_char[len=unbounded]"),
694 }
695 }
696}
697
698#[derive(
699 Ord,
700 PartialOrd,
701 Clone,
702 Debug,
703 Eq,
704 PartialEq,
705 Serialize,
706 Deserialize,
707 Hash
708)]
709pub struct CastStringToRange<E = MirScalarExpr> {
710 pub return_ty: SqlScalarType,
712 pub cast_expr: Box<E>,
715}
716
717impl<E: Eval> LazyUnaryFunc for CastStringToRange<E> {
718 fn eval<'a>(
719 &'a self,
720 datums: &[Datum<'a>],
721 temp_storage: &'a RowArena,
722 a: &'a impl Eval,
723 ) -> Result<Datum<'a>, EvalError> {
724 let a = a.eval(datums, temp_storage)?;
725 if a.is_null() {
726 return Ok(Datum::Null);
727 }
728 let mut range = strconv::parse_range(a.unwrap_str(), |elem_text| {
729 let elem_text = match elem_text {
730 Cow::Owned(s) => temp_storage.push_string(s),
731 Cow::Borrowed(s) => s,
732 };
733 self.cast_expr
734 .eval(&[Datum::String(elem_text)], temp_storage)
735 })?;
736
737 range.canonicalize()?;
738
739 Ok(temp_storage.make_datum(|packer| {
740 packer
741 .push_range(range)
742 .expect("must have already handled errors")
743 }))
744 }
745
746 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
748 self.return_ty
749 .without_modifiers()
750 .nullable(input_type.nullable)
751 }
752
753 fn propagates_nulls(&self) -> bool {
755 true
756 }
757
758 fn introduces_nulls(&self) -> bool {
760 false
761 }
762
763 fn preserves_uniqueness(&self) -> bool {
765 false
766 }
767
768 fn inverse(&self) -> Option<crate::UnaryFunc> {
769 to_unary!(super::CastRangeToString {
770 ty: self.return_ty.clone(),
771 })
772 }
773
774 fn is_monotone(&self) -> bool {
775 false
776 }
777
778 fn is_eliminable_cast(&self) -> bool {
779 false
780 }
781}
782
783impl<E> CastStringToRange<E> {
784 pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(
787 &'a self,
788 ) -> Result<CastStringToRange<E2>, E2::Error> {
789 Ok(CastStringToRange {
790 return_ty: self.return_ty.clone(),
791 cast_expr: Box::new(E2::try_from(&*self.cast_expr)?),
792 })
793 }
794
795 pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> CastStringToRange<E2> {
798 CastStringToRange {
799 return_ty: self.return_ty.clone(),
800 cast_expr: Box::new(E2::from(&*self.cast_expr)),
801 }
802 }
803}
804
805impl<E> fmt::Display for CastStringToRange<E> {
806 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
807 f.write_str("strtorange")
808 }
809}
810
811#[derive(
812 Ord,
813 PartialOrd,
814 Clone,
815 Debug,
816 Eq,
817 PartialEq,
818 Serialize,
819 Deserialize,
820 Hash
821)]
822pub struct CastStringToVarChar {
823 pub length: Option<VarCharMaxLength>,
824 pub fail_on_len: bool,
825}
826
827#[sqlfunc(
828 CastStringToVarChar,
829 skip_display = true,
830 output_type_expr = SqlScalarType::VarChar { max_length: self.length }
831 .nullable(input_type.nullable),
832 could_error = self.fail_on_len && self.length.is_some(),
833 inverse = super::CastVarCharToString,
834 preserves_uniqueness = self.length.is_none(),
835 is_eliminable_cast = self.length.is_none()
836)]
837fn cast_string_to_var_char<'a>(&self, a: &'a str) -> Result<VarChar<&'a str>, EvalError> {
838 let s = mz_repr::adt::varchar::format_str(a, self.length, self.fail_on_len).map_err(|_| {
839 assert!(self.fail_on_len);
840 EvalError::StringValueTooLong {
841 target_type: "character varying".into(),
842 length: usize::cast_from(self.length.unwrap().into_u32()),
843 }
844 })?;
845
846 Ok(VarChar(s))
847}
848
849impl fmt::Display for CastStringToVarChar {
850 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
851 match self.length {
852 Some(length) => {
853 write!(
854 f,
855 "text_to_varchar[len={}, fail_on_len={}]",
856 length.into_u32(),
857 self.fail_on_len
858 )
859 }
860 None => f.write_str("text_to_varchar[len=unbounded]"),
861 }
862 }
863}
864
865static INT2VECTOR_CAST_EXPR: LazyLock<MirScalarExpr> = LazyLock::new(|| MirScalarExpr::CallUnary {
868 func: UnaryFunc::CastStringToInt16(CastStringToInt16),
869 expr: Box::new(MirScalarExpr::column(0)),
870});
871
872#[sqlfunc(
873 sqlname = "strtoint2vector",
874 inverse = super::CastInt2VectorToString
875)]
876fn cast_string_to_int2_vector<'a>(
877 a: &'a str,
878 temp_storage: &'a RowArena,
879) -> Result<Int2Vector<'a>, EvalError> {
880 let datums = strconv::parse_legacy_vector(a, |elem_text| {
881 let elem_text = match elem_text {
882 Cow::Owned(s) => temp_storage.push_string(s),
883 Cow::Borrowed(s) => s,
884 };
885 INT2VECTOR_CAST_EXPR.eval(&[Datum::String(elem_text)], temp_storage)
886 })?;
887 Ok(Int2Vector(array_create_scalar(&datums, temp_storage)?))
888}
889
890#[sqlfunc(
891 sqlname = "text_to_jsonb",
892 preserves_uniqueness = false,
893 inverse = super::CastJsonbToString
894)]
895fn cast_string_to_jsonb<'a>(a: &'a str) -> Result<Jsonb, EvalError> {
897 Ok(strconv::parse_jsonb(a)?)
898}
899
900#[sqlfunc(sqlname = "btrim")]
901fn trim_whitespace<'a>(a: &'a str) -> &'a str {
902 a.trim_matches(' ')
903}
904
905#[sqlfunc(sqlname = "ltrim")]
906fn trim_leading_whitespace<'a>(a: &'a str) -> &'a str {
907 a.trim_start_matches(' ')
908}
909
910#[sqlfunc(sqlname = "rtrim")]
911fn trim_trailing_whitespace<'a>(a: &'a str) -> &'a str {
912 a.trim_end_matches(' ')
913}
914
915#[sqlfunc(sqlname = "initcap")]
916fn initcap<'a>(a: &'a str) -> String {
917 let mut out = String::new();
918 let mut capitalize_next = true;
919 for ch in a.chars() {
920 if capitalize_next {
921 out.extend(ch.to_uppercase())
922 } else {
923 out.extend(ch.to_lowercase())
924 };
925 capitalize_next = !ch.is_alphanumeric();
926 }
927 out
928}
929
930#[sqlfunc(sqlname = "ascii")]
931fn ascii<'a>(a: &'a str) -> i32 {
932 a.chars()
933 .next()
934 .and_then(|c| i32::try_from(u32::from(c)).ok())
935 .unwrap_or(0)
936}
937
938#[sqlfunc(sqlname = "char_length")]
939fn char_length<'a>(a: &'a str) -> Result<i32, EvalError> {
940 let length = a.chars().count();
941 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
942}
943
944#[sqlfunc(sqlname = "bit_length")]
945fn bit_length_string<'a>(a: &'a str) -> Result<i32, EvalError> {
946 let length = a.as_bytes().len() * 8;
947 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
948}
949
950#[sqlfunc(sqlname = "octet_length")]
951fn byte_length_string<'a>(a: &'a str) -> Result<i32, EvalError> {
952 let length = a.as_bytes().len();
953 i32::try_from(length).or_else(|_| Err(EvalError::Int32OutOfRange(length.to_string().into())))
954}
955
956#[sqlfunc]
957fn upper<'a>(a: &'a str) -> String {
958 a.to_uppercase()
959}
960
961#[sqlfunc]
962fn lower<'a>(a: &'a str) -> String {
963 a.to_lowercase()
964}
965
966#[sqlfunc]
967fn normalize(text: &str, form_str: &str) -> Result<String, EvalError> {
968 use unicode_normalization::UnicodeNormalization;
969
970 match form_str.to_uppercase().as_str() {
971 "NFC" => Ok(text.nfc().collect()),
972 "NFD" => Ok(text.nfd().collect()),
973 "NFKC" => Ok(text.nfkc().collect()),
974 "NFKD" => Ok(text.nfkd().collect()),
975 _ => Err(EvalError::InvalidParameterValue(
976 format!("invalid normalization form: {}", form_str).into(),
977 )),
978 }
979}
980
981#[derive(
982 Ord,
983 PartialOrd,
984 Clone,
985 Debug,
986 Eq,
987 PartialEq,
988 Serialize,
989 Deserialize,
990 Hash
991)]
992pub struct IsLikeMatch(pub like_pattern::Matcher);
993
994#[sqlfunc(IsLikeMatch, skip_display = true)]
995fn is_like_match<'a>(&self, haystack: &'a str) -> bool {
996 self.0.is_match(haystack)
997}
998
999impl fmt::Display for IsLikeMatch {
1000 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1001 write!(
1002 f,
1003 "{}like[{}]",
1004 if self.0.case_insensitive { "i" } else { "" },
1005 self.0.pattern.escaped()
1006 )
1007 }
1008}
1009
1010#[derive(
1011 Ord,
1012 PartialOrd,
1013 Clone,
1014 Debug,
1015 Eq,
1016 PartialEq,
1017 Serialize,
1018 Deserialize,
1019 Hash
1020)]
1021pub struct IsRegexpMatch(pub Regex);
1022
1023#[sqlfunc(IsRegexpMatch, skip_display = true)]
1024fn is_regexp_match<'a>(&self, haystack: &'a str) -> bool {
1025 self.0.is_match(haystack)
1026}
1027
1028impl fmt::Display for IsRegexpMatch {
1029 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1030 write!(
1031 f,
1032 "is_regexp_match[{}, case_insensitive={}]",
1033 self.0.pattern().escaped(),
1034 self.0.case_insensitive
1035 )
1036 }
1037}
1038
1039#[derive(
1040 Ord,
1041 PartialOrd,
1042 Clone,
1043 Debug,
1044 Eq,
1045 PartialEq,
1046 Serialize,
1047 Deserialize,
1048 Hash
1049)]
1050#[serde(rename = "RegexpMatchStatic")]
1055pub struct RegexpMatch(pub Regex);
1056
1057#[sqlfunc(
1058 RegexpMatch,
1059 skip_display = true,
1060 output_type_expr = SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)
1062)]
1063fn regexp_match<'a>(
1064 &self,
1065 haystack: &'a str,
1066 temp_storage: &'a RowArena,
1067) -> Result<Option<Array<'a>>, EvalError> {
1068 regexp_match_static(haystack, temp_storage, &self.0)
1069}
1070
1071impl fmt::Display for RegexpMatch {
1072 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1073 write!(
1074 f,
1075 "regexp_match[{}, case_insensitive={}]",
1076 self.0.pattern().escaped(),
1077 self.0.case_insensitive
1078 )
1079 }
1080}
1081
1082#[derive(
1083 Ord,
1084 PartialOrd,
1085 Clone,
1086 Debug,
1087 Eq,
1088 PartialEq,
1089 Serialize,
1090 Deserialize,
1091 Hash
1092)]
1093#[serde(rename = "RegexpSplitToArrayStatic")]
1095pub struct RegexpSplitToArray(pub Regex);
1096
1097#[sqlfunc(
1098 RegexpSplitToArray,
1099 skip_display = true,
1100 output_type_expr = SqlScalarType::Array(Box::new(SqlScalarType::String))
1101 .nullable(input_type.nullable)
1102)]
1103fn regexp_split_to_array<'a>(
1104 &self,
1105 haystack: &'a str,
1106 temp_storage: &'a RowArena,
1107) -> Result<Array<'a>, EvalError> {
1108 regexp_split_to_array_re(haystack, &self.0, temp_storage)
1109}
1110
1111impl fmt::Display for RegexpSplitToArray {
1112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1113 write!(
1114 f,
1115 "regexp_split_to_array[{}, case_insensitive={}]",
1116 self.0.pattern().escaped(),
1117 self.0.case_insensitive
1118 )
1119 }
1120}
1121
1122#[sqlfunc(sqlname = "mz_panic")]
1123fn panic<'a>(a: &'a str) -> String {
1124 print!("{}", a);
1125 panic!("{}", a)
1126}
1127
1128#[sqlfunc(sqlname = "quote_ident", preserves_uniqueness = true)]
1129fn quote_ident<'a>(a: &'a str) -> Result<String, EvalError> {
1130 let i = mz_sql_parser::ast::Ident::new(a).map_err(|err| EvalError::InvalidIdentifier {
1131 ident: a.into(),
1132 detail: Some(err.to_string().into()),
1133 })?;
1134 Ok(i.to_string())
1135}
1136
1137#[derive(
1138 Ord,
1139 PartialOrd,
1140 Clone,
1141 Debug,
1142 Eq,
1143 PartialEq,
1144 Serialize,
1145 Deserialize,
1146 Hash
1147)]
1148#[serde(rename = "RegexpReplaceStatic")]
1150pub struct RegexpReplace {
1151 pub regex: Regex,
1152 pub limit: usize,
1153}
1154
1155#[sqlfunc(RegexpReplace, skip_display = true)]
1156fn regexp_replace<'a>(&self, source: &'a str, replacement: &'a str) -> Cow<'a, str> {
1157 self.regex.replacen(source, self.limit, replacement)
1161}
1162
1163impl fmt::Display for RegexpReplace {
1164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1165 write!(
1166 f,
1167 "regexp_replace[{}, case_insensitive={}, limit={}]",
1168 self.regex.pattern().escaped(),
1169 self.regex.case_insensitive,
1170 self.limit
1171 )
1172 }
1173}