1use super::*;
11
12pub(super) fn value_cmp_for_min_max(a: &Value, b: &Value, mysql: bool) -> core::cmp::Ordering {
16 use core::cmp::Ordering;
17 if mysql {
21 if let (Some(x), Some(y)) = (
23 spg_storage::mysql_fold_value(a),
24 spg_storage::mysql_fold_value(b),
25 ) {
26 return x.cmp(&y);
27 }
28 }
29 if let Some(ord) = crate::orderby::numeric_bignum_cmp(a, b) {
31 return ord;
32 }
33 {
37 use spg_storage::NumericKind as NK;
38 let kind = |v: &Value| -> Option<NK> {
39 match v {
40 Value::Numeric { kind, .. } => Some(*kind),
41 Value::Int(_) | Value::BigInt(_) | Value::SmallInt(_) => Some(NK::Finite),
42 _ => None,
43 }
44 };
45 if let (Some(lk), Some(rk)) = (kind(a), kind(b)) {
46 if lk != NK::Finite || rk != NK::Finite {
47 let rank = |k: NK| match k {
48 NK::NegInf => -2,
49 NK::Finite => 0,
50 NK::PosInf => 1,
51 NK::NaN => 2,
52 };
53 return rank(lk).cmp(&rank(rk));
54 }
55 }
56 }
57 let a_int = match a {
59 Value::SmallInt(x) => Some(i64::from(*x)),
60 Value::Int(x) => Some(i64::from(*x)),
61 Value::BigInt(x) => Some(*x),
62 _ => None,
63 };
64 let b_int = match b {
65 Value::SmallInt(x) => Some(i64::from(*x)),
66 Value::Int(x) => Some(i64::from(*x)),
67 Value::BigInt(x) => Some(*x),
68 _ => None,
69 };
70 if let (Some(av), Some(bv)) = (a_int, b_int) {
71 return av.cmp(&bv);
72 }
73 let a_f = value_to_f64(a);
75 let b_f = value_to_f64(b);
76 if let (Some(av), Some(bv)) = (a_f, b_f) {
77 return av.partial_cmp(&bv).unwrap_or(Ordering::Equal);
78 }
79 match (a, b) {
83 (Value::Text(av), Value::Text(bv)) => av.cmp(bv),
84 (Value::Bytes(av), Value::Bytes(bv)) => av.cmp(bv),
85 (Value::Date(av), Value::Date(bv)) => av.cmp(bv),
86 (Value::Timestamp(av), Value::Timestamp(bv)) => av.cmp(bv),
87 (Value::Date(av), Value::Timestamp(bv)) => {
89 (i64::from(*av).saturating_mul(86_400_000_000)).cmp(bv)
90 }
91 (Value::Timestamp(av), Value::Date(bv)) => {
92 av.cmp(&i64::from(*bv).saturating_mul(86_400_000_000))
93 }
94 (Value::Time(av), Value::Time(bv)) => av.cmp(bv),
95 (Value::Bool(av), Value::Bool(bv)) => av.cmp(bv),
96 (
99 Value::Interval {
100 months: am,
101 days: ad,
102 micros: au,
103 kind: akind,
104 },
105 Value::Interval {
106 months: bm,
107 days: bd,
108 micros: bu,
109 kind: bkind,
110 },
111 ) => {
112 let total = |m: i32, d: i32, u: i64| -> i128 {
113 i128::from(m) * 30 * 86_400_000_000 + i128::from(d) * 86_400_000_000 + i128::from(u)
114 };
115 akind
116 .rank()
117 .cmp(&bkind.rank())
118 .then_with(|| total(*am, *ad, *au).cmp(&total(*bm, *bd, *bu)))
119 }
120 (Value::Tid(b1, o1), Value::Tid(b2, o2)) => b1.cmp(b2).then(o1.cmp(o2)),
124 (Value::Xid(a), Value::Xid(b)) => a.cmp(b),
125 (Value::Cid(a), Value::Cid(b)) => a.cmp(b),
126 _ => crate::eval::binop::compare(spg_sql::ast::BinOp::Lt, a, b)
136 .ok()
137 .and_then(|v| match v {
138 Value::Bool(true) => Some(Ordering::Less),
139 Value::Bool(false) => {
140 match crate::eval::binop::compare(spg_sql::ast::BinOp::Gt, a, b) {
141 Ok(Value::Bool(true)) => Some(Ordering::Greater),
142 Ok(Value::Bool(false)) => Some(Ordering::Equal),
143 _ => None,
144 }
145 }
146 _ => None,
147 })
148 .unwrap_or(Ordering::Equal),
149 }
150}
151
152pub(super) fn value_to_f64(v: &Value) -> Option<f64> {
153 match v {
154 Value::Float(x) => Some(*x),
155 Value::Real(x) => Some(f64::from(*x)),
156 Value::SmallInt(x) => Some(f64::from(*x)),
157 Value::Int(x) => Some(f64::from(*x)),
158 Value::BigInt(x) => Some(*x as f64),
159 Value::Numeric { scaled, scale, .. } => {
160 Some((*scaled as f64) / f64_powi(10.0, i32::from(*scale)))
161 }
162 _ => None,
163 }
164}
165
166pub(super) fn values_equal_for_nullif(a: &Value, b: &Value) -> bool {
171 if a == b {
173 return true;
174 }
175 let a_int = match a {
177 Value::SmallInt(x) => Some(i64::from(*x)),
178 Value::Int(x) => Some(i64::from(*x)),
179 Value::BigInt(x) => Some(*x),
180 _ => None,
181 };
182 let b_int = match b {
183 Value::SmallInt(x) => Some(i64::from(*x)),
184 Value::Int(x) => Some(i64::from(*x)),
185 Value::BigInt(x) => Some(*x),
186 _ => None,
187 };
188 if let (Some(a), Some(b)) = (a_int, b_int) {
189 return a == b;
190 }
191 let a_f = match a {
193 Value::Float(x) => Some(*x),
194 Value::SmallInt(x) => Some(f64::from(*x)),
195 Value::Int(x) => Some(f64::from(*x)),
196 Value::BigInt(x) => Some(*x as f64),
197 Value::Numeric { scaled, scale, .. } => {
198 Some((*scaled as f64) / f64_powi(10.0, i32::from(*scale)))
199 }
200 _ => None,
201 };
202 let b_f = match b {
203 Value::Float(x) => Some(*x),
204 Value::SmallInt(x) => Some(f64::from(*x)),
205 Value::Int(x) => Some(f64::from(*x)),
206 Value::BigInt(x) => Some(*x as f64),
207 Value::Numeric { scaled, scale, .. } => {
208 Some((*scaled as f64) / f64_powi(10.0, i32::from(*scale)))
209 }
210 _ => None,
211 };
212 if let (Some(a), Some(b)) = (a_f, b_f) {
213 return a == b;
214 }
215 false
216}
217
218pub fn gen_random_uuid_bytes() -> [u8; 16] {
225 let mut out = [0u8; 16];
226 let hi = prng_next_u64().to_be_bytes();
227 let lo = prng_next_u64().to_be_bytes();
228 out[..8].copy_from_slice(&hi);
229 out[8..].copy_from_slice(&lo);
230 out[6] = (out[6] & 0x0f) | 0x40;
232 out[8] = (out[8] & 0x3f) | 0x80;
234 out
235}
236
237#[must_use]
248pub fn value_to_text_with_fsp(v: &Value, fsp: Option<u8>) -> String {
249 let Some(fsp) = fsp else {
250 return value_to_text(v);
251 };
252 let (whole, micros) = match v {
253 Value::Timestamp(us) => (
254 crate::eval::format_timestamp(us.div_euclid(1_000_000) * 1_000_000),
255 us.rem_euclid(1_000_000),
256 ),
257 Value::Time(us) => (
258 crate::eval::format_time(us.div_euclid(1_000_000) * 1_000_000),
259 us.rem_euclid(1_000_000),
260 ),
261 other => return value_to_text(other),
262 };
263 if fsp == 0 {
264 return whole;
265 }
266 let digits = usize::from(fsp.min(6));
267 let frac = format!("{micros:06}");
270 format!("{whole}.{}", &frac[..digits])
271}
272
273pub fn value_to_text(v: &Value) -> String {
274 value_to_text_styled(v, &crate::eval::RenderStyle::default())
275}
276
277pub fn value_to_text_typed(v: &Value, dt: &spg_storage::DataType) -> String {
288 value_to_text_typed_styled(v, dt, &crate::eval::RenderStyle::default())
289}
290
291pub fn value_to_text_typed_styled(
293 v: &Value,
294 dt: &spg_storage::DataType,
295 style: &crate::eval::RenderStyle,
296) -> String {
297 match (dt, v) {
298 (spg_storage::DataType::Timestamptz, Value::Timestamp(us)) => {
299 crate::eval::format_timestamptz_styled(*us, style)
300 }
301 _ => value_to_text_styled(v, style),
302 }
303}
304
305pub fn value_to_text_styled(v: &Value, style: &crate::eval::RenderStyle) -> String {
309 match v {
310 Value::SmallInt(n) => format!("{n}"),
314 Value::Int(n) => format!("{n}"),
315 Value::BigInt(n) => format!("{n}"),
316 Value::Float(x) => crate::eval::format_float_styled(*x, style),
320 Value::Real(x) => crate::eval::format_real_styled(*x, style),
322 Value::BpChar(s) => s.to_string(),
325 Value::Text(s) | Value::Json(s) => s.to_string(),
327 Value::Bool(b) => (if *b { "true" } else { "false" }).into(),
328 Value::NumericBig(b) => b.to_decimal_str(),
331 Value::Composite(fields) => {
334 let mut out = String::from("(");
335 for (i, (_, fv)) in fields.iter().enumerate() {
336 if i > 0 {
337 out.push(',');
338 }
339 if matches!(fv, Value::Null) {
340 continue;
341 }
342 let field = super::strings::value_to_format_text(fv);
343 let needs_quote = field.is_empty()
344 || field
345 .chars()
346 .any(|c| matches!(c, ',' | '(' | ')' | '"' | '\\') || c.is_whitespace());
347 if needs_quote {
348 out.push('"');
349 for c in field.chars() {
350 match c {
351 '"' => out.push_str("\"\""),
352 '\\' => out.push_str("\\\\"),
353 other => out.push(other),
354 }
355 }
356 out.push('"');
357 } else {
358 out.push_str(&field);
359 }
360 }
361 out.push(')');
362 out
363 }
364 Value::Vector(v) => {
365 let cells: Vec<String> = v.iter().map(|x| format!("{x}")).collect();
366 format!("[{}]", cells.join(","))
367 }
368 Value::Sq8Vector(q) => {
373 let cells: Vec<String> = spg_storage::quantize::dequantize(q)
374 .iter()
375 .map(|x| format!("{x}"))
376 .collect();
377 format!("[{}]", cells.join(","))
378 }
379 Value::HalfVector(h) => {
382 let cells: Vec<String> = h.to_f32_vec().iter().map(|x| format!("{x}")).collect();
383 format!("[{}]", cells.join(","))
384 }
385 Value::Numeric {
386 scaled,
387 scale,
388 kind,
389 } => format_numeric_kind(*kind, *scaled, *scale),
390 Value::Date(d) => crate::eval::format_date_styled(*d, style),
391 Value::Timestamp(t) => crate::eval::format_timestamp_styled(*t, style),
392 Value::Interval {
393 months,
394 days,
395 micros,
396 kind,
397 } if kind.is_finite() => {
398 crate::eval::format_interval_styled(*months, *days, *micros, style)
399 }
400 Value::Interval { kind, .. } => crate::eval::format_interval_kinded(0, 0, 0, *kind),
401 Value::Null => "NULL".into(),
402 Value::Bytes(b) => {
405 if style.bytea_escape {
406 crate::eval::format::format_bytea_escape(b)
407 } else {
408 format_bytea_hex(b)
409 }
410 }
411 Value::TextArray(items) => format_text_array(items),
413 Value::IntArray(items) => format_int_array(items),
414 Value::BigIntArray(items) => format_bigint_array(items),
415 Value::TsVector(lexs) => format_tsvector(lexs),
417 Value::TsQuery(ast) => format_tsquery(ast),
418 Value::Uuid(b) => spg_storage::format_uuid(b),
421 Value::Time(us) => format_time(*us),
423 Value::TimeTz { us, offset_secs } => format_timetz(*us, *offset_secs),
425 Value::Year(y) => format!("{y:04}"),
427 Value::Money(c) => format_money(*c),
429 Value::Range { .. } => crate::conversions::format_range_text(v),
433 Value::Hstore(pairs) => crate::conversions::format_hstore_text(pairs),
435 Value::IntArray2D(rows) => crate::conversions::format_int_2d_text_pub(rows),
437 Value::BigIntArray2D(rows) => crate::conversions::format_bigint_2d_text_pub(rows),
438 Value::TextArray2D(rows) => crate::conversions::format_text_2d_text_pub(rows),
439 Value::BoolArray2D(rows) => crate::conversions::format_bool_2d_text_pub(rows),
440 Value::BoolArray(items) => crate::eval::format_bool_array(items),
443 Value::SmallIntArray(items) => crate::eval::format_smallint_array(items),
444 Value::Int2Vector(items) => items
448 .iter()
449 .map(alloc::string::ToString::to_string)
450 .collect::<alloc::vec::Vec<_>>()
451 .join(" "),
452 Value::OidVector(items) => items
453 .iter()
454 .map(alloc::string::ToString::to_string)
455 .collect::<alloc::vec::Vec<_>>()
456 .join(" "),
457 Value::FloatArray(items) => crate::eval::format_float_array_styled(items, style),
458 Value::NumericArray(items) => crate::eval::format_numeric_array(items),
459 Value::DateArray(items) => crate::eval::format_date_array_styled(items, style),
460 Value::TimestampArray(items) => {
461 crate::eval::format_timestamp_array_styled(items, false, style)
462 }
463 Value::TimestamptzArray(items) => {
464 crate::eval::format_timestamp_array_styled(items, true, style)
465 }
466 Value::UuidArray(items) => crate::eval::format_uuid_array(items),
467 Value::JsonArray(items) | Value::JsonbArray(items) | Value::XmlArray(items) => {
468 crate::eval::format_text_array(items)
469 }
470 Value::RealArray(items) => crate::eval::format_real_array(items, style),
472 Value::TimeArray(items) => crate::eval::format_time_array(items),
473 Value::TimeTzArray(items) => crate::eval::format_timetz_array(items),
474 Value::InetArray(items) => crate::eval::format_inet_array(items),
475 Value::BytesArray(items) => crate::eval::format_bytea_array(items),
476 Value::IntervalArray(items) => crate::eval::format_interval_array_styled(items, style),
477 Value::MoneyArray(items) => crate::conversions::format_money_array(items),
478 Value::Point(p) => crate::conversions::format_point(*p),
480 Value::Lseg(a, b) => crate::conversions::format_lseg(*a, *b),
481 Value::Path { points, closed } => crate::conversions::format_path(points, *closed),
482 Value::PgBox(ur, ll) => crate::conversions::format_pg_box(*ur, *ll),
483 Value::Polygon(points) => crate::conversions::format_polygon(points),
484 Value::Line { a, b, c } => crate::conversions::format_line(*a, *b, *c),
485 Value::Circle { center, radius } => crate::conversions::format_circle(*center, *radius),
486 Value::Multirange { ranges, .. } => crate::conversions::format_multirange(ranges),
488 Value::Inet { family, bits, addr } => crate::conversions::format_inet(*family, *bits, addr),
490 Value::Cidr { family, bits, addr } => {
496 crate::conversions::format_inet_full(*family, *bits, addr)
497 }
498 Value::Macaddr(b) => crate::conversions::format_macaddr(b),
499 Value::Macaddr8(b) => crate::conversions::format_macaddr8(b),
500 Value::PgLsn(l) => crate::conversions::format_pg_lsn(*l),
501 Value::RegClass(_, name) | Value::RegProc(_, name) => name.to_string(),
502 Value::RegType(_, name) => name.to_string(),
503 Value::Tid(b, o) => alloc::format!("({b},{o})"),
505 Value::Xid(x) => alloc::format!("{x}"),
507 Value::Cid(c) => alloc::format!("{c}"),
508 Value::BitString { nbits, bytes } => crate::conversions::format_bit_string(*nbits, bytes),
509 Value::Xml(s) => s.to_string(),
510 Value::Char1(b) => format!("{}", *b as char),
511 _ => format!("{v:?}"),
513 }
514}
515
516pub(crate) fn array_len(v: &Value) -> Option<usize> {
521 match v {
522 Value::TextArray(items)
523 | Value::VarcharArray(items)
524 | Value::CharArray(items)
525 | Value::JsonArray(items)
526 | Value::JsonbArray(items) => Some(items.len()),
527 Value::IntArray(items) => Some(items.len()),
528 Value::BigIntArray(items) => Some(items.len()),
529 Value::SmallIntArray(items) => Some(items.len()),
530 Value::Int2Vector(items) => Some(items.len()),
533 Value::OidVector(items) => Some(items.len()),
534 Value::BoolArray(items) => Some(items.len()),
535 Value::FloatArray(items) => Some(items.len()),
536 Value::NumericArray(items) => Some(items.len()),
537 Value::DateArray(items) => Some(items.len()),
538 Value::TimestampArray(items) | Value::TimestamptzArray(items) => Some(items.len()),
539 Value::MoneyArray(items) => Some(items.len()),
540 Value::IntervalArray(items) => Some(items.len()),
541 Value::UuidArray(items) => Some(items.len()),
542 Value::BytesArray(items) => Some(items.len()),
543 Value::RealArray(items) => Some(items.len()),
544 Value::TimeArray(items) => Some(items.len()),
545 Value::TimeTzArray(items) => Some(items.len()),
546 Value::InetArray(items) => Some(items.len()),
547 Value::XmlArray(items) => Some(items.len()),
548 _ => None,
549 }
550}
551
552pub(crate) fn array_elements(v: &Value) -> Option<alloc::vec::Vec<Value<'static>>> {
563 if let Some(n) = array_len(v) {
564 let mut out = alloc::vec::Vec::with_capacity(n);
565 for i in 0..n {
566 out.push(array_element_at(v, i)?);
567 }
568 return Some(out);
569 }
570 macro_rules! rows {
572 ($m:expr, $variant:ident) => {
573 Some($m.iter().map(|r| Value::$variant(r.clone())).collect())
574 };
575 }
576 match v {
577 Value::IntArray2D(m) => rows!(m, IntArray),
578 Value::BigIntArray2D(m) => rows!(m, BigIntArray),
579 Value::TextArray2D(m) => rows!(m, TextArray),
580 Value::BoolArray2D(m) => rows!(m, BoolArray),
581 _ => None,
582 }
583}
584
585pub(super) fn array_2d_dims(v: &Value) -> Option<(usize, usize)> {
588 match v {
589 Value::IntArray2D(m) => Some((m.len(), m.first().map_or(0, alloc::vec::Vec::len))),
590 Value::BigIntArray2D(m) => Some((m.len(), m.first().map_or(0, alloc::vec::Vec::len))),
591 Value::TextArray2D(m) => Some((m.len(), m.first().map_or(0, alloc::vec::Vec::len))),
592 Value::BoolArray2D(m) => Some((m.len(), m.first().map_or(0, alloc::vec::Vec::len))),
593 _ => None,
594 }
595}
596
597pub(crate) fn array_element_at(v: &Value, pos: usize) -> Option<Value<'static>> {
604 use alloc::borrow::Cow;
605 macro_rules! nth {
606 ($items:expr, $map:expr) => {
607 $items
608 .get(pos)
609 .map(|e| e.as_ref().map_or(Value::Null, $map))
610 };
611 }
612 match v {
613 Value::TextArray(items) | Value::VarcharArray(items) | Value::CharArray(items) => {
614 nth!(items, |s| Value::Text(Cow::Owned(s.clone())))
615 }
616 Value::JsonArray(items) | Value::JsonbArray(items) => {
617 nth!(items, |s| Value::Json(Cow::Owned(s.clone())))
618 }
619 Value::IntArray(items) => nth!(items, |n| Value::Int(*n)),
620 Value::BigIntArray(items) => nth!(items, |n| Value::BigInt(*n)),
621 Value::SmallIntArray(items) => nth!(items, |n| Value::SmallInt(*n)),
622 Value::Int2Vector(items) => items.get(pos).map(|n| Value::SmallInt(*n)),
624 Value::OidVector(items) => items.get(pos).map(|n| Value::BigInt(i64::from(*n))),
625 Value::BoolArray(items) => nth!(items, |b| Value::Bool(*b)),
626 Value::FloatArray(items) => nth!(items, |f| Value::Float(*f)),
627 Value::NumericArray(items) => {
628 nth!(items, |t: &(i128, u16)| Value::Numeric {
629 scaled: t.0,
630 scale: t.1,
631 kind: spg_storage::NumericKind::Finite
632 })
633 }
634 Value::DateArray(items) => nth!(items, |d| Value::Date(*d)),
635 Value::TimestampArray(items) | Value::TimestamptzArray(items) => {
636 nth!(items, |t| Value::Timestamp(*t))
637 }
638 Value::MoneyArray(items) => nth!(items, |m| Value::Money(*m)),
639 Value::IntervalArray(items) => nth!(items, |s| Value::Interval {
640 months: s.months,
641 days: s.days,
642 micros: s.micros,
643 kind: s.kind,
644 }),
645 Value::UuidArray(items) => nth!(items, |u| Value::Uuid(*u)),
646 Value::BytesArray(items) => nth!(items, |b| Value::Bytes(Cow::Owned(b.clone()))),
647 Value::RealArray(items) => nth!(items, |x| Value::Real(*x)),
648 Value::TimeArray(items) => nth!(items, |us| Value::Time(*us)),
649 Value::TimeTzArray(items) => nth!(items, |(us, off)| Value::TimeTz {
650 us: *us,
651 offset_secs: *off,
652 }),
653 Value::InetArray(items) => nth!(items, |(family, bits, addr)| Value::Inet {
654 family: *family,
655 bits: *bits,
656 addr: *addr,
657 }),
658 Value::XmlArray(items) => nth!(items, |x| Value::Xml(Cow::Owned(x.clone()))),
659 _ => None,
660 }
661}
662
663pub(super) fn array_rebuild(model: &Value<'_>, elems: &[Value<'static>]) -> Option<Value<'static>> {
672 macro_rules! build {
673 ($variant:ident, $conv:expr) => {{
674 let mut out = alloc::vec::Vec::with_capacity(elems.len());
675 for e in elems {
676 if matches!(e, Value::Null) {
677 out.push(None);
678 continue;
679 }
680 out.push(Some(($conv)(e)?));
681 }
682 Some(Value::$variant(out))
683 }};
684 }
685 let as_i64 = |v: &Value<'_>| -> Option<i64> {
686 match v {
687 Value::SmallInt(n) => Some(i64::from(*n)),
688 Value::Int(n) => Some(i64::from(*n)),
689 Value::BigInt(n) => Some(*n),
690 _ => None,
691 }
692 };
693 match model {
694 Value::TextArray(_) => build!(TextArray, |e: &Value<'_>| match e {
695 Value::Text(s) => Some(s.as_ref().to_string()),
696 _ => None,
697 }),
698 Value::VarcharArray(_) => build!(VarcharArray, |e: &Value<'_>| match e {
699 Value::Text(s) => Some(s.as_ref().to_string()),
700 _ => None,
701 }),
702 Value::JsonArray(_) => build!(JsonArray, |e: &Value<'_>| match e {
703 Value::Json(s) | Value::Text(s) => Some(s.as_ref().to_string()),
704 _ => None,
705 }),
706 Value::JsonbArray(_) => build!(JsonbArray, |e: &Value<'_>| match e {
707 Value::Json(s) | Value::Text(s) => Some(s.as_ref().to_string()),
708 _ => None,
709 }),
710 Value::IntArray(_) => build!(IntArray, |e: &Value<'_>| as_i64(e)
711 .and_then(|n| i32::try_from(n).ok())),
712 Value::BigIntArray(_) => build!(BigIntArray, as_i64),
713 Value::SmallIntArray(_) => build!(SmallIntArray, |e: &Value<'_>| as_i64(e)
714 .and_then(|n| i16::try_from(n).ok())),
715 Value::BoolArray(_) => build!(BoolArray, |e: &Value<'_>| match e {
716 Value::Bool(b) => Some(*b),
717 _ => None,
718 }),
719 Value::FloatArray(_) => build!(FloatArray, |e: &Value<'_>| match e {
720 Value::Float(f) => Some(*f),
721 Value::Real(f) => Some(f64::from(*f)),
722 other => as_i64(other).map(|n| n as f64),
723 }),
724 Value::NumericArray(_) => build!(NumericArray, |e: &Value<'_>| match e {
725 Value::Numeric { scaled, scale, .. } => Some((*scaled, *scale)),
726 other => as_i64(other).map(|n| (i128::from(n), 0u16)),
727 }),
728 Value::DateArray(_) => build!(DateArray, |e: &Value<'_>| match e {
729 Value::Date(d) => Some(*d),
730 _ => None,
731 }),
732 Value::TimestampArray(_) => build!(TimestampArray, |e: &Value<'_>| match e {
733 Value::Timestamp(t) => Some(*t),
734 _ => None,
735 }),
736 Value::TimestamptzArray(_) => build!(TimestamptzArray, |e: &Value<'_>| match e {
737 Value::Timestamp(t) => Some(*t),
738 _ => None,
739 }),
740 Value::MoneyArray(_) => build!(MoneyArray, |e: &Value<'_>| match e {
741 Value::Money(m) => Some(*m),
742 _ => None,
743 }),
744 Value::UuidArray(_) => build!(UuidArray, |e: &Value<'_>| match e {
745 Value::Uuid(u) => Some(*u),
746 _ => None,
747 }),
748 Value::BytesArray(_) => build!(BytesArray, |e: &Value<'_>| match e {
749 Value::Bytes(b) => Some(b.as_ref().to_vec()),
750 _ => None,
751 }),
752 Value::RealArray(_) => build!(RealArray, |e: &Value<'_>| match e {
753 Value::Real(x) => Some(*x),
754 _ => None,
755 }),
756 Value::TimeArray(_) => build!(TimeArray, |e: &Value<'_>| match e {
757 Value::Time(us) => Some(*us),
758 _ => None,
759 }),
760 Value::TimeTzArray(_) => build!(TimeTzArray, |e: &Value<'_>| match e {
761 Value::TimeTz { us, offset_secs } => Some((*us, *offset_secs)),
762 _ => None,
763 }),
764 Value::InetArray(_) => build!(InetArray, |e: &Value<'_>| match e {
765 Value::Inet { family, bits, addr } => Some((*family, *bits, *addr)),
766 _ => None,
767 }),
768 Value::XmlArray(_) => build!(XmlArray, |e: &Value<'_>| match e {
769 Value::Xml(x) => Some(x.as_ref().into()),
770 _ => None,
771 }),
772 Value::IntervalArray(_) => build!(IntervalArray, |e: &Value<'_>| match e {
773 Value::Interval {
774 months,
775 days,
776 micros,
777 kind,
778 } => Some(spg_storage::IntervalSpan {
779 months: *months,
780 days: *days,
781 micros: *micros,
782 kind: *kind,
783 }),
784 _ => None,
785 }),
786 _ => None,
787 }
788}
789
790pub(crate) fn build_array_from_values(vals: &[Value<'static>]) -> Value<'static> {
804 if let Some(v) = homogeneous_typed_array(vals) {
805 return v;
806 }
807 let mut has_text = false;
808 let mut has_float = false;
809 let mut has_real = false;
813 let mut has_numeric = false;
814 let mut has_bigint = false;
815 let mut has_int = false;
816 for v in vals {
817 match v {
818 Value::Null => {}
819 Value::Int(_) | Value::SmallInt(_) => has_int = true,
820 Value::BigInt(_) => has_bigint = true,
821 Value::Numeric { .. } | Value::NumericBig(_) => has_numeric = true,
822 Value::Float(_) => has_float = true,
823 Value::Real(_) => has_real = true,
824 _ => has_text = true,
825 }
826 }
827 let as_i64 = |v: &Value<'_>| -> Option<i64> {
828 match v {
829 Value::SmallInt(n) => Some(i64::from(*n)),
830 Value::Int(n) => Some(i64::from(*n)),
831 Value::BigInt(n) => Some(*n),
832 _ => None,
833 }
834 };
835 if !has_text {
836 if has_real && !has_float {
837 #[allow(clippy::cast_possible_truncation)]
838 return Value::RealArray(
839 vals.iter()
840 .map(|v| match v {
841 Value::Null => None,
842 Value::Real(f) => Some(*f),
843 #[allow(clippy::cast_precision_loss)]
844 Value::Numeric { scaled, scale, .. } => {
845 Some((*scaled as f64 / libm::pow(10.0, f64::from(*scale))) as f32)
846 }
847 other => as_i64(other).map(|n| n as f32),
848 })
849 .collect(),
850 );
851 }
852 if has_float || has_real {
853 return Value::FloatArray(
854 vals.iter()
855 .map(|v| match v {
856 Value::Null => None,
857 Value::Float(f) => Some(*f),
858 Value::Real(f) => Some(f64::from(*f)),
859 #[allow(clippy::cast_precision_loss)]
860 Value::Numeric { scaled, scale, .. } => {
861 Some(*scaled as f64 / libm::pow(10.0, f64::from(*scale)))
862 }
863 other => as_i64(other).map(|n| n as f64),
864 })
865 .collect(),
866 );
867 }
868 if has_numeric {
869 if vals.iter().all(|v| {
872 matches!(
873 v,
874 Value::Null
875 | Value::SmallInt(_)
876 | Value::Int(_)
877 | Value::BigInt(_)
878 | Value::Numeric {
879 kind: spg_storage::NumericKind::Finite,
880 ..
881 }
882 )
883 }) {
884 return Value::NumericArray(
885 vals.iter()
886 .map(|v| match v {
887 Value::Null => None,
888 Value::Numeric { scaled, scale, .. } => Some((*scaled, *scale)),
889 other => as_i64(other).map(|n| (i128::from(n), 0u16)),
890 })
891 .collect(),
892 );
893 }
894 } else if has_bigint {
895 return Value::BigIntArray(vals.iter().map(as_i64).collect());
896 } else if has_int {
897 return Value::IntArray(
898 vals.iter()
899 .map(|v| as_i64(v).and_then(|n| i32::try_from(n).ok()))
900 .collect(),
901 );
902 }
903 }
904 Value::TextArray(
905 vals.iter()
906 .map(|v| match v {
907 Value::Null => None,
908 Value::Text(s) | Value::Json(s) => Some(s.as_ref().to_string()),
909 other => Some(crate::eval::value_to_text(other)),
910 })
911 .collect(),
912 )
913}
914
915pub(crate) fn homogeneous_typed_array(vals: &[Value<'static>]) -> Option<Value<'static>> {
918 let first = vals.iter().find(|v| !matches!(v, Value::Null))?;
919 macro_rules! collect {
920 ($variant:ident, $pat:pat => $val:expr) => {{
921 let mut out = alloc::vec::Vec::with_capacity(vals.len());
922 for v in vals {
923 match v {
924 Value::Null => out.push(None),
925 $pat => out.push(Some($val)),
926 _ => return None,
927 }
928 }
929 Some(Value::$variant(out))
930 }};
931 }
932 match first {
933 Value::Bool(_) => collect!(BoolArray, Value::Bool(b) => *b),
934 Value::Date(_) => collect!(DateArray, Value::Date(d) => *d),
935 Value::Timestamp(_) => collect!(TimestampArray, Value::Timestamp(t) => *t),
936 Value::Uuid(_) => collect!(UuidArray, Value::Uuid(u) => *u),
937 Value::Money(_) => collect!(MoneyArray, Value::Money(m) => *m),
938 Value::Bytes(_) => collect!(BytesArray, Value::Bytes(b) => b.as_ref().to_vec()),
939 Value::Real(_) => collect!(RealArray, Value::Real(x) => *x),
941 Value::Time(_) => collect!(TimeArray, Value::Time(us) => *us),
942 Value::TimeTz { .. } => {
943 collect!(TimeTzArray, Value::TimeTz { us, offset_secs } => (*us, *offset_secs))
944 }
945 Value::Inet { .. } | Value::Cidr { .. } => {
948 let mut out = alloc::vec::Vec::with_capacity(vals.len());
949 for v in vals {
950 match v {
951 Value::Null => out.push(None),
952 Value::Inet { family, bits, addr } | Value::Cidr { family, bits, addr } => {
953 out.push(Some((*family, *bits, *addr)));
954 }
955 _ => return None,
956 }
957 }
958 Some(Value::InetArray(out))
959 }
960 Value::Xml(_) => collect!(XmlArray, Value::Xml(x) => x.as_ref().into()),
961 Value::Interval { .. } => {
962 let mut out = alloc::vec::Vec::with_capacity(vals.len());
963 for v in vals {
964 match v {
965 Value::Null => out.push(None),
966 Value::Interval {
967 months,
968 days,
969 micros,
970 kind,
971 } => out.push(Some(spg_storage::IntervalSpan {
972 months: *months,
973 days: *days,
974 micros: *micros,
975 kind: *kind,
976 })),
977 _ => return None,
978 }
979 }
980 Some(Value::IntervalArray(out))
981 }
982 _ => None,
983 }
984}
985
986pub(crate) fn split_2d_rows(s: &str) -> Option<Vec<alloc::string::String>> {
1000 let trimmed = s.trim();
1001 let inner = trimmed
1002 .strip_prefix('{')
1003 .and_then(|x| x.strip_suffix('}'))?
1004 .trim();
1005 if !inner.starts_with('{') {
1006 return None;
1007 }
1008 let mut rows = alloc::vec::Vec::new();
1009 let bytes = inner.as_bytes();
1010 let mut depth = 0i32;
1011 let mut start = 0usize;
1012 let mut in_quote = false;
1013 let mut i = 0;
1014 while i < bytes.len() {
1015 let c = bytes[i];
1016 if in_quote {
1017 if c == b'\\' {
1018 i += 2;
1019 continue;
1020 }
1021 if c == b'"' {
1022 in_quote = false;
1023 }
1024 } else {
1025 match c {
1026 b'"' => in_quote = true,
1027 b'{' => depth += 1,
1028 b'}' => depth -= 1,
1029 b',' if depth == 0 => {
1030 rows.push(inner[start..i].trim().to_string());
1031 start = i + 1;
1032 }
1033 _ => {}
1034 }
1035 }
1036 i += 1;
1037 }
1038 rows.push(inner[start..].trim().to_string());
1039 Some(rows)
1040}
1041
1042pub(crate) fn build_2d_from_rows(rows: &[Value<'static>]) -> Option<Value<'static>> {
1043 if rows.is_empty() || !rows.iter().all(|v| array_len(v).is_some()) {
1044 return None;
1045 }
1046 let width = array_len(&rows[0])?;
1047 if !rows.iter().all(|v| array_len(v) == Some(width)) {
1048 return None;
1049 }
1050 if rows.iter().all(|v| matches!(v, Value::BoolArray(_))) {
1051 return Some(Value::BoolArray2D(
1052 rows.iter()
1053 .map(|v| match v {
1054 Value::BoolArray(r) => r.clone(),
1055 _ => unreachable!("checked"),
1056 })
1057 .collect(),
1058 ));
1059 }
1060 if rows.iter().all(|v| matches!(v, Value::IntArray(_))) {
1061 return Some(Value::IntArray2D(
1062 rows.iter()
1063 .map(|v| match v {
1064 Value::IntArray(r) => r.clone(),
1065 _ => unreachable!("checked"),
1066 })
1067 .collect(),
1068 ));
1069 }
1070 if rows
1071 .iter()
1072 .all(|v| matches!(v, Value::IntArray(_) | Value::BigIntArray(_)))
1073 {
1074 return Some(Value::BigIntArray2D(
1075 rows.iter()
1076 .map(|v| match v {
1077 Value::BigIntArray(r) => r.clone(),
1078 Value::IntArray(r) => r.iter().map(|c| c.map(i64::from)).collect(),
1079 _ => unreachable!("checked"),
1080 })
1081 .collect(),
1082 ));
1083 }
1084 Some(Value::TextArray2D(
1088 rows.iter()
1089 .map(|v| {
1090 let n = array_len(v).unwrap_or(0);
1091 (0..n)
1092 .map(|i| match array_element_at(v, i) {
1093 None | Some(Value::Null) => None,
1094 Some(x) => Some(crate::eval::value_to_text(&x)),
1095 })
1096 .collect()
1097 })
1098 .collect(),
1099 ))
1100}
1101
1102pub(crate) fn flatten_2d(v: &Value<'_>) -> Option<Value<'static>> {
1109 Some(match v {
1110 Value::IntArray2D(rows) => Value::IntArray(rows.iter().flatten().copied().collect()),
1111 Value::BigIntArray2D(rows) => Value::BigIntArray(rows.iter().flatten().copied().collect()),
1112 Value::BoolArray2D(rows) => Value::BoolArray(rows.iter().flatten().copied().collect()),
1113 Value::TextArray2D(rows) => Value::TextArray(rows.iter().flatten().cloned().collect()),
1114 _ => return None,
1115 })
1116}