1use std::fmt;
43
44use crate::basic::Type;
45use crate::data_type::private::ParquetValueType;
46use crate::data_type::*;
47use crate::errors::{ParquetError, Result};
48use crate::file::metadata::thrift::PageStatistics;
49use crate::util::bit_util::FromBytes;
50
51pub(crate) mod private {
52 use super::*;
53
54 pub trait MakeStatistics {
55 fn make_statistics(statistics: ValueStatistics<Self>) -> Statistics
56 where
57 Self: Sized;
58 }
59
60 macro_rules! gen_make_statistics {
61 ($value_ty:ty, $stat:ident) => {
62 impl MakeStatistics for $value_ty {
63 fn make_statistics(statistics: ValueStatistics<Self>) -> Statistics
64 where
65 Self: Sized,
66 {
67 Statistics::$stat(statistics)
68 }
69 }
70 };
71 }
72
73 gen_make_statistics!(bool, Boolean);
74 gen_make_statistics!(i32, Int32);
75 gen_make_statistics!(i64, Int64);
76 gen_make_statistics!(Int96, Int96);
77 gen_make_statistics!(f32, Float);
78 gen_make_statistics!(f64, Double);
79 gen_make_statistics!(ByteArray, ByteArray);
80 gen_make_statistics!(FixedLenByteArray, FixedLenByteArray);
81}
82
83macro_rules! statistics_new_func {
85 ($func:ident, $vtype:ty, $stat:ident) => {
86 #[doc = concat!("Creates new statistics for `", stringify!($stat), "` column type.")]
87 pub fn $func(
88 min: $vtype,
89 max: $vtype,
90 distinct: Option<u64>,
91 nulls: Option<u64>,
92 is_deprecated: bool,
93 ) -> Self {
94 Statistics::$stat(ValueStatistics::new(
95 min,
96 max,
97 distinct,
98 nulls,
99 is_deprecated,
100 ))
101 }
102 };
103}
104
105macro_rules! statistics_enum_func {
107 ($self:ident, $func:ident) => {{
108 match *$self {
109 Statistics::Boolean(ref typed) => typed.$func(),
110 Statistics::Int32(ref typed) => typed.$func(),
111 Statistics::Int64(ref typed) => typed.$func(),
112 Statistics::Int96(ref typed) => typed.$func(),
113 Statistics::Float(ref typed) => typed.$func(),
114 Statistics::Double(ref typed) => typed.$func(),
115 Statistics::ByteArray(ref typed) => typed.$func(),
116 Statistics::FixedLenByteArray(ref typed) => typed.$func(),
117 }
118 }};
119}
120
121pub(crate) fn from_thrift_page_stats(
123 physical_type: Type,
124 thrift_stats: Option<PageStatistics>,
125) -> Result<Option<Statistics>> {
126 Ok(match thrift_stats {
127 Some(stats) => {
128 let null_count = stats
130 .null_count
131 .map(|null_count| {
132 if null_count < 0 {
133 return Err(ParquetError::General(format!(
134 "Statistics null count is negative {null_count}",
135 )));
136 }
137 Ok(null_count as u64)
138 })
139 .transpose()?;
140 let distinct_count = stats.distinct_count.map(|value| value as u64);
142 let nan_count = stats
144 .nan_count
145 .map(|nan_count| {
146 if nan_count < 0 {
147 return Err(ParquetError::General(format!(
148 "Statistics NaN count is negative {nan_count}",
149 )));
150 }
151 Ok(nan_count as u64)
152 })
153 .transpose()?;
154 let old_format = stats.min_value.is_none() && stats.max_value.is_none();
156 let min = if old_format {
158 stats.min
159 } else {
160 stats.min_value
161 };
162 let max = if old_format {
164 stats.max
165 } else {
166 stats.max_value
167 };
168
169 fn check_len(min: Option<&[u8]>, max: Option<&[u8]>, len: usize) -> Result<()> {
170 if let Some(min) = min
171 && min.len() < len
172 {
173 return Err(ParquetError::General(
174 "Insufficient bytes to parse min statistic".to_string(),
175 ));
176 }
177 if let Some(max) = max
178 && max.len() < len
179 {
180 return Err(ParquetError::General(
181 "Insufficient bytes to parse max statistic".to_string(),
182 ));
183 }
184 Ok(())
185 }
186
187 {
188 let (min, max) = (min.as_deref(), max.as_deref());
189 match physical_type {
190 Type::BOOLEAN => check_len(min, max, 1),
191 Type::INT32 | Type::FLOAT => check_len(min, max, 4),
192 Type::INT64 | Type::DOUBLE => check_len(min, max, 8),
193 Type::INT96 => check_len(min, max, 12),
194 _ => Ok(()),
195 }?;
196 }
197
198 let res = match physical_type {
203 Type::BOOLEAN => Statistics::boolean(
204 min.map(|data| data[0] != 0),
205 max.map(|data| data[0] != 0),
206 distinct_count,
207 null_count,
208 old_format,
209 ),
210 Type::INT32 => Statistics::int32(
211 min.map(|data| i32::from_le_bytes(data[..4].try_into().unwrap())),
212 max.map(|data| i32::from_le_bytes(data[..4].try_into().unwrap())),
213 distinct_count,
214 null_count,
215 old_format,
216 ),
217 Type::INT64 => Statistics::int64(
218 min.map(|data| i64::from_le_bytes(data[..8].try_into().unwrap())),
219 max.map(|data| i64::from_le_bytes(data[..8].try_into().unwrap())),
220 distinct_count,
221 null_count,
222 old_format,
223 ),
224 Type::INT96 => {
225 let min = if let Some(data) = min {
227 if data.len() != 12 {
228 return Err(ParquetError::General(
229 "Incorrect Int96 min statistics".to_string(),
230 ));
231 }
232 Some(Int96::try_from_le_slice(&data)?)
233 } else {
234 None
235 };
236 let max = if let Some(data) = max {
237 if data.len() != 12 {
238 return Err(ParquetError::General(
239 "Incorrect Int96 max statistics".to_string(),
240 ));
241 }
242 Some(Int96::try_from_le_slice(&data)?)
243 } else {
244 None
245 };
246 Statistics::int96(min, max, distinct_count, null_count, old_format)
247 }
248 Type::FLOAT => Statistics::Float(
249 ValueStatistics::new(
250 min.map(|data| f32::from_le_bytes(data[..4].try_into().unwrap())),
251 max.map(|data| f32::from_le_bytes(data[..4].try_into().unwrap())),
252 distinct_count,
253 null_count,
254 old_format,
255 )
256 .with_nan_count(nan_count)
257 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
258 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
259 ),
260 Type::DOUBLE => Statistics::Double(
261 ValueStatistics::new(
262 min.map(|data| f64::from_le_bytes(data[..8].try_into().unwrap())),
263 max.map(|data| f64::from_le_bytes(data[..8].try_into().unwrap())),
264 distinct_count,
265 null_count,
266 old_format,
267 )
268 .with_nan_count(nan_count)
269 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
270 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
271 ),
272 Type::BYTE_ARRAY => Statistics::ByteArray(
273 ValueStatistics::new(
274 min.map(ByteArray::from),
275 max.map(ByteArray::from),
276 distinct_count,
277 null_count,
278 old_format,
279 )
280 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
281 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
282 ),
283 Type::FIXED_LEN_BYTE_ARRAY => Statistics::FixedLenByteArray(
284 ValueStatistics::new(
285 min.map(ByteArray::from).map(FixedLenByteArray::from),
286 max.map(ByteArray::from).map(FixedLenByteArray::from),
287 distinct_count,
288 null_count,
289 old_format,
290 )
291 .with_nan_count(nan_count)
297 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
298 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
299 ),
300 };
301
302 Some(res)
303 }
304 None => None,
305 })
306}
307
308pub(crate) fn page_stats_to_thrift(stats: Option<&Statistics>) -> Option<PageStatistics> {
310 let stats = stats?;
311
312 let null_count = stats
314 .null_count_opt()
315 .and_then(|value| i64::try_from(value).ok());
316
317 let distinct_count = stats
319 .distinct_count_opt()
320 .and_then(|value| i64::try_from(value).ok());
321
322 let nan_count = stats
324 .nan_count_opt()
325 .and_then(|value| i64::try_from(value).ok());
326
327 let mut thrift_stats = PageStatistics {
328 max: None,
329 min: None,
330 null_count,
331 distinct_count,
332 max_value: None,
333 min_value: None,
334 is_max_value_exact: None,
335 is_min_value_exact: None,
336 nan_count,
337 };
338
339 let (min, max, min_exact, max_exact) = (
341 stats.min_bytes_opt().map(|x| x.to_vec()),
342 stats.max_bytes_opt().map(|x| x.to_vec()),
343 Some(stats.min_is_exact()),
344 Some(stats.max_is_exact()),
345 );
346 if stats.is_min_max_backwards_compatible() {
347 thrift_stats.min.clone_from(&min);
349 thrift_stats.max.clone_from(&max);
350 }
351
352 if !stats.is_min_max_deprecated() {
353 thrift_stats.min_value = min;
354 thrift_stats.max_value = max;
355 }
356
357 thrift_stats.is_min_value_exact = min_exact;
358 thrift_stats.is_max_value_exact = max_exact;
359
360 Some(thrift_stats)
361}
362
363#[derive(Debug, Clone, PartialEq)]
374pub enum Statistics {
375 Boolean(ValueStatistics<bool>),
377 Int32(ValueStatistics<i32>),
379 Int64(ValueStatistics<i64>),
381 Int96(ValueStatistics<Int96>),
383 Float(ValueStatistics<f32>),
385 Double(ValueStatistics<f64>),
387 ByteArray(ValueStatistics<ByteArray>),
389 FixedLenByteArray(ValueStatistics<FixedLenByteArray>),
391}
392
393impl<T: ParquetValueType> From<ValueStatistics<T>> for Statistics {
394 fn from(t: ValueStatistics<T>) -> Self {
395 T::make_statistics(t)
396 }
397}
398
399impl Statistics {
400 pub fn new<T: ParquetValueType>(
402 min: Option<T>,
403 max: Option<T>,
404 distinct_count: Option<u64>,
405 null_count: Option<u64>,
406 is_deprecated: bool,
407 ) -> Self {
408 Self::from(ValueStatistics::new(
409 min,
410 max,
411 distinct_count,
412 null_count,
413 is_deprecated,
414 ))
415 }
416
417 statistics_new_func![boolean, Option<bool>, Boolean];
418
419 statistics_new_func![int32, Option<i32>, Int32];
420
421 statistics_new_func![int64, Option<i64>, Int64];
422
423 statistics_new_func![int96, Option<Int96>, Int96];
424
425 statistics_new_func![float, Option<f32>, Float];
426
427 statistics_new_func![double, Option<f64>, Double];
428
429 statistics_new_func![byte_array, Option<ByteArray>, ByteArray];
430
431 statistics_new_func![
432 fixed_len_byte_array,
433 Option<FixedLenByteArray>,
434 FixedLenByteArray
435 ];
436
437 pub fn is_min_max_deprecated(&self) -> bool {
444 statistics_enum_func![self, is_min_max_deprecated]
445 }
446
447 pub fn is_min_max_backwards_compatible(&self) -> bool {
458 statistics_enum_func![self, is_min_max_backwards_compatible]
459 }
460
461 pub fn distinct_count_opt(&self) -> Option<u64> {
464 statistics_enum_func![self, distinct_count]
465 }
466
467 pub fn null_count_opt(&self) -> Option<u64> {
485 statistics_enum_func![self, null_count_opt]
486 }
487
488 pub fn nan_count_opt(&self) -> Option<u64> {
490 statistics_enum_func![self, nan_count_opt]
491 }
492
493 pub fn min_is_exact(&self) -> bool {
495 statistics_enum_func![self, min_is_exact]
496 }
497
498 pub fn max_is_exact(&self) -> bool {
500 statistics_enum_func![self, max_is_exact]
501 }
502
503 pub fn min_bytes_opt(&self) -> Option<&[u8]> {
505 statistics_enum_func![self, min_bytes_opt]
506 }
507
508 pub fn max_bytes_opt(&self) -> Option<&[u8]> {
510 statistics_enum_func![self, max_bytes_opt]
511 }
512
513 pub fn physical_type(&self) -> Type {
515 match self {
516 Statistics::Boolean(_) => Type::BOOLEAN,
517 Statistics::Int32(_) => Type::INT32,
518 Statistics::Int64(_) => Type::INT64,
519 Statistics::Int96(_) => Type::INT96,
520 Statistics::Float(_) => Type::FLOAT,
521 Statistics::Double(_) => Type::DOUBLE,
522 Statistics::ByteArray(_) => Type::BYTE_ARRAY,
523 Statistics::FixedLenByteArray(_) => Type::FIXED_LEN_BYTE_ARRAY,
524 }
525 }
526}
527
528impl fmt::Display for Statistics {
529 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
530 match self {
531 Statistics::Boolean(typed) => write!(f, "{typed}"),
532 Statistics::Int32(typed) => write!(f, "{typed}"),
533 Statistics::Int64(typed) => write!(f, "{typed}"),
534 Statistics::Int96(typed) => write!(f, "{typed}"),
535 Statistics::Float(typed) => write!(f, "{typed}"),
536 Statistics::Double(typed) => write!(f, "{typed}"),
537 Statistics::ByteArray(typed) => write!(f, "{typed}"),
538 Statistics::FixedLenByteArray(typed) => write!(f, "{typed}"),
539 }
540 }
541}
542
543pub type TypedStatistics<T> = ValueStatistics<<T as DataType>::T>;
545
546#[derive(Clone, Eq, PartialEq)]
550pub struct ValueStatistics<T> {
551 min: Option<T>,
552 max: Option<T>,
553 distinct_count: Option<u64>,
555 null_count: Option<u64>,
556 nan_count: Option<u64>,
558
559 is_max_value_exact: bool,
561 is_min_value_exact: bool,
562
563 is_min_max_deprecated: bool,
566
567 is_min_max_backwards_compatible: bool,
570}
571
572impl<T> ValueStatistics<T> {
573 pub fn new(
575 min: Option<T>,
576 max: Option<T>,
577 distinct_count: Option<u64>,
578 null_count: Option<u64>,
579 is_min_max_deprecated: bool,
580 ) -> Self {
581 Self {
582 is_max_value_exact: max.is_some(),
583 is_min_value_exact: min.is_some(),
584 min,
585 max,
586 distinct_count,
587 null_count,
588 nan_count: None,
589 is_min_max_deprecated,
590 is_min_max_backwards_compatible: is_min_max_deprecated,
591 }
592 }
593
594 pub fn with_min_is_exact(self, is_min_value_exact: bool) -> Self {
599 Self {
600 is_min_value_exact,
601 ..self
602 }
603 }
604
605 pub fn with_max_is_exact(self, is_max_value_exact: bool) -> Self {
610 Self {
611 is_max_value_exact,
612 ..self
613 }
614 }
615
616 pub fn with_backwards_compatible_min_max(self, backwards_compatible: bool) -> Self {
622 Self {
623 is_min_max_backwards_compatible: backwards_compatible,
624 ..self
625 }
626 }
627
628 pub fn nan_count_opt(&self) -> Option<u64> {
630 self.nan_count
631 }
632
633 pub fn with_nan_count(self, nan_count: Option<u64>) -> Self {
635 Self { nan_count, ..self }
636 }
637
638 pub fn min_opt(&self) -> Option<&T> {
640 self.min.as_ref()
641 }
642
643 pub fn max_opt(&self) -> Option<&T> {
645 self.max.as_ref()
646 }
647
648 pub(crate) fn _internal_has_min_max_set(&self) -> bool {
651 self.min.is_some() && self.max.is_some()
652 }
653
654 pub fn max_is_exact(&self) -> bool {
656 self.max.is_some() && self.is_max_value_exact
657 }
658
659 pub fn min_is_exact(&self) -> bool {
661 self.min.is_some() && self.is_min_value_exact
662 }
663
664 pub fn distinct_count(&self) -> Option<u64> {
666 self.distinct_count
667 }
668
669 pub fn null_count_opt(&self) -> Option<u64> {
671 self.null_count
672 }
673
674 fn is_min_max_deprecated(&self) -> bool {
676 self.is_min_max_deprecated
677 }
678
679 pub fn is_min_max_backwards_compatible(&self) -> bool {
690 self.is_min_max_backwards_compatible
691 }
692}
693
694impl<T: AsBytes> ValueStatistics<T> {
695 pub fn min_bytes_opt(&self) -> Option<&[u8]> {
697 self.min_opt().map(AsBytes::as_bytes)
698 }
699
700 pub fn max_bytes_opt(&self) -> Option<&[u8]> {
702 self.max_opt().map(AsBytes::as_bytes)
703 }
704}
705
706impl<T: ParquetValueType> fmt::Display for ValueStatistics<T> {
707 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
708 write!(f, "{{")?;
709 write!(f, "min: ")?;
710 match self.min {
711 Some(ref value) => write!(f, "{value}")?,
712 None => write!(f, "N/A")?,
713 }
714 write!(f, ", max: ")?;
715 match self.max {
716 Some(ref value) => write!(f, "{value}")?,
717 None => write!(f, "N/A")?,
718 }
719 write!(f, ", distinct_count: ")?;
720 match self.distinct_count {
721 Some(value) => write!(f, "{value}")?,
722 None => write!(f, "N/A")?,
723 }
724 write!(f, ", null_count: ")?;
725 match self.null_count {
726 Some(value) => write!(f, "{value}")?,
727 None => write!(f, "N/A")?,
728 }
729 write!(f, ", min_max_deprecated: {}", self.is_min_max_deprecated)?;
730 write!(f, ", max_value_exact: {}", self.is_max_value_exact)?;
731 write!(f, ", min_value_exact: {}", self.is_min_value_exact)?;
732 write!(f, "}}")
733 }
734}
735
736impl<T: ParquetValueType> fmt::Debug for ValueStatistics<T> {
737 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
738 write!(
739 f,
740 "{{min: {:?}, max: {:?}, distinct_count: {:?}, null_count: {:?}, \
741 min_max_deprecated: {}, min_max_backwards_compatible: {}, max_value_exact: {}, min_value_exact: {}}}",
742 self.min,
743 self.max,
744 self.distinct_count,
745 self.null_count,
746 self.is_min_max_deprecated,
747 self.is_min_max_backwards_compatible,
748 self.is_max_value_exact,
749 self.is_min_value_exact
750 )
751 }
752}
753
754#[cfg(test)]
755mod tests {
756 use core::f32;
757
758 use super::*;
759
760 #[test]
761 fn test_statistics_min_max_bytes() {
762 let stats = Statistics::int32(Some(-123), Some(234), None, Some(1), false);
763 assert_eq!(stats.min_bytes_opt(), Some((-123).as_bytes()));
764 assert_eq!(stats.max_bytes_opt(), Some(234.as_bytes()));
765
766 let stats = Statistics::byte_array(
767 Some(ByteArray::from(vec![1, 2, 3])),
768 Some(ByteArray::from(vec![3, 4, 5])),
769 None,
770 Some(1),
771 true,
772 );
773 assert_eq!(stats.min_bytes_opt().unwrap(), &[1, 2, 3]);
774 assert_eq!(stats.max_bytes_opt().unwrap(), &[3, 4, 5]);
775 }
776
777 #[test]
778 #[should_panic(expected = "General(\"Statistics null count is negative -10\")")]
779 fn test_statistics_negative_null_count() {
780 let thrift_stats = PageStatistics {
781 max: None,
782 min: None,
783 null_count: Some(-10),
784 distinct_count: None,
785 max_value: None,
786 min_value: None,
787 is_max_value_exact: None,
788 is_min_value_exact: None,
789 nan_count: None,
790 };
791
792 from_thrift_page_stats(Type::INT32, Some(thrift_stats)).unwrap();
793 }
794
795 #[test]
796 fn test_statistics_thrift_none() {
797 assert_eq!(from_thrift_page_stats(Type::INT32, None).unwrap(), None);
798 assert_eq!(
799 from_thrift_page_stats(Type::BYTE_ARRAY, None).unwrap(),
800 None
801 );
802 }
803
804 #[test]
805 fn test_statistics_debug() {
806 let stats = Statistics::int32(Some(1), Some(12), None, Some(12), true);
807 assert_eq!(
808 format!("{stats:?}"),
809 "Int32({min: Some(1), max: Some(12), distinct_count: None, null_count: Some(12), \
810 min_max_deprecated: true, min_max_backwards_compatible: true, max_value_exact: true, min_value_exact: true})"
811 );
812
813 let stats = Statistics::int32(None, None, None, Some(7), false);
814 assert_eq!(
815 format!("{stats:?}"),
816 "Int32({min: None, max: None, distinct_count: None, null_count: Some(7), \
817 min_max_deprecated: false, min_max_backwards_compatible: false, max_value_exact: false, min_value_exact: false})"
818 )
819 }
820
821 #[test]
822 fn test_statistics_display() {
823 let stats = Statistics::int32(Some(1), Some(12), None, Some(12), true);
824 assert_eq!(
825 format!("{stats}"),
826 "{min: 1, max: 12, distinct_count: N/A, null_count: 12, min_max_deprecated: true, max_value_exact: true, min_value_exact: true}"
827 );
828
829 let stats = Statistics::int64(None, None, None, Some(7), false);
830 assert_eq!(
831 format!("{stats}"),
832 "{min: N/A, max: N/A, distinct_count: N/A, null_count: 7, min_max_deprecated: \
833 false, max_value_exact: false, min_value_exact: false}"
834 );
835
836 let stats = Statistics::int96(
837 Some(Int96::from(vec![1, 0, 0])),
838 Some(Int96::from(vec![2, 3, 4])),
839 None,
840 Some(3),
841 true,
842 );
843 assert_eq!(
844 format!("{stats}"),
845 "{min: [1, 0, 0], max: [2, 3, 4], distinct_count: N/A, null_count: 3, \
846 min_max_deprecated: true, max_value_exact: true, min_value_exact: true}"
847 );
848
849 let stats = Statistics::ByteArray(
850 ValueStatistics::new(
851 Some(ByteArray::from(vec![1u8])),
852 Some(ByteArray::from(vec![2u8])),
853 Some(5),
854 Some(7),
855 false,
856 )
857 .with_max_is_exact(false)
858 .with_min_is_exact(false),
859 );
860 assert_eq!(
861 format!("{stats}"),
862 "{min: [1], max: [2], distinct_count: 5, null_count: 7, min_max_deprecated: false, max_value_exact: false, min_value_exact: false}"
863 );
864 }
865
866 #[test]
867 fn test_statistics_partial_eq() {
868 let expected = Statistics::int32(Some(12), Some(45), None, Some(11), true);
869
870 assert_eq!(
871 Statistics::int32(Some(12), Some(45), None, Some(11), true),
872 expected
873 );
874 assert_ne!(
875 Statistics::int32(Some(11), Some(45), None, Some(11), true),
876 expected
877 );
878 assert_ne!(
879 Statistics::int32(Some(12), Some(44), None, Some(11), true),
880 expected
881 );
882 assert_ne!(
883 Statistics::int32(Some(12), Some(45), None, Some(23), true),
884 expected
885 );
886 assert_ne!(
887 Statistics::int32(Some(12), Some(45), None, Some(11), false),
888 expected
889 );
890
891 assert_ne!(
892 Statistics::int32(Some(12), Some(45), None, Some(11), false),
893 Statistics::int64(Some(12), Some(45), None, Some(11), false)
894 );
895
896 assert_ne!(
897 Statistics::boolean(Some(false), Some(true), None, None, true),
898 Statistics::double(Some(1.2), Some(4.5), None, None, true)
899 );
900
901 assert_ne!(
902 Statistics::byte_array(
903 Some(ByteArray::from(vec![1, 2, 3])),
904 Some(ByteArray::from(vec![1, 2, 3])),
905 None,
906 None,
907 true
908 ),
909 Statistics::fixed_len_byte_array(
910 Some(ByteArray::from(vec![1, 2, 3]).into()),
911 Some(ByteArray::from(vec![1, 2, 3]).into()),
912 None,
913 None,
914 true,
915 )
916 );
917
918 assert_ne!(
919 Statistics::byte_array(
920 Some(ByteArray::from(vec![1, 2, 3])),
921 Some(ByteArray::from(vec![1, 2, 3])),
922 None,
923 None,
924 true,
925 ),
926 Statistics::ByteArray(
927 ValueStatistics::new(
928 Some(ByteArray::from(vec![1, 2, 3])),
929 Some(ByteArray::from(vec![1, 2, 3])),
930 None,
931 None,
932 true,
933 )
934 .with_max_is_exact(false)
935 )
936 );
937
938 assert_ne!(
939 Statistics::fixed_len_byte_array(
940 Some(FixedLenByteArray::from(vec![1, 2, 3])),
941 Some(FixedLenByteArray::from(vec![1, 2, 3])),
942 None,
943 None,
944 true,
945 ),
946 Statistics::FixedLenByteArray(
947 ValueStatistics::new(
948 Some(FixedLenByteArray::from(vec![1, 2, 3])),
949 Some(FixedLenByteArray::from(vec![1, 2, 3])),
950 None,
951 None,
952 true,
953 )
954 .with_min_is_exact(false)
955 )
956 );
957 }
958
959 #[test]
960 fn test_statistics_from_thrift() {
961 fn check_stats(stats: Statistics) {
963 let type_ = stats.physical_type();
964 let thrift_stats = page_stats_to_thrift(Some(&stats));
965 assert_eq!(
966 from_thrift_page_stats(type_, thrift_stats).unwrap(),
967 Some(stats)
968 );
969 }
970
971 check_stats(Statistics::boolean(
972 Some(false),
973 Some(true),
974 None,
975 Some(7),
976 true,
977 ));
978 check_stats(Statistics::boolean(
979 Some(false),
980 Some(true),
981 None,
982 Some(7),
983 true,
984 ));
985 check_stats(Statistics::boolean(
986 Some(false),
987 Some(true),
988 None,
989 Some(0),
990 false,
991 ));
992 check_stats(Statistics::boolean(
993 Some(true),
994 Some(true),
995 None,
996 Some(7),
997 true,
998 ));
999 check_stats(Statistics::boolean(
1000 Some(false),
1001 Some(false),
1002 None,
1003 Some(7),
1004 true,
1005 ));
1006 check_stats(Statistics::boolean(None, None, None, Some(7), true));
1007
1008 check_stats(Statistics::int32(
1009 Some(-100),
1010 Some(500),
1011 None,
1012 Some(7),
1013 true,
1014 ));
1015 check_stats(Statistics::int32(
1016 Some(-100),
1017 Some(500),
1018 None,
1019 Some(0),
1020 false,
1021 ));
1022 check_stats(Statistics::int32(None, None, None, Some(7), true));
1023
1024 check_stats(Statistics::int64(
1025 Some(-100),
1026 Some(200),
1027 None,
1028 Some(7),
1029 true,
1030 ));
1031 check_stats(Statistics::int64(
1032 Some(-100),
1033 Some(200),
1034 None,
1035 Some(0),
1036 false,
1037 ));
1038 check_stats(Statistics::int64(None, None, None, Some(7), true));
1039
1040 check_stats(Statistics::float(Some(1.2), Some(3.4), None, Some(7), true));
1041 check_stats(Statistics::float(
1042 Some(1.2),
1043 Some(3.4),
1044 None,
1045 Some(0),
1046 false,
1047 ));
1048 check_stats(Statistics::float(None, None, None, Some(7), true));
1049
1050 check_stats(Statistics::double(
1051 Some(1.2),
1052 Some(3.4),
1053 None,
1054 Some(7),
1055 true,
1056 ));
1057 check_stats(Statistics::double(
1058 Some(1.2),
1059 Some(3.4),
1060 None,
1061 Some(0),
1062 false,
1063 ));
1064 check_stats(Statistics::double(None, None, None, Some(7), true));
1065
1066 check_stats(Statistics::byte_array(
1067 Some(ByteArray::from(vec![1, 2, 3])),
1068 Some(ByteArray::from(vec![3, 4, 5])),
1069 None,
1070 Some(7),
1071 true,
1072 ));
1073 check_stats(Statistics::byte_array(None, None, None, Some(7), true));
1074
1075 check_stats(Statistics::fixed_len_byte_array(
1076 Some(ByteArray::from(vec![1, 2, 3]).into()),
1077 Some(ByteArray::from(vec![3, 4, 5]).into()),
1078 None,
1079 Some(7),
1080 true,
1081 ));
1082 check_stats(Statistics::fixed_len_byte_array(
1083 None,
1084 None,
1085 None,
1086 Some(7),
1087 true,
1088 ));
1089 }
1090
1091 #[test]
1092 fn test_count_encoding() {
1093 statistics_count_test(None, None);
1094 statistics_count_test(Some(0), Some(0));
1095 statistics_count_test(Some(100), Some(2000));
1096 statistics_count_test(Some(1), None);
1097 statistics_count_test(None, Some(1));
1098 }
1099
1100 #[test]
1101 fn test_count_encoding_distinct_too_large() {
1102 let statistics = make_bool_stats(Some(u64::MAX), Some(100));
1104 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1105 assert_eq!(thrift_stats.distinct_count, None); assert_eq!(thrift_stats.null_count, Some(100));
1107 }
1108
1109 #[test]
1110 fn test_count_encoding_null_too_large() {
1111 let statistics = make_bool_stats(Some(100), Some(u64::MAX));
1113 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1114 assert_eq!(thrift_stats.distinct_count, Some(100));
1115 assert_eq!(thrift_stats.null_count, None); }
1117
1118 #[test]
1119 fn test_count_decoding_null_invalid() {
1120 let tstatistics = PageStatistics {
1121 null_count: Some(-42),
1122 max: None,
1123 min: None,
1124 distinct_count: None,
1125 max_value: None,
1126 min_value: None,
1127 is_max_value_exact: None,
1128 is_min_value_exact: None,
1129 nan_count: None,
1130 };
1131 let err = from_thrift_page_stats(Type::BOOLEAN, Some(tstatistics)).unwrap_err();
1132 assert_eq!(
1133 err.to_string(),
1134 "Parquet error: Statistics null count is negative -42"
1135 );
1136 }
1137
1138 fn statistics_count_test(distinct_count: Option<u64>, null_count: Option<u64>) {
1142 let statistics = make_bool_stats(distinct_count, null_count);
1143
1144 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1145 assert_eq!(thrift_stats.null_count.map(|c| c as u64), null_count);
1146 assert_eq!(
1147 thrift_stats.distinct_count.map(|c| c as u64),
1148 distinct_count
1149 );
1150
1151 let round_tripped = from_thrift_page_stats(Type::BOOLEAN, Some(thrift_stats))
1152 .unwrap()
1153 .unwrap();
1154 assert_eq!(round_tripped, statistics);
1155 }
1156
1157 fn make_bool_stats(distinct_count: Option<u64>, null_count: Option<u64>) -> Statistics {
1158 let min = Some(true);
1159 let max = Some(false);
1160 let is_min_max_deprecated = false;
1161
1162 Statistics::Boolean(ValueStatistics::new(
1164 min,
1165 max,
1166 distinct_count,
1167 null_count,
1168 is_min_max_deprecated,
1169 ))
1170 }
1171
1172 #[test]
1173 fn test_int96_invalid_statistics() {
1174 let mut thrift_stats = PageStatistics {
1175 max: None,
1176 min: Some((0..13).collect()),
1177 null_count: Some(0),
1178 distinct_count: None,
1179 max_value: None,
1180 min_value: None,
1181 is_max_value_exact: None,
1182 is_min_value_exact: None,
1183 nan_count: None,
1184 };
1185
1186 let err = from_thrift_page_stats(Type::INT96, Some(thrift_stats.clone())).unwrap_err();
1187 assert_eq!(
1188 err.to_string(),
1189 "Parquet error: Incorrect Int96 min statistics"
1190 );
1191
1192 thrift_stats.min = None;
1193 thrift_stats.max = Some((0..13).collect());
1194 let err = from_thrift_page_stats(Type::INT96, Some(thrift_stats)).unwrap_err();
1195 assert_eq!(
1196 err.to_string(),
1197 "Parquet error: Incorrect Int96 max statistics"
1198 );
1199 }
1200
1201 fn generic_statistics_handler<T: std::fmt::Display>(stats: ValueStatistics<T>) -> String {
1204 match stats.min_opt() {
1205 Some(s) => format!("min: {s}"),
1206 None => "min: NA".to_string(),
1207 }
1208 }
1209
1210 #[test]
1211 fn test_generic_access() {
1212 let stats = Statistics::int32(Some(12), Some(45), None, Some(11), false);
1213
1214 match stats {
1215 Statistics::Int32(v) => {
1216 let stats_string = generic_statistics_handler(v);
1217 assert_eq!(&stats_string, "min: 12");
1218 }
1219 _ => unreachable!(),
1220 }
1221 }
1222
1223 #[test]
1224 fn test_nan_count_float() {
1225 let stats = Statistics::Float(
1227 ValueStatistics::new(Some(1.0_f32), Some(5.0_f32), None, Some(0), false)
1228 .with_nan_count(Some(3)),
1229 );
1230
1231 assert_eq!(stats.nan_count_opt(), Some(3));
1232
1233 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1235 assert_eq!(thrift_stats.nan_count, Some(3));
1236
1237 let round_tripped = from_thrift_page_stats(Type::FLOAT, Some(thrift_stats))
1238 .unwrap()
1239 .unwrap();
1240 assert_eq!(round_tripped.nan_count_opt(), Some(3));
1241 }
1242
1243 #[test]
1244 fn test_nan_count_double() {
1245 let stats = Statistics::Double(
1247 ValueStatistics::new(Some(1.0_f64), Some(5.0_f64), None, Some(0), false)
1248 .with_nan_count(Some(5)),
1249 );
1250
1251 assert_eq!(stats.nan_count_opt(), Some(5));
1252
1253 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1255 assert_eq!(thrift_stats.nan_count, Some(5));
1256
1257 let round_tripped = from_thrift_page_stats(Type::DOUBLE, Some(thrift_stats))
1258 .unwrap()
1259 .unwrap();
1260 assert_eq!(round_tripped.nan_count_opt(), Some(5));
1261 }
1262
1263 #[test]
1264 fn test_nan_count_none_for_non_float() {
1265 let stats = Statistics::int32(Some(1), Some(100), None, Some(0), false);
1267 assert_eq!(stats.nan_count_opt(), None);
1268
1269 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1270 assert_eq!(thrift_stats.nan_count, None);
1271 }
1272
1273 #[test]
1274 fn test_nan_count_backwards_compatible() {
1275 let thrift_stats = PageStatistics {
1277 min: None,
1278 max: None,
1279 min_value: Some(vec![0, 0, 0, 0]), max_value: Some(vec![0, 0, 128, 63]), null_count: Some(0),
1282 distinct_count: None,
1283 nan_count: None, is_min_value_exact: None,
1285 is_max_value_exact: None,
1286 };
1287
1288 let stats = from_thrift_page_stats(Type::FLOAT, Some(thrift_stats))
1289 .unwrap()
1290 .unwrap();
1291
1292 assert_eq!(stats.nan_count_opt(), None);
1294 }
1295
1296 #[test]
1297 fn test_statistics_with_nan_min_max() {
1298 let stats = Statistics::Float(
1300 ValueStatistics::new(
1301 Some(f32::NAN), Some(f32::NAN),
1303 None,
1304 Some(0),
1305 false,
1306 )
1307 .with_nan_count(Some(10)), );
1309
1310 assert_eq!(stats.min_bytes_opt(), Some(f32::NAN.as_bytes()));
1311 assert_eq!(stats.max_bytes_opt(), Some(f32::NAN.as_bytes()));
1312 assert_eq!(stats.nan_count_opt(), Some(10));
1313
1314 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1316 assert_eq!(thrift_stats.min_value, Some(f32::NAN.as_bytes().to_vec()));
1317 assert_eq!(thrift_stats.max_value, Some(f32::NAN.as_bytes().to_vec()));
1318 assert_eq!(thrift_stats.nan_count, Some(10));
1319 }
1320
1321 #[test]
1322 fn test_nan_count_too_large() {
1323 let stats = Statistics::Float(
1325 ValueStatistics::new(Some(1.0_f32), Some(2.0_f32), None, Some(0), false)
1326 .with_nan_count(Some(u64::MAX)),
1327 );
1328
1329 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1330 assert_eq!(thrift_stats.nan_count, None);
1332 }
1333
1334 #[test]
1335 fn test_nan_counts_in_column_index() {
1336 use crate::file::metadata::ColumnIndexBuilder;
1338
1339 let mut float_builder = ColumnIndexBuilder::new(Type::FLOAT);
1341 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, Some(5));
1342 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 2, Some(3));
1343 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, Some(0)); let float_column_index = float_builder.build().unwrap();
1346 assert_eq!(float_column_index.nan_counts(), Some(&vec![5, 3, 0]));
1348
1349 let mut int_builder = ColumnIndexBuilder::new(Type::INT32);
1351 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, None);
1352 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 2, None);
1353 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, None);
1354
1355 let int_column_index = int_builder.build().unwrap();
1356 assert_eq!(int_column_index.nan_counts(), None);
1358 }
1359}