1use alloc::string::ToString;
9use alloc::vec::Vec;
10
11use spg_sql::ast::{ColumnTypeName, Expr, Literal, UnOp, VecEncoding as SqlVecEncoding};
12use spg_storage::{ColumnSchema, DataType, StorageError, Value, VecEncoding};
13
14use crate::EngineError;
15use crate::eval::{self, EvalContext, EvalError};
16use crate::numeric::{
17 numeric_from_float, numeric_from_integer, numeric_rescale, numeric_round_to_integer,
18 parse_numeric_text,
19};
20
21pub(crate) fn pseudo_type(name: &str) -> Option<&'static str> {
43 const NAMES: &[&str] = &[
44 "anyarray",
45 "anycompatible",
46 "anycompatiblearray",
47 "anycompatiblemultirange",
48 "anycompatiblenonarray",
49 "anycompatiblerange",
50 "anyelement",
51 "anyenum",
52 "anymultirange",
53 "anynonarray",
54 "anyrange",
55 "cstring",
56 "event_trigger",
57 "fdw_handler",
58 "index_am_handler",
59 "internal",
60 "language_handler",
61 "pg_ddl_command",
62 "record",
63 "table_am_handler",
64 "trigger",
65 "tsm_handler",
66 "unknown",
67 "void",
68 ];
69 NAMES.iter().find(|n| n.eq_ignore_ascii_case(name)).copied()
70}
71
72pub(crate) fn decode_bytea_literal(s: &str) -> Result<alloc::vec::Vec<u8>, alloc::string::String> {
73 let s = s.trim();
74 if let Some(hex) = s.strip_prefix("\\x").or_else(|| s.strip_prefix("\\X")) {
75 let cleaned: alloc::string::String = hex.chars().filter(|c| !c.is_whitespace()).collect();
77 if cleaned.len() % 2 != 0 {
78 return Err(alloc::string::String::from(
79 "invalid hexadecimal data: odd number of digits",
80 ));
81 }
82 let mut out = alloc::vec::Vec::with_capacity(cleaned.len() / 2);
83 let cleaned_bytes = cleaned.as_bytes();
84 for i in (0..cleaned_bytes.len()).step_by(2) {
85 let hi = hex_nibble(cleaned_bytes[i]).map_err(|()| bad_hex_digit(cleaned_bytes[i]))?;
86 let lo = hex_nibble(cleaned_bytes[i + 1])
87 .map_err(|()| bad_hex_digit(cleaned_bytes[i + 1]))?;
88 out.push((hi << 4) | lo);
89 }
90 return Ok(out);
91 }
92 let bytes = s.as_bytes();
95 let mut out = alloc::vec::Vec::with_capacity(bytes.len());
96 let mut i = 0;
97 while i < bytes.len() {
98 let b = bytes[i];
99 if b == b'\\' && i + 1 < bytes.len() {
100 let n = bytes[i + 1];
101 if n == b'\\' {
102 out.push(b'\\');
103 i += 2;
104 continue;
105 }
106 if n.is_ascii_digit()
107 && i + 3 < bytes.len()
108 && bytes[i + 2].is_ascii_digit()
109 && bytes[i + 3].is_ascii_digit()
110 {
111 let oct = |x: u8| (x - b'0') as u32;
112 let v = oct(n) * 64 + oct(bytes[i + 2]) * 8 + oct(bytes[i + 3]);
113 if v <= 0xFF {
114 out.push(v as u8);
115 i += 4;
116 continue;
117 }
118 }
119 }
120 out.push(b);
121 i += 1;
122 }
123 Ok(out)
124}
125
126pub(crate) fn hex_nibble(b: u8) -> Result<u8, ()> {
127 match b {
128 b'0'..=b'9' => Ok(b - b'0'),
129 b'a'..=b'f' => Ok(b - b'a' + 10),
130 b'A'..=b'F' => Ok(b - b'A' + 10),
131 _ => Err(()),
132 }
133}
134
135fn bad_hex_digit(b: u8) -> alloc::string::String {
137 alloc::format!("invalid hexadecimal digit: \"{}\"", b as char)
138}
139
140#[derive(Clone, Copy)]
145enum UniformArrayKind {
146 Bool,
147 Float,
148 Numeric,
149 Date,
150 Timestamp,
151 Uuid,
152 Bytes,
153 Interval,
154 Money,
155}
156
157impl UniformArrayKind {
158 fn build(self, items: alloc::vec::Vec<Value<'static>>) -> Value<'static> {
159 match self {
160 Self::Bool => Value::BoolArray(
161 items
162 .into_iter()
163 .map(|v| match v {
164 Value::Null => None,
165 Value::Bool(b) => Some(b),
166 _ => unreachable!("uniform Bool"),
167 })
168 .collect(),
169 ),
170 Self::Float => Value::FloatArray(
171 items
172 .into_iter()
173 .map(|v| match v {
174 Value::Null => None,
175 Value::Float(x) => Some(x),
176 _ => unreachable!("uniform Float"),
177 })
178 .collect(),
179 ),
180 Self::Numeric => Value::NumericArray(
181 items
182 .into_iter()
183 .map(|v| match v {
184 Value::Null => None,
185 Value::Numeric { scaled, scale, .. } => Some((scaled, scale)),
186 _ => unreachable!("uniform Numeric"),
187 })
188 .collect(),
189 ),
190 Self::Date => Value::DateArray(
191 items
192 .into_iter()
193 .map(|v| match v {
194 Value::Null => None,
195 Value::Date(d) => Some(d),
196 _ => unreachable!("uniform Date"),
197 })
198 .collect(),
199 ),
200 Self::Timestamp => Value::TimestampArray(
201 items
202 .into_iter()
203 .map(|v| match v {
204 Value::Null => None,
205 Value::Timestamp(t) => Some(t),
206 _ => unreachable!("uniform Timestamp"),
207 })
208 .collect(),
209 ),
210 Self::Uuid => Value::UuidArray(
211 items
212 .into_iter()
213 .map(|v| match v {
214 Value::Null => None,
215 Value::Uuid(b) => Some(b),
216 _ => unreachable!("uniform Uuid"),
217 })
218 .collect(),
219 ),
220 Self::Bytes => Value::BytesArray(
221 items
222 .into_iter()
223 .map(|v| match v {
224 Value::Null => None,
225 Value::Bytes(b) => Some(b.into_owned()),
226 _ => unreachable!("uniform Bytes"),
227 })
228 .collect(),
229 ),
230 Self::Interval => Value::IntervalArray(
231 items
232 .into_iter()
233 .map(|v| match v {
234 Value::Null => None,
235 Value::Interval {
236 months,
237 days,
238 micros,
239 kind,
240 } => Some(spg_storage::IntervalSpan {
241 months,
242 days,
243 micros,
244 kind,
245 }),
246 _ => unreachable!("uniform Interval"),
247 })
248 .collect(),
249 ),
250 Self::Money => Value::MoneyArray(
251 items
252 .into_iter()
253 .map(|v| match v {
254 Value::Null => None,
255 Value::Money(c) => Some(c),
256 _ => unreachable!("uniform Money"),
257 })
258 .collect(),
259 ),
260 }
261 }
262}
263
264fn widen_uniform_typed(items: &[Value<'static>]) -> Option<UniformArrayKind> {
265 let mut kind: Option<UniformArrayKind> = None;
266 let mut saw_non_null = false;
267 for v in items {
268 let this = match v {
269 Value::Null => continue,
270 Value::Bool(_) => UniformArrayKind::Bool,
271 Value::Float(_) => UniformArrayKind::Float,
272 Value::Numeric { .. } => UniformArrayKind::Numeric,
273 Value::Date(_) => UniformArrayKind::Date,
274 Value::Timestamp(_) => UniformArrayKind::Timestamp,
275 Value::Uuid(_) => UniformArrayKind::Uuid,
276 Value::Bytes(_) => UniformArrayKind::Bytes,
277 Value::Interval { .. } => UniformArrayKind::Interval,
278 Value::Money(_) => UniformArrayKind::Money,
279 _ => return None,
283 };
284 match kind {
285 None => kind = Some(this),
286 Some(prev) if discriminant_eq(prev, this) => {}
287 Some(_) => return None,
288 }
289 saw_non_null = true;
290 }
291 if saw_non_null { kind } else { None }
292}
293
294fn discriminant_eq(a: UniformArrayKind, b: UniformArrayKind) -> bool {
295 matches!(
296 (a, b),
297 (UniformArrayKind::Bool, UniformArrayKind::Bool)
298 | (UniformArrayKind::Float, UniformArrayKind::Float)
299 | (UniformArrayKind::Numeric, UniformArrayKind::Numeric)
300 | (UniformArrayKind::Date, UniformArrayKind::Date)
301 | (UniformArrayKind::Timestamp, UniformArrayKind::Timestamp)
302 | (UniformArrayKind::Uuid, UniformArrayKind::Uuid)
303 | (UniformArrayKind::Bytes, UniformArrayKind::Bytes)
304 | (UniformArrayKind::Interval, UniformArrayKind::Interval)
305 | (UniformArrayKind::Money, UniformArrayKind::Money)
306 )
307}
308
309pub(crate) fn array_literal_widen(items: alloc::vec::Vec<Value<'static>>) -> Value<'static> {
323 if let Some(m) = crate::eval::values::build_2d_from_rows(&items) {
334 return m;
335 }
336 if let Some(arr) = widen_uniform_typed(&items) {
337 return arr.build(items);
338 }
339 let mut has_text = false;
340 let mut has_bigint = false;
341 let mut has_int = false;
342 for v in &items {
343 match v {
344 Value::Null => {}
345 Value::Text(_) | Value::Json(_) => has_text = true,
346 Value::BigInt(_) => has_bigint = true,
347 Value::Int(_) | Value::SmallInt(_) => has_int = true,
348 _ => has_text = true,
349 }
350 }
351 if has_text || (!has_bigint && !has_int) {
352 let out: alloc::vec::Vec<Option<alloc::string::String>> = items
353 .into_iter()
354 .map(|v| match v {
355 Value::Null => None,
356 Value::Text(s) | Value::Json(s) => Some(s.into_owned()),
357 other => Some(alloc::format!("{other:?}")),
358 })
359 .collect();
360 return Value::TextArray(out);
361 }
362 if has_bigint {
363 let out: alloc::vec::Vec<Option<i64>> = items
364 .into_iter()
365 .map(|v| match v {
366 Value::Null => None,
367 Value::Int(n) => Some(i64::from(n)),
368 Value::SmallInt(n) => Some(i64::from(n)),
369 Value::BigInt(n) => Some(n),
370 _ => unreachable!("widen: unexpected non-integer in BigInt path"),
371 })
372 .collect();
373 return Value::BigIntArray(out);
374 }
375 let out: alloc::vec::Vec<Option<i32>> = items
376 .into_iter()
377 .map(|v| match v {
378 Value::Null => None,
379 Value::Int(n) => Some(n),
380 Value::SmallInt(n) => Some(i32::from(n)),
381 _ => unreachable!("widen: unexpected non-i32-compatible in Int path"),
382 })
383 .collect();
384 Value::IntArray(out)
385}
386
387#[must_use]
403pub(crate) fn malformed_array_literal(text: &str) -> alloc::string::String {
404 let t = text.trim();
405 let detail = if !t.starts_with('{') {
406 "Array value must start with \"{\" or dimension information."
407 } else {
408 match first_unquoted_close_brace(&t[1..]) {
412 None => "Unexpected end of input.",
413 Some(close) => {
414 let inner = &t[1..1 + close];
415 if !t[1 + close + 1..].trim().is_empty() {
416 "Junk after closing right brace."
417 } else if inner.trim_end().ends_with(',') {
418 "Unexpected \"}\" character."
419 } else {
420 "Unexpected end of input."
421 }
422 }
423 }
424 };
425 alloc::format!("malformed array literal: \"{text}\" DETAIL: {detail}")
426}
427
428fn first_unquoted_close_brace(body: &str) -> Option<usize> {
430 let bs = body.as_bytes();
431 let mut in_quote = false;
432 let mut k = 0;
433 while k < bs.len() {
434 match bs[k] {
435 b'\\' if in_quote => k += 1,
436 b'"' => in_quote = !in_quote,
437 b'}' if !in_quote => return Some(k),
438 _ => {}
439 }
440 k += 1;
441 }
442 None
443}
444
445pub(crate) fn decode_text_array_literal(
446 s: &str,
447) -> Result<alloc::vec::Vec<Option<alloc::string::String>>, &'static str> {
448 let trimmed = s.trim();
449 let body = trimmed
455 .strip_prefix('{')
456 .ok_or("TEXT[] literal must be enclosed in '{...}'")?;
457 let close =
458 first_unquoted_close_brace(body).ok_or("TEXT[] literal must be enclosed in '{...}'")?;
459 if !body[close + 1..].trim().is_empty() {
460 return Err("junk after closing right brace");
461 }
462 let inner = &body[..close];
463 let mut out: alloc::vec::Vec<Option<alloc::string::String>> = alloc::vec::Vec::new();
464 if inner.trim().is_empty() {
465 return Ok(out);
466 }
467 let bytes = inner.as_bytes();
468 let mut i = 0;
469 while i <= bytes.len() {
470 while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') {
472 i += 1;
473 }
474 if i < bytes.len() && bytes[i] == b'"' {
476 i += 1; let mut buf = alloc::string::String::new();
478 while i < bytes.len() && bytes[i] != b'"' {
479 if bytes[i] == b'\\' && i + 1 < bytes.len() {
480 buf.push(bytes[i + 1] as char);
481 i += 2;
482 } else {
483 buf.push(bytes[i] as char);
484 i += 1;
485 }
486 }
487 if i >= bytes.len() {
488 return Err("unterminated quoted element");
489 }
490 i += 1; out.push(Some(buf));
492 } else {
493 let start = i;
495 while i < bytes.len() && bytes[i] != b',' {
496 i += 1;
497 }
498 let raw = inner[start..i].trim();
499 if raw.is_empty() {
504 return Err("empty array element");
505 }
506 if raw.eq_ignore_ascii_case("NULL") {
507 out.push(None);
508 } else {
509 out.push(Some(alloc::string::ToString::to_string(raw)));
510 }
511 }
512 while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') {
514 i += 1;
515 }
516 if i >= bytes.len() {
517 break;
518 }
519 if bytes[i] != b',' {
520 return Err("expected ',' between TEXT[] elements");
521 }
522 i += 1;
523 }
524 Ok(out)
525}
526
527pub(crate) fn encode_text_array(items: &[Option<alloc::string::String>]) -> alloc::string::String {
532 let mut out = alloc::string::String::with_capacity(2 + items.len() * 8);
533 out.push('{');
534 for (i, item) in items.iter().enumerate() {
535 if i > 0 {
536 out.push(',');
537 }
538 match item {
539 None => out.push_str("NULL"),
540 Some(s) => {
541 let needs_quote = s.is_empty()
542 || s.eq_ignore_ascii_case("NULL")
543 || s.chars()
544 .any(|c| matches!(c, ',' | '{' | '}' | '"' | '\\' | ' ' | '\t'));
545 if needs_quote {
546 out.push('"');
547 for c in s.chars() {
548 if c == '"' || c == '\\' {
549 out.push('\\');
550 }
551 out.push(c);
552 }
553 out.push('"');
554 } else {
555 out.push_str(s);
556 }
557 }
558 }
559 }
560 out.push('}');
561 out
562}
563
564pub(crate) fn encode_bytea_hex(b: &[u8]) -> alloc::string::String {
568 let mut out = alloc::string::String::with_capacity(2 + 2 * b.len());
569 out.push_str("\\x");
570 for byte in b {
571 let hi = byte >> 4;
572 let lo = byte & 0x0F;
573 out.push(hex_digit(hi));
574 out.push(hex_digit(lo));
575 }
576 out
577}
578
579pub(crate) const fn hex_digit(n: u8) -> char {
580 match n {
581 0..=9 => (b'0' + n) as char,
582 10..=15 => (b'a' + n - 10) as char,
583 _ => '?',
584 }
585}
586
587pub(crate) fn parse_hstore_str(
600 s: &str,
601) -> Option<Vec<(alloc::string::String, Option<alloc::string::String>)>> {
602 let bytes = s.as_bytes();
603 let mut i = 0;
604 let mut out: Vec<(alloc::string::String, Option<alloc::string::String>)> = Vec::new();
605 let skip_ws = |bytes: &[u8], i: &mut usize| {
606 while *i < bytes.len() && matches!(bytes[*i], b' ' | b'\t' | b'\n' | b'\r') {
607 *i += 1;
608 }
609 };
610 let parse_token = |bytes: &[u8], i: &mut usize| -> Option<alloc::string::String> {
611 if *i >= bytes.len() {
612 return None;
613 }
614 if bytes[*i] == b'"' {
615 *i += 1;
616 let mut out = alloc::string::String::new();
617 while *i < bytes.len() {
618 match bytes[*i] {
619 b'"' => {
620 *i += 1;
621 return Some(out);
622 }
623 b'\\' if *i + 1 < bytes.len() => {
624 out.push(bytes[*i + 1] as char);
625 *i += 2;
626 }
627 c => {
628 out.push(c as char);
629 *i += 1;
630 }
631 }
632 }
633 None
634 } else {
635 let start = *i;
636 while *i < bytes.len()
637 && !matches!(bytes[*i], b' ' | b'\t' | b'\n' | b'\r' | b',' | b'=')
638 {
639 *i += 1;
640 }
641 if *i == start {
642 return None;
643 }
644 Some(alloc::str::from_utf8(&bytes[start..*i]).ok()?.to_string())
645 }
646 };
647 skip_ws(bytes, &mut i);
648 while i < bytes.len() {
649 let key = parse_token(bytes, &mut i)?;
650 skip_ws(bytes, &mut i);
651 if i + 1 >= bytes.len() || bytes[i] != b'=' || bytes[i + 1] != b'>' {
652 return None;
653 }
654 i += 2;
655 skip_ws(bytes, &mut i);
656 let val_token = if i + 4 <= bytes.len()
658 && bytes[i..i + 4].eq_ignore_ascii_case(b"NULL")
659 && (i + 4 == bytes.len() || matches!(bytes[i + 4], b' ' | b'\t' | b',' | b'\n' | b'\r'))
660 {
661 i += 4;
662 None
663 } else {
664 Some(parse_token(bytes, &mut i)?)
665 };
666 if out.iter().any(|(k, _)| k == &key) {
671 } else {
673 out.push((key, val_token));
674 }
675 skip_ws(bytes, &mut i);
676 if i >= bytes.len() {
677 break;
678 }
679 if bytes[i] == b',' {
680 i += 1;
681 skip_ws(bytes, &mut i);
682 continue;
683 }
684 return None;
685 }
686 Some(out)
687}
688
689pub(crate) fn format_hstore_str(
693 pairs: &[(alloc::string::String, Option<alloc::string::String>)],
694) -> alloc::string::String {
695 let mut out = alloc::string::String::new();
696 for (i, (k, v)) in pairs.iter().enumerate() {
697 if i > 0 {
698 out.push_str(", ");
699 }
700 out.push('"');
701 out.push_str(k);
702 out.push_str("\"=>");
703 match v {
704 None => out.push_str("NULL"),
705 Some(val) => {
706 out.push('"');
707 out.push_str(val);
708 out.push('"');
709 }
710 }
711 }
712 out
713}
714
715pub fn format_hstore_text(
718 pairs: &[(alloc::string::String, Option<alloc::string::String>)],
719) -> alloc::string::String {
720 format_hstore_str(pairs)
721}
722
723pub(crate) fn split_2d_literal(s: &str) -> Result<Vec<Vec<alloc::string::String>>, &'static str> {
728 let s = s.trim();
729 let outer = s
730 .strip_prefix('{')
731 .and_then(|x| x.strip_suffix('}'))
732 .ok_or("missing outer '{...}' braces")?;
733 let trimmed = outer.trim();
734 if trimmed.is_empty() {
735 return Ok(Vec::new());
736 }
737 let mut rows: Vec<Vec<alloc::string::String>> = Vec::new();
738 let mut i = 0;
739 let bytes = trimmed.as_bytes();
740 while i < bytes.len() {
741 while i < bytes.len() && matches!(bytes[i], b' ' | b'\t' | b'\n' | b'\r' | b',') {
742 i += 1;
743 }
744 if i >= bytes.len() {
745 break;
746 }
747 if bytes[i] != b'{' {
748 return Err("expected '{' opening a row");
749 }
750 i += 1;
751 let row_start = i;
752 let mut depth = 1;
753 while i < bytes.len() && depth > 0 {
754 match bytes[i] {
755 b'{' => depth += 1,
756 b'}' => depth -= 1,
757 _ => {}
758 }
759 if depth > 0 {
760 i += 1;
761 }
762 }
763 if depth != 0 {
764 return Err("unbalanced '{...}' in row");
765 }
766 let row_text = &trimmed[row_start..i];
767 i += 1;
768 let cells: Vec<alloc::string::String> = if row_text.trim().is_empty() {
769 Vec::new()
770 } else {
771 row_text.split(',').map(|t| t.trim().to_string()).collect()
772 };
773 rows.push(cells);
774 }
775 if let Some(first) = rows.first() {
776 let cols = first.len();
777 for r in &rows {
778 if r.len() != cols {
779 return Err("ragged 2D array (rows have different column counts)");
780 }
781 }
782 }
783 Ok(rows)
784}
785
786pub(crate) fn parse_int_2d_literal(s: &str) -> Result<Vec<Vec<Option<i32>>>, &'static str> {
787 let raw = split_2d_literal(s)?;
788 raw.into_iter()
789 .map(|row| {
790 row.into_iter()
791 .map(|cell| {
792 if cell.eq_ignore_ascii_case("NULL") {
793 Ok(None)
794 } else {
795 cell.parse::<i32>()
796 .map(Some)
797 .map_err(|_| "invalid int element")
798 }
799 })
800 .collect()
801 })
802 .collect()
803}
804
805pub(crate) fn parse_bigint_2d_literal(s: &str) -> Result<Vec<Vec<Option<i64>>>, &'static str> {
806 let raw = split_2d_literal(s)?;
807 raw.into_iter()
808 .map(|row| {
809 row.into_iter()
810 .map(|cell| {
811 if cell.eq_ignore_ascii_case("NULL") {
812 Ok(None)
813 } else {
814 cell.parse::<i64>()
815 .map(Some)
816 .map_err(|_| "invalid bigint element")
817 }
818 })
819 .collect()
820 })
821 .collect()
822}
823
824pub(crate) fn parse_text_2d_literal(
825 s: &str,
826) -> Result<Vec<Vec<Option<alloc::string::String>>>, &'static str> {
827 let raw = split_2d_literal(s)?;
828 Ok(raw
829 .into_iter()
830 .map(|row| {
831 row.into_iter()
832 .map(|cell| {
833 if cell.eq_ignore_ascii_case("NULL") {
834 None
835 } else {
836 Some(cell.trim_matches('"').to_string())
837 }
838 })
839 .collect()
840 })
841 .collect())
842}
843
844pub(crate) fn format_int_2d_text(rows: &[Vec<Option<i32>>]) -> alloc::string::String {
845 let mut out = alloc::string::String::from("{");
846 for (i, row) in rows.iter().enumerate() {
847 if i > 0 {
848 out.push(',');
849 }
850 out.push('{');
851 for (j, cell) in row.iter().enumerate() {
852 if j > 0 {
853 out.push(',');
854 }
855 match cell {
856 None => out.push_str("NULL"),
857 Some(n) => out.push_str(&alloc::format!("{n}")),
858 }
859 }
860 out.push('}');
861 }
862 out.push('}');
863 out
864}
865
866pub(crate) fn format_bigint_2d_text(rows: &[Vec<Option<i64>>]) -> alloc::string::String {
867 let mut out = alloc::string::String::from("{");
868 for (i, row) in rows.iter().enumerate() {
869 if i > 0 {
870 out.push(',');
871 }
872 out.push('{');
873 for (j, cell) in row.iter().enumerate() {
874 if j > 0 {
875 out.push(',');
876 }
877 match cell {
878 None => out.push_str("NULL"),
879 Some(n) => out.push_str(&alloc::format!("{n}")),
880 }
881 }
882 out.push('}');
883 }
884 out.push('}');
885 out
886}
887
888pub(crate) fn format_text_2d_text(
889 rows: &[Vec<Option<alloc::string::String>>],
890) -> alloc::string::String {
891 let mut out = alloc::string::String::from("{");
892 for (i, row) in rows.iter().enumerate() {
893 if i > 0 {
894 out.push(',');
895 }
896 out.push('{');
897 for (j, cell) in row.iter().enumerate() {
898 if j > 0 {
899 out.push(',');
900 }
901 match cell {
902 None => out.push_str("NULL"),
903 Some(s) => out.push_str(s),
904 }
905 }
906 out.push('}');
907 }
908 out.push('}');
909 out
910}
911
912pub fn format_int_2d_text_pub(rows: &[Vec<Option<i32>>]) -> alloc::string::String {
915 format_int_2d_text(rows)
916}
917pub fn format_bigint_2d_text_pub(rows: &[Vec<Option<i64>>]) -> alloc::string::String {
918 format_bigint_2d_text(rows)
919}
920pub fn format_text_2d_text_pub(
921 rows: &[Vec<Option<alloc::string::String>>],
922) -> alloc::string::String {
923 format_text_2d_text(rows)
924}
925
926#[must_use]
930pub fn format_bool_2d_text_pub(rows: &[Vec<Option<bool>>]) -> alloc::string::String {
931 use core::fmt::Write as _;
932 let mut out = alloc::string::String::from("{");
933 for (i, row) in rows.iter().enumerate() {
934 if i > 0 {
935 out.push(',');
936 }
937 out.push('{');
938 for (j, cell) in row.iter().enumerate() {
939 if j > 0 {
940 out.push(',');
941 }
942 let _ = match cell {
943 None => write!(out, "NULL"),
944 Some(true) => write!(out, "t"),
945 Some(false) => write!(out, "f"),
946 };
947 }
948 out.push('}');
949 }
950 out.push('}');
951 out
952}
953
954pub(crate) type CanonRangeBounds = (
969 Option<Value<'static>>,
970 Option<Value<'static>>,
971 bool,
972 bool,
973 bool,
974);
975
976pub(crate) fn canonicalize_range_bounds(
978 kind: spg_storage::RangeKind,
979 lower: Option<Value<'static>>,
980 upper: Option<Value<'static>>,
981 lower_inc: bool,
982 upper_inc: bool,
983) -> Option<CanonRangeBounds> {
984 use spg_storage::RangeKind as K;
985 let mut lower_inc = lower.is_some() && lower_inc;
987 let mut upper_inc = upper.is_some() && upper_inc;
988 let mut lower = lower;
989 let mut upper = upper;
990 if matches!(kind, K::Int4 | K::Int8 | K::Date) {
991 fn succ(v: Value<'static>) -> Option<Value<'static>> {
992 Some(match v {
993 Value::Int(n) => Value::Int(n.checked_add(1)?),
994 Value::BigInt(n) => Value::BigInt(n.checked_add(1)?),
995 Value::Date(d) => Value::Date(d.checked_add(1)?),
996 other => other,
997 })
998 }
999 if let Some(l) = lower {
1000 lower = Some(if lower_inc { l } else { succ(l)? });
1001 lower_inc = true;
1002 }
1003 if let Some(u) = upper {
1004 upper = Some(if upper_inc { succ(u)? } else { u });
1005 upper_inc = false;
1006 }
1007 }
1008 let empty = match (&lower, &upper) {
1010 (Some(l), Some(u)) => l == u && !(lower_inc && upper_inc),
1011 _ => false,
1012 };
1013 Some((lower, upper, lower_inc, upper_inc, empty))
1014}
1015
1016pub(crate) enum RangeParseError {
1020 Malformed,
1021 Misordered,
1022 BadElement(alloc::string::String),
1028}
1029
1030fn range_element_type_name(kind: spg_storage::RangeKind) -> &'static str {
1033 match kind {
1034 spg_storage::RangeKind::Int4 => "integer",
1035 spg_storage::RangeKind::Int8 => "bigint",
1036 spg_storage::RangeKind::Num => "numeric",
1037 spg_storage::RangeKind::Ts => "timestamp",
1038 spg_storage::RangeKind::TsTz => "timestamp with time zone",
1039 spg_storage::RangeKind::Date => "date",
1040 }
1041}
1042
1043pub(crate) fn range_bounds_misordered(
1046 lower: &Option<Value<'static>>,
1047 upper: &Option<Value<'static>>,
1048) -> bool {
1049 match (lower, upper) {
1050 (Some(l), Some(u)) => crate::orderby::value_cmp(l, u) == core::cmp::Ordering::Greater,
1051 _ => false,
1052 }
1053}
1054
1055pub(crate) fn parse_range_str(
1056 s: &str,
1057 kind: spg_storage::RangeKind,
1058) -> Result<Value<'static>, RangeParseError> {
1059 let s = s.trim();
1060 if s.eq_ignore_ascii_case("empty") {
1061 return Ok(Value::Range {
1062 kind,
1063 lower: None,
1064 upper: None,
1065 lower_inc: false,
1066 upper_inc: false,
1067 empty: true,
1068 });
1069 }
1070 let bytes = s.as_bytes();
1071 if bytes.len() < 3 {
1072 return Err(RangeParseError::Malformed);
1073 }
1074 let lower_inc = match bytes[0] {
1075 b'[' => true,
1076 b'(' => false,
1077 _ => return Err(RangeParseError::Malformed),
1078 };
1079 let upper_inc = match bytes[bytes.len() - 1] {
1080 b']' => true,
1081 b')' => false,
1082 _ => return Err(RangeParseError::Malformed),
1083 };
1084 let inner = &s[1..s.len() - 1];
1085 let (lo_text, up_text) = inner.split_once(',').ok_or(RangeParseError::Malformed)?;
1086 let lower = if lo_text.is_empty() {
1087 None
1088 } else {
1089 Some(
1090 parse_range_element(lo_text, kind)
1091 .ok_or_else(|| RangeParseError::BadElement(lo_text.trim().into()))?,
1092 )
1093 };
1094 let upper = if up_text.is_empty() {
1095 None
1096 } else {
1097 Some(
1098 parse_range_element(up_text, kind)
1099 .ok_or_else(|| RangeParseError::BadElement(up_text.trim().into()))?,
1100 )
1101 };
1102 if range_bounds_misordered(&lower, &upper) {
1105 return Err(RangeParseError::Misordered);
1106 }
1107 let (lower, upper, lower_inc, upper_inc, empty) =
1110 canonicalize_range_bounds(kind, lower, upper, lower_inc, upper_inc)
1111 .ok_or(RangeParseError::Malformed)?;
1112 Ok(Value::Range {
1113 kind,
1114 lower: lower.map(alloc::boxed::Box::new),
1115 upper: upper.map(alloc::boxed::Box::new),
1116 lower_inc,
1117 upper_inc,
1118 empty,
1119 })
1120}
1121
1122pub(crate) fn parse_multirange_str(
1129 s: &str,
1130 kind: spg_storage::RangeKind,
1131) -> Option<Vec<spg_storage::RangeSpan>> {
1132 let s = s.trim();
1133 let inner = s.strip_prefix('{').and_then(|x| x.strip_suffix('}'))?;
1134 let inner = inner.trim();
1135 if inner.is_empty() {
1136 return Some(Vec::new());
1137 }
1138 let mut spans: Vec<spg_storage::RangeSpan> = Vec::new();
1142 let bytes = inner.as_bytes();
1143 let mut depth: i32 = 0;
1144 let mut start = 0usize;
1145 for i in 0..=bytes.len() {
1146 let cut = i == bytes.len() || (depth == 0 && bytes[i] == b',');
1147 if !cut {
1148 match bytes.get(i) {
1149 Some(b'[') | Some(b'(') => depth += 1,
1150 Some(b']') | Some(b')') => depth -= 1,
1151 _ => {}
1152 }
1153 continue;
1154 }
1155 let piece = inner[start..i].trim();
1156 if piece.is_empty() {
1157 return None;
1158 }
1159 let r = parse_range_str(piece, kind).ok()?;
1160 let Value::Range {
1161 lower,
1162 upper,
1163 lower_inc,
1164 upper_inc,
1165 empty,
1166 ..
1167 } = r
1168 else {
1169 return None;
1170 };
1171 spans.push(spg_storage::RangeSpan {
1172 lower,
1173 upper,
1174 lower_inc,
1175 upper_inc,
1176 empty,
1177 });
1178 start = i + 1;
1179 }
1180 Some(spans)
1181}
1182
1183fn parse_hhmm_offset_secs(off: &str) -> Option<i32> {
1187 let (h, m) = match off.split_once(':') {
1188 Some((h, m)) => (h, m),
1189 None => (off, "0"),
1190 };
1191 let h: i32 = h.parse().ok()?;
1192 let m: i32 = m.parse().ok()?;
1193 if !(0..=15).contains(&h) || !(0..60).contains(&m) {
1194 return None;
1195 }
1196 Some(h * 3600 + m * 60)
1197}
1198
1199pub(crate) fn regtype_name_to_oid(name: &str) -> Option<i64> {
1203 if let Some(base) = name.trim().strip_suffix("[]") {
1207 return array_oid_for_element(regtype_name_to_oid(base)?);
1208 }
1209 Some(match name.trim() {
1210 "bool" | "boolean" => 16,
1211 "bytea" => 17,
1212 "name" => 19,
1213 "int8" | "bigint" => 20,
1214 "int2" | "smallint" => 21,
1215 "int4" | "int" | "integer" => 23,
1216 "text" => 25,
1217 "oid" => 26,
1218 "json" => 114,
1219 "xml" => 142,
1220 "float4" | "real" => 700,
1221 "float8" | "double precision" => 701,
1222 "cidr" => 650,
1223 "inet" => 869,
1224 "macaddr" => 829,
1225 "macaddr8" => 774,
1226 "money" => 790,
1227 "bpchar" | "char" | "character" => 1042,
1228 "varchar" | "character varying" => 1043,
1229 "date" => 1082,
1230 "time" | "time without time zone" => 1083,
1231 "timestamp" | "timestamp without time zone" => 1114,
1232 "timestamptz" | "timestamp with time zone" => 1184,
1233 "interval" => 1186,
1234 "timetz" | "time with time zone" => 1266,
1235 "numeric" | "decimal" => 1700,
1236 "uuid" => 2950,
1237 "jsonb" => 3802,
1238 "tsvector" => 3614,
1239 "tsquery" => 3615,
1240 "pg_lsn" => 3220,
1241 "regtype" => 2206,
1242 "regclass" => 2205,
1243 "regproc" => 24,
1244 "xid" => 28,
1249 "xid8" => 5069,
1250 "tid" => 27,
1251 "cid" => 29,
1252 _ => return None,
1253 })
1254}
1255
1256pub(crate) fn regtype_canonical_name(name: &str) -> Option<alloc::string::String> {
1260 let t = name.trim();
1261 if let Some(base) = t.strip_suffix("[]") {
1262 let inner = regtype_canonical_name(base)?;
1263 return Some(alloc::format!("{inner}[]"));
1264 }
1265 if let Some(base) = t.strip_prefix('_') {
1267 let inner = regtype_canonical_name(base)?;
1268 return Some(alloc::format!("{inner}[]"));
1269 }
1270 let oid = regtype_name_to_oid(&t.to_lowercase())?;
1271 regtype_oid_to_name(oid).map(alloc::string::String::from)
1272}
1273
1274pub(crate) fn parse_range_element(
1275 text: &str,
1276 kind: spg_storage::RangeKind,
1277) -> Option<Value<'static>> {
1278 let text = text.trim().trim_matches('"');
1279 use spg_storage::RangeKind as K;
1280 match kind {
1281 K::Int4 => text.parse::<i32>().ok().map(Value::Int),
1282 K::Int8 => text.parse::<i64>().ok().map(Value::BigInt),
1283 K::Num => {
1284 let dot = text.find('.');
1287 let scale: u16 = dot.map_or(0, |p| (text.len() - p - 1) as u16);
1288 let digits: alloc::string::String = text
1289 .chars()
1290 .filter(|c| *c == '-' || c.is_ascii_digit())
1291 .collect();
1292 let scaled: i128 = digits.parse().ok()?;
1293 Some(Value::Numeric {
1294 scaled,
1295 scale,
1296 kind: spg_storage::NumericKind::Finite,
1297 })
1298 }
1299 K::Ts | K::TsTz => {
1300 crate::eval::parse_timestamp_literal(text)
1304 .or_else(|| {
1305 let (date_part, off) = text.split_once(['+'])?;
1306 if !off.chars().all(|c| c.is_ascii_digit() || c == ':') {
1307 return None;
1308 }
1309 let d = crate::eval::parse_date_literal(date_part.trim())?;
1310 let mut t = i64::from(d) * 86_400_000_000;
1311 let secs = parse_hhmm_offset_secs(off)?;
1313 t -= i64::from(secs) * 1_000_000;
1314 Some(t)
1315 })
1316 .map(Value::Timestamp)
1317 }
1318 K::Date => crate::eval::parse_date_literal(text).map(Value::Date),
1319 }
1320}
1321
1322pub fn format_range_text(v: &Value) -> alloc::string::String {
1326 format_range_str(v)
1327}
1328
1329pub(crate) fn format_range_str(v: &Value) -> alloc::string::String {
1330 let Value::Range {
1331 kind,
1332 lower,
1333 upper,
1334 lower_inc,
1335 upper_inc,
1336 empty,
1337 } = v
1338 else {
1339 return alloc::string::String::new();
1340 };
1341 if *empty {
1342 return "empty".into();
1343 }
1344 let elem = |v: &Value| -> alloc::string::String {
1349 let base = format_range_element(v);
1350 if matches!(kind, spg_storage::RangeKind::TsTz) && matches!(v, Value::Timestamp(_)) {
1351 alloc::format!("{base}+00")
1352 } else {
1353 base
1354 }
1355 };
1356 let mut out = alloc::string::String::new();
1357 out.push(if *lower_inc { '[' } else { '(' });
1358 if let Some(l) = lower {
1359 out.push_str("e_range_bound(&elem(l)));
1360 }
1361 out.push(',');
1362 if let Some(u) = upper {
1363 out.push_str("e_range_bound(&elem(u)));
1364 }
1365 out.push(if *upper_inc { ']' } else { ')' });
1366 out
1367}
1368
1369fn quote_range_bound(s: &str) -> alloc::string::String {
1376 let needs_quote = s.is_empty()
1377 || s.chars()
1378 .any(|c| matches!(c, '"' | '\\' | '(' | ')' | '[' | ']' | ',') || c.is_whitespace());
1379 if !needs_quote {
1380 return s.into();
1381 }
1382 let mut out = alloc::string::String::with_capacity(s.len() + 2);
1383 out.push('"');
1384 for c in s.chars() {
1385 if c == '"' || c == '\\' {
1386 out.push('\\');
1387 }
1388 out.push(c);
1389 }
1390 out.push('"');
1391 out
1392}
1393
1394pub fn format_point(p: spg_storage::Point2D) -> alloc::string::String {
1396 alloc::format!("({},{})", p.x, p.y)
1397}
1398
1399pub fn format_lseg(p1: spg_storage::Point2D, p2: spg_storage::Point2D) -> alloc::string::String {
1401 alloc::format!("[({},{}),({},{})]", p1.x, p1.y, p2.x, p2.y)
1402}
1403
1404pub fn format_pg_box(ur: spg_storage::Point2D, ll: spg_storage::Point2D) -> alloc::string::String {
1409 alloc::format!("({},{}),({},{})", ur.x, ur.y, ll.x, ll.y)
1410}
1411
1412pub fn format_line(a: f64, b: f64, c: f64) -> alloc::string::String {
1414 alloc::format!("{{{},{},{}}}", a, b, c)
1415}
1416
1417pub fn format_circle(center: spg_storage::Point2D, radius: f64) -> alloc::string::String {
1419 alloc::format!("<({},{}),{}>", center.x, center.y, radius)
1420}
1421
1422pub fn format_path(points: &[spg_storage::Point2D], closed: bool) -> alloc::string::String {
1425 let (open, close) = if closed { ('(', ')') } else { ('[', ']') };
1426 let mut out = alloc::string::String::new();
1427 out.push(open);
1428 for (i, p) in points.iter().enumerate() {
1429 if i > 0 {
1430 out.push(',');
1431 }
1432 out.push_str(&alloc::format!("({},{})", p.x, p.y));
1433 }
1434 out.push(close);
1435 out
1436}
1437
1438pub fn format_polygon(points: &[spg_storage::Point2D]) -> alloc::string::String {
1440 let mut out = alloc::string::String::new();
1441 out.push('(');
1442 for (i, p) in points.iter().enumerate() {
1443 if i > 0 {
1444 out.push(',');
1445 }
1446 out.push_str(&alloc::format!("({},{})", p.x, p.y));
1447 }
1448 out.push(')');
1449 out
1450}
1451
1452fn parse_point(s: &str) -> Option<spg_storage::Point2D> {
1455 let s = s.trim();
1456 let inner = s
1457 .strip_prefix('(')
1458 .and_then(|x| x.strip_suffix(')'))
1459 .unwrap_or(s);
1460 let (xs, ys) = inner.split_once(',')?;
1461 let x: f64 = xs.trim().parse().ok()?;
1462 let y: f64 = ys.trim().parse().ok()?;
1463 Some(spg_storage::Point2D { x, y })
1464}
1465
1466fn parse_point_list(s: &str) -> Option<Vec<spg_storage::Point2D>> {
1471 let bytes = s.as_bytes();
1472 let mut out: Vec<spg_storage::Point2D> = Vec::new();
1473 let mut depth: i32 = 0;
1474 let mut start = 0usize;
1475 for i in 0..=bytes.len() {
1476 let cut = i == bytes.len() || (depth == 0 && bytes[i] == b',');
1477 if !cut {
1478 match bytes.get(i) {
1479 Some(b'(') | Some(b'[') | Some(b'<') => depth += 1,
1480 Some(b')') | Some(b']') | Some(b'>') => depth -= 1,
1481 _ => {}
1482 }
1483 continue;
1484 }
1485 let piece = s[start..i].trim();
1486 if !piece.is_empty() {
1487 out.push(parse_point(piece)?);
1488 }
1489 start = i + 1;
1490 }
1491 Some(out)
1492}
1493
1494pub fn parse_lseg_text(s: &str) -> Option<(spg_storage::Point2D, spg_storage::Point2D)> {
1496 let s = s.trim();
1497 let inner = s
1500 .strip_prefix('[')
1501 .and_then(|x| x.strip_suffix(']'))
1502 .unwrap_or(s);
1503 let two_points = |v: Option<alloc::vec::Vec<spg_storage::Point2D>>| v.filter(|p| p.len() == 2);
1504 let pts = if let Some(p) = two_points(parse_point_list(inner)) {
1505 p
1506 } else {
1507 inner
1508 .strip_prefix('(')
1509 .and_then(|x| x.strip_suffix(')'))
1510 .and_then(|w| two_points(parse_point_list(w)))?
1511 };
1512 Some((pts[0], pts[1]))
1513}
1514
1515pub fn parse_box_text(s: &str) -> Option<(spg_storage::Point2D, spg_storage::Point2D)> {
1519 let s = s.trim();
1523 let two_points = |v: Option<alloc::vec::Vec<spg_storage::Point2D>>| v.filter(|p| p.len() == 2);
1524 let pts = if let Some(p) = two_points(parse_point_list(s)) {
1525 p
1526 } else if let Some(p) = s
1527 .strip_prefix('(')
1528 .and_then(|x| x.strip_suffix(')'))
1529 .and_then(|inner| two_points(parse_point_list(inner)))
1530 {
1531 p
1532 } else {
1533 let nums: Option<alloc::vec::Vec<f64>> =
1534 s.split(',').map(|t| t.trim().parse::<f64>().ok()).collect();
1535 let nums = nums?;
1536 if nums.len() != 4 {
1537 return None;
1538 }
1539 alloc::vec![
1540 spg_storage::Point2D {
1541 x: nums[0],
1542 y: nums[1]
1543 },
1544 spg_storage::Point2D {
1545 x: nums[2],
1546 y: nums[3]
1547 },
1548 ]
1549 };
1550 if pts.len() != 2 {
1551 return None;
1552 }
1553 let (a, b) = (pts[0], pts[1]);
1554 let ur = spg_storage::Point2D {
1556 x: a.x.max(b.x),
1557 y: a.y.max(b.y),
1558 };
1559 let ll = spg_storage::Point2D {
1560 x: a.x.min(b.x),
1561 y: a.y.min(b.y),
1562 };
1563 Some((ur, ll))
1564}
1565
1566pub fn parse_line_text(s: &str) -> Option<(f64, f64, f64)> {
1568 let s = s.trim();
1569 if let Some(inner) = s.strip_prefix('{').and_then(|x| x.strip_suffix('}')) {
1570 let parts: Vec<&str> = inner.split(',').collect();
1571 if parts.len() != 3 {
1572 return None;
1573 }
1574 let a: f64 = parts[0].trim().parse().ok()?;
1575 let b: f64 = parts[1].trim().parse().ok()?;
1576 if a == 0.0 && b == 0.0 {
1578 return None;
1579 }
1580 let c: f64 = parts[2].trim().parse().ok()?;
1581 return Some((a, b, c));
1582 }
1583 let (p1, p2) = parse_lseg_text(s)?;
1588 if p1.x == p2.x && p1.y == p2.y {
1589 return None;
1590 }
1591 Some(line_from_points(p1, p2))
1592}
1593
1594pub fn line_from_points(p1: spg_storage::Point2D, p2: spg_storage::Point2D) -> (f64, f64, f64) {
1596 if p1.x == p2.x {
1597 (-1.0, 0.0, p1.x)
1598 } else if p1.y == p2.y {
1599 (0.0, -1.0, p1.y)
1600 } else {
1601 let m = (p1.y - p2.y) / (p1.x - p2.x);
1602 let c = p1.y - m * p1.x;
1603 (m, -1.0, if c == 0.0 { 0.0 } else { c })
1604 }
1605}
1606
1607pub fn parse_circle_text(s: &str) -> Option<(spg_storage::Point2D, f64)> {
1609 let s = s.trim();
1610 let inner = if let Some(i) = s.strip_prefix('<').and_then(|x| x.strip_suffix('>')) {
1612 i
1613 } else if let Some(i) = s.strip_prefix('(').and_then(|x| x.strip_suffix(')')) {
1614 i
1615 } else {
1616 s
1617 };
1618 let bytes = inner.as_bytes();
1620 let mut depth = 0i32;
1621 let mut split_at: Option<usize> = None;
1622 for (i, &b) in bytes.iter().enumerate() {
1623 match b {
1624 b'(' | b'[' | b'<' => depth += 1,
1625 b')' | b']' | b'>' => depth -= 1,
1626 b',' if depth == 0 => split_at = Some(i),
1627 _ => {}
1628 }
1629 }
1630 let i = split_at?;
1631 let center = parse_point(&inner[..i])?;
1632 let radius: f64 = inner[i + 1..].trim().parse().ok()?;
1633 Some((center, radius))
1634}
1635
1636pub fn parse_path_text(s: &str) -> Option<(Vec<spg_storage::Point2D>, bool)> {
1639 let s = s.trim();
1640 if let Some(i) = s.strip_prefix('[').and_then(|x| x.strip_suffix(']')) {
1645 if let Some(pts) = parse_point_list(i) {
1646 return Some((pts, false));
1647 }
1648 }
1649 if let Some(i) = s.strip_prefix('(').and_then(|x| x.strip_suffix(')')) {
1650 if let Some(pts) = parse_point_list(i) {
1651 return Some((pts, true));
1652 }
1653 }
1654 parse_point_list(s).map(|pts| (pts, true))
1655}
1656
1657pub fn parse_polygon_text(s: &str) -> Option<Vec<spg_storage::Point2D>> {
1659 let s = s.trim();
1660 if let Some(inner) = s.strip_prefix('(').and_then(|x| x.strip_suffix(')')) {
1664 if let Some(pts) = parse_point_list(inner) {
1665 return Some(pts);
1666 }
1667 }
1668 parse_point_list(s)
1669}
1670
1671pub fn format_inet_full(family: u8, bits: u8, addr: &[u8; 16]) -> alloc::string::String {
1679 let max = if family == 4 { 32 } else { 128 };
1680 let base = format_inet(family, max, addr);
1681 alloc::format!("{base}/{bits}")
1682}
1683
1684pub fn format_inet(family: u8, bits: u8, addr: &[u8; 16]) -> alloc::string::String {
1685 match family {
1686 4 => {
1687 let s = alloc::format!("{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3]);
1688 if bits == 32 {
1689 s
1690 } else {
1691 alloc::format!("{s}/{bits}")
1692 }
1693 }
1694 6 => {
1695 let mut groups = [0u16; 8];
1699 for (i, g) in groups.iter_mut().enumerate() {
1700 *g = (u16::from(addr[i * 2]) << 8) | u16::from(addr[i * 2 + 1]);
1701 }
1702 if groups[..5].iter().all(|&g| g == 0) && groups[5] == 0xffff {
1706 let s =
1707 alloc::format!("::ffff:{}.{}.{}.{}", addr[12], addr[13], addr[14], addr[15]);
1708 return if bits == 128 {
1709 s
1710 } else {
1711 alloc::format!("{s}/{bits}")
1712 };
1713 }
1714 let (mut best_start, mut best_len) = (usize::MAX, 0usize);
1715 let mut i = 0;
1716 while i < 8 {
1717 if groups[i] == 0 {
1718 let start = i;
1719 while i < 8 && groups[i] == 0 {
1720 i += 1;
1721 }
1722 if i - start > best_len {
1723 best_start = start;
1724 best_len = i - start;
1725 }
1726 } else {
1727 i += 1;
1728 }
1729 }
1730 let mut out = alloc::string::String::new();
1731 if best_len >= 2 {
1732 for (idx, g) in groups.iter().enumerate().take(best_start) {
1733 if idx > 0 {
1734 out.push(':');
1735 }
1736 out.push_str(&alloc::format!("{g:x}"));
1737 }
1738 out.push_str("::");
1739 for (idx, g) in groups.iter().enumerate().skip(best_start + best_len) {
1740 if idx > best_start + best_len {
1741 out.push(':');
1742 }
1743 out.push_str(&alloc::format!("{g:x}"));
1744 }
1745 } else {
1746 for (idx, g) in groups.iter().enumerate() {
1747 if idx > 0 {
1748 out.push(':');
1749 }
1750 out.push_str(&alloc::format!("{g:x}"));
1751 }
1752 }
1753 if bits == 128 {
1754 out
1755 } else {
1756 alloc::format!("{out}/{bits}")
1757 }
1758 }
1759 _ => alloc::format!("?invalid-inet-family-{family}"),
1760 }
1761}
1762
1763pub fn format_macaddr(m: &[u8; 6]) -> alloc::string::String {
1765 alloc::format!(
1766 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
1767 m[0],
1768 m[1],
1769 m[2],
1770 m[3],
1771 m[4],
1772 m[5]
1773 )
1774}
1775
1776pub fn format_macaddr8(m: &[u8; 8]) -> alloc::string::String {
1778 alloc::format!(
1779 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
1780 m[0],
1781 m[1],
1782 m[2],
1783 m[3],
1784 m[4],
1785 m[5],
1786 m[6],
1787 m[7]
1788 )
1789}
1790
1791pub fn format_bit_string(nbits: u32, bytes: &[u8]) -> alloc::string::String {
1796 let mut out = alloc::string::String::with_capacity(nbits as usize);
1797 for i in 0..nbits as usize {
1798 let byte = bytes[i / 8];
1799 let bit = (byte >> (7 - (i % 8))) & 1;
1800 out.push(if bit == 1 { '1' } else { '0' });
1801 }
1802 out
1803}
1804
1805pub fn bit_string_to_i64(nbits: u32, bytes: &[u8]) -> i64 {
1807 let mut val: i64 = 0;
1808 for i in 0..nbits as usize {
1809 let byte = bytes.get(i / 8).copied().unwrap_or(0);
1810 val = (val << 1) | i64::from((byte >> (7 - (i % 8))) & 1);
1811 }
1812 val
1813}
1814
1815pub fn format_money_array(items: &[Option<i64>]) -> alloc::string::String {
1819 let mut out = alloc::string::String::new();
1820 out.push('{');
1821 for (i, item) in items.iter().enumerate() {
1822 if i > 0 {
1823 out.push(',');
1824 }
1825 match item {
1826 None => out.push_str("NULL"),
1827 Some(c) => out.push_str(&crate::eval::format_money(*c)),
1828 }
1829 }
1830 out.push('}');
1831 out
1832}
1833
1834pub fn parse_inet_text(s: &str) -> Option<(u8, u8, [u8; 16])> {
1839 let s = s.trim();
1840 let (addr_s, bits_s) = match s.split_once('/') {
1841 Some((a, b)) => (a, Some(b)),
1842 None => (s, None),
1843 };
1844 if addr_s.contains(':') {
1845 let (head, tail) = match addr_s.find("::") {
1850 Some(idx) => (&addr_s[..idx], Some(&addr_s[idx + 2..])),
1851 None => (addr_s, None),
1852 };
1853 let mut head_groups: alloc::vec::Vec<&str> = if head.is_empty() {
1854 alloc::vec::Vec::new()
1855 } else {
1856 head.split(':').collect()
1857 };
1858 let mut tail_groups: alloc::vec::Vec<&str> = match tail {
1859 Some(t) if !t.is_empty() => t.split(':').collect(),
1860 _ => alloc::vec::Vec::new(),
1861 };
1862 let mut dotted_words: Option<[u16; 2]> = None;
1866 if let Some(g) = tail_groups.last().or_else(|| head_groups.last()) {
1867 if g.contains('.') {
1868 let oct: alloc::vec::Vec<&str> = g.split('.').collect();
1869 if oct.len() != 4 {
1870 return None;
1871 }
1872 let mut b = [0u8; 4];
1873 for (i, o) in oct.iter().enumerate() {
1874 b[i] = o.parse::<u8>().ok()?;
1875 }
1876 dotted_words = Some([
1877 (u16::from(b[0]) << 8) | u16::from(b[1]),
1878 (u16::from(b[2]) << 8) | u16::from(b[3]),
1879 ]);
1880 if !tail_groups.is_empty() {
1881 tail_groups.pop();
1882 } else {
1883 head_groups.pop();
1884 }
1885 }
1886 }
1887 let dq = if dotted_words.is_some() { 2 } else { 0 };
1888 let head_len = head_groups.len();
1889 let tail_len = tail_groups.len();
1890 if tail.is_none() {
1891 if head_len + dq != 8 {
1892 return None;
1893 }
1894 } else if head_len + tail_len + dq > 7 {
1895 return None;
1896 }
1897 let mut words = [0u16; 8];
1898 for (i, g) in head_groups.iter().enumerate() {
1899 words[i] = u16::from_str_radix(g, 16).ok()?;
1900 }
1901 let trailing_start = 8 - dq - tail_len;
1904 for (i, g) in tail_groups.iter().enumerate() {
1905 words[trailing_start + i] = u16::from_str_radix(g, 16).ok()?;
1906 }
1907 if let Some(dw) = dotted_words {
1908 words[6] = dw[0];
1909 words[7] = dw[1];
1910 }
1911 let mut addr = [0u8; 16];
1912 for (i, w) in words.iter().enumerate() {
1913 addr[i * 2] = (w >> 8) as u8;
1914 addr[i * 2 + 1] = (w & 0xff) as u8;
1915 }
1916 let bits = match bits_s {
1917 Some(b) => b.parse::<u8>().ok().filter(|&n| n <= 128)?,
1918 None => 128,
1919 };
1920 Some((6, bits, addr))
1921 } else {
1922 let parts: alloc::vec::Vec<&str> = addr_s.split('.').collect();
1924 if parts.len() != 4 {
1925 return None;
1926 }
1927 let mut addr = [0u8; 16];
1928 for (i, p) in parts.iter().enumerate() {
1929 addr[i] = p.parse::<u8>().ok()?;
1930 }
1931 let bits = match bits_s {
1932 Some(b) => b.parse::<u8>().ok().filter(|&n| n <= 32)?,
1933 None => 32,
1934 };
1935 Some((4, bits, addr))
1936 }
1937}
1938
1939pub fn parse_cidr_text(s: &str) -> Result<Option<(u8, u8, [u8; 16])>, ()> {
1946 let s = s.trim();
1947 let parsed = if !s.contains(':') {
1948 let (addr_s, bits_s) = match s.split_once('/') {
1949 Some((a, b)) => (a, Some(b)),
1950 None => (s, None),
1951 };
1952 let parts: alloc::vec::Vec<&str> = addr_s.split('.').collect();
1953 if parts.is_empty() || parts.len() > 4 || parts.iter().any(|p| p.is_empty()) {
1954 return Ok(None);
1955 }
1956 let mut addr = [0u8; 16];
1957 for (i, p) in parts.iter().enumerate() {
1958 match p.parse::<u8>() {
1959 Ok(v) => addr[i] = v,
1960 Err(_) => return Ok(None),
1961 }
1962 }
1963 let bits = match bits_s {
1964 Some(b) => match b.parse::<u8>() {
1965 Ok(n) if n <= 32 => n,
1966 _ => return Ok(None),
1967 },
1968 None => (parts.len() as u8) * 8,
1969 };
1970 Some((4u8, bits, addr))
1971 } else {
1972 parse_inet_text(s).map(|(f, b, a)| {
1973 (f, if s.contains('/') { b } else { 128 }, a)
1975 })
1976 };
1977 let Some((family, bits, addr)) = parsed else {
1978 return Ok(None);
1979 };
1980 let total = if family == 4 { 32u16 } else { 128 };
1982 let nbytes = if family == 4 { 4 } else { 16 };
1983 for byte in 0..nbytes {
1984 let bit_base = (byte as u16) * 8;
1985 let keep = (u16::from(bits)).saturating_sub(bit_base).min(8) as u8;
1986 let mask: u8 = if keep == 0 { 0 } else { 0xffu8 << (8 - keep) };
1987 if addr[byte] & !mask != 0 {
1988 return Err(());
1989 }
1990 if bit_base >= total {
1991 break;
1992 }
1993 }
1994 Ok(Some((family, bits, addr)))
1995}
1996
1997pub fn parse_macaddr_text(s: &str) -> Option<[u8; 6]> {
2000 let s = s.trim();
2001 let cleaned: alloc::string::String = s.chars().filter(|c| c.is_ascii_hexdigit()).collect();
2002 if cleaned.len() != 12 {
2003 return None;
2004 }
2005 let mut out = [0u8; 6];
2006 for i in 0..6 {
2007 out[i] = u8::from_str_radix(&cleaned[i * 2..i * 2 + 2], 16).ok()?;
2008 }
2009 Some(out)
2010}
2011
2012#[must_use]
2019pub fn date_days_to_micros(d: i32) -> i64 {
2020 match d {
2021 i32::MAX => i64::MAX,
2022 i32::MIN => i64::MIN,
2023 _ => i64::from(d) * 86_400_000_000,
2024 }
2025}
2026
2027pub fn parse_pg_lsn_text(s: &str) -> Option<u64> {
2028 let t = s.trim();
2029 let (hi, lo) = t.split_once('/')?;
2030 if hi.is_empty() || lo.is_empty() || hi.len() > 8 || lo.len() > 8 {
2031 return None;
2032 }
2033 let hi = u32::from_str_radix(hi, 16).ok()?;
2034 let lo = u32::from_str_radix(lo, 16).ok()?;
2035 Some((u64::from(hi) << 32) | u64::from(lo))
2036}
2037
2038#[must_use]
2040pub fn format_pg_lsn(l: u64) -> alloc::string::String {
2041 alloc::format!("{:X}/{:X}", l >> 32, l & 0xFFFF_FFFF)
2042}
2043
2044pub fn parse_macaddr8_text(s: &str) -> Option<[u8; 8]> {
2045 let s = s.trim();
2046 let cleaned: alloc::string::String = s.chars().filter(|c| c.is_ascii_hexdigit()).collect();
2047 if cleaned.len() == 12 {
2050 let mut six = [0u8; 6];
2051 for i in 0..6 {
2052 six[i] = u8::from_str_radix(&cleaned[i * 2..i * 2 + 2], 16).ok()?;
2053 }
2054 return Some([six[0], six[1], six[2], 0xff, 0xfe, six[3], six[4], six[5]]);
2055 }
2056 if cleaned.len() != 16 {
2057 return None;
2058 }
2059 let mut out = [0u8; 8];
2060 for i in 0..8 {
2061 out[i] = u8::from_str_radix(&cleaned[i * 2..i * 2 + 2], 16).ok()?;
2062 }
2063 Some(out)
2064}
2065
2066pub fn parse_bit_string_text(s: &str) -> Option<(u32, alloc::vec::Vec<u8>)> {
2070 let s = s.trim();
2071 let nbits = u32::try_from(s.len()).ok()?;
2072 let nbytes = (s.len()).div_ceil(8);
2073 let mut bytes = alloc::vec![0u8; nbytes];
2074 for (i, c) in s.chars().enumerate() {
2075 let bit = match c {
2076 '0' => 0u8,
2077 '1' => 1u8,
2078 _ => return None,
2079 };
2080 if bit == 1 {
2081 bytes[i / 8] |= 1 << (7 - (i % 8));
2082 }
2083 }
2084 Some((nbits, bytes))
2085}
2086
2087pub fn format_multirange(ranges: &[spg_storage::RangeSpan]) -> alloc::string::String {
2094 let mut out = alloc::string::String::new();
2095 out.push('{');
2096 for (i, r) in ranges.iter().enumerate() {
2097 if i > 0 {
2098 out.push(',');
2099 }
2100 if r.empty {
2101 out.push_str("empty");
2102 continue;
2103 }
2104 out.push(if r.lower_inc { '[' } else { '(' });
2105 if let Some(l) = &r.lower {
2106 out.push_str("e_range_bound(&format_range_element(l)));
2107 }
2108 out.push(',');
2109 if let Some(u) = &r.upper {
2110 out.push_str("e_range_bound(&format_range_element(u)));
2111 }
2112 out.push(if r.upper_inc { ']' } else { ')' });
2113 }
2114 out.push('}');
2115 out
2116}
2117
2118pub(crate) fn format_range_element(v: &Value) -> alloc::string::String {
2119 match v {
2120 Value::Int(n) => alloc::format!("{n}"),
2121 Value::BigInt(n) => alloc::format!("{n}"),
2122 Value::Date(d) => crate::eval::format_date(*d),
2123 Value::Timestamp(t) => crate::eval::format_timestamp(*t),
2124 Value::Numeric {
2125 scaled,
2126 scale,
2127 kind,
2128 } => crate::eval::format_numeric_kind(*kind, *scaled, *scale),
2129 other => alloc::format!("{other:?}"),
2130 }
2131}
2132
2133pub(crate) fn parse_money_str(s: &str) -> Option<i64> {
2144 let mut rest = s.trim();
2149 let mut neg = false;
2150 loop {
2153 let before = rest;
2154 rest = rest.trim_start();
2155 if let Some(r) = rest.strip_prefix('$') {
2156 rest = r;
2157 } else if let Some(r) = rest.strip_prefix('-') {
2158 neg = true;
2159 rest = r;
2160 } else if let Some(r) = rest.strip_prefix('(') {
2161 neg = true;
2162 rest = r;
2163 } else if let Some(r) = rest.strip_prefix('+') {
2164 rest = r;
2165 }
2166 if rest == before {
2167 break;
2168 }
2169 }
2170 let (int_part, tail) = {
2171 let end = rest
2172 .find(|c: char| !(c.is_ascii_digit() || c == ','))
2173 .unwrap_or(rest.len());
2174 (&rest[..end], &rest[end..])
2175 };
2176 let mut int_digits = alloc::string::String::with_capacity(int_part.len());
2178 for b in int_part.bytes() {
2179 match b {
2180 b',' => {}
2181 b'0'..=b'9' => int_digits.push(b as char),
2182 _ => return None,
2183 }
2184 }
2185 if int_digits.is_empty() {
2186 return None;
2187 }
2188 let dollars: i64 = int_digits.parse().ok()?;
2189 let (mut cents, tail) = match tail.strip_prefix('.') {
2191 None => (0i64, tail),
2192 Some(f) => {
2193 let end = f.find(|c: char| !c.is_ascii_digit()).unwrap_or(f.len());
2194 let (digits, rest_tail) = (&f[..end], &f[end..]);
2195 if digits.is_empty() {
2196 return None;
2197 }
2198 let b = digits.as_bytes();
2199 let mut c = i64::from(b[0] - b'0') * 10;
2200 if b.len() >= 2 {
2201 c += i64::from(b[1] - b'0');
2202 }
2203 if b.len() >= 3 && b[2] >= b'5' {
2204 c += 1;
2205 }
2206 (c, rest_tail)
2207 }
2208 };
2209 let mut tail = tail;
2211 while !tail.is_empty() {
2212 let t = tail.trim_start();
2213 if let Some(r) = t.strip_prefix(')') {
2214 tail = r;
2215 } else if let Some(r) = t.strip_prefix('-') {
2216 neg = true;
2217 tail = r;
2218 } else if let Some(r) = t.strip_prefix('+') {
2219 tail = r;
2220 } else if let Some(r) = t.strip_prefix('$') {
2221 tail = r;
2222 } else if t.is_empty() {
2223 break;
2224 } else {
2225 return None;
2226 }
2227 }
2228 let carry = cents / 100;
2230 cents %= 100;
2231 let total = dollars
2232 .checked_add(carry)?
2233 .checked_mul(100)?
2234 .checked_add(cents)?;
2235 Some(if neg { -total } else { total })
2236}
2237
2238pub(crate) fn parse_timetz_str(s: &str) -> Option<(i64, i32)> {
2249 let s = s.trim();
2250 let bytes = s.as_bytes();
2254 let sign_pos = bytes
2255 .iter()
2256 .enumerate()
2257 .rev()
2258 .find(|&(_, &b)| b == b'+' || b == b'-')
2259 .map(|(i, _)| i)?;
2260 if sign_pos == 0 {
2261 return None; }
2263 let time_part = &s[..sign_pos];
2264 let offset_part = &s[sign_pos..];
2265 let us = parse_time_str(time_part)?;
2266 let sign: i32 = if offset_part.starts_with('+') { 1 } else { -1 };
2267 let offset_body = &offset_part[1..];
2268 let (hh_str, mm_str) = match offset_body.split_once(':') {
2271 Some((h, m)) => (h, m),
2272 None if offset_body.len() == 4 => offset_body.split_at(2),
2273 None if offset_body.len() == 3 => offset_body.split_at(1),
2274 None => (offset_body, "0"),
2275 };
2276 let hh: i32 = hh_str.parse().ok()?;
2277 let mm: i32 = mm_str.parse().ok()?;
2278 if !(0..=14).contains(&hh) || !(0..=59).contains(&mm) {
2279 return None;
2280 }
2281 let total = sign * (hh * 3600 + mm * 60);
2282 if total.abs() > 50_400 {
2283 return None;
2284 }
2285 Some((us, total))
2286}
2287
2288pub(crate) fn coerce_int_to_year(n: i64, col_name: &str) -> Result<Value<'static>, EngineError> {
2293 if n == 0 || (1901..=2155).contains(&n) {
2294 return Ok(Value::Year(n as u16));
2297 }
2298 Err(EngineError::Eval(EvalError::TypeMismatch {
2299 detail: alloc::format!(
2300 "year value out of range: {n} (column `{col_name}`; \
2301 MySQL accepts 0 or 1901..=2155)"
2302 ),
2303 }))
2304}
2305
2306pub(crate) fn parse_time_str(s: &str) -> Option<i64> {
2319 let s = s.trim();
2320 if s.eq_ignore_ascii_case("allballs") {
2322 return Some(0);
2323 }
2324 if let Some((head, tail)) = s.split_once(' ') {
2332 let date_shaped = head.split('-').count() == 3
2333 && head
2334 .split('-')
2335 .all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()));
2336 if date_shaped {
2337 return parse_time_str(tail);
2338 }
2339 }
2340 let (hms, frac) = match s.split_once('.') {
2341 Some((h, f)) => (h, Some(f)),
2342 None => (s, None),
2343 };
2344 let mut parts = hms.split(':');
2345 let hh: u32 = parts.next()?.parse().ok()?;
2346 let mm: u32 = parts.next()?.parse().ok()?;
2347 let ss: u32 = match parts.next() {
2350 Some(x) => x.parse().ok()?,
2351 None => 0,
2352 };
2353 if parts.next().is_some() {
2354 return None;
2355 }
2356 if hh > 24 || mm > 59 || ss > 59 || (hh == 24 && (mm != 0 || ss != 0)) {
2358 return None;
2359 }
2360 let frac_us: i64 = match frac {
2361 None => 0,
2362 Some(f) => {
2363 if f.is_empty() || f.len() > 6 || !f.bytes().all(|b| b.is_ascii_digit()) {
2364 return None;
2365 }
2366 let mut padded = alloc::string::String::with_capacity(6);
2368 padded.push_str(f);
2369 while padded.len() < 6 {
2370 padded.push('0');
2371 }
2372 padded.parse().ok()?
2373 }
2374 };
2375 if hh == 24 && frac_us != 0 {
2376 return None;
2377 }
2378 Some(
2379 i64::from(hh) * 3_600_000_000
2380 + i64::from(mm) * 60_000_000
2381 + i64::from(ss) * 1_000_000
2382 + frac_us,
2383 )
2384}
2385
2386pub(crate) fn numeric_typmod_in_range(precision: u16, scale: i16) -> bool {
2390 (1..=1000).contains(&precision) && (-1000..=1000).contains(&scale)
2391}
2392
2393pub(crate) fn numeric_typmod_error(name: &str) -> Option<alloc::string::String> {
2397 let lower = name.trim().to_ascii_lowercase();
2398 let (head, rest) = lower.split_once('(')?;
2399 if !matches!(head.trim(), "numeric" | "decimal") {
2400 return None;
2401 }
2402 let args = rest.strip_suffix(')')?;
2403 let mut it = args.split(',').map(str::trim);
2404 let p: i64 = it.next()?.parse().ok()?;
2405 if !(1..=1000).contains(&p) {
2406 return Some(alloc::format!(
2407 "NUMERIC precision {p} must be between 1 and 1000"
2408 ));
2409 }
2410 if let Some(s) = it.next() {
2411 let s: i64 = s.parse().ok()?;
2412 if !(-1000..=1000).contains(&s) {
2413 return Some(alloc::format!(
2414 "NUMERIC scale {s} must be between -1000 and 1000"
2415 ));
2416 }
2417 }
2418 None
2419}
2420
2421pub(crate) fn type_name_to_data_type(name: &str) -> Option<DataType> {
2428 with_lower_name(name.trim(), type_name_to_data_type_lower)
2429}
2430
2431pub(crate) fn with_lower_name<R>(name: &str, f: impl FnOnce(&str) -> R) -> R {
2444 const CAP: usize = 64;
2445 if name.len() <= CAP {
2446 let mut buf = [0u8; CAP];
2447 buf[..name.len()].copy_from_slice(name.as_bytes());
2448 buf[..name.len()].make_ascii_lowercase();
2449 if let Ok(s) = core::str::from_utf8(&buf[..name.len()]) {
2450 return f(s);
2451 }
2452 }
2453 f(&name.to_ascii_lowercase())
2454}
2455
2456fn type_name_to_data_type_lower(n: &str) -> Option<DataType> {
2457 if let Some((head, paren)) = n.split_once('(')
2460 && let Some(args) = paren.strip_suffix(')')
2461 {
2462 let mut wide: [Option<i32>; 2] = [None, None];
2470 for (slot, s) in wide.iter_mut().zip(args.split(',')) {
2471 *slot = s.trim().parse::<i32>().ok();
2472 }
2473 let nums: [u8; 2] = [
2474 wide[0].and_then(|v| u8::try_from(v).ok()).unwrap_or(0),
2475 wide[1].and_then(|v| u8::try_from(v).ok()).unwrap_or(0),
2476 ];
2477 match head {
2478 "bit" => {
2480 return Some(DataType::Bit(
2481 u32::try_from(wide.first().copied().flatten()?).ok()?,
2482 ));
2483 }
2484 "varbit" | "bit varying" => {
2485 return Some(DataType::BitVarying(
2486 u32::try_from(wide.first().copied().flatten()?).ok()?,
2487 ));
2488 }
2489 "numeric" | "decimal" => {
2490 let precision = u16::try_from(wide.first().copied().flatten()?).ok()?;
2491 let scale = i16::try_from(wide.get(1).copied().flatten().unwrap_or(0)).ok()?;
2493 if !numeric_typmod_in_range(precision, scale) {
2494 return None;
2495 }
2496 return Some(DataType::Numeric { precision, scale });
2497 }
2498 "varchar" => {
2504 return Some(DataType::Varchar(nums.first().copied().unwrap_or(0).into()));
2505 }
2506 "char" | "character" => {
2507 return Some(DataType::Char(nums.first().copied().unwrap_or(0).into()));
2508 }
2509 _ => {}
2510 }
2511 }
2512 Some(match n {
2513 "smallint" | "int2" => DataType::SmallInt,
2514 "numeric" | "decimal" => DataType::Numeric {
2515 precision: 0,
2516 scale: 0,
2517 },
2518 "inet" => DataType::Inet,
2521 "cidr" => DataType::Cidr,
2522 "macaddr" => DataType::Macaddr,
2523 "macaddr8" => DataType::Macaddr8,
2524 "pg_lsn" => DataType::PgLsn,
2525 "__bit_literal" => DataType::BitVarying(0),
2527 "xid" => DataType::Xid,
2532 "xid8" => DataType::Xid8,
2533 "bit" => DataType::Bit(0),
2534 "varbit" | "bit varying" => DataType::BitVarying(0),
2535 "xml" => DataType::Xml,
2536 "tsvector" => DataType::TsVector,
2546 "tsquery" => DataType::TsQuery,
2547 "money" => DataType::Money,
2554 "char1" => DataType::Char1,
2555 "point" => DataType::Point,
2557 "lseg" => DataType::Lseg,
2558 "path" => DataType::Path,
2559 "box" => DataType::PgBox,
2560 "polygon" => DataType::Polygon,
2561 "line" => DataType::Line,
2562 "circle" => DataType::Circle,
2563 "int4multirange" => DataType::Multirange(spg_storage::RangeKind::Int4),
2565 "int8multirange" => DataType::Multirange(spg_storage::RangeKind::Int8),
2566 "nummultirange" => DataType::Multirange(spg_storage::RangeKind::Num),
2567 "tsmultirange" => DataType::Multirange(spg_storage::RangeKind::Ts),
2568 "tstzmultirange" => DataType::Multirange(spg_storage::RangeKind::TsTz),
2569 "datemultirange" => DataType::Multirange(spg_storage::RangeKind::Date),
2570 "int4range" => DataType::Range(spg_storage::RangeKind::Int4),
2572 "int8range" => DataType::Range(spg_storage::RangeKind::Int8),
2573 "numrange" => DataType::Range(spg_storage::RangeKind::Num),
2574 "tsrange" => DataType::Range(spg_storage::RangeKind::Ts),
2575 "tstzrange" => DataType::Range(spg_storage::RangeKind::TsTz),
2576 "daterange" => DataType::Range(spg_storage::RangeKind::Date),
2577 "bool_array" | "boolean_array" => DataType::BoolArray,
2581 "smallint_array" | "int2_array" => DataType::SmallIntArray,
2582 "int_array" | "integer_array" | "int4_array" => DataType::IntArray,
2583 "bigint_array" | "int8_array" => DataType::BigIntArray,
2584 "float_array" | "double_array" | "float8_array" => DataType::FloatArray,
2585 "real_array" | "float4_array" => DataType::RealArray,
2588 "float4" | "real" => DataType::Real,
2591 "float8" | "double precision" | "float" => DataType::Float,
2592 "oid" => DataType::Oid,
2597 "oid_array" => DataType::OidArray,
2611 "name_array" | "regtype_array" | "regclass_array" | "regproc_array" => DataType::TextArray,
2612 "time" | "time without time zone" => DataType::Time,
2615 "timetz" | "time with time zone" => DataType::TimeTz,
2616 "hstore" => DataType::Hstore,
2623 "numeric_array" | "decimal_array" => DataType::NumericArray,
2624 "varchar_array" | "character varying_array" | "char_array" | "bpchar_array" => {
2625 DataType::TextArray
2626 }
2627 "text_array" => DataType::TextArray,
2628 "date_array" => DataType::DateArray,
2629 "timestamp_array" => DataType::TimestampArray,
2630 "timestamptz_array" => DataType::TimestamptzArray,
2631 "uuid_array" => DataType::UuidArray,
2632 "json_array" => DataType::JsonArray,
2633 "jsonb_array" => DataType::JsonbArray,
2634 "bytea_array" => DataType::BytesArray,
2635 "interval_array" => DataType::IntervalArray,
2636 "money_array" => DataType::MoneyArray,
2637 "time_array" => DataType::TimeArray,
2638 "timetz_array" => DataType::TimeTzArray,
2639 "inet_array" => DataType::InetArray,
2640 "xml_array" => DataType::XmlArray,
2641 "int" | "int4" | "integer" => DataType::Int,
2646 "bigint" | "int8" => DataType::BigInt,
2647 "text" => DataType::Text,
2648 "name" => DataType::Name,
2652 "varchar" | "character varying" => DataType::Varchar(0),
2653 "char" | "character" => DataType::Char(1),
2657 "bpchar" => DataType::Char(0),
2658 "bool" | "boolean" => DataType::Bool,
2659 "date" => DataType::Date,
2660 "timestamp" | "timestamp without time zone" => DataType::Timestamp,
2661 "timestamptz" | "timestamp with time zone" => DataType::Timestamptz,
2662 "uuid" => DataType::Uuid,
2663 "json" => DataType::Json,
2664 "jsonb" => DataType::Jsonb,
2665 "bytea" => DataType::Bytes,
2666 "interval" => DataType::Interval,
2667 _ => return None,
2668 })
2669}
2670
2671pub(crate) const fn column_type_to_data_type(t: ColumnTypeName) -> DataType {
2672 match t {
2673 ColumnTypeName::SmallInt => DataType::SmallInt,
2674 ColumnTypeName::Int => DataType::Int,
2675 ColumnTypeName::BigInt => DataType::BigInt,
2676 ColumnTypeName::Float => DataType::Float,
2677 ColumnTypeName::Real => DataType::Real,
2678 ColumnTypeName::Text => DataType::Text,
2679 ColumnTypeName::Name => DataType::Name,
2680 ColumnTypeName::Xid => DataType::Xid,
2681 ColumnTypeName::Xid8 => DataType::Xid8,
2682 ColumnTypeName::Oid => DataType::Oid,
2683 ColumnTypeName::Varchar(n) => DataType::Varchar(n),
2684 ColumnTypeName::Char(n) => DataType::Char(n),
2685 ColumnTypeName::Bool => DataType::Bool,
2686 ColumnTypeName::Vector { dim, encoding } => DataType::Vector {
2687 dim,
2688 encoding: match encoding {
2689 SqlVecEncoding::F32 => VecEncoding::F32,
2690 SqlVecEncoding::Sq8 => VecEncoding::Sq8,
2691 SqlVecEncoding::F16 => VecEncoding::F16,
2692 },
2693 },
2694 ColumnTypeName::Numeric(precision, scale) => DataType::Numeric { precision, scale },
2695 ColumnTypeName::Date => DataType::Date,
2696 ColumnTypeName::Timestamp => DataType::Timestamp,
2697 ColumnTypeName::Timestamptz => DataType::Timestamptz,
2698 ColumnTypeName::Json => DataType::Json,
2699 ColumnTypeName::Jsonb => DataType::Jsonb,
2700 ColumnTypeName::Bytes => DataType::Bytes,
2701 ColumnTypeName::TextArray => DataType::TextArray,
2702 ColumnTypeName::IntArray => DataType::IntArray,
2703 ColumnTypeName::BigIntArray => DataType::BigIntArray,
2704 ColumnTypeName::OidArray => DataType::OidArray,
2705 ColumnTypeName::TsVector => DataType::TsVector,
2706 ColumnTypeName::TsQuery => DataType::TsQuery,
2707 ColumnTypeName::Uuid => DataType::Uuid,
2708 ColumnTypeName::Time => DataType::Time,
2709 ColumnTypeName::Year => DataType::Year,
2710 ColumnTypeName::TimeTz => DataType::TimeTz,
2711 ColumnTypeName::Money => DataType::Money,
2712 ColumnTypeName::Range(k) => DataType::Range(match k {
2713 spg_sql::ast::RangeKindAst::Int4 => spg_storage::RangeKind::Int4,
2714 spg_sql::ast::RangeKindAst::Int8 => spg_storage::RangeKind::Int8,
2715 spg_sql::ast::RangeKindAst::Num => spg_storage::RangeKind::Num,
2716 spg_sql::ast::RangeKindAst::Ts => spg_storage::RangeKind::Ts,
2717 spg_sql::ast::RangeKindAst::TsTz => spg_storage::RangeKind::TsTz,
2718 spg_sql::ast::RangeKindAst::Date => spg_storage::RangeKind::Date,
2719 }),
2720 ColumnTypeName::Hstore => DataType::Hstore,
2721 ColumnTypeName::IntArray2D => DataType::IntArray2D,
2722 ColumnTypeName::BigIntArray2D => DataType::BigIntArray2D,
2723 ColumnTypeName::TextArray2D => DataType::TextArray2D,
2724 ColumnTypeName::BoolArray2D => DataType::BoolArray2D,
2725 ColumnTypeName::Interval => DataType::Interval,
2726 ColumnTypeName::IntervalArray => DataType::IntervalArray,
2727 ColumnTypeName::BoolArray => DataType::BoolArray,
2728 ColumnTypeName::SmallIntArray => DataType::SmallIntArray,
2729 ColumnTypeName::FloatArray => DataType::FloatArray,
2730 ColumnTypeName::NumericArray => DataType::NumericArray,
2731 ColumnTypeName::DateArray => DataType::DateArray,
2732 ColumnTypeName::TimestampArray => DataType::TimestampArray,
2733 ColumnTypeName::TimestamptzArray => DataType::TimestamptzArray,
2734 ColumnTypeName::UuidArray => DataType::UuidArray,
2735 ColumnTypeName::JsonArray => DataType::JsonArray,
2736 ColumnTypeName::JsonbArray => DataType::JsonbArray,
2737 ColumnTypeName::BytesArray => DataType::BytesArray,
2738 ColumnTypeName::VarcharArray => DataType::VarcharArray,
2739 ColumnTypeName::CharArray => DataType::CharArray,
2740 ColumnTypeName::RealArray => DataType::RealArray,
2741 ColumnTypeName::TimeArray => DataType::TimeArray,
2742 ColumnTypeName::TimeTzArray => DataType::TimeTzArray,
2743 ColumnTypeName::InetArray => DataType::InetArray,
2744 ColumnTypeName::XmlArray => DataType::XmlArray,
2745 ColumnTypeName::Multirange(k) => DataType::Multirange(match k {
2746 spg_sql::ast::RangeKindAst::Int4 => spg_storage::RangeKind::Int4,
2747 spg_sql::ast::RangeKindAst::Int8 => spg_storage::RangeKind::Int8,
2748 spg_sql::ast::RangeKindAst::Num => spg_storage::RangeKind::Num,
2749 spg_sql::ast::RangeKindAst::Ts => spg_storage::RangeKind::Ts,
2750 spg_sql::ast::RangeKindAst::TsTz => spg_storage::RangeKind::TsTz,
2751 spg_sql::ast::RangeKindAst::Date => spg_storage::RangeKind::Date,
2752 }),
2753 ColumnTypeName::Point => DataType::Point,
2754 ColumnTypeName::Lseg => DataType::Lseg,
2755 ColumnTypeName::Path => DataType::Path,
2756 ColumnTypeName::PgBox => DataType::PgBox,
2757 ColumnTypeName::Polygon => DataType::Polygon,
2758 ColumnTypeName::Line => DataType::Line,
2759 ColumnTypeName::Circle => DataType::Circle,
2760 ColumnTypeName::Inet => DataType::Inet,
2761 ColumnTypeName::Cidr => DataType::Cidr,
2762 ColumnTypeName::Macaddr => DataType::Macaddr,
2763 ColumnTypeName::Macaddr8 => DataType::Macaddr8,
2764 ColumnTypeName::Bit(n) => DataType::Bit(n),
2765 ColumnTypeName::BitVarying(n) => DataType::BitVarying(n),
2766 ColumnTypeName::Xml => DataType::Xml,
2767 ColumnTypeName::Char1 => DataType::Char1,
2768 ColumnTypeName::MoneyArray => DataType::MoneyArray,
2769 }
2770}
2771
2772pub(crate) fn literal_expr_to_value(expr: Expr) -> Result<Value<'static>, EngineError> {
2776 literal_expr_to_value_in(expr, None)
2777}
2778
2779pub(crate) fn literal_expr_to_value_in(
2787 expr: Expr,
2788 catalog: Option<&spg_storage::Catalog>,
2789) -> Result<Value<'static>, EngineError> {
2790 match expr {
2791 Expr::Literal(l) => Ok(literal_to_value(l)),
2792 Expr::Cast { expr, target } => {
2793 if catalog.is_some()
2796 && matches!(
2797 target,
2798 spg_sql::ast::CastTarget::Named(_) | spg_sql::ast::CastTarget::RegClass
2799 )
2800 {
2801 return eval_expr_with_catalog(Expr::Cast { expr, target }, catalog);
2802 }
2803 let inner_value = literal_expr_to_value_in(*expr, catalog)?;
2804 crate::eval::cast_value(inner_value, target).map_err(EngineError::Eval)
2805 }
2806 Expr::Unary {
2807 op: UnOp::Neg,
2808 expr,
2809 } => match *expr {
2810 Expr::Literal(Literal::Integer(n)) => {
2811 let neg = n.checked_neg().ok_or_else(|| {
2814 EngineError::Unsupported("integer literal overflow on negation".into())
2815 })?;
2816 Ok(int_value_for(neg))
2817 }
2818 Expr::Literal(Literal::Float(x)) => Ok(Value::Float(-x)),
2819 Expr::Literal(Literal::Numeric { unscaled, scale }) => Ok(Value::Numeric {
2821 scaled: -unscaled,
2822 scale,
2823 kind: spg_storage::NumericKind::Finite,
2824 }),
2825 Expr::Literal(Literal::NumericBig(ref s)) => {
2828 let flipped = if let Some(rest) = s.strip_prefix('-') {
2829 rest.to_string()
2830 } else {
2831 alloc::format!("-{s}")
2832 };
2833 Ok(big_literal_to_value(&flipped))
2834 }
2835 Expr::Cast {
2841 expr: inner,
2842 target,
2843 } => {
2844 let negated_inner = match *inner {
2845 Expr::Literal(Literal::Integer(n)) => {
2846 let neg = n.checked_neg().ok_or_else(|| {
2847 EngineError::Unsupported("integer literal overflow on negation".into())
2848 })?;
2849 Expr::Literal(Literal::Integer(neg))
2850 }
2851 Expr::Literal(Literal::Float(x)) => Expr::Literal(Literal::Float(-x)),
2852 Expr::Literal(Literal::Numeric { unscaled, scale }) => {
2853 Expr::Literal(Literal::Numeric {
2854 unscaled: -unscaled,
2855 scale,
2856 })
2857 }
2858 Expr::Literal(Literal::NumericBig(ref s)) => {
2861 let flipped = if let Some(rest) = s.strip_prefix('-') {
2862 rest.to_string()
2863 } else {
2864 alloc::format!("-{s}")
2865 };
2866 Expr::Literal(Literal::NumericBig(flipped))
2867 }
2868 other => Expr::Unary {
2869 op: spg_sql::ast::UnOp::Neg,
2870 expr: alloc::boxed::Box::new(other),
2871 },
2872 };
2873 literal_expr_to_value_in(
2874 Expr::Cast {
2875 expr: alloc::boxed::Box::new(negated_inner),
2876 target,
2877 },
2878 catalog,
2879 )
2880 }
2881 other => Err(EngineError::Unsupported(alloc::format!(
2882 "unary minus over non-literal expression: {other:?}"
2883 ))),
2884 },
2885 Expr::Array(items) => {
2893 let mut materialised: alloc::vec::Vec<Value<'static>> =
2894 alloc::vec::Vec::with_capacity(items.len());
2895 for elem in &items {
2896 materialised.push(literal_expr_to_value_in(elem.clone(), catalog)?);
2897 }
2898 Ok(crate::describe::upgrade_timestamptz_array(
2899 array_literal_widen(materialised),
2900 &items,
2901 &[],
2902 ))
2903 }
2904 other => eval_expr_with_catalog(other, catalog),
2917 }
2918}
2919
2920fn eval_expr_with_catalog(
2923 expr: Expr,
2924 catalog: Option<&spg_storage::Catalog>,
2925) -> Result<Value<'static>, EngineError> {
2926 let empty_schema: alloc::vec::Vec<spg_storage::ColumnSchema> = alloc::vec::Vec::new();
2927 let mut ctx = EvalContext::new(&empty_schema, None);
2928 if let Some(cat) = catalog {
2929 ctx = ctx.with_catalog(cat);
2930 }
2931 let empty_row = spg_storage::Row::new(alloc::vec::Vec::new());
2932 crate::eval::eval_expr(&expr, &empty_row, &ctx).map_err(EngineError::Eval)
2933}
2934
2935pub(crate) fn literal_to_value(l: Literal) -> Value<'static> {
2936 match l {
2937 Literal::Integer(n) => int_value_for(n),
2938 Literal::Float(x) => Value::Float(x),
2939 Literal::Numeric { unscaled, scale } => Value::Numeric {
2940 scaled: unscaled,
2941 scale,
2942 kind: spg_storage::NumericKind::Finite,
2943 },
2944 Literal::NumericBig(s) => big_literal_to_value(&s),
2945 Literal::Timestamp { micros, .. } => Value::Timestamp(micros),
2946 Literal::Date { days, .. } => Value::Date(days),
2947 Literal::String(s) => Value::text(s),
2948 Literal::Bool(b) => Value::Bool(b),
2949 Literal::Null => Value::Null,
2950 Literal::Vector(v) => Value::vector(v),
2951 Literal::TextArray(items) => Value::TextArray(items),
2952 Literal::IntArray(items) => Value::IntArray(items),
2953 Literal::BigIntArray(items) => Value::BigIntArray(items),
2954 Literal::Interval {
2955 months,
2956 days,
2957 micros,
2958 ..
2959 } => Value::Interval {
2960 months,
2961 days,
2962 micros,
2963 kind: spg_storage::IntervalKind::Finite,
2964 },
2965 }
2966}
2967
2968pub(crate) fn int_value_for(n: i64) -> Value<'static> {
2972 if let Ok(small) = i32::try_from(n) {
2973 Value::Int(small)
2974 } else {
2975 Value::BigInt(n)
2976 }
2977}
2978
2979pub(crate) fn truncate_to_column_fsp(v: Value<'static>, schema: &ColumnSchema) -> Value<'static> {
3001 let Some(fsp) = schema.mysql_fsp else {
3002 return v;
3003 };
3004 if fsp >= 6 {
3005 return v;
3006 }
3007 let scale = 10i64.pow(u32::from(6 - fsp));
3008 let cut = |micros: i64| (micros / scale) * scale;
3010 match v {
3011 Value::Timestamp(m) => Value::Timestamp(cut(m)),
3012 Value::Time(m) => Value::Time(cut(m)),
3013 other => other,
3014 }
3015}
3016
3017pub(crate) fn round_to_column_float_md(
3037 v: Value<'static>,
3038 schema: &ColumnSchema,
3039) -> Result<Value<'static>, EvalError> {
3040 let Some((m, d)) = schema.mysql_float_md else {
3041 return Ok(v);
3042 };
3043 let round = |x: f64| -> f64 {
3044 if d == 0 {
3045 let f = x.floor();
3047 return if x - f == 0.5 { f } else { x.round() };
3048 }
3049 alloc::format!("{x:.*}", usize::from(d))
3056 .parse::<f64>()
3057 .unwrap_or(x)
3058 };
3059 let limit = 10f64.powi(i32::from(m.saturating_sub(d)));
3063 let checked = |x: f64| -> Result<f64, EvalError> {
3064 let r = round(x);
3065 if r.abs() >= limit {
3066 return Err(EvalError::TypeMismatch {
3067 detail: alloc::format!("Out of range value for column '{}' at row 1", schema.name),
3068 });
3069 }
3070 Ok(r)
3071 };
3072 match v {
3073 Value::Float(x) => Ok(Value::Float(checked(x)?)),
3074 #[allow(clippy::cast_possible_truncation)]
3075 Value::Real(x) => Ok(Value::Real(checked(f64::from(x))? as f32)),
3076 other => Ok(other),
3077 }
3078}
3079
3080fn column_int_bounds(schema: &ColumnSchema) -> Option<(i128, i128)> {
3085 if let Some(width) = schema.mysql_int_width {
3086 return Some(match (width, schema.is_unsigned) {
3087 (spg_storage::MysqlIntWidth::Tiny, false) => (-128, 127),
3088 (spg_storage::MysqlIntWidth::Tiny, true) => (0, 255),
3089 (spg_storage::MysqlIntWidth::Small, false) => (-32_768, 32_767),
3090 (spg_storage::MysqlIntWidth::Small, true) => (0, 65_535),
3091 (spg_storage::MysqlIntWidth::Medium, false) => (-8_388_608, 8_388_607),
3092 (spg_storage::MysqlIntWidth::Medium, true) => (0, 16_777_215),
3093 (spg_storage::MysqlIntWidth::Int, false) => (-2_147_483_648, 2_147_483_647),
3094 (spg_storage::MysqlIntWidth::Int, true) => (0, 4_294_967_295),
3095 (spg_storage::MysqlIntWidth::Big, false) => {
3098 (i128::from(i64::MIN), i128::from(i64::MAX))
3099 }
3100 (spg_storage::MysqlIntWidth::Big, true) => (0, i128::from(u64::MAX)),
3101 });
3102 }
3103 let (lo, hi) = match schema.ty {
3104 DataType::SmallInt => (i128::from(i16::MIN), i128::from(i16::MAX)),
3105 DataType::Int => (i128::from(i32::MIN), i128::from(i32::MAX)),
3106 DataType::BigInt => (i128::from(i64::MIN), i128::from(i64::MAX)),
3107 _ => return None,
3108 };
3109 Some(if schema.is_unsigned {
3110 (0, hi)
3111 } else {
3112 (lo, hi)
3113 })
3114}
3115
3116pub(crate) fn mysql_fit_error(
3179 before: &Value<'_>,
3180 after: &Value<'_>,
3181 schema: &ColumnSchema,
3182 row: usize,
3183 omitted: bool,
3184) -> Option<(u16, &'static str, alloc::string::String)> {
3185 let w = mysql_fit_warning(before, after, schema, row, omitted)?;
3186 let col = &schema.name;
3187 Some(match w.code {
3188 1265 if matches!(
3191 schema.ty,
3192 DataType::Varchar(_) | DataType::Char(_) | DataType::Text
3193 ) =>
3194 {
3195 (
3196 1406,
3197 "22001",
3198 alloc::format!("Data too long for column '{col}' at row {row}"),
3199 )
3200 }
3201 1265 => (1265, "01000", w.message),
3202 1264 => (1264, "22003", w.message),
3203 1366 => (1366, "HY000", w.message),
3204 1364 => (1364, "HY000", w.message),
3205 other => (other, "HY000", w.message),
3206 })
3207}
3208
3209pub(crate) fn mysql_fit_warning(
3210 before: &Value<'_>,
3211 after: &Value<'_>,
3212 schema: &ColumnSchema,
3213 row: usize,
3214 omitted: bool,
3215) -> Option<crate::MysqlWarning> {
3216 if before == after {
3217 return None;
3218 }
3219 let col = &schema.name;
3220 if omitted || before.is_null() {
3223 return Some(crate::MysqlWarning {
3224 level: "Warning",
3225 code: 1364,
3226 message: alloc::format!("Field '{col}' doesn't have a default value"),
3227 });
3228 }
3229 let numeric_col = matches!(
3233 schema.ty,
3234 DataType::SmallInt
3235 | DataType::Int
3236 | DataType::BigInt
3237 | DataType::Float
3238 | DataType::Real
3239 | DataType::Numeric { .. }
3240 );
3241 if numeric_col {
3242 let noun = match schema.ty {
3254 DataType::Numeric { .. } => "decimal",
3255 DataType::Real => "FLOAT",
3256 DataType::Float => "DOUBLE",
3257 _ => "integer",
3258 };
3259 return Some(if matches!(before, Value::Text(_) | Value::BpChar(_)) {
3262 crate::MysqlWarning {
3263 level: "Warning",
3264 code: 1366,
3265 message: alloc::format!(
3266 "Incorrect {noun} value: '{}' for column '{col}' at row {row}",
3267 crate::eval::value_to_text(before)
3268 ),
3269 }
3270 } else {
3271 crate::MysqlWarning {
3272 level: "Warning",
3273 code: 1264,
3274 message: alloc::format!("Out of range value for column '{col}' at row {row}"),
3275 }
3276 });
3277 }
3278 Some(crate::MysqlWarning {
3279 level: "Warning",
3280 code: 1265,
3281 message: alloc::format!("Data truncated for column '{col}' at row {row}"),
3282 })
3283}
3284
3285fn numeric_untouched(v: Value<'static>, _schema: &ColumnSchema) -> Value<'static> {
3288 v
3289}
3290
3291fn restate_scaled(scaled: i128, from: u16, to: u16) -> i128 {
3295 if from == to {
3296 return scaled;
3297 }
3298 if to > from {
3299 let f = 10i128.checked_pow(u32::from(to - from)).unwrap_or(1);
3300 return scaled.saturating_mul(f);
3301 }
3302 let f = 10i128.checked_pow(u32::from(from - to)).unwrap_or(1);
3303 if f == 0 {
3304 return scaled;
3305 }
3306 let half = f / 2;
3307 if scaled >= 0 {
3308 (scaled + half) / f
3309 } else {
3310 (scaled - half) / f
3311 }
3312}
3313
3314pub(crate) fn mysql_ignore_fit(v: Value<'static>, schema: &ColumnSchema) -> Value<'static> {
3315 if v.is_null() {
3316 if schema.nullable {
3317 return v;
3318 }
3319 return match schema.ty {
3321 DataType::SmallInt | DataType::Int | DataType::BigInt => Value::BigInt(0),
3322 DataType::Float | DataType::Real => Value::Float(0.0),
3323 DataType::Text | DataType::Varchar(_) | DataType::Char(_) => Value::text(""),
3324 _ => v,
3325 };
3326 }
3327 if let Value::Text(ref s) = v
3330 && matches!(
3331 schema.ty,
3332 DataType::SmallInt | DataType::Int | DataType::BigInt
3333 )
3334 && s.trim().parse::<i64>().is_err()
3335 {
3336 return Value::BigInt(leading_numeric_prefix(s));
3337 }
3338 if let DataType::Numeric { precision, scale } = schema.ty
3356 && precision != 0
3357 && scale >= 0
3358 {
3359 let col_scale = u16::try_from(scale).unwrap_or(0);
3360 let (scaled, val_scale) = match v {
3361 Value::Numeric {
3362 scaled,
3363 scale: vs,
3364 kind: spg_storage::NumericKind::Finite,
3365 } => (scaled, vs),
3366 Value::SmallInt(n) => (i128::from(n), 0),
3367 Value::Int(n) => (i128::from(n), 0),
3368 Value::BigInt(n) => (i128::from(n), 0),
3369 _ => return numeric_untouched(v, schema),
3370 };
3371 let restated = restate_scaled(scaled, val_scale, col_scale);
3385 let limit = 10i128
3386 .checked_pow(u32::from(precision))
3387 .map_or(i128::MAX, |p| p - 1);
3388 if restated < -limit || restated > limit {
3389 return Value::numeric(restated.clamp(-limit, limit), col_scale);
3390 }
3391 return numeric_untouched(v, schema);
3392 }
3393 let as_int = match v {
3395 Value::SmallInt(n) => Some(i128::from(n)),
3396 Value::Int(n) => Some(i128::from(n)),
3397 Value::BigInt(n) => Some(i128::from(n)),
3398 Value::Numeric {
3400 scaled, scale: 0, ..
3401 } => Some(scaled),
3402 _ => None,
3403 };
3404 if let Some(n) = as_int
3405 && let Some((lo, hi)) = column_int_bounds(schema)
3406 && (n < lo || n > hi)
3407 {
3408 return int_value_for_column(n.clamp(lo, hi));
3409 }
3410 if let Value::Text(ref s) = v {
3412 let max = match schema.ty {
3413 DataType::Varchar(m) | DataType::Char(m) if m > 0 => m as usize,
3414 _ => return v,
3415 };
3416 if s.chars().count() > max {
3417 return Value::text(s.chars().take(max).collect::<alloc::string::String>());
3418 }
3419 }
3420 v
3421}
3422
3423fn leading_numeric_prefix(s: &str) -> i64 {
3432 let t = s.trim_start();
3433 let b = t.as_bytes();
3434 let mut i = 0;
3435 if i < b.len() && (b[i] == b'-' || b[i] == b'+') {
3436 i += 1;
3437 }
3438 let int_start = i;
3439 while i < b.len() && b[i].is_ascii_digit() {
3440 i += 1;
3441 }
3442 let mut end = i;
3443 if i < b.len() && b[i] == b'.' {
3444 i += 1;
3445 while i < b.len() && b[i].is_ascii_digit() {
3446 i += 1;
3447 }
3448 if i > int_start + 1 {
3451 end = i;
3452 }
3453 }
3454 if end > int_start && i < b.len() && (b[i] == b'e' || b[i] == b'E') {
3456 let mut j = i + 1;
3457 if j < b.len() && (b[j] == b'-' || b[j] == b'+') {
3458 j += 1;
3459 }
3460 let digits_start = j;
3461 while j < b.len() && b[j].is_ascii_digit() {
3462 j += 1;
3463 }
3464 if j > digits_start {
3465 end = j;
3466 }
3467 }
3468 let Ok(f) = t[..end].parse::<f64>() else {
3469 return 0;
3470 };
3471 let r = f.round();
3473 if r >= i64::MAX as f64 {
3474 i64::MAX
3475 } else if r <= i64::MIN as f64 {
3476 i64::MIN
3477 } else {
3478 r as i64
3479 }
3480}
3481
3482fn int_value_for_column(n: i128) -> Value<'static> {
3486 match i64::try_from(n) {
3487 Ok(v) => Value::BigInt(v),
3488 Err(_) => Value::numeric(n, 0),
3489 }
3490}
3491
3492pub(crate) fn check_unsigned_range(
3493 v: &Value,
3494 schema: &ColumnSchema,
3495 position: usize,
3496) -> Result<(), EngineError> {
3497 let n: i128 = match v {
3498 Value::SmallInt(x) => i128::from(*x),
3499 Value::Int(x) => i128::from(*x),
3500 Value::BigInt(x) => i128::from(*x),
3501 Value::Numeric { scaled, scale, .. } if *scale == 0 => *scaled,
3504 _ => return Ok(()), };
3506 if let Some(width) = schema.mysql_int_width {
3510 let _ = width;
3516 let (lo, hi) = column_int_bounds(schema).unwrap_or((i128::MIN, i128::MAX));
3517 if n < lo || n > hi {
3518 return Err(EngineError::Unsupported(alloc::format!(
3521 "Out of range value for column '{}'",
3522 schema.name
3523 )));
3524 }
3525 return Ok(());
3526 }
3527 if schema.is_unsigned && n < 0 {
3529 return Err(EngineError::Unsupported(alloc::format!(
3530 "column {:?} is UNSIGNED but got negative value {n} at position {position}",
3531 schema.name
3532 )));
3533 }
3534 Ok(())
3535}
3536
3537fn coerce_text_array_to(
3543 items: alloc::vec::Vec<Option<alloc::string::String>>,
3544 target: DataType,
3545 col: &str,
3546) -> Result<Option<Value<'static>>, EngineError> {
3547 let elem_dt = match target {
3548 DataType::BoolArray => DataType::Bool,
3549 DataType::NumericArray => DataType::Numeric {
3550 precision: 0,
3551 scale: 0,
3552 },
3553 DataType::DateArray => DataType::Date,
3554 DataType::TimestampArray => DataType::Timestamp,
3555 DataType::TimestamptzArray => DataType::Timestamptz,
3556 DataType::UuidArray => DataType::Uuid,
3557 DataType::IntervalArray => DataType::Interval,
3560 DataType::RealArray => DataType::Real,
3563 DataType::TimeArray => DataType::Time,
3564 DataType::TimeTzArray => DataType::TimeTz,
3565 DataType::InetArray => DataType::Inet,
3566 DataType::XmlArray => DataType::Xml,
3567 _ => return Ok(None),
3568 };
3569 let mut scal: alloc::vec::Vec<Option<Value<'static>>> =
3570 alloc::vec::Vec::with_capacity(items.len());
3571 for item in items {
3572 match item {
3573 None => scal.push(None),
3574 Some(s) => scal.push(Some(coerce_value(Value::text(s), elem_dt, col, 0)?)),
3575 }
3576 }
3577 let out = match target {
3578 DataType::BoolArray => Value::BoolArray(
3579 scal.into_iter()
3580 .map(|o| o.map(|v| matches!(v, Value::Bool(true))))
3581 .collect(),
3582 ),
3583 DataType::NumericArray => Value::NumericArray(
3584 scal.into_iter()
3585 .map(|o| {
3586 o.map(|v| match v {
3587 Value::Numeric { scaled, scale, .. } => (scaled, scale),
3588 _ => (0, 0),
3589 })
3590 })
3591 .collect(),
3592 ),
3593 DataType::DateArray => Value::DateArray(
3594 scal.into_iter()
3595 .map(|o| {
3596 o.map(|v| match v {
3597 Value::Date(d) => d,
3598 _ => 0,
3599 })
3600 })
3601 .collect(),
3602 ),
3603 DataType::TimestampArray => Value::TimestampArray(
3604 scal.into_iter()
3605 .map(|o| {
3606 o.map(|v| match v {
3607 Value::Timestamp(t) => t,
3608 _ => 0,
3609 })
3610 })
3611 .collect(),
3612 ),
3613 DataType::TimestamptzArray => Value::TimestamptzArray(
3614 scal.into_iter()
3615 .map(|o| {
3616 o.map(|v| match v {
3617 Value::Timestamp(t) => t,
3618 _ => 0,
3619 })
3620 })
3621 .collect(),
3622 ),
3623 DataType::UuidArray => Value::UuidArray(
3624 scal.into_iter()
3625 .map(|o| {
3626 o.map(|v| match v {
3627 Value::Uuid(u) => u,
3628 _ => [0u8; 16],
3629 })
3630 })
3631 .collect(),
3632 ),
3633 DataType::IntervalArray => Value::IntervalArray(
3634 scal.into_iter()
3635 .map(|o| {
3636 o.and_then(|v| match v {
3637 Value::Interval {
3638 months,
3639 days,
3640 micros,
3641 kind,
3642 } => Some(spg_storage::IntervalSpan {
3643 months,
3644 days,
3645 micros,
3646 kind,
3647 }),
3648 _ => None,
3649 })
3650 })
3651 .collect(),
3652 ),
3653 DataType::RealArray => Value::RealArray(
3654 scal.into_iter()
3655 .map(|o| {
3656 o.and_then(|v| match v {
3657 Value::Real(x) => Some(x),
3658 _ => None,
3659 })
3660 })
3661 .collect(),
3662 ),
3663 DataType::TimeArray => Value::TimeArray(
3664 scal.into_iter()
3665 .map(|o| {
3666 o.and_then(|v| match v {
3667 Value::Time(us) => Some(us),
3668 _ => None,
3669 })
3670 })
3671 .collect(),
3672 ),
3673 DataType::TimeTzArray => Value::TimeTzArray(
3674 scal.into_iter()
3675 .map(|o| {
3676 o.and_then(|v| match v {
3677 Value::TimeTz { us, offset_secs } => Some((us, offset_secs)),
3678 _ => None,
3679 })
3680 })
3681 .collect(),
3682 ),
3683 DataType::InetArray => Value::InetArray(
3684 scal.into_iter()
3685 .map(|o| {
3686 o.and_then(|v| match v {
3687 Value::Inet { family, bits, addr } => Some((family, bits, addr)),
3688 _ => None,
3689 })
3690 })
3691 .collect(),
3692 ),
3693 DataType::XmlArray => Value::XmlArray(
3694 scal.into_iter()
3695 .map(|o| {
3696 o.and_then(|v| match v {
3697 Value::Xml(x) => Some(x.into_owned()),
3698 _ => None,
3699 })
3700 })
3701 .collect(),
3702 ),
3703 _ => return Ok(None),
3704 };
3705 Ok(Some(out))
3706}
3707
3708pub(crate) fn array_oid_element(oid: i64) -> Option<i64> {
3719 Some(match oid {
3720 1000 => 16, 1001 => 17, 1002 => 18, 1003 => 19, 1016 => 20, 1005 => 21, 1007 => 23, 1009 => 25, 1028 => 26, 199 => 114, 143 => 142, 651 => 650, 1021 => 700, 1022 => 701, 775 => 774, 791 => 790, 1040 => 829, 1041 => 869, 1014 => 1042, 1015 => 1043, 1182 => 1082, 1183 => 1083, 1115 => 1114, 1185 => 1184, 1187 => 1186, 1270 => 1266, 1561 => 1560, 1563 => 1562, 1231 => 1700, 2951 => 2950, 3643 => 3614, 3645 => 3615, 3807 => 3802, _ => return None,
3754 })
3755}
3756
3757pub(crate) fn regtype_oid_to_name_owned(oid: i64) -> Option<alloc::string::String> {
3764 if let Some(scalar) = regtype_oid_to_name(oid) {
3765 return Some(alloc::string::String::from(scalar));
3766 }
3767 let (_, _, elem) = crate::system_catalog::ARRAY_TYPE_OIDS
3768 .iter()
3769 .find(|(arr, _, _)| *arr == oid)?;
3770 Some(alloc::format!("{}[]", regtype_oid_to_name(*elem)?))
3771}
3772
3773pub(crate) fn array_oid_for_element(elem: i64) -> Option<i64> {
3775 crate::system_catalog::ARRAY_TYPE_OIDS
3776 .iter()
3777 .find(|(_, _, e)| *e == elem)
3778 .map(|(arr, _, _)| *arr)
3779}
3780
3781pub(crate) fn regtype_oid_to_name(oid: i64) -> Option<&'static str> {
3782 Some(match oid {
3783 4600 => "pg_brin_bloom_summary",
3784 16 => "boolean",
3785 17 => "bytea",
3786 18 => "\"char\"",
3787 19 => "name",
3788 20 => "bigint",
3789 21 => "smallint",
3790 23 => "integer",
3791 25 => "text",
3792 26 => "oid",
3793 27 => "tid",
3795 28 => "xid",
3796 29 => "cid",
3797 5069 => "xid8",
3798 22 => "int2vector",
3807 30 => "oidvector",
3808 114 => "json",
3809 142 => "xml",
3810 650 => "cidr",
3811 700 => "real",
3812 701 => "double precision",
3813 774 => "macaddr8",
3814 790 => "money",
3815 829 => "macaddr",
3816 869 => "inet",
3817 1042 => "character",
3818 1043 => "character varying",
3819 1082 => "date",
3820 1083 => "time without time zone",
3821 1114 => "timestamp without time zone",
3822 1184 => "timestamp with time zone",
3823 1186 => "interval",
3824 1266 => "time with time zone",
3825 1560 => "bit",
3826 1562 => "bit varying",
3827 1700 => "numeric",
3828 2950 => "uuid",
3829 3614 => "tsvector",
3830 3615 => "tsquery",
3831 3802 => "jsonb",
3832 3904 => "int4range",
3833 3906 => "numrange",
3834 3908 => "tsrange",
3835 3910 => "tstzrange",
3836 3912 => "daterange",
3837 3926 => "int8range",
3838 _ => return None,
3839 })
3840}
3841
3842pub(crate) fn parse_pg_int(s: &str) -> Option<i64> {
3843 let s = s.trim();
3844 let (neg, rest) = if let Some(r) = s.strip_prefix('-') {
3845 (true, r)
3846 } else if let Some(r) = s.strip_prefix('+') {
3847 (false, r)
3848 } else {
3849 (false, s)
3850 };
3851 let (radix, digits, has_prefix) =
3856 if let Some(h) = rest.strip_prefix("0x").or_else(|| rest.strip_prefix("0X")) {
3857 (16u32, h, true)
3858 } else if let Some(o) = rest.strip_prefix("0o").or_else(|| rest.strip_prefix("0O")) {
3859 (8, o, true)
3860 } else if let Some(b) = rest.strip_prefix("0b").or_else(|| rest.strip_prefix("0B")) {
3861 (2, b, true)
3862 } else {
3863 (10, rest, false)
3864 };
3865 let db = digits.as_bytes();
3866 if db.last() == Some(&b'_')
3870 || digits.contains("__")
3871 || (!has_prefix && db.first() == Some(&b'_'))
3872 {
3873 return None;
3874 }
3875 let cleaned: alloc::string::String = digits.chars().filter(|&c| c != '_').collect();
3876 if cleaned.is_empty() {
3877 return None;
3878 }
3879 let mag = i64::from_str_radix(&cleaned, radix).ok()?;
3880 Some(if neg { mag.checked_neg()? } else { mag })
3881}
3882
3883fn xml_content_is_well_formed(s: &str) -> bool {
3892 let b = s.as_bytes();
3893 let is_name =
3894 |c: u8| c.is_ascii_alphanumeric() || matches!(c, b'-' | b'_' | b'.' | b':') || c >= 0x80;
3895 let mut stack: alloc::vec::Vec<&[u8]> = alloc::vec::Vec::new();
3896 let mut i = 0;
3897 while i < b.len() {
3898 if b[i] != b'<' {
3899 i += 1;
3900 continue;
3901 }
3902 let rest = &s[i..];
3903 if rest.starts_with("<!--") {
3904 match rest.find("-->") {
3905 Some(p) => i += p + 3,
3906 None => return false,
3907 }
3908 } else if rest.starts_with("<![CDATA[") {
3909 match rest.find("]]>") {
3910 Some(p) => i += p + 3,
3911 None => return false,
3912 }
3913 } else if rest.starts_with("<?") {
3914 match rest.find("?>") {
3915 Some(p) => i += p + 2,
3916 None => return false,
3917 }
3918 } else if rest.starts_with("<!") {
3919 match rest.find('>') {
3920 Some(p) => i += p + 1,
3921 None => return false,
3922 }
3923 } else {
3924 let close = i + 1 < b.len() && b[i + 1] == b'/';
3926 let name_start = if close { i + 2 } else { i + 1 };
3927 let mut j = name_start;
3928 while j < b.len() && is_name(b[j]) {
3929 j += 1;
3930 }
3931 if j == name_start {
3932 return false; }
3934 let name = &b[name_start..j];
3935 let mut k = j;
3937 let mut quote = 0u8;
3938 let mut prev = 0u8;
3939 loop {
3940 if k >= b.len() {
3941 return false; }
3943 let c = b[k];
3944 if quote != 0 {
3945 if c == quote {
3946 quote = 0;
3947 }
3948 } else if c == b'"' || c == b'\'' {
3949 quote = c;
3950 } else if c == b'>' {
3951 break;
3952 }
3953 prev = c;
3954 k += 1;
3955 }
3956 let self_closing = prev == b'/';
3957 i = k + 1;
3958 if close {
3959 match stack.pop() {
3960 Some(top) if top == name => {}
3961 _ => return false,
3962 }
3963 } else if !self_closing {
3964 stack.push(name);
3965 }
3966 }
3967 }
3968 stack.is_empty()
3969}
3970
3971pub(crate) fn parse_float8(s: &str) -> Option<f64> {
3977 let t = s.trim();
3978 let parsed = t.parse::<f64>().ok()?;
3979 let body = t.strip_prefix(['+', '-']).unwrap_or(t);
3980 let numeric_looking = body
3981 .bytes()
3982 .next()
3983 .is_some_and(|c| c.is_ascii_digit() || c == b'.');
3984 if numeric_looking {
3985 if parsed.is_infinite() {
3986 return None; }
3988 if parsed == 0.0 {
3989 let mantissa = body.split(['e', 'E']).next().unwrap_or(body);
3991 if mantissa.bytes().any(|c| c.is_ascii_digit() && c != b'0') {
3992 return None;
3993 }
3994 }
3995 }
3996 Some(parsed)
3997}
3998
3999fn decode_array_elems(
4003 s: &str,
4004 elem: DataType,
4005 col_name: &str,
4006 position: usize,
4007) -> Result<Vec<Option<Value<'static>>>, EngineError> {
4008 let raw = decode_text_array_literal(s).map_err(|_| {
4014 EngineError::Eval(EvalError::TypeMismatch {
4015 detail: malformed_array_literal(s),
4016 })
4017 })?;
4018 let mut out = Vec::with_capacity(raw.len());
4019 for e in raw {
4020 match e {
4021 None => out.push(None),
4022 Some(t) => out.push(Some(coerce_value(
4023 Value::text(t),
4024 elem,
4025 col_name,
4026 position,
4027 )?)),
4028 }
4029 }
4030 Ok(out)
4031}
4032
4033fn coerce_untyped_value(
4037 v: Value<'static>,
4038 expected: DataType,
4039 col_name: &str,
4040 position: usize,
4041) -> Result<Value<'static>, EngineError> {
4042 match (&v, expected) {
4043 (
4054 Value::RegClass(oid, _) | Value::RegProc(oid, _) | Value::RegType(oid, _),
4055 DataType::BigInt | DataType::Oid,
4056 ) => Ok(Value::BigInt(*oid)),
4057 (
4058 Value::RegClass(oid, _) | Value::RegProc(oid, _) | Value::RegType(oid, _),
4059 DataType::Int,
4060 ) => Ok(Value::Int(i32::try_from(*oid).unwrap_or(i32::MAX))),
4061 (
4062 Value::RegClass(_, name) | Value::RegProc(_, name) | Value::RegType(_, name),
4063 DataType::Text,
4064 ) => Ok(Value::text(alloc::string::String::from(name.as_ref()))),
4065 (Value::Composite(fields), DataType::Jsonb | DataType::Json) => {
4071 let mut obj = alloc::string::String::from("{");
4072 for (i, (name, val)) in fields.iter().enumerate() {
4073 if i > 0 {
4074 obj.push(',');
4075 }
4076 obj.push_str(&crate::json::value_to_json_text(&Value::text(
4078 alloc::string::String::from(name.as_str()),
4079 )));
4080 obj.push(':');
4081 obj.push_str(&crate::json::value_to_json_text(val));
4082 }
4083 obj.push('}');
4084 Ok(Value::Json(alloc::borrow::Cow::Owned(obj)))
4085 }
4086 (Value::Composite(_), DataType::Text) => Ok(Value::text(crate::eval::value_to_text(&v))),
4088 _ => Err(EngineError::Unsupported(alloc::format!(
4089 "cannot coerce {:?} to {expected:?} for column {col_name:?} (position {position})",
4090 v
4091 ))),
4092 }
4093}
4094
4095fn invalid_input_syntax(ty: &str, value: &str) -> EngineError {
4099 EngineError::Eval(EvalError::TypeMismatch {
4100 detail: alloc::format!("invalid input syntax for type {ty}: \"{value}\""),
4101 })
4102}
4103
4104fn real_out_of_range(value: &str) -> EngineError {
4107 float_out_of_range(value, "real")
4108}
4109
4110fn float_out_of_range(value: &str, ty: &str) -> EngineError {
4112 EngineError::Eval(EvalError::TypeMismatch {
4113 detail: alloc::format!("\"{value}\" is out of range for type {ty}"),
4114 })
4115}
4116
4117fn float_text_error(s: &str, ty: &str) -> EngineError {
4123 let t = s.trim();
4124 let body = t.strip_prefix(['+', '-']).unwrap_or(t);
4125 let numeric_looking = body
4126 .bytes()
4127 .next()
4128 .is_some_and(|c| c.is_ascii_digit() || c == b'.');
4129 if numeric_looking && t.parse::<f64>().is_ok() {
4130 float_out_of_range(t, ty)
4131 } else {
4132 invalid_input_syntax(ty, s)
4133 }
4134}
4135
4136fn float_text_is_nonzero(t: &str) -> bool {
4140 let body = t.strip_prefix(['+', '-']).unwrap_or(t);
4141 let mantissa = body.split(['e', 'E']).next().unwrap_or(body);
4142 mantissa.bytes().any(|c| c.is_ascii_digit() && c != b'0')
4143}
4144
4145fn text_is_explicit_infinity(t: &str) -> bool {
4148 let t = t.trim_start_matches(['+', '-']);
4149 t.eq_ignore_ascii_case("inf") || t.eq_ignore_ascii_case("infinity")
4150}
4151
4152fn datetime_parse_error(ty: &str, s: &str) -> EngineError {
4161 let t = s.trim();
4162 let date_shaped = t.chars().any(|c| c.is_ascii_digit())
4163 && t.chars().all(|c| {
4164 c.is_ascii_digit() || matches!(c, '-' | '/' | ':' | '.' | ' ' | '+' | 'T' | 't')
4165 });
4166 let detail = if date_shaped {
4167 alloc::format!("date/time field value out of range: \"{t}\"")
4168 } else {
4169 alloc::format!("invalid input syntax for type {ty}: \"{t}\"")
4170 };
4171 EngineError::Eval(EvalError::TypeMismatch { detail })
4172}
4173
4174pub(crate) enum JsonbScalar {
4180 Numeric(Value<'static>),
4181 Bool(bool),
4182 Null,
4183}
4184
4185pub(crate) fn jsonb_cast_type_error(kind: &str, target: &str) -> EvalError {
4187 EvalError::TypeMismatch {
4188 detail: alloc::format!("cannot cast jsonb {kind} to type {target}"),
4189 }
4190}
4191
4192pub(crate) fn jsonb_scalar_for_cast(s: &str, target: &str) -> Result<JsonbScalar, EvalError> {
4195 use crate::json::JsonValue;
4196 match crate::json::parse(s) {
4197 Ok(JsonValue::Null) => Ok(JsonbScalar::Null),
4198 Ok(JsonValue::Bool(b)) => Ok(JsonbScalar::Bool(b)),
4199 Ok(JsonValue::Number(x)) => {
4203 let num = coerce_value(
4204 Value::text(alloc::format!("{x}")),
4205 DataType::Numeric {
4206 precision: 0,
4207 scale: 0,
4208 },
4209 "",
4210 0,
4211 )
4212 .map_err(|e| match e {
4213 EngineError::Eval(ev) => ev,
4214 _ => jsonb_cast_type_error("numeric", target),
4215 })?;
4216 Ok(JsonbScalar::Numeric(num))
4217 }
4218 Ok(JsonValue::NumberText(text)) => {
4219 let num = coerce_value(
4220 Value::text(text),
4221 DataType::Numeric {
4222 precision: 0,
4223 scale: 0,
4224 },
4225 "",
4226 0,
4227 )
4228 .map_err(|e| match e {
4229 EngineError::Eval(ev) => ev,
4230 _ => jsonb_cast_type_error("numeric", target),
4231 })?;
4232 Ok(JsonbScalar::Numeric(num))
4233 }
4234 Ok(JsonValue::String(_)) => Err(jsonb_cast_type_error("string", target)),
4235 Ok(JsonValue::Array(_)) => Err(jsonb_cast_type_error("array", target)),
4236 Ok(JsonValue::Object(_)) => Err(jsonb_cast_type_error("object", target)),
4237 Err(_) => Err(jsonb_cast_type_error("value", target)),
4238 }
4239}
4240pub(crate) fn normalize_composite_for_column(
4258 v: Value<'static>,
4259 col: &ColumnSchema,
4260 catalog: Option<&spg_storage::Catalog>,
4261) -> Result<Value<'static>, EngineError> {
4262 let Some(tname) = col.user_composite_type.as_deref() else {
4263 return Ok(v);
4264 };
4265 if matches!(v, Value::Null) {
4266 return Ok(v);
4267 }
4268 let Some(def) = catalog.and_then(|c| c.composite_types().get(tname)) else {
4271 return Ok(v);
4272 };
4273 if matches!(v, Value::Json(_)) {
4276 return Ok(v);
4277 }
4278 crate::eval::apply_composite_cast_pub(v, def, catalog).map_err(EngineError::Eval)
4279}
4280
4281fn try_coerce_json_scalar(
4285 s: &str,
4286 expected: DataType,
4287 col_name: &str,
4288 position: usize,
4289) -> Option<Result<Value<'static>, EngineError>> {
4290 let target = match expected {
4291 DataType::Int => "integer",
4292 DataType::BigInt => "bigint",
4293 DataType::SmallInt => "smallint",
4294 DataType::Numeric { .. } => "numeric",
4295 DataType::Real => "real",
4296 DataType::Float => "double precision",
4297 DataType::Bool => "boolean",
4298 _ => return None,
4299 };
4300 Some(
4301 (|| match jsonb_scalar_for_cast(s, target).map_err(EngineError::Eval)? {
4302 JsonbScalar::Null => Ok(Value::Null),
4303 JsonbScalar::Bool(b) => {
4304 if matches!(expected, DataType::Bool) {
4305 Ok(Value::Bool(b))
4306 } else {
4307 Err(EngineError::Eval(jsonb_cast_type_error("boolean", target)))
4308 }
4309 }
4310 JsonbScalar::Numeric(n) => {
4311 if matches!(expected, DataType::Bool) {
4312 Err(EngineError::Eval(jsonb_cast_type_error("numeric", target)))
4313 } else {
4314 coerce_value(n, expected, col_name, position)
4315 }
4316 }
4317 })(),
4318 )
4319}
4320
4321pub(crate) fn mysql_bytes_for_column(
4331 v: Value<'static>,
4332 expected: DataType,
4333 mysql: bool,
4334) -> Value<'static> {
4335 if !mysql {
4336 return v;
4337 }
4338 let Value::Bytes(ref b) = v else {
4339 return v;
4340 };
4341 match expected {
4342 DataType::SmallInt
4343 | DataType::Int
4344 | DataType::BigInt
4345 | DataType::Float
4346 | DataType::Real
4347 | DataType::Numeric { .. } => {
4348 let start = b.len().saturating_sub(16);
4349 let acc = b[start..]
4350 .iter()
4351 .fold(0u128, |a, &x| (a << 8) | u128::from(x));
4352 if acc <= i64::MAX as u128 {
4353 #[allow(clippy::cast_possible_truncation)]
4354 Value::BigInt(acc as i64)
4355 } else {
4356 big_literal_to_value(&alloc::format!("{acc}"))
4357 }
4358 }
4359 DataType::Text | DataType::Varchar(_) | DataType::Char(_) => Value::text(
4360 b.iter()
4361 .map(|&x| x as char)
4362 .collect::<alloc::string::String>(),
4363 ),
4364 _ => v,
4365 }
4366}
4367
4368fn try_coerce_time_family(
4387 v: &Value<'static>,
4388 expected: DataType,
4389) -> Option<Result<Value<'static>, EngineError>> {
4390 const DAY_US: i64 = 86_400_000_000;
4391 if expected != DataType::Time {
4392 return None;
4393 }
4394 match v {
4395 Value::TimeTz { us, .. } => Some(Ok(Value::Time(*us))),
4396 Value::Interval { micros, .. } => Some(Ok(Value::Time(micros.rem_euclid(DAY_US)))),
4397 _ => None,
4398 }
4399}
4400
4401pub(crate) fn coerce_to_oid(v: &Value<'_>) -> Result<Option<Value<'static>>, EvalError> {
4411 let as_i64 = match v {
4412 Value::Null => return Ok(Some(Value::Null)),
4413 Value::SmallInt(n) => i64::from(*n),
4414 Value::Int(n) => i64::from(*n),
4415 Value::BigInt(n) => *n,
4416 Value::Text(t) => match t.trim().parse::<i64>() {
4417 Ok(n) => n,
4418 Err(_) => {
4419 return Err(EvalError::TypeMismatch {
4420 detail: alloc::format!("invalid input syntax for type oid: {:?}", t.trim()),
4421 });
4422 }
4423 },
4424 _ => return Ok(None),
4425 };
4426 if (-(1i64 << 31)..0).contains(&as_i64) {
4428 return Ok(Some(Value::BigInt(as_i64 + (1i64 << 32))));
4429 }
4430 if !(0..=i64::from(u32::MAX)).contains(&as_i64) {
4431 return Err(EvalError::TypeMismatch {
4432 detail: "OID out of range".into(),
4433 });
4434 }
4435 Ok(Some(Value::BigInt(as_i64)))
4436}
4437
4438pub(crate) fn coerce_value(
4439 v: Value<'static>,
4440 expected: DataType,
4441 col_name: &str,
4442 position: usize,
4443) -> Result<Value<'static>, EngineError> {
4444 if v.is_null() {
4445 return Ok(Value::Null);
4446 }
4447 if let Value::Json(ref s) = v {
4452 if let Some(res) = try_coerce_json_scalar(s, expected, col_name, position) {
4453 return res;
4454 }
4455 }
4456 if let Some(res) = try_coerce_time_family(&v, expected) {
4460 return res;
4461 }
4462 if let Value::Numeric { kind, .. } = v
4478 && kind != spg_storage::NumericKind::Finite
4479 {
4480 use spg_storage::NumericKind as K;
4481 let as_f64 = match kind {
4482 K::NaN => f64::NAN,
4483 K::PosInf => f64::INFINITY,
4484 K::NegInf => f64::NEG_INFINITY,
4485 K::Finite => unreachable!("checked above"),
4486 };
4487 let what = if kind == K::NaN { "NaN" } else { "infinity" };
4489 let int_err = |target: &str| {
4490 Err(EngineError::Eval(EvalError::TypeMismatch {
4491 detail: alloc::format!("cannot convert {what} to {target}"),
4492 }))
4493 };
4494 match expected {
4495 DataType::Float => return Ok(Value::Float(as_f64)),
4496 #[allow(clippy::cast_possible_truncation)]
4497 DataType::Real => return Ok(Value::Real(as_f64 as f32)),
4498 DataType::Int => return int_err("integer"),
4499 DataType::BigInt => return int_err("bigint"),
4500 DataType::SmallInt => return int_err("smallint"),
4501 DataType::Numeric { precision, scale } => {
4502 if precision != 0 && kind != K::NaN {
4506 return Err(EngineError::Eval(EvalError::TypeMismatch {
4507 detail: alloc::string::String::from("numeric field overflow"),
4508 }));
4509 }
4510 let _ = scale;
4511 return Ok(v);
4512 }
4513 _ => {}
4514 }
4515 }
4516 if let DataType::Numeric { precision, .. } = expected {
4520 let f = match v {
4521 Value::Float(f) if !f.is_finite() => Some(f),
4522 #[allow(clippy::cast_lossless)]
4523 Value::Real(f) if !f.is_finite() => Some(f as f64),
4524 _ => None,
4525 };
4526 if let Some(f) = f {
4527 use spg_storage::NumericKind as K;
4528 if f.is_nan() {
4529 return Ok(Value::numeric_special(K::NaN));
4530 }
4531 if precision != 0 {
4532 return Err(EngineError::Eval(EvalError::TypeMismatch {
4533 detail: alloc::string::String::from("numeric field overflow"),
4534 }));
4535 }
4536 return Ok(Value::numeric_special(if f > 0.0 {
4537 K::PosInf
4538 } else {
4539 K::NegInf
4540 }));
4541 }
4542 }
4543 let Some(actual) = v.data_type() else {
4544 return coerce_untyped_value(v, expected, col_name, position);
4545 };
4546 if actual == expected {
4547 return Ok(v);
4548 }
4549 if matches!(expected, DataType::Json | DataType::Jsonb)
4573 && let Value::Text(ref s) | Value::Json(ref s) = v
4574 {
4575 let bad = || {
4576 EngineError::Eval(crate::eval::EvalError::TypeMismatch {
4577 detail: alloc::string::String::from("invalid input syntax for type json"),
4578 })
4579 };
4580 return if expected == DataType::Jsonb {
4581 crate::json::canonicalize_jsonb(s.as_ref())
4582 .map(Value::json)
4583 .map_err(|_| bad())
4584 } else {
4585 crate::json::parse(s.as_ref())
4586 .map_err(|_| bad())
4587 .map(|_| Value::json(s.clone()))
4588 };
4589 }
4590 let coerced: Option<Value<'static>> = match (v, expected) {
4591 (Value::Int(n), DataType::BigInt) => Some(Value::BigInt(i64::from(n))),
4592 (Value::Int(n), DataType::Float) => Some(Value::Float(f64::from(n))),
4593 (Value::Int(n), DataType::SmallInt) => match i16::try_from(n) {
4596 Ok(v) => Some(Value::SmallInt(v)),
4597 Err(_) => {
4598 return Err(EngineError::Eval(EvalError::TypeMismatch {
4599 detail: "smallint out of range".into(),
4600 }));
4601 }
4602 },
4603 (Value::Int(n), DataType::Numeric { precision, scale }) => Some(numeric_from_integer(
4604 i128::from(n),
4605 precision,
4606 scale,
4607 col_name,
4608 )?),
4609 (Value::SmallInt(n), DataType::Int) => Some(Value::Int(i32::from(n))),
4610 (Value::SmallInt(n), DataType::BigInt) => Some(Value::BigInt(i64::from(n))),
4611 (Value::SmallInt(n), DataType::Float) => Some(Value::Float(f64::from(n))),
4612 (Value::SmallInt(n), DataType::Numeric { precision, scale }) => Some(numeric_from_integer(
4613 i128::from(n),
4614 precision,
4615 scale,
4616 col_name,
4617 )?),
4618 (Value::BigInt(n), DataType::Int) => match i32::try_from(n) {
4619 Ok(v) => Some(Value::Int(v)),
4620 Err(_) => {
4621 return Err(EngineError::Eval(EvalError::TypeMismatch {
4622 detail: "integer out of range".into(),
4623 }));
4624 }
4625 },
4626 (Value::BigInt(n), DataType::SmallInt) => match i16::try_from(n) {
4627 Ok(v) => Some(Value::SmallInt(v)),
4628 Err(_) => {
4629 return Err(EngineError::Eval(EvalError::TypeMismatch {
4630 detail: "smallint out of range".into(),
4631 }));
4632 }
4633 },
4634 #[allow(clippy::cast_precision_loss)]
4635 (Value::BigInt(n), DataType::Float) => Some(Value::Float(n as f64)),
4636 (Value::BigInt(n), DataType::Numeric { precision, scale }) => Some(numeric_from_integer(
4637 i128::from(n),
4638 precision,
4639 scale,
4640 col_name,
4641 )?),
4642 (Value::Float(x), DataType::Numeric { precision, scale }) => {
4643 if precision == 0 && scale == 0 && x.is_finite() {
4649 if let Some((mantissa, src_scale)) = parse_numeric_text(&alloc::format!("{x}")) {
4650 Some(Value::Numeric {
4651 scaled: mantissa,
4652 scale: src_scale,
4653 kind: spg_storage::NumericKind::Finite,
4654 })
4655 } else {
4656 Some(numeric_from_float(x, precision, scale, col_name)?)
4657 }
4658 } else {
4659 Some(numeric_from_float(x, precision, scale, col_name)?)
4660 }
4661 }
4662 (Value::Real(x), DataType::Numeric { precision, scale }) => {
4668 if precision == 0 && scale == 0 && x.is_finite() {
4669 let six = alloc::format!("{:.5e}", x);
4683 let six: f64 = six.parse().unwrap_or_else(|_| f64::from(x));
4684 if let Some((mantissa, src_scale)) = parse_numeric_text(&alloc::format!("{six}")) {
4685 Some(Value::Numeric {
4686 scaled: mantissa,
4687 scale: src_scale,
4688 kind: spg_storage::NumericKind::Finite,
4689 })
4690 } else {
4691 Some(numeric_from_float(
4692 f64::from(x),
4693 precision,
4694 scale,
4695 col_name,
4696 )?)
4697 }
4698 } else {
4699 Some(numeric_from_float(
4700 f64::from(x),
4701 precision,
4702 scale,
4703 col_name,
4704 )?)
4705 }
4706 }
4707 (Value::Text(s), DataType::Numeric { precision, scale }) => {
4718 if let Some(kind) = crate::numeric::parse_numeric_special(&s) {
4721 return Ok(Value::numeric_special(kind));
4722 }
4723 let Some((mantissa, src_scale)) = parse_numeric_text(&s) else {
4724 match spg_sql::parser::expand_scientific_literal(&s) {
4729 spg_sql::parser::SciExpanded::Expanded(plain) => {
4730 return coerce_value(
4731 Value::Text(plain.into()),
4732 DataType::Numeric { precision, scale },
4733 col_name,
4734 position,
4735 );
4736 }
4737 spg_sql::parser::SciExpanded::Overflow => {
4738 return Err(EngineError::Eval(EvalError::TypeMismatch {
4739 detail: "value overflows numeric format".into(),
4740 }));
4741 }
4742 spg_sql::parser::SciExpanded::NotScientific => {}
4743 }
4744 if precision == 0 && scale == 0 {
4747 if let Some(b) = spg_storage::bignum::BigNumeric::from_decimal_str(&s) {
4748 return Ok(Value::NumericBig(alloc::boxed::Box::new(b)));
4749 }
4750 }
4751 return Err(EngineError::Eval(EvalError::TypeMismatch {
4752 detail: alloc::format!("invalid input syntax for type numeric: \"{s}\""),
4753 }));
4754 };
4755 if precision == 0 && scale == 0 {
4757 Some(Value::Numeric {
4758 scaled: mantissa,
4759 scale: src_scale,
4760 kind: spg_storage::NumericKind::Finite,
4761 })
4762 } else {
4763 Some(numeric_rescale(
4764 mantissa, src_scale, precision, scale, col_name,
4765 )?)
4766 }
4767 }
4768 (Value::Text(s), DataType::Date) => {
4770 let d = eval::parse_date_literal(&s)
4777 .or_else(|| {
4778 eval::parse_timestamp_literal(&s)
4779 .and_then(|t| i32::try_from(t.div_euclid(86_400_000_000)).ok())
4780 })
4781 .ok_or_else(|| datetime_parse_error("date", &s))?;
4782 Some(Value::Date(d))
4783 }
4784 (Value::Text(s), DataType::SmallInt) => Some(Value::SmallInt(
4799 parse_pg_int(&s)
4800 .and_then(|n| i16::try_from(n).ok())
4801 .ok_or_else(|| invalid_input_syntax("smallint", &s))?,
4802 )),
4803 (Value::Text(s), DataType::Int) => Some(Value::Int(
4804 parse_pg_int(&s)
4805 .and_then(|n| i32::try_from(n).ok())
4806 .ok_or_else(|| invalid_input_syntax("integer", &s))?,
4807 )),
4808 (Value::Text(s), DataType::BigInt) => Some(Value::BigInt(
4809 parse_pg_int(&s).ok_or_else(|| invalid_input_syntax("bigint", &s))?,
4810 )),
4811 (Value::Text(s), DataType::Xid) => Some(Value::Xid(
4817 s.parse::<u32>()
4818 .map_err(|_| invalid_input_syntax("xid", &s))?,
4819 )),
4820 (Value::Xid(x), DataType::Xid) => Some(Value::Xid(x)),
4821 (Value::Text(s), DataType::Xid8) => Some(Value::BigInt(
4822 parse_pg_int(&s).ok_or_else(|| invalid_input_syntax("xid8", &s))?,
4823 )),
4824 (Value::BigInt(n), DataType::Xid8) => Some(Value::BigInt(n)),
4831 (ref other, DataType::Oid) => coerce_to_oid(other)?,
4835 (Value::Text(s), DataType::Float) => {
4836 Some(Value::Float(
4840 parse_float8(&s).ok_or_else(|| float_text_error(&s, "double precision"))?,
4841 ))
4842 }
4843 (Value::Int(n), DataType::Real) => Some(Value::Real(n as f32)),
4845 (Value::SmallInt(n), DataType::Real) => Some(Value::Real(f32::from(n))),
4846 (Value::BigInt(n), DataType::Real) => Some(Value::Real(n as f32)),
4847 (Value::Float(x), DataType::Real) => {
4848 let narrowed = x as f32;
4852 if narrowed.is_infinite() && x.is_finite() {
4853 return Err(EngineError::Eval(EvalError::TypeMismatch {
4854 detail: "value out of range: overflow".into(),
4855 }));
4856 }
4857 if narrowed == 0.0 && x != 0.0 {
4859 return Err(EngineError::Eval(EvalError::TypeMismatch {
4860 detail: "value out of range: underflow".into(),
4861 }));
4862 }
4863 Some(Value::Real(narrowed))
4864 }
4865 (
4866 Value::Numeric {
4867 scaled,
4868 scale,
4869 kind,
4870 },
4871 DataType::Real,
4872 ) => Some(Value::Real(match kind {
4873 spg_storage::NumericKind::NaN => f32::NAN,
4874 spg_storage::NumericKind::PosInf => f32::INFINITY,
4875 spg_storage::NumericKind::NegInf => f32::NEG_INFINITY,
4876 spg_storage::NumericKind::Finite => {
4877 let mut div = 1.0f64;
4878 for _ in 0..scale {
4879 div *= 10.0;
4880 }
4881 let x = (scaled as f64 / div) as f32;
4882 if x == 0.0 && scaled != 0 {
4885 return Err(real_out_of_range(&crate::eval::format_numeric(
4886 scaled, scale,
4887 )));
4888 }
4889 x
4890 }
4891 })),
4892 (Value::Real(x), DataType::Float) => Some(Value::Float(f64::from(x))),
4893 (Value::Text(s), DataType::Real) => {
4900 let t = s.trim();
4901 let x = t
4902 .parse::<f32>()
4903 .ok()
4904 .ok_or_else(|| invalid_input_syntax("real", &s))?;
4905 if x.is_infinite() && !text_is_explicit_infinity(t) {
4906 return Err(real_out_of_range(t));
4907 }
4908 if x == 0.0 && float_text_is_nonzero(t) {
4911 return Err(real_out_of_range(t));
4912 }
4913 Some(Value::Real(x))
4914 }
4915 (Value::Text(s), DataType::Bool) => match s.trim().to_ascii_lowercase().as_str() {
4919 "0" | "f" | "fa" | "fal" | "fals" | "false" | "n" | "no" | "of" | "off" => {
4920 Some(Value::Bool(false))
4921 }
4922 "1" | "t" | "tr" | "tru" | "true" | "y" | "ye" | "yes" | "on" => {
4923 Some(Value::Bool(true))
4924 }
4925 _ => return Err(invalid_input_syntax("boolean", &s)),
4926 },
4927 (Value::Int(n), DataType::Bool) => Some(Value::Bool(n != 0)),
4936 (Value::SmallInt(n), DataType::Bool) => Some(Value::Bool(n != 0)),
4937 (Value::BigInt(n), DataType::Bool) => Some(Value::Bool(n != 0)),
4938 (Value::Json(s), DataType::Text) => Some(Value::text(s)),
4960 (Value::Json(s), DataType::Json) => Some(Value::json(s)),
4968 (Value::Json(s), DataType::Jsonb) => Some(Value::json(
4969 crate::json::canonicalize_jsonb(s.as_ref()).unwrap_or_else(|_| s.into_owned()),
4970 )),
4971 (Value::Text(s), DataType::Bytes) => {
4978 let bytes = decode_bytea_literal(&s)
4979 .map_err(|e| EngineError::Eval(EvalError::TypeMismatch { detail: e }))?;
4980 Some(Value::bytes(bytes))
4981 }
4982 (Value::Bytes(b), DataType::Text) => Some(Value::text(encode_bytea_hex(&b))),
4986 (Value::Text(s), DataType::Uuid) => match spg_storage::parse_uuid_str(&s) {
4994 Some(b) => Some(Value::Uuid(b)),
4995 None => {
4996 return Err(EngineError::Eval(EvalError::TypeMismatch {
4997 detail: alloc::format!("invalid input syntax for type uuid: {s:?}"),
4998 }));
4999 }
5000 },
5001 (Value::Uuid(b), DataType::Text) => Some(Value::text(spg_storage::format_uuid(&b))),
5006 (Value::Text(s), DataType::Time) => match parse_time_str(&s) {
5012 Some(us) => Some(Value::Time(us)),
5013 None => {
5014 let time_shaped = {
5020 let core = s.trim().split('.').next().unwrap_or("");
5021 !core.is_empty()
5022 && core.split(':').count() >= 2
5023 && core
5024 .split(':')
5025 .all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()))
5026 };
5027 let detail = if time_shaped {
5028 alloc::format!("date/time field value out of range: {s:?}")
5029 } else {
5030 alloc::format!("invalid input syntax for type time: {s:?}")
5031 };
5032 return Err(EngineError::Eval(EvalError::TypeMismatch { detail }));
5033 }
5034 },
5035 (Value::Time(us), DataType::Text) => Some(Value::text(eval::format_time(us))),
5037 (Value::SmallInt(n), DataType::Year) => Some(coerce_int_to_year(i64::from(n), col_name)?),
5042 (Value::Int(n), DataType::Year) => Some(coerce_int_to_year(i64::from(n), col_name)?),
5043 (Value::BigInt(n), DataType::Year) => Some(coerce_int_to_year(n, col_name)?),
5044 (Value::Text(s), DataType::Year) => match s.trim().parse::<i64>() {
5048 Ok(n) => Some(coerce_int_to_year(n, col_name)?),
5049 Err(_) => {
5050 return Err(EngineError::Eval(EvalError::TypeMismatch {
5051 detail: alloc::format!("invalid input syntax for type year: {s:?}"),
5052 }));
5053 }
5054 },
5055 (Value::Year(y), DataType::Text) => Some(Value::text(alloc::format!("{y:04}"))),
5057 (Value::Time(t), DataType::TimeTz) => Some(Value::TimeTz {
5071 us: t,
5072 offset_secs: 0,
5073 }),
5074 (Value::Timestamp(t), DataType::TimeTz) => Some(Value::TimeTz {
5075 us: t.rem_euclid(86_400_000_000),
5076 offset_secs: 0,
5077 }),
5078 (Value::Text(s), DataType::TimeTz) => {
5079 match parse_timetz_str(&s).or_else(|| parse_time_str(s.trim()).map(|us| (us, 0))) {
5080 Some((us, offset_secs)) => Some(Value::TimeTz { us, offset_secs }),
5081 None => {
5082 return Err(EngineError::Eval(EvalError::TypeMismatch {
5083 detail: alloc::format!(
5084 "invalid input syntax for type time with time zone: \
5085 {s:?}"
5086 ),
5087 }));
5088 }
5089 }
5090 }
5091 (Value::TimeTz { us, offset_secs }, DataType::Text) => {
5093 Some(Value::text(eval::format_timetz(us, offset_secs)))
5094 }
5095 (Value::Text(s), DataType::Money) => match parse_money_str(&s) {
5099 Some(c) => Some(Value::Money(c)),
5100 None => {
5101 return Err(EngineError::Eval(EvalError::TypeMismatch {
5102 detail: alloc::format!("invalid input syntax for type money: {s:?}"),
5103 }));
5104 }
5105 },
5106 (Value::SmallInt(n), DataType::Money) => {
5110 Some(Value::Money(i64::from(n).saturating_mul(100)))
5111 }
5112 (Value::Int(n), DataType::Money) => Some(Value::Money(i64::from(n).saturating_mul(100))),
5113 (Value::BigInt(n), DataType::Money) => Some(Value::Money(n.saturating_mul(100))),
5114 (Value::Float(x), DataType::Money) => {
5115 let scaled = x * 100.0;
5118 let cents = if scaled >= 0.0 {
5119 (scaled + 0.5) as i64
5120 } else {
5121 (scaled - 0.5) as i64
5122 };
5123 Some(Value::Money(cents))
5124 }
5125 (Value::Numeric { scaled, scale, .. }, DataType::Money) => {
5126 let cents = if scale == 2 {
5129 scaled
5130 } else if scale < 2 {
5131 let mult = 10_i128.pow(u32::from(2 - scale));
5132 scaled.saturating_mul(mult)
5133 } else {
5134 let div = 10_i128.pow(u32::from(scale - 2));
5135 let half = div / 2;
5136 let bias = if scaled >= 0 { half } else { -half };
5137 (scaled + bias) / div
5138 };
5139 Some(Value::Money(i64::try_from(cents).unwrap_or(i64::MAX)))
5140 }
5141 (Value::Money(c), DataType::Text) => Some(Value::text(eval::format_money(c))),
5143 (Value::Money(c), DataType::Numeric { .. }) => Some(Value::Numeric {
5145 scaled: i128::from(c),
5146 scale: 2,
5147 kind: spg_storage::NumericKind::Finite,
5148 }),
5149 (Value::Text(s), DataType::Range(kind)) => match parse_range_str(&s, kind) {
5153 Ok(v) => Some(v),
5154 Err(RangeParseError::Misordered) => {
5156 return Err(EngineError::Eval(EvalError::TypeMismatch {
5157 detail: alloc::string::String::from(
5158 "range lower bound must be less than or equal to range upper bound",
5159 ),
5160 }));
5161 }
5162 Err(RangeParseError::Malformed) => {
5163 return Err(EngineError::Eval(EvalError::TypeMismatch {
5164 detail: alloc::format!("malformed range literal: \"{s}\""),
5165 }));
5166 }
5167 Err(RangeParseError::BadElement(bad)) => {
5168 return Err(EngineError::Eval(EvalError::TypeMismatch {
5169 detail: alloc::format!(
5170 "invalid input syntax for type {}: \"{bad}\"",
5171 range_element_type_name(kind)
5172 ),
5173 }));
5174 }
5175 },
5176 (v @ Value::Range { .. }, DataType::Text) => Some(Value::text(format_range_str(&v))),
5178 (Value::Text(s), DataType::Inet) => match parse_inet_text(&s) {
5180 Some((family, bits, addr)) => Some(Value::Inet { family, bits, addr }),
5181 None => {
5182 return Err(EngineError::Eval(EvalError::TypeMismatch {
5186 detail: alloc::format!("invalid input syntax for type inet: {s:?}"),
5187 }));
5188 }
5189 },
5190 (Value::Inet { family, bits, addr }, DataType::Cidr) => {
5197 let full = if family == 6 { 128 } else { 32 };
5198 let bits = if bits > full { full } else { bits };
5199 let mut masked = addr;
5200 for i in 0..16usize {
5201 let bit_start = i * 8;
5202 if bit_start >= usize::from(bits) {
5203 masked[i] = 0;
5204 } else if bit_start + 8 > usize::from(bits) {
5205 let keep = usize::from(bits) - bit_start;
5206 masked[i] &= 0xffu8 << (8 - keep);
5207 }
5208 }
5209 Some(Value::Cidr {
5210 family,
5211 bits,
5212 addr: masked,
5213 })
5214 }
5215 (Value::Cidr { family, bits, addr }, DataType::Inet) => {
5216 Some(Value::Inet { family, bits, addr })
5217 }
5218 (Value::Text(s), DataType::Cidr) => match parse_cidr_text(&s) {
5219 Ok(Some((family, bits, addr))) => Some(Value::Cidr { family, bits, addr }),
5220 Err(()) => {
5221 return Err(EngineError::Eval(EvalError::TypeMismatch {
5222 detail: alloc::format!(
5223 "invalid cidr value: {s:?} DETAIL: Value has bits set to right of mask."
5224 ),
5225 }));
5226 }
5227 Ok(None) => {
5228 return Err(EngineError::Eval(EvalError::TypeMismatch {
5229 detail: alloc::format!("invalid input syntax for type cidr: {s:?}"),
5230 }));
5231 }
5232 },
5233 (Value::Text(s), DataType::Interval) => match spg_sql::parser::parse_interval_text(&s) {
5236 Some((months, days, micros)) => Some(Value::Interval {
5237 months,
5238 days,
5239 micros,
5240 kind: spg_storage::IntervalKind::from_fields(months, days, micros),
5241 }),
5242 None => {
5243 return Err(EngineError::Eval(EvalError::TypeMismatch {
5244 detail: alloc::format!("invalid input syntax for type interval: {s:?}"),
5245 }));
5246 }
5247 },
5248 (Value::Text(s), DataType::Macaddr) => match parse_macaddr_text(&s) {
5249 Some(m) => Some(Value::Macaddr(m)),
5250 None => {
5251 return Err(EngineError::Eval(EvalError::TypeMismatch {
5252 detail: alloc::format!("invalid input syntax for type macaddr: {s:?}"),
5253 }));
5254 }
5255 },
5256 (Value::Text(s), DataType::PgLsn) => match parse_pg_lsn_text(&s) {
5258 Some(l) => Some(Value::PgLsn(l)),
5259 None => {
5260 return Err(EngineError::Eval(EvalError::TypeMismatch {
5261 detail: alloc::format!("invalid input syntax for type pg_lsn: \"{s}\""),
5262 }));
5263 }
5264 },
5265 (Value::Text(s), DataType::Macaddr8) => match parse_macaddr8_text(&s) {
5266 Some(m) => Some(Value::Macaddr8(m)),
5267 None => {
5268 return Err(EngineError::Eval(EvalError::TypeMismatch {
5269 detail: alloc::format!("invalid input syntax for type macaddr8: {s:?}"),
5270 }));
5271 }
5272 },
5273 (Value::BitString { nbits, bytes }, DataType::Bit(n)) => {
5285 let want = if n == 0 { 1 } else { n };
5287 if nbits != want {
5288 return Err(EngineError::Unsupported(alloc::format!(
5289 "bit string length {nbits} does not match type bit({want})"
5290 )));
5291 }
5292 Some(Value::BitString { nbits, bytes })
5293 }
5294 (Value::BitString { nbits, bytes }, DataType::BitVarying(n)) => {
5295 if n != 0 && nbits > n {
5296 return Err(EngineError::Unsupported(alloc::format!(
5297 "bit string too long for type bit varying({n})"
5298 )));
5299 }
5300 Some(Value::BitString { nbits, bytes })
5301 }
5302 (Value::Text(s), bit_ty @ (DataType::Bit(_) | DataType::BitVarying(_))) => {
5303 match parse_bit_string_text(&s) {
5304 Some((nbits, bytes)) => {
5305 match bit_ty {
5315 DataType::Bit(n) => {
5317 let want = if n == 0 { 1 } else { n };
5318 if nbits != want {
5319 return Err(EngineError::Unsupported(alloc::format!(
5320 "bit string length {nbits} does not match type bit({want})"
5321 )));
5322 }
5323 }
5324 DataType::BitVarying(n) if n != 0 && nbits > n => {
5325 return Err(EngineError::Unsupported(alloc::format!(
5326 "bit string too long for type bit varying({n})"
5327 )));
5328 }
5329 _ => {}
5330 }
5331 Some(Value::bit_string(nbits, bytes))
5332 }
5333 None => {
5334 let bad = s.chars().find(|c| *c != '0' && *c != '1');
5336 return Err(EngineError::Eval(EvalError::TypeMismatch {
5337 detail: match bad {
5338 Some(c) => {
5339 alloc::format!("\"{c}\" is not a valid binary digit")
5340 }
5341 None => alloc::format!("invalid input syntax for BIT: {s:?}"),
5342 },
5343 }));
5344 }
5345 }
5346 }
5347 (Value::Text(s), DataType::Xml) => {
5348 if !xml_content_is_well_formed(&s) {
5353 return Err(EngineError::Eval(EvalError::TypeMismatch {
5354 detail: alloc::format!("invalid XML content: {s:?}"),
5355 }));
5356 }
5357 Some(Value::xml(s))
5358 }
5359 (Value::BpChar(s), DataType::Char1) => {
5366 Some(Value::Char1(s.as_bytes().first().copied().unwrap_or(0)))
5367 }
5368 (Value::BpChar(s), DataType::Xml) => {
5369 let stripped = s.trim_end_matches(' ');
5370 if !xml_content_is_well_formed(stripped) {
5371 return Err(EngineError::Eval(EvalError::TypeMismatch {
5372 detail: alloc::format!("invalid XML content: {stripped:?}"),
5373 }));
5374 }
5375 Some(Value::xml(alloc::string::String::from(stripped)))
5376 }
5377 (Value::Bytes(b), DataType::SmallInt | DataType::Int | DataType::BigInt) => {
5383 let mut acc: i128 = 0;
5384 for byte in b.iter() {
5385 acc = acc.saturating_mul(256).saturating_add(i128::from(*byte));
5386 }
5387 let (fits, made) = match expected {
5388 DataType::SmallInt => (
5389 i16::try_from(acc).is_ok(),
5390 i16::try_from(acc).map(Value::SmallInt).ok(),
5391 ),
5392 DataType::Int => (
5393 i32::try_from(acc).is_ok(),
5394 i32::try_from(acc).map(Value::Int).ok(),
5395 ),
5396 _ => (
5397 i64::try_from(acc).is_ok(),
5398 i64::try_from(acc).map(Value::BigInt).ok(),
5399 ),
5400 };
5401 if !fits {
5402 return Err(EngineError::Eval(EvalError::TypeMismatch {
5403 detail: alloc::format!("{} out of range", pg_type_name_for_error(expected)),
5404 }));
5405 }
5406 made
5407 }
5408 (Value::Int(n), DataType::Char1) => Some(Value::Char1((n & 0xff) as u8)),
5411 (Value::SmallInt(n), DataType::Char1) => Some(Value::Char1((n & 0xff) as u8)),
5412 (Value::BigInt(n), DataType::Char1) => Some(Value::Char1((n & 0xff) as u8)),
5413 (Value::Text(s), DataType::Char1) => {
5414 let bytes = s.as_bytes();
5420 if bytes.len() == 4
5421 && bytes[0] == b'\\'
5422 && bytes[1..].iter().all(|b| (b'0'..=b'7').contains(b))
5423 {
5424 let v = ((bytes[1] - b'0') << 6) | ((bytes[2] - b'0') << 3) | (bytes[3] - b'0');
5425 Some(Value::Char1(v))
5426 } else {
5427 let b = s.bytes().next().unwrap_or(0);
5428 Some(Value::Char1(b))
5429 }
5430 }
5431 (Value::Inet { family, bits, addr }, DataType::Text) => {
5433 let base = format_inet(family, bits, &addr);
5437 Some(Value::text(if base.contains('/') {
5438 base
5439 } else {
5440 alloc::format!("{base}/{bits}")
5441 }))
5442 }
5443 (Value::Cidr { family, bits, addr }, DataType::Text) => {
5444 Some(Value::text(format_inet(family, bits, &addr)))
5445 }
5446 (Value::Macaddr(m), DataType::Text) => Some(Value::text(format_macaddr(&m))),
5447 (Value::Macaddr8(m), DataType::Text) => Some(Value::text(format_macaddr8(&m))),
5448 (Value::PgLsn(l), DataType::Text) => Some(Value::text(format_pg_lsn(l))),
5449 (Value::Macaddr(m), DataType::Macaddr8) => Some(Value::Macaddr8([
5452 m[0], m[1], m[2], 0xff, 0xfe, m[3], m[4], m[5],
5453 ])),
5454 (Value::BitString { nbits, bytes }, DataType::Text) => {
5455 Some(Value::text(format_bit_string(nbits, &bytes)))
5456 }
5457 #[allow(clippy::cast_possible_truncation)]
5459 (Value::BitString { nbits, bytes }, DataType::SmallInt) => {
5460 Some(Value::SmallInt(bit_string_to_i64(nbits, &bytes) as i16))
5461 }
5462 #[allow(clippy::cast_possible_truncation)]
5463 (Value::BitString { nbits, bytes }, DataType::Int) => {
5464 Some(Value::Int(bit_string_to_i64(nbits, &bytes) as i32))
5465 }
5466 (Value::BitString { nbits, bytes }, DataType::BigInt) => {
5467 Some(Value::BigInt(bit_string_to_i64(nbits, &bytes)))
5468 }
5469 (Value::Xml(s), DataType::Text) => Some(Value::text(s)),
5470 (Value::Char1(b), DataType::Text) => Some(Value::text((b as char).to_string())),
5471 (Value::Text(s), DataType::Point) => match parse_point(&s) {
5475 Some(p) => Some(Value::Point(p)),
5476 None => {
5477 return Err(EngineError::Eval(EvalError::TypeMismatch {
5478 detail: alloc::format!("invalid input syntax for type point: {s:?}"),
5479 }));
5480 }
5481 },
5482 (Value::Text(s), DataType::Lseg) => match parse_lseg_text(&s) {
5483 Some((p1, p2)) => Some(Value::Lseg(p1, p2)),
5484 None => {
5485 return Err(EngineError::Eval(EvalError::TypeMismatch {
5486 detail: alloc::format!("invalid input syntax for type lseg: {s:?}"),
5487 }));
5488 }
5489 },
5490 (Value::Text(s), DataType::PgBox) => match parse_box_text(&s) {
5491 Some((ur, ll)) => Some(Value::PgBox(ur, ll)),
5492 None => {
5493 return Err(EngineError::Eval(EvalError::TypeMismatch {
5494 detail: alloc::format!("invalid input syntax for type box: {s:?}"),
5495 }));
5496 }
5497 },
5498 (Value::Text(s), DataType::Line) => match parse_line_text(&s) {
5499 Some((a, b, c)) => Some(Value::Line { a, b, c }),
5500 None => {
5501 let zero_ab = s
5505 .trim()
5506 .strip_prefix('{')
5507 .and_then(|x| x.strip_suffix('}'))
5508 .map(|inner| inner.split(',').collect::<alloc::vec::Vec<_>>())
5509 .is_some_and(|parts| {
5510 parts.len() == 3
5511 && parts[0].trim().parse::<f64>() == Ok(0.0)
5512 && parts[1].trim().parse::<f64>() == Ok(0.0)
5513 && parts[2].trim().parse::<f64>().is_ok()
5514 });
5515 let detail = if zero_ab {
5516 alloc::string::String::from(
5517 "invalid line specification: A and B cannot both be zero",
5518 )
5519 } else {
5520 alloc::format!("invalid input syntax for type line: {s:?}")
5521 };
5522 return Err(EngineError::Eval(EvalError::TypeMismatch { detail }));
5523 }
5524 },
5525 (Value::Text(s), DataType::Circle) => match parse_circle_text(&s) {
5526 Some((center, radius)) => Some(Value::Circle { center, radius }),
5527 None => {
5528 return Err(EngineError::Eval(EvalError::TypeMismatch {
5529 detail: alloc::format!("invalid input syntax for type circle: {s:?}"),
5530 }));
5531 }
5532 },
5533 (Value::Text(s), DataType::Path) => match parse_path_text(&s) {
5534 Some((points, closed)) => Some(Value::Path { points, closed }),
5535 None => {
5536 return Err(EngineError::Eval(EvalError::TypeMismatch {
5537 detail: alloc::format!("invalid input syntax for type path: {s:?}"),
5538 }));
5539 }
5540 },
5541 (Value::PgBox(a, b), DataType::Polygon) => {
5544 let (hx, hy) = (a.x.max(b.x), a.y.max(b.y));
5545 let (lx, ly) = (a.x.min(b.x), a.y.min(b.y));
5546 let p = |x: f64, y: f64| spg_storage::Point2D { x, y };
5547 Some(Value::Polygon(alloc::vec![
5548 p(lx, ly),
5549 p(lx, hy),
5550 p(hx, hy),
5551 p(hx, ly),
5552 ]))
5553 }
5554 (Value::Text(s), DataType::Polygon) => match parse_polygon_text(&s) {
5555 Some(points) => Some(Value::Polygon(points)),
5556 None => {
5557 return Err(EngineError::Eval(EvalError::TypeMismatch {
5558 detail: alloc::format!("invalid input syntax for type polygon: {s:?}"),
5559 }));
5560 }
5561 },
5562 (Value::Point(p), DataType::Text) => Some(Value::text(format_point(p))),
5564 (Value::Lseg(p1, p2), DataType::Text) => Some(Value::text(format_lseg(p1, p2))),
5565 (Value::PgBox(ur, ll), DataType::Text) => Some(Value::text(format_pg_box(ur, ll))),
5566 (Value::Line { a, b, c }, DataType::Text) => Some(Value::text(format_line(a, b, c))),
5567 (Value::Circle { center, radius }, DataType::Text) => {
5568 Some(Value::text(format_circle(center, radius)))
5569 }
5570 (Value::Path { points, closed }, DataType::Text) => {
5571 Some(Value::text(format_path(&points, closed)))
5572 }
5573 (Value::Polygon(points), DataType::Text) => Some(Value::text(format_polygon(&points))),
5574 (ref rv @ Value::Range { kind: rk, .. }, DataType::Multirange(kind)) => {
5581 if rk != kind {
5582 return Err(EngineError::Eval(EvalError::TypeMismatch {
5583 detail: alloc::format!(
5584 "cannot cast type {} to {}",
5585 DataType::Range(rk),
5586 DataType::Multirange(kind)
5587 ),
5588 }));
5589 }
5590 crate::eval::binop::range_as_multirange(rv)
5591 }
5592 (Value::Text(s), DataType::Multirange(kind)) => match parse_multirange_str(&s, kind) {
5593 Some(ranges) => Some(Value::Multirange {
5599 kind,
5600 ranges: crate::eval::binop::normalize_multirange_spans(kind, &ranges),
5601 }),
5602 None => {
5603 return Err(EngineError::Eval(EvalError::TypeMismatch {
5604 detail: alloc::format!("invalid input syntax for multirange type: {s:?}"),
5605 }));
5606 }
5607 },
5608 (Value::Multirange { ranges, .. }, DataType::Text) => {
5610 Some(Value::text(format_multirange(&ranges)))
5611 }
5612 (Value::Text(s), DataType::Hstore) => match parse_hstore_str(&s) {
5614 Some(pairs) => Some(Value::Hstore(pairs)),
5615 None => {
5616 return Err(EngineError::Eval(EvalError::TypeMismatch {
5617 detail: alloc::format!("invalid input syntax for type hstore: {s:?}"),
5618 }));
5619 }
5620 },
5621 (Value::Hstore(pairs), DataType::Text) => Some(Value::text(format_hstore_str(&pairs))),
5623 (Value::Text(s), DataType::IntArray2D) => match parse_int_2d_literal(&s) {
5626 Ok(m) => Some(Value::IntArray2D(m)),
5627 Err(e) => {
5628 return Err(EngineError::Eval(EvalError::TypeMismatch {
5629 detail: alloc::format!("invalid input syntax for INT[][]: {s:?}: {e}"),
5630 }));
5631 }
5632 },
5633 (Value::Text(s), DataType::BigIntArray2D) => match parse_bigint_2d_literal(&s) {
5634 Ok(m) => Some(Value::BigIntArray2D(m)),
5635 Err(e) => {
5636 return Err(EngineError::Eval(EvalError::TypeMismatch {
5637 detail: alloc::format!("invalid input syntax for BIGINT[][]: {s:?}: {e}"),
5638 }));
5639 }
5640 },
5641 (Value::Text(s), DataType::TextArray2D) => match parse_text_2d_literal(&s) {
5642 Ok(m) => Some(Value::TextArray2D(m)),
5643 Err(e) => {
5644 return Err(EngineError::Eval(EvalError::TypeMismatch {
5645 detail: alloc::format!("invalid input syntax for TEXT[][]: {s:?}: {e}"),
5646 }));
5647 }
5648 },
5649 (Value::IntArray2D(rows), DataType::Text) => Some(Value::text(format_int_2d_text(&rows))),
5651 (Value::BigIntArray2D(rows), DataType::Text) => {
5652 Some(Value::text(format_bigint_2d_text(&rows)))
5653 }
5654 (Value::TextArray2D(rows), DataType::Text) => Some(Value::text(format_text_2d_text(&rows))),
5655 (Value::Text(s), DataType::TextArray) => {
5660 let arr = decode_text_array_literal(&s).map_err(|_| {
5664 EngineError::Eval(EvalError::TypeMismatch {
5665 detail: malformed_array_literal(&s),
5666 })
5667 })?;
5668 Some(Value::TextArray(arr))
5669 }
5670 (Value::Text(s), DataType::IntArray) => {
5676 let arr = decode_text_array_literal(&s).map_err(|_| {
5680 EngineError::Eval(EvalError::TypeMismatch {
5681 detail: malformed_array_literal(&s),
5682 })
5683 })?;
5684 let mut out: Vec<Option<i32>> = Vec::with_capacity(arr.len());
5685 for elem in arr {
5686 match elem {
5687 None => out.push(None),
5688 Some(t) => {
5689 let n: i32 = t.parse().map_err(|_| {
5690 EngineError::Eval(EvalError::TypeMismatch {
5691 detail: alloc::format!(
5692 "invalid input syntax for type integer: {t:?}"
5693 ),
5694 })
5695 })?;
5696 out.push(Some(n));
5697 }
5698 }
5699 }
5700 Some(Value::IntArray(out))
5701 }
5702 (Value::Text(s), DataType::SmallIntArray) => Some(Value::SmallIntArray(
5706 decode_array_elems(&s, DataType::SmallInt, col_name, position)?
5707 .into_iter()
5708 .map(|o| match o {
5709 Some(Value::SmallInt(n)) => Some(n),
5710 _ => None,
5711 })
5712 .collect(),
5713 )),
5714 (Value::Text(s), DataType::BoolArray) => {
5715 if let Some(rows) = crate::eval::values::split_2d_rows(&s) {
5720 let mut row_vals: Vec<Value<'static>> = Vec::with_capacity(rows.len());
5721 for r in &rows {
5722 let bools: Vec<Option<bool>> =
5723 decode_array_elems(r, DataType::Bool, col_name, position)?
5724 .into_iter()
5725 .map(|o| match o {
5726 Some(Value::Bool(b)) => Some(b),
5727 _ => None,
5728 })
5729 .collect();
5730 row_vals.push(Value::BoolArray(bools));
5731 }
5732 return crate::eval::values::build_2d_from_rows(&row_vals).ok_or_else(|| {
5733 EngineError::Eval(EvalError::TypeMismatch {
5734 detail: malformed_array_literal(&s),
5735 })
5736 });
5737 }
5738 Some(Value::BoolArray(
5739 decode_array_elems(&s, DataType::Bool, col_name, position)?
5740 .into_iter()
5741 .map(|o| match o {
5742 Some(Value::Bool(b)) => Some(b),
5743 _ => None,
5744 })
5745 .collect(),
5746 ))
5747 }
5748 (Value::Text(s), DataType::FloatArray) => Some(Value::FloatArray(
5749 decode_array_elems(&s, DataType::Float, col_name, position)?
5750 .into_iter()
5751 .map(|o| match o {
5752 Some(Value::Float(f)) => Some(f),
5753 _ => None,
5754 })
5755 .collect(),
5756 )),
5757 (Value::Text(s), DataType::NumericArray) => Some(Value::NumericArray(
5758 decode_array_elems(
5759 &s,
5760 DataType::Numeric {
5761 precision: 0,
5762 scale: 0,
5763 },
5764 col_name,
5765 position,
5766 )?
5767 .into_iter()
5768 .map(|o| match o {
5769 Some(Value::Numeric { scaled, scale, .. }) => Some((scaled, scale)),
5770 _ => None,
5771 })
5772 .collect(),
5773 )),
5774 (Value::Text(s), DataType::DateArray) => Some(Value::DateArray(
5775 decode_array_elems(&s, DataType::Date, col_name, position)?
5776 .into_iter()
5777 .map(|o| match o {
5778 Some(Value::Date(d)) => Some(d),
5779 _ => None,
5780 })
5781 .collect(),
5782 )),
5783 (Value::Text(s), DataType::UuidArray) => Some(Value::UuidArray(
5784 decode_array_elems(&s, DataType::Uuid, col_name, position)?
5785 .into_iter()
5786 .map(|o| match o {
5787 Some(Value::Uuid(u)) => Some(u),
5788 _ => None,
5789 })
5790 .collect(),
5791 )),
5792 (Value::Text(s), DataType::BigIntArray | DataType::OidArray) => {
5799 let arr = decode_text_array_literal(&s).map_err(|_| {
5803 EngineError::Eval(EvalError::TypeMismatch {
5804 detail: malformed_array_literal(&s),
5805 })
5806 })?;
5807 let mut out: Vec<Option<i64>> = Vec::with_capacity(arr.len());
5808 for elem in arr {
5809 match elem {
5810 None => out.push(None),
5811 Some(t) => {
5812 let n: i64 = t.parse().map_err(|_| {
5813 EngineError::Eval(EvalError::TypeMismatch {
5814 detail: alloc::format!(
5815 "invalid input syntax for type bigint: {t:?}"
5816 ),
5817 })
5818 })?;
5819 out.push(Some(n));
5820 }
5821 }
5822 }
5823 Some(Value::BigIntArray(out))
5824 }
5825 (Value::TextArray(items), DataType::Text) => Some(Value::text(encode_text_array(&items))),
5829 (Value::TextArray(items), DataType::BoolArray) if items.is_empty() => {
5837 Some(Value::BoolArray(alloc::vec::Vec::new()))
5838 }
5839 (Value::TextArray(items), DataType::SmallIntArray) if items.is_empty() => {
5840 Some(Value::SmallIntArray(alloc::vec::Vec::new()))
5841 }
5842 (Value::TextArray(items), DataType::IntArray) if items.is_empty() => {
5843 Some(Value::IntArray(alloc::vec::Vec::new()))
5844 }
5845 (Value::TextArray(items), DataType::BigIntArray) if items.is_empty() => {
5846 Some(Value::BigIntArray(alloc::vec::Vec::new()))
5847 }
5848 (Value::TextArray(items), DataType::FloatArray) if items.is_empty() => {
5849 Some(Value::FloatArray(alloc::vec::Vec::new()))
5850 }
5851 (Value::TextArray(items), DataType::FloatArray) => {
5854 let mut out = alloc::vec::Vec::with_capacity(items.len());
5855 let mut ok = true;
5856 for item in items {
5857 match item {
5858 None => out.push(None),
5859 Some(s) => match s.trim().parse::<f64>() {
5860 Ok(x) => out.push(Some(x)),
5861 Err(_) => {
5862 ok = false;
5863 break;
5864 }
5865 },
5866 }
5867 }
5868 if ok {
5869 Some(Value::FloatArray(out))
5870 } else {
5871 None
5872 }
5873 }
5874 (Value::FloatArray(items), DataType::FloatArray) => Some(Value::FloatArray(items)),
5877 #[allow(clippy::cast_precision_loss)]
5878 (Value::IntArray(items), DataType::FloatArray) => Some(Value::FloatArray(
5879 items.into_iter().map(|o| o.map(|n| f64::from(n))).collect(),
5880 )),
5881 #[allow(clippy::cast_precision_loss)]
5882 (Value::BigIntArray(items), DataType::FloatArray) => Some(Value::FloatArray(
5883 items.into_iter().map(|o| o.map(|n| n as f64)).collect(),
5884 )),
5885 #[allow(clippy::cast_precision_loss)]
5889 (Value::NumericArray(items), DataType::FloatArray) => Some(Value::FloatArray(
5890 items
5891 .into_iter()
5892 .map(|o| {
5893 o.map(|(scaled, scale)| {
5894 crate::eval::format_numeric(scaled, scale)
5895 .parse()
5896 .unwrap_or(f64::NAN)
5897 })
5898 })
5899 .collect(),
5900 )),
5901 (Value::RealArray(items), DataType::RealArray) => Some(Value::RealArray(items)),
5905 #[allow(clippy::cast_possible_truncation)]
5906 (Value::FloatArray(items), DataType::RealArray) => Some(Value::RealArray(
5907 items.into_iter().map(|o| o.map(|x| x as f32)).collect(),
5908 )),
5909 #[allow(clippy::cast_precision_loss)]
5910 (Value::IntArray(items), DataType::RealArray) => Some(Value::RealArray(
5911 items.into_iter().map(|o| o.map(|n| n as f32)).collect(),
5912 )),
5913 #[allow(clippy::cast_precision_loss)]
5914 (Value::BigIntArray(items), DataType::RealArray) => Some(Value::RealArray(
5915 items.into_iter().map(|o| o.map(|n| n as f32)).collect(),
5916 )),
5917 (Value::NumericArray(items), DataType::RealArray) => Some(Value::RealArray(
5918 items
5919 .into_iter()
5920 .map(|o| {
5921 o.map(|(scaled, scale)| {
5922 crate::eval::format_numeric(scaled, scale)
5923 .parse()
5924 .unwrap_or(f32::NAN)
5925 })
5926 })
5927 .collect(),
5928 )),
5929 #[allow(clippy::cast_possible_truncation)]
5930 (Value::RealArray(items), DataType::FloatArray) => Some(Value::FloatArray(
5931 items.into_iter().map(|o| o.map(f64::from)).collect(),
5932 )),
5933 (Value::IntArray(items), DataType::BigIntArray) => Some(Value::BigIntArray(
5938 items.into_iter().map(|o| o.map(i64::from)).collect(),
5939 )),
5940 (Value::BigIntArray(items), DataType::IntArray) => {
5941 let mut out = alloc::vec::Vec::with_capacity(items.len());
5942 let mut ok = true;
5943 for o in items {
5944 match o {
5945 None => out.push(None),
5946 Some(n) => match i32::try_from(n) {
5947 Ok(v) => out.push(Some(v)),
5948 Err(_) => {
5949 ok = false;
5950 break;
5951 }
5952 },
5953 }
5954 }
5955 if ok { Some(Value::IntArray(out)) } else { None }
5956 }
5957 (Value::IntArray(items), DataType::NumericArray) => Some(Value::NumericArray(
5958 items
5959 .into_iter()
5960 .map(|o| o.map(|n| (i128::from(n), 0_u16)))
5961 .collect(),
5962 )),
5963 (Value::BigIntArray(items), DataType::NumericArray) => Some(Value::NumericArray(
5964 items
5965 .into_iter()
5966 .map(|o| o.map(|n| (i128::from(n), 0_u16)))
5967 .collect(),
5968 )),
5969 (Value::FloatArray(items), DataType::NumericArray) => {
5970 let mut out = alloc::vec::Vec::with_capacity(items.len());
5971 let mut ok = true;
5972 for o in items {
5973 match o {
5974 None => out.push(None),
5975 Some(x) => match parse_numeric_text(&alloc::format!("{x}")) {
5976 Some((mantissa, scale)) => out.push(Some((mantissa, scale))),
5977 None => {
5978 ok = false;
5979 break;
5980 }
5981 },
5982 }
5983 }
5984 if ok {
5985 Some(Value::NumericArray(out))
5986 } else {
5987 None
5988 }
5989 }
5990 (Value::NumericArray(items), DataType::IntArray) => {
5994 let mut out = alloc::vec::Vec::with_capacity(items.len());
5995 let mut ok = true;
5996 for o in items {
5997 match o {
5998 None => out.push(None),
5999 Some((scaled, scale)) => {
6000 match i32::try_from(numeric_round_to_integer(scaled, scale)) {
6001 Ok(v) => out.push(Some(v)),
6002 Err(_) => {
6003 ok = false;
6004 break;
6005 }
6006 }
6007 }
6008 }
6009 }
6010 if ok { Some(Value::IntArray(out)) } else { None }
6011 }
6012 (Value::NumericArray(items), DataType::BigIntArray) => {
6013 let mut out = alloc::vec::Vec::with_capacity(items.len());
6014 let mut ok = true;
6015 for o in items {
6016 match o {
6017 None => out.push(None),
6018 Some((scaled, scale)) => {
6019 match i64::try_from(numeric_round_to_integer(scaled, scale)) {
6020 Ok(v) => out.push(Some(v)),
6021 Err(_) => {
6022 ok = false;
6023 break;
6024 }
6025 }
6026 }
6027 }
6028 }
6029 if ok {
6030 Some(Value::BigIntArray(out))
6031 } else {
6032 None
6033 }
6034 }
6035 #[allow(clippy::cast_possible_truncation)]
6039 (Value::FloatArray(items), DataType::IntArray) => {
6040 let mut out = alloc::vec::Vec::with_capacity(items.len());
6041 let mut ok = true;
6042 for o in items {
6043 match o {
6044 None => out.push(None),
6045 Some(x) if x.is_finite() => {
6046 let r = crate::eval::math::f64_round_half_even(x);
6047 if r >= f64::from(i32::MIN) && r <= f64::from(i32::MAX) {
6048 out.push(Some(r as i32));
6049 } else {
6050 ok = false;
6051 break;
6052 }
6053 }
6054 Some(_) => {
6055 ok = false;
6056 break;
6057 }
6058 }
6059 }
6060 if ok { Some(Value::IntArray(out)) } else { None }
6061 }
6062 #[allow(clippy::cast_possible_truncation)]
6063 (Value::FloatArray(items), DataType::BigIntArray) => {
6064 let mut out = alloc::vec::Vec::with_capacity(items.len());
6065 let mut ok = true;
6066 for o in items {
6067 match o {
6068 None => out.push(None),
6069 Some(x) if x.is_finite() => {
6070 out.push(Some(crate::eval::math::f64_round_half_even(x) as i64));
6071 }
6072 Some(_) => {
6073 ok = false;
6074 break;
6075 }
6076 }
6077 }
6078 if ok {
6079 Some(Value::BigIntArray(out))
6080 } else {
6081 None
6082 }
6083 }
6084 (Value::TextArray(items), DataType::NumericArray) if items.is_empty() => {
6085 Some(Value::NumericArray(alloc::vec::Vec::new()))
6086 }
6087 (Value::TextArray(items), DataType::DateArray) if items.is_empty() => {
6088 Some(Value::DateArray(alloc::vec::Vec::new()))
6089 }
6090 (Value::TextArray(items), DataType::TimestampArray) if items.is_empty() => {
6091 Some(Value::TimestampArray(alloc::vec::Vec::new()))
6092 }
6093 (Value::TextArray(items), DataType::TimestamptzArray) if items.is_empty() => {
6094 Some(Value::TimestamptzArray(alloc::vec::Vec::new()))
6095 }
6096 (Value::TextArray(items), DataType::UuidArray) if items.is_empty() => {
6097 Some(Value::UuidArray(alloc::vec::Vec::new()))
6098 }
6099 (Value::TextArray(items), DataType::RealArray) if items.is_empty() => {
6100 Some(Value::RealArray(alloc::vec::Vec::new()))
6101 }
6102 (Value::TextArray(items), DataType::TimeArray) if items.is_empty() => {
6103 Some(Value::TimeArray(alloc::vec::Vec::new()))
6104 }
6105 (Value::TextArray(items), DataType::TimeTzArray) if items.is_empty() => {
6106 Some(Value::TimeTzArray(alloc::vec::Vec::new()))
6107 }
6108 (Value::TextArray(items), DataType::InetArray) if items.is_empty() => {
6109 Some(Value::InetArray(alloc::vec::Vec::new()))
6110 }
6111 (Value::TextArray(items), DataType::XmlArray) if items.is_empty() => {
6112 Some(Value::XmlArray(alloc::vec::Vec::new()))
6113 }
6114 (Value::TextArray(items), DataType::JsonArray) if items.is_empty() => {
6115 Some(Value::JsonArray(alloc::vec::Vec::new()))
6116 }
6117 (Value::TextArray(items), DataType::JsonbArray) if items.is_empty() => {
6118 Some(Value::JsonbArray(alloc::vec::Vec::new()))
6119 }
6120 (Value::TextArray(items), DataType::BytesArray) if items.is_empty() => {
6121 Some(Value::BytesArray(alloc::vec::Vec::new()))
6122 }
6123 (Value::TextArray(items), DataType::IntervalArray) if items.is_empty() => {
6124 Some(Value::IntervalArray(alloc::vec::Vec::new()))
6125 }
6126 (
6130 Value::TextArray(items),
6131 dt @ (DataType::BoolArray
6132 | DataType::NumericArray
6133 | DataType::DateArray
6134 | DataType::TimestampArray
6135 | DataType::TimestamptzArray
6136 | DataType::IntervalArray
6137 | DataType::RealArray
6138 | DataType::TimeArray
6139 | DataType::TimeTzArray
6140 | DataType::InetArray
6141 | DataType::XmlArray
6142 | DataType::UuidArray),
6143 ) => coerce_text_array_to(items, dt, col_name)?,
6144 (
6150 Value::Text(s),
6151 dt @ (DataType::TimestampArray
6152 | DataType::TimestamptzArray
6153 | DataType::IntervalArray
6154 | DataType::RealArray
6155 | DataType::TimeArray
6156 | DataType::TimeTzArray
6157 | DataType::InetArray
6158 | DataType::XmlArray),
6159 ) => {
6160 let items = decode_text_array_literal(&s).map_err(|_| {
6161 EngineError::Eval(EvalError::TypeMismatch {
6162 detail: malformed_array_literal(&s),
6163 })
6164 })?;
6165 coerce_text_array_to(items, dt, col_name)?
6166 }
6167 (Value::TextArray(items), DataType::MoneyArray) if items.is_empty() => {
6168 Some(Value::MoneyArray(alloc::vec::Vec::new()))
6169 }
6170 (Value::IntArray(items), DataType::SmallIntArray) => {
6175 let mut out = alloc::vec::Vec::with_capacity(items.len());
6176 let mut ok = true;
6177 for item in items {
6178 match item {
6179 None => out.push(None),
6180 Some(n) => match i16::try_from(n) {
6181 Ok(x) => out.push(Some(x)),
6182 Err(_) => {
6183 ok = false;
6184 break;
6185 }
6186 },
6187 }
6188 }
6189 if ok {
6190 Some(Value::SmallIntArray(out))
6191 } else {
6192 None
6193 }
6194 }
6195 (Value::Text(s), DataType::Vector { dim, encoding }) => {
6204 let parsed = eval::parse_vector_text(&s).ok_or_else(|| {
6205 EngineError::Eval(EvalError::TypeMismatch {
6206 detail: alloc::format!("cannot parse {s:?} as VECTOR"),
6207 })
6208 })?;
6209 if parsed.len() != dim as usize {
6210 return Err(EngineError::Eval(EvalError::TypeMismatch {
6211 detail: alloc::format!(
6212 "VECTOR({dim}) column `{col_name}` rejects literal of length {}",
6213 parsed.len()
6214 ),
6215 }));
6216 }
6217 Some(match encoding {
6218 VecEncoding::F32 => Value::vector(parsed),
6219 VecEncoding::Sq8 => Value::Sq8Vector(spg_storage::quantize::quantize(&parsed)),
6220 VecEncoding::F16 => {
6221 Value::HalfVector(spg_storage::halfvec::HalfVector::from_f32_slice(&parsed))
6222 }
6223 })
6224 }
6225 (Value::Text(s), DataType::TsVector) => {
6235 let lexs = eval::decode_tsvector_external(&s).map_err(|e| {
6236 EngineError::Eval(EvalError::TypeMismatch {
6237 detail: alloc::format!("cannot parse {s:?} as TSVECTOR: {e}"),
6238 })
6239 })?;
6240 Some(Value::TsVector(lexs))
6241 }
6242 (Value::Text(s), DataType::Timestamp | DataType::Timestamptz) => {
6243 let t = eval::parse_timestamp_literal(&s)
6244 .ok_or_else(|| datetime_parse_error("timestamp", &s))?;
6245 Some(Value::Timestamp(t))
6246 }
6247 (Value::Date(i32::MAX), DataType::Timestamp | DataType::Timestamptz) => {
6250 Some(Value::Timestamp(i64::MAX))
6251 }
6252 (Value::Date(i32::MIN), DataType::Timestamp | DataType::Timestamptz) => {
6253 Some(Value::Timestamp(i64::MIN))
6254 }
6255 (Value::Date(d), DataType::Timestamp | DataType::Timestamptz) => {
6256 Some(Value::Timestamp(i64::from(d) * 86_400_000_000))
6257 }
6258 (Value::Timestamp(t), DataType::Timestamptz) => Some(Value::Timestamp(t)),
6262 (Value::Timestamp(t), DataType::Date) => {
6263 let days = t.div_euclid(86_400_000_000);
6264 i32::try_from(days).ok().map(Value::Date)
6265 }
6266 (Value::Timestamp(t), DataType::Time) => Some(Value::Time(t.rem_euclid(86_400_000_000))),
6275 (
6279 Value::NumericBig(b),
6280 DataType::Numeric {
6281 precision: 0,
6282 scale: 0,
6283 },
6284 ) => Some(Value::NumericBig(b)),
6285 (
6286 Value::Numeric {
6287 scaled,
6288 scale: src_scale,
6289 ..
6290 },
6291 DataType::Numeric { precision, scale },
6292 ) => {
6293 if precision == 0 && scale == 0 {
6299 Some(Value::Numeric {
6300 scaled,
6301 scale: src_scale,
6302 kind: spg_storage::NumericKind::Finite,
6303 })
6304 } else {
6305 Some(numeric_rescale(
6306 scaled, src_scale, precision, scale, col_name,
6307 )?)
6308 }
6309 }
6310 (Value::NumericBig(b), DataType::Numeric { precision, scale }) => {
6315 if precision == 0 && scale == 0 {
6316 Some(Value::NumericBig(b))
6317 } else {
6318 #[allow(clippy::cast_sign_loss)]
6319 let rounded = if scale < 0 {
6320 b.round_to(0)
6322 } else {
6323 b.round_to(scale as u16)
6324 };
6325 let out = crate::eval::binop::bignum_to_value(rounded);
6326 crate::numeric::check_precision_text(&out, precision, scale, col_name)?;
6329 Some(out)
6330 }
6331 }
6332 #[allow(clippy::cast_precision_loss)]
6333 (Value::Numeric { scaled, scale, .. }, DataType::Float) => {
6334 let text = crate::eval::format_numeric(scaled, scale);
6341 let x: f64 = text.parse().unwrap_or(f64::NAN);
6342 if x == 0.0 && scaled != 0 {
6346 return Err(float_out_of_range(
6347 &crate::eval::format_numeric(scaled, scale),
6348 "double precision",
6349 ));
6350 }
6351 Some(Value::Float(x))
6352 }
6353 (Value::NumericBig(b), DataType::Real) => {
6361 let text = b.to_decimal_str();
6362 let x: f32 = text.parse().map_err(|_| real_out_of_range(&text))?;
6363 if !x.is_finite() || (x == 0.0 && float_text_is_nonzero(&text)) {
6364 return Err(real_out_of_range(&text));
6365 }
6366 Some(Value::Real(x))
6367 }
6368 (Value::NumericBig(b), DataType::Float) => {
6369 let text = b.to_decimal_str();
6373 let x: f64 = text
6374 .parse()
6375 .map_err(|_| float_out_of_range(&text, "double precision"))?;
6376 if !x.is_finite() || (x == 0.0 && float_text_is_nonzero(&text)) {
6377 return Err(float_out_of_range(&text, "double precision"));
6378 }
6379 Some(Value::Float(x))
6380 }
6381 (Value::Float(x), DataType::Int) => {
6389 let r = crate::eval::math::f64_round_half_even(x);
6390 if !r.is_finite() || !(-2_147_483_648.0..=2_147_483_647.0).contains(&r) {
6391 return Err(EngineError::Eval(EvalError::TypeMismatch {
6392 detail: "integer out of range".into(),
6393 }));
6394 }
6395 #[allow(clippy::cast_possible_truncation)]
6396 Some(Value::Int(r as i32))
6397 }
6398 (Value::Float(x), DataType::BigInt) => {
6399 let r = crate::eval::math::f64_round_half_even(x);
6400 if !r.is_finite()
6401 || !(-9.223_372_036_854_776e18..=9.223_372_036_854_776e18).contains(&r)
6402 {
6403 return Err(EngineError::Eval(EvalError::TypeMismatch {
6404 detail: "bigint out of range".into(),
6405 }));
6406 }
6407 #[allow(clippy::cast_possible_truncation)]
6408 Some(Value::BigInt(r as i64))
6409 }
6410 (Value::Float(x), DataType::SmallInt) => {
6411 let r = crate::eval::math::f64_round_half_even(x);
6412 if !r.is_finite() || !(-32768.0..=32767.0).contains(&r) {
6413 return Err(EngineError::Eval(EvalError::TypeMismatch {
6414 detail: "smallint out of range".into(),
6415 }));
6416 }
6417 #[allow(clippy::cast_possible_truncation)]
6418 Some(Value::SmallInt(r as i16))
6419 }
6420 (Value::Real(x), DataType::Int) => {
6424 let r = crate::eval::math::f64_round_half_even(f64::from(x));
6425 if !r.is_finite() || !(-2_147_483_648.0..=2_147_483_647.0).contains(&r) {
6426 return Err(EngineError::Eval(EvalError::TypeMismatch {
6427 detail: "integer out of range".into(),
6428 }));
6429 }
6430 #[allow(clippy::cast_possible_truncation)]
6431 Some(Value::Int(r as i32))
6432 }
6433 (Value::Real(x), DataType::BigInt) => {
6434 let r = crate::eval::math::f64_round_half_even(f64::from(x));
6435 if !r.is_finite()
6436 || !(-9.223_372_036_854_776e18..=9.223_372_036_854_776e18).contains(&r)
6437 {
6438 return Err(EngineError::Eval(EvalError::TypeMismatch {
6439 detail: "bigint out of range".into(),
6440 }));
6441 }
6442 #[allow(clippy::cast_possible_truncation)]
6443 Some(Value::BigInt(r as i64))
6444 }
6445 (Value::Real(x), DataType::SmallInt) => {
6446 let r = crate::eval::math::f64_round_half_even(f64::from(x));
6447 if !r.is_finite() || !(-32768.0..=32767.0).contains(&r) {
6448 return Err(EngineError::Eval(EvalError::TypeMismatch {
6449 detail: "smallint out of range".into(),
6450 }));
6451 }
6452 #[allow(clippy::cast_possible_truncation)]
6453 Some(Value::SmallInt(r as i16))
6454 }
6455 (Value::Numeric { scaled, scale, .. }, DataType::Int) => {
6456 let rounded = numeric_round_to_integer(scaled, scale);
6457 i32::try_from(rounded).ok().map(Value::Int)
6458 }
6459 (Value::Numeric { scaled, scale, .. }, DataType::BigInt) => {
6460 let rounded = numeric_round_to_integer(scaled, scale);
6461 i64::try_from(rounded).ok().map(Value::BigInt)
6462 }
6463 (Value::Numeric { scaled, scale, .. }, DataType::SmallInt) => {
6464 let rounded = numeric_round_to_integer(scaled, scale);
6465 i16::try_from(rounded).ok().map(Value::SmallInt)
6466 }
6467 (Value::Text(s), DataType::Name) => {
6474 let mut cut = s.into_owned();
6475 if cut.len() > 63 {
6476 let mut idx = 63;
6477 while !cut.is_char_boundary(idx) {
6478 idx -= 1;
6479 }
6480 cut.truncate(idx);
6481 }
6482 Some(Value::text(cut))
6483 }
6484 (Value::Text(s), DataType::Varchar(max)) => {
6485 if max == 0 || u32::try_from(s.chars().count()).unwrap_or(u32::MAX) <= max {
6486 Some(Value::text(s))
6487 } else {
6488 let excess_all_blanks = s.chars().skip(max as usize).all(|c| c == ' ');
6493 if excess_all_blanks {
6494 Some(Value::text(
6495 s.chars()
6496 .take(max as usize)
6497 .collect::<alloc::string::String>(),
6498 ))
6499 } else {
6500 return Err(EngineError::Unsupported(alloc::format!(
6501 "value too long for type character varying({max})"
6502 )));
6503 }
6504 }
6505 }
6506 (
6514 Value::Vector(v),
6515 DataType::Vector {
6516 dim,
6517 encoding: VecEncoding::Sq8,
6518 },
6519 ) if v.len() == dim as usize => Some(Value::Sq8Vector(spg_storage::quantize::quantize(&v))),
6520 (
6525 Value::Vector(v),
6526 DataType::Vector {
6527 dim,
6528 encoding: VecEncoding::F16,
6529 },
6530 ) if v.len() == dim as usize => Some(Value::HalfVector(
6531 spg_storage::halfvec::HalfVector::from_f32_slice(&v),
6532 )),
6533 (Value::Text(s), DataType::Char(size)) => {
6537 if size == 0 {
6541 return Ok(Value::BpChar(alloc::borrow::Cow::Owned(
6542 s.trim_end_matches(' ').to_string(),
6543 )));
6544 }
6545 let len = u32::try_from(s.chars().count()).unwrap_or(u32::MAX);
6546 let body = if len > size {
6547 let trimmed = s.trim_end_matches(' ');
6548 let tlen = u32::try_from(trimmed.chars().count()).unwrap_or(u32::MAX);
6549 if tlen > size {
6550 return Err(EngineError::Unsupported(alloc::format!(
6551 "value too long for type character({size})"
6552 )));
6553 }
6554 trimmed.to_string()
6555 } else {
6556 s.into_owned()
6557 };
6558 let need = (size as usize) - body.chars().count();
6559 let mut padded = body;
6560 padded.reserve(need);
6561 for _ in 0..need {
6562 padded.push(' ');
6563 }
6564 Some(Value::BpChar(alloc::borrow::Cow::Owned(padded)))
6568 }
6569 _ => None,
6570 };
6571 coerced.ok_or_else(|| {
6572 EngineError::Storage(StorageError::TypeMismatch {
6573 column: col_name.into(),
6574 expected,
6575 actual,
6576 position,
6577 })
6578 })
6579}
6580
6581pub(crate) fn big_literal_to_value(s: &str) -> Value<'static> {
6584 let b = spg_storage::bignum::BigNumeric::from_decimal_str(s).expect("lexer-validated decimal");
6585 match b.to_i128() {
6586 Some(scaled) => Value::Numeric {
6587 scaled,
6588 scale: b.scale(),
6589 kind: spg_storage::NumericKind::Finite,
6590 },
6591 None => Value::NumericBig(alloc::boxed::Box::new(b)),
6592 }
6593}
6594
6595pub(crate) fn types_unify(a: DataType, b: DataType) -> bool {
6604 fn category(t: DataType) -> Option<u8> {
6605 Some(match t {
6606 DataType::SmallInt
6607 | DataType::Int
6608 | DataType::BigInt
6609 | DataType::Numeric { .. }
6610 | DataType::Real
6611 | DataType::Float
6612 | DataType::Year => 1,
6619 DataType::Text | DataType::Varchar(_) | DataType::Char(_) => 2,
6620 DataType::Date | DataType::Timestamp | DataType::Timestamptz => 3,
6621 _ => return None,
6622 })
6623 }
6624 if a == b {
6625 return true;
6626 }
6627 match (category(a), category(b)) {
6628 (Some(x), Some(y)) => x == y,
6629 _ => false,
6632 }
6633}
6634
6635pub(crate) fn pg_type_name_for_error_opt(t: Option<DataType>) -> alloc::string::String {
6649 match t {
6650 Some(t) => pg_type_name_for_error(t),
6651 None => alloc::string::String::from("unknown"),
6652 }
6653}
6654
6655pub(crate) fn array_element_type(t: DataType) -> Option<DataType> {
6662 use spg_storage::DataType as D;
6663 Some(match t {
6664 D::TextArray => D::Text,
6665 D::IntArray => D::Int,
6666 D::BigIntArray => D::BigInt,
6667 D::OidArray => D::Oid,
6668 D::SmallIntArray => D::SmallInt,
6669 D::FloatArray => D::Float,
6670 D::NumericArray => D::Numeric {
6671 precision: 0,
6672 scale: 0,
6673 },
6674 D::BoolArray => D::Bool,
6675 D::DateArray => D::Date,
6676 D::TimestampArray => D::Timestamp,
6677 D::TimestamptzArray => D::Timestamptz,
6678 D::IntervalArray => D::Interval,
6679 D::UuidArray => D::Uuid,
6680 D::JsonArray | D::JsonbArray => D::Jsonb,
6681 D::BytesArray => D::Bytes,
6682 D::MoneyArray => D::Money,
6683 D::VarcharArray => D::Varchar(0),
6684 D::CharArray => D::Char(0),
6685 D::RealArray => D::Real,
6686 D::TimeArray => D::Time,
6687 D::TimeTzArray => D::TimeTz,
6688 D::InetArray => D::Inet,
6689 D::XmlArray => D::Xml,
6690 _ => return None,
6691 })
6692}
6693
6694pub(crate) fn pg_type_name_for_error(t: DataType) -> alloc::string::String {
6695 use spg_storage::DataType as D;
6696 let elem = match t {
6697 D::TextArray => Some(D::Text),
6698 D::IntArray => Some(D::Int),
6699 D::BigIntArray => Some(D::BigInt),
6700 D::SmallIntArray => Some(D::SmallInt),
6701 D::FloatArray => Some(D::Float),
6702 D::NumericArray => Some(D::Numeric {
6703 precision: 0,
6704 scale: 0,
6705 }),
6706 D::BoolArray => Some(D::Bool),
6707 D::DateArray => Some(D::Date),
6708 D::TimestampArray => Some(D::Timestamp),
6709 D::TimestamptzArray => Some(D::Timestamptz),
6710 D::IntervalArray => Some(D::Interval),
6711 D::UuidArray => Some(D::Uuid),
6712 D::JsonArray | D::JsonbArray => Some(D::Jsonb),
6713 D::BytesArray => Some(D::Bytes),
6714 D::MoneyArray => Some(D::Money),
6715 D::RealArray => Some(D::Real),
6716 D::TimeArray => Some(D::Time),
6717 D::TimeTzArray => Some(D::TimeTz),
6718 D::InetArray => Some(D::Inet),
6719 D::XmlArray => Some(D::Xml),
6720 _ => None,
6721 };
6722 match elem {
6723 Some(e) => alloc::format!("{}[]", crate::system_catalog::pg_data_type_text(e)),
6724 None => crate::system_catalog::pg_data_type_text(t),
6725 }
6726}