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<Vec<u8>>, max: &Option<Vec<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 match physical_type {
188 Type::BOOLEAN => check_len(&min, &max, 1),
189 Type::INT32 | Type::FLOAT => check_len(&min, &max, 4),
190 Type::INT64 | Type::DOUBLE => check_len(&min, &max, 8),
191 Type::INT96 => check_len(&min, &max, 12),
192 _ => Ok(()),
193 }?;
194
195 let res = match physical_type {
200 Type::BOOLEAN => Statistics::boolean(
201 min.map(|data| data[0] != 0),
202 max.map(|data| data[0] != 0),
203 distinct_count,
204 null_count,
205 old_format,
206 ),
207 Type::INT32 => Statistics::int32(
208 min.map(|data| i32::from_le_bytes(data[..4].try_into().unwrap())),
209 max.map(|data| i32::from_le_bytes(data[..4].try_into().unwrap())),
210 distinct_count,
211 null_count,
212 old_format,
213 ),
214 Type::INT64 => Statistics::int64(
215 min.map(|data| i64::from_le_bytes(data[..8].try_into().unwrap())),
216 max.map(|data| i64::from_le_bytes(data[..8].try_into().unwrap())),
217 distinct_count,
218 null_count,
219 old_format,
220 ),
221 Type::INT96 => {
222 let min = if let Some(data) = min {
224 if data.len() != 12 {
225 return Err(ParquetError::General(
226 "Incorrect Int96 min statistics".to_string(),
227 ));
228 }
229 Some(Int96::try_from_le_slice(&data)?)
230 } else {
231 None
232 };
233 let max = if let Some(data) = max {
234 if data.len() != 12 {
235 return Err(ParquetError::General(
236 "Incorrect Int96 max statistics".to_string(),
237 ));
238 }
239 Some(Int96::try_from_le_slice(&data)?)
240 } else {
241 None
242 };
243 Statistics::int96(min, max, distinct_count, null_count, old_format)
244 }
245 Type::FLOAT => Statistics::Float(
246 ValueStatistics::new(
247 min.map(|data| f32::from_le_bytes(data[..4].try_into().unwrap())),
248 max.map(|data| f32::from_le_bytes(data[..4].try_into().unwrap())),
249 distinct_count,
250 null_count,
251 old_format,
252 )
253 .with_nan_count(nan_count)
254 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
255 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
256 ),
257 Type::DOUBLE => Statistics::Double(
258 ValueStatistics::new(
259 min.map(|data| f64::from_le_bytes(data[..8].try_into().unwrap())),
260 max.map(|data| f64::from_le_bytes(data[..8].try_into().unwrap())),
261 distinct_count,
262 null_count,
263 old_format,
264 )
265 .with_nan_count(nan_count)
266 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
267 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
268 ),
269 Type::BYTE_ARRAY => Statistics::ByteArray(
270 ValueStatistics::new(
271 min.map(ByteArray::from),
272 max.map(ByteArray::from),
273 distinct_count,
274 null_count,
275 old_format,
276 )
277 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
278 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
279 ),
280 Type::FIXED_LEN_BYTE_ARRAY => Statistics::FixedLenByteArray(
281 ValueStatistics::new(
282 min.map(ByteArray::from).map(FixedLenByteArray::from),
283 max.map(ByteArray::from).map(FixedLenByteArray::from),
284 distinct_count,
285 null_count,
286 old_format,
287 )
288 .with_nan_count(nan_count)
294 .with_max_is_exact(stats.is_max_value_exact.unwrap_or(false))
295 .with_min_is_exact(stats.is_min_value_exact.unwrap_or(false)),
296 ),
297 };
298
299 Some(res)
300 }
301 None => None,
302 })
303}
304
305pub(crate) fn page_stats_to_thrift(stats: Option<&Statistics>) -> Option<PageStatistics> {
307 let stats = stats?;
308
309 let null_count = stats
311 .null_count_opt()
312 .and_then(|value| i64::try_from(value).ok());
313
314 let distinct_count = stats
316 .distinct_count_opt()
317 .and_then(|value| i64::try_from(value).ok());
318
319 let nan_count = stats
321 .nan_count_opt()
322 .and_then(|value| i64::try_from(value).ok());
323
324 let mut thrift_stats = PageStatistics {
325 max: None,
326 min: None,
327 null_count,
328 distinct_count,
329 max_value: None,
330 min_value: None,
331 is_max_value_exact: None,
332 is_min_value_exact: None,
333 nan_count,
334 };
335
336 let (min, max, min_exact, max_exact) = (
338 stats.min_bytes_opt().map(|x| x.to_vec()),
339 stats.max_bytes_opt().map(|x| x.to_vec()),
340 Some(stats.min_is_exact()),
341 Some(stats.max_is_exact()),
342 );
343 if stats.is_min_max_backwards_compatible() {
344 thrift_stats.min.clone_from(&min);
346 thrift_stats.max.clone_from(&max);
347 }
348
349 if !stats.is_min_max_deprecated() {
350 thrift_stats.min_value = min;
351 thrift_stats.max_value = max;
352 }
353
354 thrift_stats.is_min_value_exact = min_exact;
355 thrift_stats.is_max_value_exact = max_exact;
356
357 Some(thrift_stats)
358}
359
360#[derive(Debug, Clone, PartialEq)]
371pub enum Statistics {
372 Boolean(ValueStatistics<bool>),
374 Int32(ValueStatistics<i32>),
376 Int64(ValueStatistics<i64>),
378 Int96(ValueStatistics<Int96>),
380 Float(ValueStatistics<f32>),
382 Double(ValueStatistics<f64>),
384 ByteArray(ValueStatistics<ByteArray>),
386 FixedLenByteArray(ValueStatistics<FixedLenByteArray>),
388}
389
390impl<T: ParquetValueType> From<ValueStatistics<T>> for Statistics {
391 fn from(t: ValueStatistics<T>) -> Self {
392 T::make_statistics(t)
393 }
394}
395
396impl Statistics {
397 pub fn new<T: ParquetValueType>(
399 min: Option<T>,
400 max: Option<T>,
401 distinct_count: Option<u64>,
402 null_count: Option<u64>,
403 is_deprecated: bool,
404 ) -> Self {
405 Self::from(ValueStatistics::new(
406 min,
407 max,
408 distinct_count,
409 null_count,
410 is_deprecated,
411 ))
412 }
413
414 statistics_new_func![boolean, Option<bool>, Boolean];
415
416 statistics_new_func![int32, Option<i32>, Int32];
417
418 statistics_new_func![int64, Option<i64>, Int64];
419
420 statistics_new_func![int96, Option<Int96>, Int96];
421
422 statistics_new_func![float, Option<f32>, Float];
423
424 statistics_new_func![double, Option<f64>, Double];
425
426 statistics_new_func![byte_array, Option<ByteArray>, ByteArray];
427
428 statistics_new_func![
429 fixed_len_byte_array,
430 Option<FixedLenByteArray>,
431 FixedLenByteArray
432 ];
433
434 pub fn is_min_max_deprecated(&self) -> bool {
441 statistics_enum_func![self, is_min_max_deprecated]
442 }
443
444 pub fn is_min_max_backwards_compatible(&self) -> bool {
455 statistics_enum_func![self, is_min_max_backwards_compatible]
456 }
457
458 pub fn distinct_count_opt(&self) -> Option<u64> {
461 statistics_enum_func![self, distinct_count]
462 }
463
464 pub fn null_count_opt(&self) -> Option<u64> {
482 statistics_enum_func![self, null_count_opt]
483 }
484
485 pub fn nan_count_opt(&self) -> Option<u64> {
487 statistics_enum_func![self, nan_count_opt]
488 }
489
490 pub fn min_is_exact(&self) -> bool {
492 statistics_enum_func![self, min_is_exact]
493 }
494
495 pub fn max_is_exact(&self) -> bool {
497 statistics_enum_func![self, max_is_exact]
498 }
499
500 pub fn min_bytes_opt(&self) -> Option<&[u8]> {
502 statistics_enum_func![self, min_bytes_opt]
503 }
504
505 pub fn max_bytes_opt(&self) -> Option<&[u8]> {
507 statistics_enum_func![self, max_bytes_opt]
508 }
509
510 pub fn physical_type(&self) -> Type {
512 match self {
513 Statistics::Boolean(_) => Type::BOOLEAN,
514 Statistics::Int32(_) => Type::INT32,
515 Statistics::Int64(_) => Type::INT64,
516 Statistics::Int96(_) => Type::INT96,
517 Statistics::Float(_) => Type::FLOAT,
518 Statistics::Double(_) => Type::DOUBLE,
519 Statistics::ByteArray(_) => Type::BYTE_ARRAY,
520 Statistics::FixedLenByteArray(_) => Type::FIXED_LEN_BYTE_ARRAY,
521 }
522 }
523}
524
525impl fmt::Display for Statistics {
526 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
527 match self {
528 Statistics::Boolean(typed) => write!(f, "{typed}"),
529 Statistics::Int32(typed) => write!(f, "{typed}"),
530 Statistics::Int64(typed) => write!(f, "{typed}"),
531 Statistics::Int96(typed) => write!(f, "{typed}"),
532 Statistics::Float(typed) => write!(f, "{typed}"),
533 Statistics::Double(typed) => write!(f, "{typed}"),
534 Statistics::ByteArray(typed) => write!(f, "{typed}"),
535 Statistics::FixedLenByteArray(typed) => write!(f, "{typed}"),
536 }
537 }
538}
539
540pub type TypedStatistics<T> = ValueStatistics<<T as DataType>::T>;
542
543#[derive(Clone, Eq, PartialEq)]
547pub struct ValueStatistics<T> {
548 min: Option<T>,
549 max: Option<T>,
550 distinct_count: Option<u64>,
552 null_count: Option<u64>,
553 nan_count: Option<u64>,
555
556 is_max_value_exact: bool,
558 is_min_value_exact: bool,
559
560 is_min_max_deprecated: bool,
563
564 is_min_max_backwards_compatible: bool,
567}
568
569impl<T> ValueStatistics<T> {
570 pub fn new(
572 min: Option<T>,
573 max: Option<T>,
574 distinct_count: Option<u64>,
575 null_count: Option<u64>,
576 is_min_max_deprecated: bool,
577 ) -> Self {
578 Self {
579 is_max_value_exact: max.is_some(),
580 is_min_value_exact: min.is_some(),
581 min,
582 max,
583 distinct_count,
584 null_count,
585 nan_count: None,
586 is_min_max_deprecated,
587 is_min_max_backwards_compatible: is_min_max_deprecated,
588 }
589 }
590
591 pub fn with_min_is_exact(self, is_min_value_exact: bool) -> Self {
596 Self {
597 is_min_value_exact,
598 ..self
599 }
600 }
601
602 pub fn with_max_is_exact(self, is_max_value_exact: bool) -> Self {
607 Self {
608 is_max_value_exact,
609 ..self
610 }
611 }
612
613 pub fn with_backwards_compatible_min_max(self, backwards_compatible: bool) -> Self {
619 Self {
620 is_min_max_backwards_compatible: backwards_compatible,
621 ..self
622 }
623 }
624
625 pub fn nan_count_opt(&self) -> Option<u64> {
627 self.nan_count
628 }
629
630 pub fn with_nan_count(self, nan_count: Option<u64>) -> Self {
632 Self { nan_count, ..self }
633 }
634
635 pub fn min_opt(&self) -> Option<&T> {
637 self.min.as_ref()
638 }
639
640 pub fn max_opt(&self) -> Option<&T> {
642 self.max.as_ref()
643 }
644
645 pub(crate) fn _internal_has_min_max_set(&self) -> bool {
648 self.min.is_some() && self.max.is_some()
649 }
650
651 pub fn max_is_exact(&self) -> bool {
653 self.max.is_some() && self.is_max_value_exact
654 }
655
656 pub fn min_is_exact(&self) -> bool {
658 self.min.is_some() && self.is_min_value_exact
659 }
660
661 pub fn distinct_count(&self) -> Option<u64> {
663 self.distinct_count
664 }
665
666 pub fn null_count_opt(&self) -> Option<u64> {
668 self.null_count
669 }
670
671 fn is_min_max_deprecated(&self) -> bool {
673 self.is_min_max_deprecated
674 }
675
676 pub fn is_min_max_backwards_compatible(&self) -> bool {
687 self.is_min_max_backwards_compatible
688 }
689}
690
691impl<T: AsBytes> ValueStatistics<T> {
692 pub fn min_bytes_opt(&self) -> Option<&[u8]> {
694 self.min_opt().map(AsBytes::as_bytes)
695 }
696
697 pub fn max_bytes_opt(&self) -> Option<&[u8]> {
699 self.max_opt().map(AsBytes::as_bytes)
700 }
701}
702
703impl<T: ParquetValueType> fmt::Display for ValueStatistics<T> {
704 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
705 write!(f, "{{")?;
706 write!(f, "min: ")?;
707 match self.min {
708 Some(ref value) => write!(f, "{value}")?,
709 None => write!(f, "N/A")?,
710 }
711 write!(f, ", max: ")?;
712 match self.max {
713 Some(ref value) => write!(f, "{value}")?,
714 None => write!(f, "N/A")?,
715 }
716 write!(f, ", distinct_count: ")?;
717 match self.distinct_count {
718 Some(value) => write!(f, "{value}")?,
719 None => write!(f, "N/A")?,
720 }
721 write!(f, ", null_count: ")?;
722 match self.null_count {
723 Some(value) => write!(f, "{value}")?,
724 None => write!(f, "N/A")?,
725 }
726 write!(f, ", min_max_deprecated: {}", self.is_min_max_deprecated)?;
727 write!(f, ", max_value_exact: {}", self.is_max_value_exact)?;
728 write!(f, ", min_value_exact: {}", self.is_min_value_exact)?;
729 write!(f, "}}")
730 }
731}
732
733impl<T: ParquetValueType> fmt::Debug for ValueStatistics<T> {
734 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
735 write!(
736 f,
737 "{{min: {:?}, max: {:?}, distinct_count: {:?}, null_count: {:?}, \
738 min_max_deprecated: {}, min_max_backwards_compatible: {}, max_value_exact: {}, min_value_exact: {}}}",
739 self.min,
740 self.max,
741 self.distinct_count,
742 self.null_count,
743 self.is_min_max_deprecated,
744 self.is_min_max_backwards_compatible,
745 self.is_max_value_exact,
746 self.is_min_value_exact
747 )
748 }
749}
750
751#[cfg(test)]
752mod tests {
753 use core::f32;
754
755 use super::*;
756
757 #[test]
758 fn test_statistics_min_max_bytes() {
759 let stats = Statistics::int32(Some(-123), Some(234), None, Some(1), false);
760 assert_eq!(stats.min_bytes_opt(), Some((-123).as_bytes()));
761 assert_eq!(stats.max_bytes_opt(), Some(234.as_bytes()));
762
763 let stats = Statistics::byte_array(
764 Some(ByteArray::from(vec![1, 2, 3])),
765 Some(ByteArray::from(vec![3, 4, 5])),
766 None,
767 Some(1),
768 true,
769 );
770 assert_eq!(stats.min_bytes_opt().unwrap(), &[1, 2, 3]);
771 assert_eq!(stats.max_bytes_opt().unwrap(), &[3, 4, 5]);
772 }
773
774 #[test]
775 #[should_panic(expected = "General(\"Statistics null count is negative -10\")")]
776 fn test_statistics_negative_null_count() {
777 let thrift_stats = PageStatistics {
778 max: None,
779 min: None,
780 null_count: Some(-10),
781 distinct_count: None,
782 max_value: None,
783 min_value: None,
784 is_max_value_exact: None,
785 is_min_value_exact: None,
786 nan_count: None,
787 };
788
789 from_thrift_page_stats(Type::INT32, Some(thrift_stats)).unwrap();
790 }
791
792 #[test]
793 fn test_statistics_thrift_none() {
794 assert_eq!(from_thrift_page_stats(Type::INT32, None).unwrap(), None);
795 assert_eq!(
796 from_thrift_page_stats(Type::BYTE_ARRAY, None).unwrap(),
797 None
798 );
799 }
800
801 #[test]
802 fn test_statistics_debug() {
803 let stats = Statistics::int32(Some(1), Some(12), None, Some(12), true);
804 assert_eq!(
805 format!("{stats:?}"),
806 "Int32({min: Some(1), max: Some(12), distinct_count: None, null_count: Some(12), \
807 min_max_deprecated: true, min_max_backwards_compatible: true, max_value_exact: true, min_value_exact: true})"
808 );
809
810 let stats = Statistics::int32(None, None, None, Some(7), false);
811 assert_eq!(
812 format!("{stats:?}"),
813 "Int32({min: None, max: None, distinct_count: None, null_count: Some(7), \
814 min_max_deprecated: false, min_max_backwards_compatible: false, max_value_exact: false, min_value_exact: false})"
815 )
816 }
817
818 #[test]
819 fn test_statistics_display() {
820 let stats = Statistics::int32(Some(1), Some(12), None, Some(12), true);
821 assert_eq!(
822 format!("{stats}"),
823 "{min: 1, max: 12, distinct_count: N/A, null_count: 12, min_max_deprecated: true, max_value_exact: true, min_value_exact: true}"
824 );
825
826 let stats = Statistics::int64(None, None, None, Some(7), false);
827 assert_eq!(
828 format!("{stats}"),
829 "{min: N/A, max: N/A, distinct_count: N/A, null_count: 7, min_max_deprecated: \
830 false, max_value_exact: false, min_value_exact: false}"
831 );
832
833 let stats = Statistics::int96(
834 Some(Int96::from(vec![1, 0, 0])),
835 Some(Int96::from(vec![2, 3, 4])),
836 None,
837 Some(3),
838 true,
839 );
840 assert_eq!(
841 format!("{stats}"),
842 "{min: [1, 0, 0], max: [2, 3, 4], distinct_count: N/A, null_count: 3, \
843 min_max_deprecated: true, max_value_exact: true, min_value_exact: true}"
844 );
845
846 let stats = Statistics::ByteArray(
847 ValueStatistics::new(
848 Some(ByteArray::from(vec![1u8])),
849 Some(ByteArray::from(vec![2u8])),
850 Some(5),
851 Some(7),
852 false,
853 )
854 .with_max_is_exact(false)
855 .with_min_is_exact(false),
856 );
857 assert_eq!(
858 format!("{stats}"),
859 "{min: [1], max: [2], distinct_count: 5, null_count: 7, min_max_deprecated: false, max_value_exact: false, min_value_exact: false}"
860 );
861 }
862
863 #[test]
864 fn test_statistics_partial_eq() {
865 let expected = Statistics::int32(Some(12), Some(45), None, Some(11), true);
866
867 assert!(Statistics::int32(Some(12), Some(45), None, Some(11), true) == expected);
868 assert!(Statistics::int32(Some(11), Some(45), None, Some(11), true) != expected);
869 assert!(Statistics::int32(Some(12), Some(44), None, Some(11), true) != expected);
870 assert!(Statistics::int32(Some(12), Some(45), None, Some(23), true) != expected);
871 assert!(Statistics::int32(Some(12), Some(45), None, Some(11), false) != expected);
872
873 assert!(
874 Statistics::int32(Some(12), Some(45), None, Some(11), false)
875 != Statistics::int64(Some(12), Some(45), None, Some(11), false)
876 );
877
878 assert!(
879 Statistics::boolean(Some(false), Some(true), None, None, true)
880 != Statistics::double(Some(1.2), Some(4.5), None, None, true)
881 );
882
883 assert!(
884 Statistics::byte_array(
885 Some(ByteArray::from(vec![1, 2, 3])),
886 Some(ByteArray::from(vec![1, 2, 3])),
887 None,
888 None,
889 true
890 ) != Statistics::fixed_len_byte_array(
891 Some(ByteArray::from(vec![1, 2, 3]).into()),
892 Some(ByteArray::from(vec![1, 2, 3]).into()),
893 None,
894 None,
895 true,
896 )
897 );
898
899 assert!(
900 Statistics::byte_array(
901 Some(ByteArray::from(vec![1, 2, 3])),
902 Some(ByteArray::from(vec![1, 2, 3])),
903 None,
904 None,
905 true,
906 ) != Statistics::ByteArray(
907 ValueStatistics::new(
908 Some(ByteArray::from(vec![1, 2, 3])),
909 Some(ByteArray::from(vec![1, 2, 3])),
910 None,
911 None,
912 true,
913 )
914 .with_max_is_exact(false)
915 )
916 );
917
918 assert!(
919 Statistics::fixed_len_byte_array(
920 Some(FixedLenByteArray::from(vec![1, 2, 3])),
921 Some(FixedLenByteArray::from(vec![1, 2, 3])),
922 None,
923 None,
924 true,
925 ) != Statistics::FixedLenByteArray(
926 ValueStatistics::new(
927 Some(FixedLenByteArray::from(vec![1, 2, 3])),
928 Some(FixedLenByteArray::from(vec![1, 2, 3])),
929 None,
930 None,
931 true,
932 )
933 .with_min_is_exact(false)
934 )
935 );
936 }
937
938 #[test]
939 fn test_statistics_from_thrift() {
940 fn check_stats(stats: Statistics) {
942 let tpe = stats.physical_type();
943 let thrift_stats = page_stats_to_thrift(Some(&stats));
944 assert_eq!(
945 from_thrift_page_stats(tpe, thrift_stats).unwrap(),
946 Some(stats)
947 );
948 }
949
950 check_stats(Statistics::boolean(
951 Some(false),
952 Some(true),
953 None,
954 Some(7),
955 true,
956 ));
957 check_stats(Statistics::boolean(
958 Some(false),
959 Some(true),
960 None,
961 Some(7),
962 true,
963 ));
964 check_stats(Statistics::boolean(
965 Some(false),
966 Some(true),
967 None,
968 Some(0),
969 false,
970 ));
971 check_stats(Statistics::boolean(
972 Some(true),
973 Some(true),
974 None,
975 Some(7),
976 true,
977 ));
978 check_stats(Statistics::boolean(
979 Some(false),
980 Some(false),
981 None,
982 Some(7),
983 true,
984 ));
985 check_stats(Statistics::boolean(None, None, None, Some(7), true));
986
987 check_stats(Statistics::int32(
988 Some(-100),
989 Some(500),
990 None,
991 Some(7),
992 true,
993 ));
994 check_stats(Statistics::int32(
995 Some(-100),
996 Some(500),
997 None,
998 Some(0),
999 false,
1000 ));
1001 check_stats(Statistics::int32(None, None, None, Some(7), true));
1002
1003 check_stats(Statistics::int64(
1004 Some(-100),
1005 Some(200),
1006 None,
1007 Some(7),
1008 true,
1009 ));
1010 check_stats(Statistics::int64(
1011 Some(-100),
1012 Some(200),
1013 None,
1014 Some(0),
1015 false,
1016 ));
1017 check_stats(Statistics::int64(None, None, None, Some(7), true));
1018
1019 check_stats(Statistics::float(Some(1.2), Some(3.4), None, Some(7), true));
1020 check_stats(Statistics::float(
1021 Some(1.2),
1022 Some(3.4),
1023 None,
1024 Some(0),
1025 false,
1026 ));
1027 check_stats(Statistics::float(None, None, None, Some(7), true));
1028
1029 check_stats(Statistics::double(
1030 Some(1.2),
1031 Some(3.4),
1032 None,
1033 Some(7),
1034 true,
1035 ));
1036 check_stats(Statistics::double(
1037 Some(1.2),
1038 Some(3.4),
1039 None,
1040 Some(0),
1041 false,
1042 ));
1043 check_stats(Statistics::double(None, None, None, Some(7), true));
1044
1045 check_stats(Statistics::byte_array(
1046 Some(ByteArray::from(vec![1, 2, 3])),
1047 Some(ByteArray::from(vec![3, 4, 5])),
1048 None,
1049 Some(7),
1050 true,
1051 ));
1052 check_stats(Statistics::byte_array(None, None, None, Some(7), true));
1053
1054 check_stats(Statistics::fixed_len_byte_array(
1055 Some(ByteArray::from(vec![1, 2, 3]).into()),
1056 Some(ByteArray::from(vec![3, 4, 5]).into()),
1057 None,
1058 Some(7),
1059 true,
1060 ));
1061 check_stats(Statistics::fixed_len_byte_array(
1062 None,
1063 None,
1064 None,
1065 Some(7),
1066 true,
1067 ));
1068 }
1069
1070 #[test]
1071 fn test_count_encoding() {
1072 statistics_count_test(None, None);
1073 statistics_count_test(Some(0), Some(0));
1074 statistics_count_test(Some(100), Some(2000));
1075 statistics_count_test(Some(1), None);
1076 statistics_count_test(None, Some(1));
1077 }
1078
1079 #[test]
1080 fn test_count_encoding_distinct_too_large() {
1081 let statistics = make_bool_stats(Some(u64::MAX), Some(100));
1083 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1084 assert_eq!(thrift_stats.distinct_count, None); assert_eq!(thrift_stats.null_count, Some(100));
1086 }
1087
1088 #[test]
1089 fn test_count_encoding_null_too_large() {
1090 let statistics = make_bool_stats(Some(100), Some(u64::MAX));
1092 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1093 assert_eq!(thrift_stats.distinct_count, Some(100));
1094 assert_eq!(thrift_stats.null_count, None); }
1096
1097 #[test]
1098 fn test_count_decoding_null_invalid() {
1099 let tstatistics = PageStatistics {
1100 null_count: Some(-42),
1101 max: None,
1102 min: None,
1103 distinct_count: None,
1104 max_value: None,
1105 min_value: None,
1106 is_max_value_exact: None,
1107 is_min_value_exact: None,
1108 nan_count: None,
1109 };
1110 let err = from_thrift_page_stats(Type::BOOLEAN, Some(tstatistics)).unwrap_err();
1111 assert_eq!(
1112 err.to_string(),
1113 "Parquet error: Statistics null count is negative -42"
1114 );
1115 }
1116
1117 fn statistics_count_test(distinct_count: Option<u64>, null_count: Option<u64>) {
1121 let statistics = make_bool_stats(distinct_count, null_count);
1122
1123 let thrift_stats = page_stats_to_thrift(Some(&statistics)).unwrap();
1124 assert_eq!(thrift_stats.null_count.map(|c| c as u64), null_count);
1125 assert_eq!(
1126 thrift_stats.distinct_count.map(|c| c as u64),
1127 distinct_count
1128 );
1129
1130 let round_tripped = from_thrift_page_stats(Type::BOOLEAN, Some(thrift_stats))
1131 .unwrap()
1132 .unwrap();
1133 assert_eq!(round_tripped, statistics);
1134 }
1135
1136 fn make_bool_stats(distinct_count: Option<u64>, null_count: Option<u64>) -> Statistics {
1137 let min = Some(true);
1138 let max = Some(false);
1139 let is_min_max_deprecated = false;
1140
1141 Statistics::Boolean(ValueStatistics::new(
1143 min,
1144 max,
1145 distinct_count,
1146 null_count,
1147 is_min_max_deprecated,
1148 ))
1149 }
1150
1151 #[test]
1152 fn test_int96_invalid_statistics() {
1153 let mut thrift_stats = PageStatistics {
1154 max: None,
1155 min: Some((0..13).collect()),
1156 null_count: Some(0),
1157 distinct_count: None,
1158 max_value: None,
1159 min_value: None,
1160 is_max_value_exact: None,
1161 is_min_value_exact: None,
1162 nan_count: None,
1163 };
1164
1165 let err = from_thrift_page_stats(Type::INT96, Some(thrift_stats.clone())).unwrap_err();
1166 assert_eq!(
1167 err.to_string(),
1168 "Parquet error: Incorrect Int96 min statistics"
1169 );
1170
1171 thrift_stats.min = None;
1172 thrift_stats.max = Some((0..13).collect());
1173 let err = from_thrift_page_stats(Type::INT96, Some(thrift_stats)).unwrap_err();
1174 assert_eq!(
1175 err.to_string(),
1176 "Parquet error: Incorrect Int96 max statistics"
1177 );
1178 }
1179
1180 fn generic_statistics_handler<T: std::fmt::Display>(stats: ValueStatistics<T>) -> String {
1183 match stats.min_opt() {
1184 Some(s) => format!("min: {}", s),
1185 None => "min: NA".to_string(),
1186 }
1187 }
1188
1189 #[test]
1190 fn test_generic_access() {
1191 let stats = Statistics::int32(Some(12), Some(45), None, Some(11), false);
1192
1193 match stats {
1194 Statistics::Int32(v) => {
1195 let stats_string = generic_statistics_handler(v);
1196 assert_eq!(&stats_string, "min: 12");
1197 }
1198 _ => unreachable!(),
1199 }
1200 }
1201
1202 #[test]
1203 fn test_nan_count_float() {
1204 let stats = Statistics::Float(
1206 ValueStatistics::new(Some(1.0_f32), Some(5.0_f32), None, Some(0), false)
1207 .with_nan_count(Some(3)),
1208 );
1209
1210 assert_eq!(stats.nan_count_opt(), Some(3));
1211
1212 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1214 assert_eq!(thrift_stats.nan_count, Some(3));
1215
1216 let round_tripped = from_thrift_page_stats(Type::FLOAT, Some(thrift_stats))
1217 .unwrap()
1218 .unwrap();
1219 assert_eq!(round_tripped.nan_count_opt(), Some(3));
1220 }
1221
1222 #[test]
1223 fn test_nan_count_double() {
1224 let stats = Statistics::Double(
1226 ValueStatistics::new(Some(1.0_f64), Some(5.0_f64), None, Some(0), false)
1227 .with_nan_count(Some(5)),
1228 );
1229
1230 assert_eq!(stats.nan_count_opt(), Some(5));
1231
1232 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1234 assert_eq!(thrift_stats.nan_count, Some(5));
1235
1236 let round_tripped = from_thrift_page_stats(Type::DOUBLE, Some(thrift_stats))
1237 .unwrap()
1238 .unwrap();
1239 assert_eq!(round_tripped.nan_count_opt(), Some(5));
1240 }
1241
1242 #[test]
1243 fn test_nan_count_none_for_non_float() {
1244 let stats = Statistics::int32(Some(1), Some(100), None, Some(0), false);
1246 assert_eq!(stats.nan_count_opt(), None);
1247
1248 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1249 assert_eq!(thrift_stats.nan_count, None);
1250 }
1251
1252 #[test]
1253 fn test_nan_count_backwards_compatible() {
1254 let thrift_stats = PageStatistics {
1256 min: None,
1257 max: None,
1258 min_value: Some(vec![0, 0, 0, 0]), max_value: Some(vec![0, 0, 128, 63]), null_count: Some(0),
1261 distinct_count: None,
1262 nan_count: None, is_min_value_exact: None,
1264 is_max_value_exact: None,
1265 };
1266
1267 let stats = from_thrift_page_stats(Type::FLOAT, Some(thrift_stats))
1268 .unwrap()
1269 .unwrap();
1270
1271 assert_eq!(stats.nan_count_opt(), None);
1273 }
1274
1275 #[test]
1276 fn test_statistics_with_nan_min_max() {
1277 let stats = Statistics::Float(
1279 ValueStatistics::new(
1280 Some(f32::NAN), Some(f32::NAN),
1282 None,
1283 Some(0),
1284 false,
1285 )
1286 .with_nan_count(Some(10)), );
1288
1289 assert_eq!(stats.min_bytes_opt(), Some(f32::NAN.as_bytes()));
1290 assert_eq!(stats.max_bytes_opt(), Some(f32::NAN.as_bytes()));
1291 assert_eq!(stats.nan_count_opt(), Some(10));
1292
1293 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1295 assert_eq!(thrift_stats.min_value, Some(f32::NAN.as_bytes().to_vec()));
1296 assert_eq!(thrift_stats.max_value, Some(f32::NAN.as_bytes().to_vec()));
1297 assert_eq!(thrift_stats.nan_count, Some(10));
1298 }
1299
1300 #[test]
1301 fn test_nan_count_too_large() {
1302 let stats = Statistics::Float(
1304 ValueStatistics::new(Some(1.0_f32), Some(2.0_f32), None, Some(0), false)
1305 .with_nan_count(Some(u64::MAX)),
1306 );
1307
1308 let thrift_stats = page_stats_to_thrift(Some(&stats)).unwrap();
1309 assert_eq!(thrift_stats.nan_count, None);
1311 }
1312
1313 #[test]
1314 fn test_nan_counts_in_column_index() {
1315 use crate::file::metadata::ColumnIndexBuilder;
1317
1318 let mut float_builder = ColumnIndexBuilder::new(Type::FLOAT);
1320 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, Some(5));
1321 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 2, Some(3));
1322 float_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, Some(0)); let float_column_index = float_builder.build().unwrap();
1325 assert_eq!(float_column_index.nan_counts(), Some(&vec![5, 3, 0]));
1327
1328 let mut int_builder = ColumnIndexBuilder::new(Type::INT32);
1330 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, None);
1331 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 2, None);
1332 int_builder.append(false, vec![0u8; 4], vec![255u8; 4], 0, None);
1333
1334 let int_column_index = int_builder.build().unwrap();
1335 assert_eq!(int_column_index.nan_counts(), None);
1337 }
1338}