1use std::borrow::Cow;
15use std::collections::BTreeMap;
16use std::fmt::{Debug, Formatter};
17
18use anyhow::Context;
19use arrow::array::{BinaryArray, FixedSizeBinaryArray};
20use chrono::{NaiveDateTime, NaiveTime};
21use dec::OrderedDecimal;
22use mz_ore::soft_panic_or_log;
23use mz_persist_types::columnar::FixedSizeCodec;
24use mz_persist_types::stats::bytes::{BytesStats, FixedSizeBytesStats, FixedSizeBytesStatsKind};
25use mz_persist_types::stats::json::{JsonMapElementStats, JsonStats};
26use mz_persist_types::stats::primitive::PrimitiveStats;
27use mz_persist_types::stats::{
28 AtomicBytesStats, ColumnNullStats, ColumnStatKinds, ColumnStats, ColumnarStats,
29 PrimitiveStatsVariants,
30};
31use ordered_float::OrderedFloat;
32use prost::Message;
33use serde::Deserializer;
34use serde::de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor};
35use uuid::Uuid;
36
37use crate::adt::date::Date;
38use crate::adt::datetime::PackedNaiveTime;
39use crate::adt::interval::{Interval, PackedInterval};
40use crate::adt::jsonb::{KeyClass, KeyClassifier, NumberParser};
41use crate::adt::numeric::{Numeric, PackedNumeric};
42use crate::adt::timestamp::{CheckedTimestamp, PackedNaiveDateTime};
43use crate::row::ProtoDatum;
44use crate::{Datum, Row, RowArena, SqlScalarType};
45
46fn soft_expect_or_log<A, B: Debug>(result: Result<A, B>) -> Option<A> {
47 match result {
48 Ok(a) => Some(a),
49 Err(e) => {
50 soft_panic_or_log!("failed to decode stats: {e:?}");
51 None
52 }
53 }
54}
55
56pub fn fixed_stats_from_column(
59 col: &FixedSizeBinaryArray,
60 kind: FixedSizeBytesStatsKind,
61) -> ColumnStatKinds {
62 let lower = col.into_iter().filter_map(|x| x).min();
69 let upper = col.into_iter().filter_map(|x| x).max();
70
71 let default = || match kind {
74 FixedSizeBytesStatsKind::PackedTime => PackedNaiveTime::from_value(NaiveTime::default())
75 .as_bytes()
76 .to_vec(),
77 FixedSizeBytesStatsKind::PackedDateTime => {
78 PackedNaiveDateTime::from_value(NaiveDateTime::default())
79 .as_bytes()
80 .to_vec()
81 }
82 FixedSizeBytesStatsKind::PackedInterval => PackedInterval::from_value(Interval::default())
83 .as_bytes()
84 .to_vec(),
85 FixedSizeBytesStatsKind::PackedNumeric => {
86 unreachable!("Numeric is not stored in a fixed size byte array")
87 }
88 FixedSizeBytesStatsKind::Uuid => Uuid::default().as_bytes().to_vec(),
89 };
90
91 BytesStats::FixedSize(FixedSizeBytesStats {
92 lower: lower.map_or_else(default, Vec::from),
93 upper: upper.map_or_else(default, Vec::from),
94 kind,
95 })
96 .into()
97}
98
99fn float_bounds<F: num_traits::Float>(lower: F, upper: F) -> Option<(F, F)> {
114 if lower.is_nan() && lower.is_sign_negative() && !(upper.is_nan() && upper.is_sign_negative()) {
115 None
116 } else {
117 Some((lower, upper))
118 }
119}
120
121pub fn col_values<'a>(
123 typ: &SqlScalarType,
124 stats: &'a ColumnStatKinds,
125 arena: &'a RowArena,
126) -> Option<(Datum<'a>, Datum<'a>)> {
127 use PrimitiveStatsVariants::*;
128
129 fn map_stats<'a, T, F>(stats: &'a T, f: F) -> Option<(Datum<'a>, Datum<'a>)>
131 where
132 T: ColumnStats,
133 F: Fn(T::Ref<'a>) -> Datum<'a>,
134 {
135 Some((f(stats.lower()?), f(stats.upper()?)))
136 }
137
138 match (typ, stats) {
139 (SqlScalarType::Bool, ColumnStatKinds::Primitive(Bool(stats))) => {
140 let map_datum = |val| if val { Datum::True } else { Datum::False };
141 map_stats(stats, map_datum)
142 }
143 (SqlScalarType::PgLegacyChar, ColumnStatKinds::Primitive(U8(stats))) => {
144 map_stats(stats, Datum::UInt8)
145 }
146 (SqlScalarType::UInt16, ColumnStatKinds::Primitive(U16(stats))) => {
147 map_stats(stats, Datum::UInt16)
148 }
149 (
150 SqlScalarType::UInt32
151 | SqlScalarType::Oid
152 | SqlScalarType::RegClass
153 | SqlScalarType::RegProc
154 | SqlScalarType::RegType,
155 ColumnStatKinds::Primitive(U32(stats)),
156 ) => map_stats(stats, Datum::UInt32),
157 (SqlScalarType::UInt64, ColumnStatKinds::Primitive(U64(stats))) => {
158 map_stats(stats, Datum::UInt64)
159 }
160 (SqlScalarType::Int16, ColumnStatKinds::Primitive(I16(stats))) => {
161 map_stats(stats, Datum::Int16)
162 }
163 (SqlScalarType::Int32, ColumnStatKinds::Primitive(I32(stats))) => {
164 map_stats(stats, Datum::Int32)
165 }
166 (SqlScalarType::Int64, ColumnStatKinds::Primitive(I64(stats))) => {
167 map_stats(stats, Datum::Int64)
168 }
169 (SqlScalarType::Float32, ColumnStatKinds::Primitive(F32(stats))) => {
170 let (lower, upper) = float_bounds(stats.lower, stats.upper)?;
171 Some((
172 Datum::Float32(OrderedFloat(lower)),
173 Datum::Float32(OrderedFloat(upper)),
174 ))
175 }
176 (SqlScalarType::Float64, ColumnStatKinds::Primitive(F64(stats))) => {
177 let (lower, upper) = float_bounds(stats.lower, stats.upper)?;
178 Some((
179 Datum::Float64(OrderedFloat(lower)),
180 Datum::Float64(OrderedFloat(upper)),
181 ))
182 }
183 (
184 SqlScalarType::Numeric { .. },
185 ColumnStatKinds::Bytes(BytesStats::FixedSize(FixedSizeBytesStats {
186 lower,
187 upper,
188 kind: FixedSizeBytesStatsKind::PackedNumeric,
189 })),
190 ) => {
191 let lower = soft_expect_or_log(PackedNumeric::from_bytes(lower))?.into_value();
192 let upper = soft_expect_or_log(PackedNumeric::from_bytes(upper))?.into_value();
193 Some((
194 Datum::Numeric(OrderedDecimal(lower)),
195 Datum::Numeric(OrderedDecimal(upper)),
196 ))
197 }
198 (
199 SqlScalarType::String
200 | SqlScalarType::PgLegacyName
201 | SqlScalarType::Char { .. }
202 | SqlScalarType::VarChar { .. },
203 ColumnStatKinds::Primitive(String(stats)),
204 ) => map_stats(stats, Datum::String),
205 (SqlScalarType::Bytes, ColumnStatKinds::Bytes(BytesStats::Primitive(stats))) => {
206 Some((Datum::Bytes(&stats.lower), Datum::Bytes(&stats.upper)))
207 }
208 (SqlScalarType::Date, ColumnStatKinds::Primitive(I32(stats))) => {
209 let lower = soft_expect_or_log(Date::from_pg_epoch(stats.lower))?;
210 let upper = soft_expect_or_log(Date::from_pg_epoch(stats.upper))?;
211 Some((Datum::Date(lower), Datum::Date(upper)))
212 }
213 (
219 SqlScalarType::Time,
220 ColumnStatKinds::Bytes(BytesStats::FixedSize(
221 stats @ FixedSizeBytesStats {
222 kind: FixedSizeBytesStatsKind::PackedTime,
223 ..
224 },
225 )),
226 ) => {
227 let lower = soft_expect_or_log(PackedNaiveTime::from_bytes(&stats.lower))?.into_value();
228 let upper = soft_expect_or_log(PackedNaiveTime::from_bytes(&stats.upper))?.into_value();
229 Some((Datum::Time(lower), Datum::Time(upper)))
230 }
231 (
232 SqlScalarType::Timestamp { .. },
233 ColumnStatKinds::Bytes(BytesStats::FixedSize(
234 stats @ FixedSizeBytesStats {
235 kind: FixedSizeBytesStatsKind::PackedDateTime,
236 ..
237 },
238 )),
239 ) => {
240 let lower =
241 soft_expect_or_log(PackedNaiveDateTime::from_bytes(&stats.lower))?.into_value();
242 let lower = soft_expect_or_log(CheckedTimestamp::from_timestamplike(lower))?;
243 let upper =
244 soft_expect_or_log(PackedNaiveDateTime::from_bytes(&stats.upper))?.into_value();
245 let upper = soft_expect_or_log(CheckedTimestamp::from_timestamplike(upper))?;
246
247 Some((Datum::Timestamp(lower), Datum::Timestamp(upper)))
248 }
249 (
250 SqlScalarType::TimestampTz { .. },
251 ColumnStatKinds::Bytes(BytesStats::FixedSize(
252 stats @ FixedSizeBytesStats {
253 kind: FixedSizeBytesStatsKind::PackedDateTime,
254 ..
255 },
256 )),
257 ) => {
258 let lower = soft_expect_or_log(PackedNaiveDateTime::from_bytes(&stats.lower))?
259 .into_value()
260 .and_utc();
261 let lower = soft_expect_or_log(CheckedTimestamp::from_timestamplike(lower))?;
262 let upper = soft_expect_or_log(PackedNaiveDateTime::from_bytes(&stats.upper))?
263 .into_value()
264 .and_utc();
265 let upper = soft_expect_or_log(CheckedTimestamp::from_timestamplike(upper))?;
266
267 Some((Datum::TimestampTz(lower), Datum::TimestampTz(upper)))
268 }
269 (SqlScalarType::MzTimestamp, ColumnStatKinds::Primitive(U64(stats))) => {
270 map_stats(stats, |x| Datum::MzTimestamp(crate::Timestamp::from(x)))
271 }
272 (
273 SqlScalarType::Interval,
274 ColumnStatKinds::Bytes(BytesStats::FixedSize(
275 stats @ FixedSizeBytesStats {
276 kind: FixedSizeBytesStatsKind::PackedInterval,
277 ..
278 },
279 )),
280 ) => {
281 let lower = soft_expect_or_log(PackedInterval::from_bytes(&stats.lower))?.into_value();
282 let upper = soft_expect_or_log(PackedInterval::from_bytes(&stats.upper))?.into_value();
283 Some((Datum::Interval(lower), Datum::Interval(upper)))
284 }
285 (
286 SqlScalarType::Uuid,
287 ColumnStatKinds::Bytes(BytesStats::FixedSize(
288 stats @ FixedSizeBytesStats {
289 kind: FixedSizeBytesStatsKind::Uuid,
290 ..
291 },
292 )),
293 ) => {
294 let lower = soft_expect_or_log(Uuid::from_slice(&stats.lower))?;
295 let upper = soft_expect_or_log(Uuid::from_slice(&stats.upper))?;
296 Some((Datum::Uuid(lower), Datum::Uuid(upper)))
297 }
298 (SqlScalarType::Jsonb, ColumnStatKinds::Bytes(BytesStats::Json(_))) => None,
300 (
302 SqlScalarType::AclItem
303 | SqlScalarType::MzAclItem
304 | SqlScalarType::Range { .. }
305 | SqlScalarType::Array(_)
306 | SqlScalarType::Map { .. }
307 | SqlScalarType::List { .. }
308 | SqlScalarType::Record { .. }
309 | SqlScalarType::Int2Vector,
310 ColumnStatKinds::None,
311 ) => None,
312 (
314 SqlScalarType::Numeric { .. }
315 | SqlScalarType::Time
316 | SqlScalarType::Timestamp { .. }
317 | SqlScalarType::TimestampTz { .. }
318 | SqlScalarType::Interval
319 | SqlScalarType::Uuid,
320 ColumnStatKinds::Bytes(BytesStats::Atomic(AtomicBytesStats { lower, upper })),
321 ) => {
322 fn decode_v0<'a>(
328 bytes: &[u8],
329 typ: &SqlScalarType,
330 arena: &'a RowArena,
331 ) -> Option<Datum<'a>> {
332 let proto = soft_expect_or_log(ProtoDatum::decode(bytes))?;
333 let mut row = Row::default();
334 soft_expect_or_log(row.packer().try_push_proto(&proto))?;
335 let datum = arena.push_unary_row(row);
336 let type_matches = matches!(
337 (typ, datum),
338 (SqlScalarType::Numeric { .. }, Datum::Numeric(_))
339 | (SqlScalarType::Time, Datum::Time(_))
340 | (SqlScalarType::Timestamp { .. }, Datum::Timestamp(_))
341 | (SqlScalarType::TimestampTz { .. }, Datum::TimestampTz(_))
342 | (SqlScalarType::Interval, Datum::Interval(_))
343 | (SqlScalarType::Uuid, Datum::Uuid(_))
344 );
345 if !type_matches {
346 soft_panic_or_log!("V0 stats bound {datum:?} does not match column {typ:?}");
347 return None;
348 }
349 Some(datum)
350 }
351 let lower = decode_v0(lower.as_slice(), typ, arena)?;
352 let upper = decode_v0(upper.as_slice(), typ, arena)?;
353
354 Some((lower, upper))
355 }
356 (typ, stats) => {
357 mz_ore::soft_panic_or_log!("found unexpected {stats:?} for column {typ:?}");
358 None
359 }
360 }
361}
362
363pub fn decode_numeric<'a>(
365 stats: &PrimitiveStats<Vec<u8>>,
366 arena: &'a RowArena,
367) -> Result<(Datum<'a>, Datum<'a>), anyhow::Error> {
368 fn decode<'a>(bytes: &[u8], arena: &'a RowArena) -> Result<Datum<'a>, anyhow::Error> {
369 let proto = ProtoDatum::decode(bytes)?;
370 let datum = arena.make_datum(|r| {
371 r.try_push_proto(&proto)
372 .expect("ProtoDatum should be valid Datum")
373 });
374 let Datum::Numeric(_) = &datum else {
375 anyhow::bail!("expected Numeric found {datum:?}");
376 };
377 Ok(datum)
378 }
379 let lower = decode(&stats.lower, arena).context("lower")?;
380 let upper = decode(&stats.upper, arena).context("upper")?;
381
382 Ok((lower, upper))
383}
384
385pub fn numeric_stats_from_column(col: &BinaryArray) -> ColumnStatKinds {
388 let mut lower = OrderedDecimal(Numeric::nan());
389 let mut upper = OrderedDecimal(-Numeric::infinity());
390
391 for val in col.iter() {
392 let Some(val) = val else {
393 continue;
394 };
395 let val = OrderedDecimal(
396 PackedNumeric::from_bytes(val)
397 .expect("failed to roundtrip Numeric")
398 .into_value(),
399 );
400 lower = val.min(lower);
401 upper = val.max(upper);
402 }
403
404 BytesStats::FixedSize(FixedSizeBytesStats {
405 lower: PackedNumeric::from_value(lower.0).as_bytes().to_vec(),
406 upper: PackedNumeric::from_value(upper.0).as_bytes().to_vec(),
407 kind: FixedSizeBytesStatsKind::PackedNumeric,
408 })
409 .into()
410}
411
412#[derive(Default)]
413struct JsonVisitor<'de> {
414 count: usize,
415 nulls: bool,
416 bools: Option<(bool, bool)>,
417 strings: Option<(Cow<'de, str>, Cow<'de, str>)>,
418 ints: Option<(i64, i64)>,
419 uints: Option<(u64, u64)>,
420 floats: Option<(f64, f64)>,
421 numerics: Option<(Numeric, Numeric)>,
422 lists: bool,
423 maps: bool,
424 fields: BTreeMap<Cow<'de, str>, JsonVisitor<'de>>,
425}
426
427impl<'de> JsonVisitor<'de> {
428 pub fn to_stats(self) -> JsonMapElementStats {
429 let mut context: dec::Context<Numeric> = Default::default();
430 let Self {
431 count,
432 nulls,
433 bools,
434 strings,
435 ints,
436 uints,
437 floats,
438 numerics,
439 lists,
440 maps,
441 fields,
442 } = self;
443 let min_numeric = [
444 numerics.map(|(n, _)| n),
445 ints.map(|(n, _)| n.into()),
446 uints.map(|(n, _)| n.into()),
447 floats.map(|(n, _)| n.into()),
448 ]
449 .into_iter()
450 .flatten()
451 .min_by(|a, b| context.total_cmp(a, b));
452 let max_numeric = [
453 numerics.map(|(_, n)| n),
454 ints.map(|(_, n)| n.into()),
455 uints.map(|(_, n)| n.into()),
456 floats.map(|(_, n)| n.into()),
457 ]
458 .into_iter()
459 .flatten()
460 .max_by(|a, b| context.total_cmp(a, b));
461
462 let stats = match (nulls, min_numeric, max_numeric, bools, strings, lists, maps) {
463 (false, None, None, None, None, false, false) => JsonStats::None,
464 (true, None, None, None, None, false, false) => JsonStats::JsonNulls,
465 (false, Some(min), Some(max), None, None, false, false) => {
466 JsonStats::Numerics(PrimitiveStats {
467 lower: ProtoDatum::from(Datum::Numeric(OrderedDecimal(min))).encode_to_vec(),
468 upper: ProtoDatum::from(Datum::Numeric(OrderedDecimal(max))).encode_to_vec(),
469 })
470 }
471 (false, None, None, Some((min, max)), None, false, false) => {
472 JsonStats::Bools(PrimitiveStats {
473 lower: min,
474 upper: max,
475 })
476 }
477 (false, None, None, None, Some((min, max)), false, false) => {
478 JsonStats::Strings(PrimitiveStats {
479 lower: min.into_owned(),
480 upper: max.into_owned(),
481 })
482 }
483 (false, None, None, None, None, true, false) => JsonStats::Lists,
484 (false, None, None, None, None, false, true) => JsonStats::Maps(
485 fields
486 .into_iter()
487 .map(|(k, v)| (k.into_owned(), v.to_stats()))
488 .collect(),
489 ),
490 _ => JsonStats::Mixed,
491 };
492
493 JsonMapElementStats { len: count, stats }
494 }
495}
496
497impl<'a, 'de> Visitor<'de> for &'a mut JsonVisitor<'de> {
498 type Value = ();
499
500 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
501 write!(formatter, "json value")
502 }
503
504 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
505 where
506 E: Error,
507 {
508 self.count += 1;
509 let (min, max) = self.bools.get_or_insert((v, v));
510 *min = v.min(*min);
511 *max = v.max(*max);
512 Ok(())
513 }
514
515 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
516 where
517 E: Error,
518 {
519 self.count += 1;
520 let (min, max) = self.ints.get_or_insert((v, v));
521 *min = v.min(*min);
522 *max = v.max(*max);
523 Ok(())
524 }
525
526 fn visit_u64<E>(self, v: u64) -> Result<(), E>
527 where
528 E: Error,
529 {
530 self.count += 1;
531 let (min, max) = self.uints.get_or_insert((v, v));
532 *min = v.min(*min);
533 *max = v.max(*max);
534 Ok(())
535 }
536
537 fn visit_f64<E>(self, v: f64) -> Result<(), E> {
538 self.count += 1;
539 let (min, max) = self.floats.get_or_insert((v, v));
540 *min = v.min(*min);
541 *max = v.max(*max);
542 Ok(())
543 }
544
545 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
546 where
547 E: Error,
548 {
549 self.count += 1;
550 match &mut self.strings {
551 None => {
552 self.strings = Some((v.to_owned().into(), v.to_owned().into()));
553 }
554 Some((min, max)) => {
555 if v < &**min {
556 *min = v.to_owned().into();
557 } else if v > &**max {
558 *max = v.to_owned().into();
559 }
560 }
561 }
562 Ok(())
563 }
564
565 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
566 where
567 E: Error,
568 {
569 self.count += 1;
570 match &mut self.strings {
571 None => {
572 self.strings = Some((v.into(), v.into()));
573 }
574 Some((min, max)) => {
575 if v < &**min {
576 *min = v.into();
577 } else if v > &**max {
578 *max = v.into();
579 }
580 }
581 }
582 Ok(())
583 }
584
585 fn visit_unit<E>(self) -> Result<Self::Value, E>
586 where
587 E: Error,
588 {
589 self.count += 1;
590 self.nulls = true;
591 Ok(())
592 }
593
594 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
595 where
596 A: SeqAccess<'de>,
597 {
598 self.count += 1;
599 self.lists = true;
600 while let Some(_) = seq.next_element::<serde::de::IgnoredAny>()? {}
601 Ok(())
602 }
603
604 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
605 where
606 A: MapAccess<'de>,
607 {
608 self.count += 1;
609 let mut normal_only = true;
612 while let Some(key) = map.next_key_seed(KeyClassifier)? {
613 match key {
614 KeyClass::Number => {
615 let v = map.next_value_seed(NumberParser)?.0;
616 let (min, max) = self.numerics.get_or_insert((v, v));
617 if v < *min {
618 *min = v;
619 }
620 if v > *max {
621 *max = v;
622 }
623 normal_only = false;
624 }
625 KeyClass::MapKey(key) => {
626 let field = self.fields.entry(key).or_default();
627 map.next_value_seed(field)?;
628 }
629 }
630 }
631 if normal_only {
632 self.maps = true;
633 }
634
635 Ok(())
636 }
637}
638
639impl<'a, 'de> DeserializeSeed<'de> for &'a mut JsonVisitor<'de> {
640 type Value = ();
641
642 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
643 where
644 D: Deserializer<'de>,
645 {
646 deserializer.deserialize_any(self)
647 }
648}
649
650pub fn stats_for_json<'a>(jsons: impl IntoIterator<Item = Option<&'a str>>) -> ColumnarStats {
651 let mut visitor = JsonVisitor::default();
652 let mut nulls = 0;
653 for json in jsons {
654 match json {
655 None => {
656 nulls += 1;
657 }
658 Some(json) => {
659 let () = serde_json::Deserializer::from_str(json)
660 .deserialize_any(&mut visitor)
661 .unwrap_or_else(|e| panic!("error {e:?} on json: {json}"));
662 }
663 }
664 }
665
666 ColumnarStats {
667 nulls: Some(ColumnNullStats { count: nulls }),
668 values: ColumnStatKinds::Bytes(BytesStats::Json(visitor.to_stats().stats)),
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use arrow::array::AsArray;
675 use mz_persist_types::codec_impls::UnitSchema;
676 use mz_persist_types::columnar::{ColumnDecoder, Schema};
677 use mz_persist_types::part::PartBuilder;
678 use mz_persist_types::stats::{ProtoStructStats, StructStats, TrimStats};
679 use mz_proto::RustType;
680 use proptest::prelude::*;
681 use uuid::Uuid;
682
683 use crate::{Datum, RelationDesc, Row, RowArena, SqlScalarType};
684
685 fn datum_stats_roundtrip_trim<'a>(
686 schema: &RelationDesc,
687 datums: impl IntoIterator<Item = &'a Row>,
688 ) {
689 let mut builder = PartBuilder::new(schema, &UnitSchema);
690 for datum in datums {
691 builder.push(datum, &(), 1u64, 1i64);
692 }
693 let part = builder.finish();
694
695 let key_col = part.key.as_struct();
696 let decoder =
697 <RelationDesc as Schema<Row>>::decoder(schema, key_col.clone()).expect("success");
698 let mut actual: ProtoStructStats = RustType::into_proto(&decoder.stats());
699
700 actual.trim();
706 let actual: StructStats = RustType::from_proto(actual).unwrap();
707 let arena = RowArena::default();
708 for (name, typ) in schema.iter() {
709 let col_stats = actual.col(name).unwrap();
710 crate::stats::col_values(&typ.scalar_type, &col_stats.values, &arena);
711 }
712 }
713
714 fn scalar_type_stats_roundtrip_trim(scalar_type: SqlScalarType) {
715 let mut rows = Vec::new();
716 for datum in scalar_type.interesting_datums() {
717 rows.push(Row::pack(std::iter::once(datum)));
718 }
719
720 let schema = RelationDesc::builder()
722 .with_column("col", scalar_type.clone().nullable(false))
723 .finish();
724 for row in rows.iter() {
725 datum_stats_roundtrip_trim(&schema, [row]);
726 }
727 datum_stats_roundtrip_trim(&schema, &rows[..]);
728
729 let schema = RelationDesc::builder()
731 .with_column("col", scalar_type.nullable(true))
732 .finish();
733 rows.push(Row::pack(std::iter::once(Datum::Null)));
734 for row in rows.iter() {
735 datum_stats_roundtrip_trim(&schema, [row]);
736 }
737 datum_stats_roundtrip_trim(&schema, &rows[..]);
738 }
739
740 #[mz_ore::test]
743 #[cfg_attr(miri, ignore)] fn all_scalar_types_stats_roundtrip_trim() {
745 proptest!(|(scalar_type in any::<SqlScalarType>())| {
746 scalar_type_stats_roundtrip_trim(scalar_type)
748 });
749 }
750
751 #[mz_ore::test]
752 #[cfg_attr(miri, ignore)] fn proptest_uuid_sort_order() {
754 fn test(mut og: Vec<Uuid>) {
755 let mut as_bytes: Vec<_> = og.iter().map(|u| u.as_bytes().clone()).collect();
756
757 og.sort();
758 as_bytes.sort();
759
760 let rnd: Vec<_> = as_bytes.into_iter().map(Uuid::from_bytes).collect();
761
762 assert_eq!(og, rnd);
763 }
764
765 let arb_uuid = any::<[u8; 16]>().prop_map(Uuid::from_bytes);
766 proptest!(|(uuids in proptest::collection::vec(arb_uuid, 0..128))| {
767 test(uuids);
768 });
769 }
770}