Skip to main content

parquet/arrow/arrow_reader/
statistics.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5//! [`StatisticsConverter`] to convert statistics in parquet format to arrow [`ArrayRef`].
6
7/// Notice that all the corresponding tests are in
8/// `arrow-rs/parquet/tests/arrow_reader/statistics.rs`.
9use crate::arrow::buffer::bit_util::sign_extend_be;
10use crate::arrow::parquet_column;
11use crate::basic::{ColumnOrder, Type as PhysicalType};
12use crate::data_type::Int96;
13use crate::errors::{ParquetError, Result};
14use crate::file::metadata::RowGroupMetaData;
15use crate::file::metadata::page_index::PageIndexProvider;
16use crate::file::page_index::column_index::ColumnIndexMetaData;
17use crate::file::statistics::Statistics as ParquetStatistics;
18use crate::schema::types::SchemaDescriptor;
19use arrow_array::builder::{
20    BinaryBuilder, BinaryViewBuilder, BooleanBuilder, Date32Builder, Date64Builder,
21    Decimal32Builder, Decimal64Builder, FixedSizeBinaryBuilder, Float16Builder, Float32Builder,
22    Float64Builder, Int8Builder, Int16Builder, Int32Builder, Int64Builder, LargeBinaryBuilder,
23    LargeStringBuilder, StringBuilder, StringViewBuilder, Time32MillisecondBuilder,
24    Time32SecondBuilder, Time64MicrosecondBuilder, Time64NanosecondBuilder,
25    TimestampMicrosecondBuilder, TimestampMillisecondBuilder, TimestampNanosecondBuilder,
26    TimestampSecondBuilder, UInt8Builder, UInt16Builder, UInt32Builder, UInt64Builder,
27};
28use arrow_array::{
29    ArrayRef, BinaryArray, BooleanArray, Date32Array, Date64Array, Decimal32Array, Decimal64Array,
30    Decimal128Array, Decimal256Array, Float16Array, Float32Array, Float64Array, Int8Array,
31    Int16Array, Int32Array, Int64Array, LargeBinaryArray, Time32MillisecondArray,
32    Time32SecondArray, Time64MicrosecondArray, Time64NanosecondArray, TimestampMicrosecondArray,
33    TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, UInt8Array,
34    UInt16Array, UInt32Array, UInt64Array, new_null_array,
35};
36use arrow_buffer::{NullBufferBuilder, i256};
37use arrow_schema::{DataType, Field, Schema, TimeUnit};
38use half::f16;
39use std::sync::Arc;
40
41// Convert the bytes array to i32.
42// The endian of the input bytes array must be big-endian.
43pub(crate) fn from_bytes_to_i32(b: &[u8]) -> i32 {
44    // The bytes array are from parquet file and must be the big-endian.
45    // The endian is defined by parquet format, and the reference document
46    // https://github.com/apache/parquet-format/blob/54e53e5d7794d383529dd30746378f19a12afd58/src/main/thrift/parquet.thrift#L66
47    i32::from_be_bytes(sign_extend_be::<4>(b))
48}
49
50// Convert the bytes array to i64.
51// The endian of the input bytes array must be big-endian.
52pub(crate) fn from_bytes_to_i64(b: &[u8]) -> i64 {
53    i64::from_be_bytes(sign_extend_be::<8>(b))
54}
55
56// Convert the bytes array to i128.
57// The endian of the input bytes array must be big-endian.
58pub(crate) fn from_bytes_to_i128(b: &[u8]) -> i128 {
59    i128::from_be_bytes(sign_extend_be::<16>(b))
60}
61
62// Convert the bytes array to i256.
63// The endian of the input bytes array must be big-endian.
64pub(crate) fn from_bytes_to_i256(b: &[u8]) -> i256 {
65    i256::from_be_bytes(sign_extend_be::<32>(b))
66}
67
68// Convert the bytes array to f16
69pub(crate) fn from_bytes_to_f16(b: &[u8]) -> Option<f16> {
70    match b {
71        [low, high] => Some(f16::from_be_bytes([*high, *low])),
72        _ => None,
73    }
74}
75
76/// Convert both bounds together: if either wraps in the reader's timestamp unit,
77/// neither bound can safely describe the decoded values.
78fn int96_statistics(min: &Int96, max: &Int96, unit: &TimeUnit) -> Option<(i64, i64)> {
79    let convert = |value: &Int96| {
80        const NANOS_PER_DAY: u64 = 86_400_000_000_000;
81        let words = value.data();
82        let nanos = (u64::from(words[1]) << 32) | u64::from(words[0]);
83        if nanos >= NANOS_PER_DAY {
84            return None;
85        }
86        let divisor = match unit {
87            TimeUnit::Second => 1_000_000_000,
88            TimeUnit::Millisecond => 1_000_000,
89            TimeUnit::Microsecond => 1_000,
90            TimeUnit::Nanosecond => 1,
91        };
92        // Canonical INT96 values fit in seconds. Reuse the reader's signed-day
93        // conversion, then scale in i128 so a valid i64::MIN is not rejected
94        // before adding the fractional second.
95        let timestamp = i128::from(value.to_seconds()) * i128::from(1_000_000_000 / divisor)
96            + i128::from((nanos % 1_000_000_000) / divisor);
97        i64::try_from(timestamp).ok()
98    };
99    Some((convert(min)?, convert(max)?))
100}
101
102/// Define an adapter iterator for extracting statistics from an iterator of
103/// `ParquetStatistics`
104///
105///
106/// Handles checking if the statistics are present and valid with the correct type.
107///
108/// Parameters:
109/// * `$iterator_type` is the name of the iterator type (e.g. `MinBooleanStatsIterator`)
110/// * `$func` is the function to call to get the value (e.g. `min` or `max`)
111/// * `$parquet_statistics_type` is the type of the statistics (e.g. `ParquetStatistics::Boolean`)
112/// * `$stat_value_type` is the type of the statistics value (e.g. `bool`)
113macro_rules! make_stats_iterator {
114    ($iterator_type:ident, $func:ident, $parquet_statistics_type:path, $stat_value_type:ty) => {
115        /// Maps an iterator of `ParquetStatistics` into an iterator of
116        /// `&$stat_value_type`
117        ///
118        /// Yielded elements:
119        /// * Some(stats) if valid
120        /// * None if the statistics are not present, not valid, or not $stat_value_type
121        struct $iterator_type<'a, I>
122        where
123            I: Iterator<Item = Option<&'a ParquetStatistics>>,
124        {
125            iter: I,
126        }
127
128        impl<'a, I> $iterator_type<'a, I>
129        where
130            I: Iterator<Item = Option<&'a ParquetStatistics>>,
131        {
132            /// Create a new iterator to extract the statistics
133            fn new(iter: I) -> Self {
134                Self { iter }
135            }
136        }
137
138        /// Implement the Iterator trait for the iterator
139        impl<'a, I> Iterator for $iterator_type<'a, I>
140        where
141            I: Iterator<Item = Option<&'a ParquetStatistics>>,
142        {
143            type Item = Option<&'a $stat_value_type>;
144
145            /// return the next statistics value
146            fn next(&mut self) -> Option<Self::Item> {
147                let next = self.iter.next();
148                next.map(|x| {
149                    x.and_then(|stats| match stats {
150                        $parquet_statistics_type(s) => s.$func(),
151                        _ => None,
152                    })
153                })
154            }
155
156            fn size_hint(&self) -> (usize, Option<usize>) {
157                self.iter.size_hint()
158            }
159        }
160    };
161}
162
163make_stats_iterator!(
164    MinBooleanStatsIterator,
165    min_opt,
166    ParquetStatistics::Boolean,
167    bool
168);
169make_stats_iterator!(
170    MaxBooleanStatsIterator,
171    max_opt,
172    ParquetStatistics::Boolean,
173    bool
174);
175make_stats_iterator!(
176    MinInt32StatsIterator,
177    min_opt,
178    ParquetStatistics::Int32,
179    i32
180);
181make_stats_iterator!(
182    MaxInt32StatsIterator,
183    max_opt,
184    ParquetStatistics::Int32,
185    i32
186);
187make_stats_iterator!(
188    MinInt64StatsIterator,
189    min_opt,
190    ParquetStatistics::Int64,
191    i64
192);
193make_stats_iterator!(
194    MaxInt64StatsIterator,
195    max_opt,
196    ParquetStatistics::Int64,
197    i64
198);
199make_stats_iterator!(
200    MinFloatStatsIterator,
201    min_opt,
202    ParquetStatistics::Float,
203    f32
204);
205make_stats_iterator!(
206    MaxFloatStatsIterator,
207    max_opt,
208    ParquetStatistics::Float,
209    f32
210);
211make_stats_iterator!(
212    MinDoubleStatsIterator,
213    min_opt,
214    ParquetStatistics::Double,
215    f64
216);
217make_stats_iterator!(
218    MaxDoubleStatsIterator,
219    max_opt,
220    ParquetStatistics::Double,
221    f64
222);
223make_stats_iterator!(
224    MinByteArrayStatsIterator,
225    min_bytes_opt,
226    ParquetStatistics::ByteArray,
227    [u8]
228);
229make_stats_iterator!(
230    MaxByteArrayStatsIterator,
231    max_bytes_opt,
232    ParquetStatistics::ByteArray,
233    [u8]
234);
235make_stats_iterator!(
236    MinFixedLenByteArrayStatsIterator,
237    min_bytes_opt,
238    ParquetStatistics::FixedLenByteArray,
239    [u8]
240);
241make_stats_iterator!(
242    MaxFixedLenByteArrayStatsIterator,
243    max_bytes_opt,
244    ParquetStatistics::FixedLenByteArray,
245    [u8]
246);
247
248/// Special iterator adapter for extracting i128 values from an iterator of
249/// `ParquetStatistics`
250///
251/// Handles checking if the statistics are present and valid with the correct type.
252///
253/// Depending on the parquet file, the statistics for `Decimal128` can be stored as
254/// `Int32`, `Int64` or `ByteArray` or `FixedSizeByteArray` :mindblown:
255///
256/// This iterator handles all cases, extracting the values
257/// and converting it to `stat_value_type`.
258///
259/// Parameters:
260/// * `$iterator_type` is the name of the iterator type (e.g. `MinBooleanStatsIterator`)
261/// * `$func` is the function to call to get the value (e.g. `min` or `max`)
262/// * `$bytes_func` is the function to call to get the value as bytes (e.g. `min_bytes` or `max_bytes`)
263/// * `$stat_value_type` is the type of the statistics value (e.g. `i128`)
264/// * `convert_func` is the function to convert the bytes to stats value (e.g. `from_bytes_to_i128`)
265macro_rules! make_decimal_stats_iterator {
266    ($iterator_type:ident, $func:ident, $bytes_func:ident, $stat_value_type:ident, $convert_func: ident) => {
267        struct $iterator_type<'a, I>
268        where
269            I: Iterator<Item = Option<&'a ParquetStatistics>>,
270        {
271            iter: I,
272        }
273
274        impl<'a, I> $iterator_type<'a, I>
275        where
276            I: Iterator<Item = Option<&'a ParquetStatistics>>,
277        {
278            fn new(iter: I) -> Self {
279                Self { iter }
280            }
281        }
282
283        impl<'a, I> Iterator for $iterator_type<'a, I>
284        where
285            I: Iterator<Item = Option<&'a ParquetStatistics>>,
286        {
287            type Item = Option<$stat_value_type>;
288
289            fn next(&mut self) -> Option<Self::Item> {
290                let next = self.iter.next();
291                next.map(|x| {
292                    x.and_then(|stats| match stats {
293                        ParquetStatistics::Int32(s) => {
294                            s.$func().map(|x| $stat_value_type::from(*x))
295                        }
296                        ParquetStatistics::Int64(s) => s
297                            .$func()
298                            .map(|x| $stat_value_type::try_from(*x).ok())
299                            .flatten(),
300                        ParquetStatistics::ByteArray(s) => s.$bytes_func().map($convert_func),
301                        ParquetStatistics::FixedLenByteArray(s) => {
302                            s.$bytes_func().map($convert_func)
303                        }
304                        _ => None,
305                    })
306                })
307            }
308
309            fn size_hint(&self) -> (usize, Option<usize>) {
310                self.iter.size_hint()
311            }
312        }
313    };
314}
315
316make_decimal_stats_iterator!(
317    MinDecimal32StatsIterator,
318    min_opt,
319    min_bytes_opt,
320    i32,
321    from_bytes_to_i32
322);
323make_decimal_stats_iterator!(
324    MaxDecimal32StatsIterator,
325    max_opt,
326    max_bytes_opt,
327    i32,
328    from_bytes_to_i32
329);
330make_decimal_stats_iterator!(
331    MinDecimal64StatsIterator,
332    min_opt,
333    min_bytes_opt,
334    i64,
335    from_bytes_to_i64
336);
337make_decimal_stats_iterator!(
338    MaxDecimal64StatsIterator,
339    max_opt,
340    max_bytes_opt,
341    i64,
342    from_bytes_to_i64
343);
344make_decimal_stats_iterator!(
345    MinDecimal128StatsIterator,
346    min_opt,
347    min_bytes_opt,
348    i128,
349    from_bytes_to_i128
350);
351make_decimal_stats_iterator!(
352    MaxDecimal128StatsIterator,
353    max_opt,
354    max_bytes_opt,
355    i128,
356    from_bytes_to_i128
357);
358make_decimal_stats_iterator!(
359    MinDecimal256StatsIterator,
360    min_opt,
361    min_bytes_opt,
362    i256,
363    from_bytes_to_i256
364);
365make_decimal_stats_iterator!(
366    MaxDecimal256StatsIterator,
367    max_opt,
368    max_bytes_opt,
369    i256,
370    from_bytes_to_i256
371);
372
373/// Extract timestamp bounds from INT64 or non-deprecated INT96 statistics.
374macro_rules! make_timestamp_stats_iterator {
375    ($iterator_type:ident, $func:ident, $bound:tt) => {
376        struct $iterator_type<'a, I>
377        where
378            I: Iterator<Item = Option<&'a ParquetStatistics>>,
379        {
380            iter: I,
381            unit: TimeUnit,
382        }
383
384        impl<'a, I> $iterator_type<'a, I>
385        where
386            I: Iterator<Item = Option<&'a ParquetStatistics>>,
387        {
388            fn new(iter: I, unit: TimeUnit) -> Self {
389                Self { iter, unit }
390            }
391        }
392
393        impl<'a, I> Iterator for $iterator_type<'a, I>
394        where
395            I: Iterator<Item = Option<&'a ParquetStatistics>>,
396        {
397            type Item = Option<i64>;
398
399            fn next(&mut self) -> Option<Self::Item> {
400                self.iter.next().map(|statistics| {
401                    let statistics = statistics?;
402                    match statistics {
403                        ParquetStatistics::Int64(values) => values.$func().copied(),
404                        // Deprecated min/max fields use signed comparison, not
405                        // the timestamp order advertised by the file footer.
406                        ParquetStatistics::Int96(values) if !statistics.is_min_max_deprecated() => {
407                            int96_statistics(values.min_opt()?, values.max_opt()?, &self.unit)
408                                .map(|bounds| bounds.$bound)
409                        }
410                        _ => None,
411                    }
412                })
413            }
414
415            fn size_hint(&self) -> (usize, Option<usize>) {
416                self.iter.size_hint()
417            }
418        }
419    };
420}
421
422make_timestamp_stats_iterator!(MinTimestampStatsIterator, min_opt, 0);
423make_timestamp_stats_iterator!(MaxTimestampStatsIterator, max_opt, 1);
424
425/// Special macro to combine the statistics iterators for min and max.
426/// This is used to avoid repeating the same code for min and max statistics extractions
427///
428/// Parameters:
429/// data_type: The data type of the statistics (e.g. `DataType::Int32`)
430/// iterator: The iterator of [`ParquetStatistics`] to extract the statistics from.
431macro_rules! get_statistics {
432    (Min, $data_type: ident, $iterator: ident, $physical_type: ident) => {
433        get_statistics!(
434            $data_type,
435            $iterator,
436            $physical_type,
437            MinBooleanStatsIterator,
438            MinInt32StatsIterator,
439            MinInt64StatsIterator,
440            MinTimestampStatsIterator,
441            MinFloatStatsIterator,
442            MinDoubleStatsIterator,
443            MinByteArrayStatsIterator,
444            MinFixedLenByteArrayStatsIterator,
445            MinDecimal32StatsIterator,
446            MinDecimal64StatsIterator,
447            MinDecimal128StatsIterator,
448            MinDecimal256StatsIterator,
449            min_statistics
450        )
451    };
452    (Max, $data_type: ident, $iterator: ident, $physical_type: ident) => {
453        get_statistics!(
454            $data_type,
455            $iterator,
456            $physical_type,
457            MaxBooleanStatsIterator,
458            MaxInt32StatsIterator,
459            MaxInt64StatsIterator,
460            MaxTimestampStatsIterator,
461            MaxFloatStatsIterator,
462            MaxDoubleStatsIterator,
463            MaxByteArrayStatsIterator,
464            MaxFixedLenByteArrayStatsIterator,
465            MaxDecimal32StatsIterator,
466            MaxDecimal64StatsIterator,
467            MaxDecimal128StatsIterator,
468            MaxDecimal256StatsIterator,
469            max_statistics
470        )
471    };
472    (
473        $data_type: ident,
474        $iterator: ident,
475        $physical_type: ident,
476        $boolean_iter: ident,
477        $int32_iter: ident,
478        $int64_iter: ident,
479        $timestamp_iter: ident,
480        $float_iter: ident,
481        $double_iter: ident,
482        $byte_array_iter: ident,
483        $fixed_len_byte_array_iter: ident,
484        $decimal32_iter: ident,
485        $decimal64_iter: ident,
486        $decimal128_iter: ident,
487        $decimal256_iter: ident,
488        $dictionary_statistics: ident
489    ) => {
490        match $data_type {
491            DataType::Boolean => Ok(Arc::new(BooleanArray::from_iter(
492                $boolean_iter::new($iterator).map(|x| x.copied()),
493            ))),
494            DataType::Int8 => Ok(Arc::new(Int8Array::from_iter(
495                $int32_iter::new($iterator).map(|x| {
496                    x.and_then(|x| i8::try_from(*x).ok())
497                }),
498            ))),
499            DataType::Int16 => Ok(Arc::new(Int16Array::from_iter(
500                $int32_iter::new($iterator).map(|x| {
501                    x.and_then(|x| i16::try_from(*x).ok())
502                }),
503            ))),
504            DataType::Int32 => Ok(Arc::new(Int32Array::from_iter(
505                $int32_iter::new($iterator).map(|x| x.copied()),
506            ))),
507            DataType::Int64 => Ok(Arc::new(Int64Array::from_iter(
508                $int64_iter::new($iterator).map(|x| x.copied()),
509            ))),
510            DataType::UInt8 => Ok(Arc::new(UInt8Array::from_iter(
511                $int32_iter::new($iterator).map(|x| {
512                    x.and_then(|x| u8::try_from(*x).ok())
513                }),
514            ))),
515            DataType::UInt16 => Ok(Arc::new(UInt16Array::from_iter(
516                $int32_iter::new($iterator).map(|x| {
517                    x.and_then(|x| u16::try_from(*x).ok())
518                }),
519            ))),
520            DataType::UInt32 => Ok(Arc::new(UInt32Array::from_iter(
521                $int32_iter::new($iterator).map(|x| x.map(|x| *x as u32)),
522            ))),
523            DataType::UInt64 => Ok(Arc::new(UInt64Array::from_iter(
524                $int64_iter::new($iterator).map(|x| x.map(|x| *x as u64)),
525            ))),
526            DataType::Float16 => Ok(Arc::new(Float16Array::from_iter(
527                $fixed_len_byte_array_iter::new($iterator).map(|x| x.and_then(|x| {
528                    from_bytes_to_f16(x)
529                })),
530            ))),
531            DataType::Float32 => Ok(Arc::new(Float32Array::from_iter(
532                $float_iter::new($iterator).map(|x| x.copied()),
533            ))),
534            DataType::Float64 => Ok(Arc::new(Float64Array::from_iter(
535                $double_iter::new($iterator).map(|x| x.copied()),
536            ))),
537            DataType::Date32 => Ok(Arc::new(Date32Array::from_iter(
538                $int32_iter::new($iterator).map(|x| x.copied()),
539            ))),
540            DataType::Date64 if $physical_type == Some(PhysicalType::INT32) => Ok(Arc::new(Date64Array::from_iter(
541                $int32_iter::new($iterator)
542                    .map(|x| x.map(|x| i64::from(*x) * 24 * 60 * 60 * 1000))))),
543            DataType::Date64 if $physical_type == Some(PhysicalType::INT64) => Ok(Arc::new(Date64Array::from_iter(
544                $int64_iter::new($iterator).map(|x| x.copied()),))),
545            DataType::Timestamp(unit, timezone) =>{
546                let iter = $timestamp_iter::new($iterator, *unit);
547                Ok(match unit {
548                    TimeUnit::Second => Arc::new(TimestampSecondArray::from_iter(iter).with_timezone_opt(timezone.clone())),
549                    TimeUnit::Millisecond => Arc::new(TimestampMillisecondArray::from_iter(iter).with_timezone_opt(timezone.clone())),
550                    TimeUnit::Microsecond => Arc::new(TimestampMicrosecondArray::from_iter(iter).with_timezone_opt(timezone.clone())),
551                    TimeUnit::Nanosecond => Arc::new(TimestampNanosecondArray::from_iter(iter).with_timezone_opt(timezone.clone())),
552                })
553            },
554            DataType::Time32(unit) => {
555                Ok(match unit {
556                    TimeUnit::Second =>  Arc::new(Time32SecondArray::from_iter(
557                        $int32_iter::new($iterator).map(|x| x.copied()),
558                    )),
559                    TimeUnit::Millisecond => Arc::new(Time32MillisecondArray::from_iter(
560                        $int32_iter::new($iterator).map(|x| x.copied()),
561                    )),
562                    _ => {
563                        let len = $iterator.count();
564                        // don't know how to extract statistics, so return a null array
565                        new_null_array($data_type, len)
566                    }
567                })
568            },
569            DataType::Time64(unit) => {
570                Ok(match unit {
571                    TimeUnit::Microsecond =>  Arc::new(Time64MicrosecondArray::from_iter(
572                        $int64_iter::new($iterator).map(|x| x.copied()),
573                    )),
574                    TimeUnit::Nanosecond => Arc::new(Time64NanosecondArray::from_iter(
575                        $int64_iter::new($iterator).map(|x| x.copied()),
576                    )),
577                    _ => {
578                        let len = $iterator.count();
579                        // don't know how to extract statistics, so return a null array
580                        new_null_array($data_type, len)
581                    }
582                })
583            },
584            DataType::Binary => Ok(Arc::new(BinaryArray::from_iter(
585                $byte_array_iter::new($iterator)
586            ))),
587            DataType::LargeBinary => Ok(Arc::new(LargeBinaryArray::from_iter(
588                $byte_array_iter::new($iterator)
589            ))),
590            DataType::Utf8 => {
591                let iterator = $byte_array_iter::new($iterator);
592                let mut builder = StringBuilder::new();
593                for x in iterator {
594                    let Some(x) = x else {
595                        builder.append_null(); // no statistics value
596                        continue;
597                    };
598
599                    let Ok(x) = std::str::from_utf8(x) else {
600                        builder.append_null();
601                        continue;
602                    };
603
604                    builder.append_value(x);
605                }
606                Ok(Arc::new(builder.finish()))
607            },
608            DataType::LargeUtf8 => {
609                let iterator = $byte_array_iter::new($iterator);
610                let mut builder = LargeStringBuilder::new();
611                for x in iterator {
612                    let Some(x) = x else {
613                        builder.append_null(); // no statistics value
614                        continue;
615                    };
616
617                    let Ok(x) = std::str::from_utf8(x) else {
618                        builder.append_null();
619                        continue;
620                    };
621
622                    builder.append_value(x);
623                }
624                Ok(Arc::new(builder.finish()))
625            },
626            DataType::FixedSizeBinary(size) => {
627                let iterator = $fixed_len_byte_array_iter::new($iterator);
628                let mut builder = FixedSizeBinaryBuilder::new(*size);
629                for x in iterator {
630                    let Some(x) = x else {
631                        builder.append_null(); // no statistics value
632                        continue;
633                    };
634
635                    // ignore invalid values
636                    if x.len().try_into() != Ok(*size){
637                        builder.append_null();
638                        continue;
639                    }
640
641                    builder.append_value(x).expect("ensure to append successfully here, because size have been checked before");
642                }
643                Ok(Arc::new(builder.finish()))
644            },
645            DataType::Decimal32(precision, scale) => {
646                let arr = Decimal32Array::from_iter(
647                    $decimal32_iter::new($iterator)
648                ).with_precision_and_scale(*precision, *scale)?;
649                Ok(Arc::new(arr))
650            },
651            DataType::Decimal64(precision, scale) => {
652                let arr = Decimal64Array::from_iter(
653                    $decimal64_iter::new($iterator)
654                ).with_precision_and_scale(*precision, *scale)?;
655                Ok(Arc::new(arr))
656            },
657            DataType::Decimal128(precision, scale) => {
658                let arr = Decimal128Array::from_iter(
659                    $decimal128_iter::new($iterator)
660                ).with_precision_and_scale(*precision, *scale)?;
661                Ok(Arc::new(arr))
662            },
663            DataType::Decimal256(precision, scale) => {
664                let arr = Decimal256Array::from_iter(
665                    $decimal256_iter::new($iterator)
666                ).with_precision_and_scale(*precision, *scale)?;
667                Ok(Arc::new(arr))
668            },
669            DataType::Dictionary(_, value_type) => {
670                $dictionary_statistics(value_type, $iterator, $physical_type)
671            },
672            DataType::Utf8View => {
673                let iterator = $byte_array_iter::new($iterator);
674                let mut builder = StringViewBuilder::new();
675                for x in iterator {
676                    let Some(x) = x else {
677                        builder.append_null(); // no statistics value
678                        continue;
679                    };
680
681                    let Ok(x) = std::str::from_utf8(x) else {
682                        builder.append_null();
683                        continue;
684                    };
685
686                    builder.append_value(x);
687                }
688                Ok(Arc::new(builder.finish()))
689            },
690            DataType::BinaryView => {
691                let iterator = $byte_array_iter::new($iterator);
692                let mut builder = BinaryViewBuilder::new();
693                for x in iterator {
694                    let Some(x) = x else {
695                        builder.append_null(); // no statistics value
696                        continue;
697                    };
698
699                    builder.append_value(x);
700                }
701                Ok(Arc::new(builder.finish()))
702            }
703
704            DataType::Map(_,_) |
705            DataType::Duration(_) |
706            DataType::Interval(_) |
707            DataType::Date64 |  // required to cover $physical_type match guard
708            DataType::Null |
709            DataType::List(_) |
710            DataType::ListView(_) |
711            DataType::FixedSizeList(_, _) |
712            DataType::LargeList(_) |
713            DataType::LargeListView(_) |
714            DataType::Struct(_) |
715            DataType::Union(_, _) |
716            DataType::RunEndEncoded(_, _) => {
717                let len = $iterator.count();
718                // don't know how to extract statistics, so return a null array
719                Ok(new_null_array($data_type, len))
720            }
721        }
722    };
723}
724
725macro_rules! get_data_page_statistics {
726    (Min, $data_type: ident, $iterator: ident, $physical_type: ident) => {
727        get_data_page_statistics!(
728            $data_type,
729            $iterator,
730            $physical_type,
731            min_values_iter,
732            true,
733            min_page_statistics
734        )
735    };
736    (Max, $data_type: ident, $iterator: ident, $physical_type: ident) => {
737        get_data_page_statistics!(
738            $data_type,
739            $iterator,
740            $physical_type,
741            max_values_iter,
742            false,
743            max_page_statistics
744        )
745    };
746    (
747        $data_type: ident,
748        $iterator: ident,
749        $physical_type: ident,
750        $values_iter: ident,
751        $is_min: expr,
752        $page_statistics: ident
753    ) => {{
754        let chunks: Vec<(usize, Option<&ColumnIndexMetaData>)> = $iterator.collect();
755        let capacity: usize = chunks.iter().map(|c| c.0).sum();
756        match $data_type {
757                DataType::Boolean => {
758                    let mut b = BooleanBuilder::with_capacity(capacity);
759                    for (len, index) in chunks {
760                        match index {
761                            Some(ColumnIndexMetaData::BOOLEAN(index)) => {
762                                for val in index.$values_iter() {
763                                    b.append_option(val.copied());
764                                }
765                            }
766                            _ => b.append_nulls(len),
767                        }
768                    }
769                    Ok(Arc::new(b.finish()))
770                },
771                DataType::UInt8 => {
772                    let mut b = UInt8Builder::with_capacity(capacity);
773                    for (len, index) in chunks {
774                        match index {
775                            Some(ColumnIndexMetaData::INT32(index)) => {
776                                b.extend_from_iter_option(
777                                    index.$values_iter()
778                                        .map(|val| val.and_then(|&x| u8::try_from(x).ok())),
779                                );
780                            }
781                            _ => b.append_nulls(len),
782                        }
783                    }
784                    Ok(Arc::new(b.finish()))
785                },
786                DataType::UInt16 => {
787                    let mut b = UInt16Builder::with_capacity(capacity);
788                    for (len, index) in chunks {
789                        match index {
790                            Some(ColumnIndexMetaData::INT32(index)) => {
791                                b.extend_from_iter_option(
792                                     index.$values_iter()
793                                        .map(|val| val.and_then(|&x| u16::try_from(x).ok())),
794                                );
795                            }
796                            _ => b.append_nulls(len),
797                        }
798                    }
799                    Ok(Arc::new(b.finish()))
800                },
801                DataType::UInt32 => {
802                    let mut b = UInt32Builder::with_capacity(capacity);
803                    for (len, index) in chunks {
804                        match index {
805                            Some(ColumnIndexMetaData::INT32(index)) => {
806                                b.extend_from_iter_option(
807                                    index.$values_iter()
808                                        .map(|val| val.map(|&x| x as u32)),
809                                );
810                            }
811                            _ => b.append_nulls(len),
812                        }
813                    }
814                    Ok(Arc::new(b.finish()))
815                },
816                DataType::UInt64 => {
817                    let mut b = UInt64Builder::with_capacity(capacity);
818                    for (len, index) in chunks {
819                        match index {
820                            Some(ColumnIndexMetaData::INT64(index)) => {
821                                b.extend_from_iter_option(
822                                    index.$values_iter()
823                                        .map(|val| val.map(|&x| x as u64)),
824                                );
825                            }
826                            _ => b.append_nulls(len),
827                        }
828                    }
829                    Ok(Arc::new(b.finish()))
830                },
831                DataType::Int8 => {
832                    let mut b = Int8Builder::with_capacity(capacity);
833                    for (len, index) in chunks {
834                        match index {
835                            Some(ColumnIndexMetaData::INT32(index)) => {
836                                b.extend_from_iter_option(
837                                    index.$values_iter()
838                                        .map(|val| val.and_then(|&x| i8::try_from(x).ok())),
839                                );
840                            }
841                            _ => b.append_nulls(len),
842                        }
843                    }
844                    Ok(Arc::new(b.finish()))
845                },
846                DataType::Int16 => {
847                    let mut b = Int16Builder::with_capacity(capacity);
848                    for (len, index) in chunks {
849                        match index {
850                            Some(ColumnIndexMetaData::INT32(index)) => {
851                                b.extend_from_iter_option(
852                                    index.$values_iter()
853                                        .map(|val| val.and_then(|&x| i16::try_from(x).ok())),
854                                );
855                            }
856                            _ => b.append_nulls(len),
857                        }
858                    }
859                    Ok(Arc::new(b.finish()))
860                },
861                DataType::Int32 => {
862                    let mut b = Int32Builder::with_capacity(capacity);
863                    for (len, index) in chunks {
864                        match index {
865                            Some(ColumnIndexMetaData::INT32(index)) => {
866                                b.extend_from_iter_option(
867                                    index.$values_iter()
868                                        .map(|val| val.copied()),
869                                );
870                            }
871                            _ => b.append_nulls(len),
872                        }
873                    }
874                    Ok(Arc::new(b.finish()))
875                },
876                DataType::Int64 => {
877                    let mut b = Int64Builder::with_capacity(capacity);
878                    for (len, index) in chunks {
879                        match index {
880                            Some(ColumnIndexMetaData::INT64(index)) => {
881                                b.extend_from_iter_option(
882                                    index.$values_iter()
883                                        .map(|val| val.copied()),
884                                );
885                            }
886                            _ => b.append_nulls(len),
887                        }
888                    }
889                    Ok(Arc::new(b.finish()))
890                },
891                DataType::Float16 => {
892                    let mut b = Float16Builder::with_capacity(capacity);
893                    for (len, index) in chunks {
894                        match index {
895                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
896                                b.extend_from_iter_option(
897                                    index.$values_iter()
898                                        .map(|val| val.and_then(|x| from_bytes_to_f16(x))),
899                                );
900                            }
901                            _ => b.append_nulls(len),
902                        }
903                    }
904                    Ok(Arc::new(b.finish()))
905                },
906                DataType::Float32 => {
907                    let mut b = Float32Builder::with_capacity(capacity);
908                    for (len, index) in chunks {
909                        match index {
910                            Some(ColumnIndexMetaData::FLOAT(index)) => {
911                                b.extend_from_iter_option(
912                                    index.$values_iter()
913                                        .map(|val| val.copied()),
914                                );
915                            }
916                            _ => b.append_nulls(len),
917                        }
918                    }
919                    Ok(Arc::new(b.finish()))
920                },
921                DataType::Float64 => {
922                    let mut b = Float64Builder::with_capacity(capacity);
923                    for (len, index) in chunks {
924                        match index {
925                            Some(ColumnIndexMetaData::DOUBLE(index)) => {
926                                b.extend_from_iter_option(
927                                    index.$values_iter()
928                                        .map(|val| val.copied()),
929                                );
930                            }
931                            _ => b.append_nulls(len),
932                        }
933                    }
934                    Ok(Arc::new(b.finish()))
935                },
936                DataType::Binary => {
937                    let mut b = BinaryBuilder::with_capacity(capacity, capacity * 10);
938                    for (len, index) in chunks {
939                        match index {
940                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
941                                for val in index.$values_iter() {
942                                    b.append_option(val.map(|x| x.as_ref()));
943                                }
944                            }
945                            _ => b.append_nulls(len),
946                        }
947                    }
948                    Ok(Arc::new(b.finish()))
949                },
950                DataType::LargeBinary => {
951                    let mut b = LargeBinaryBuilder::with_capacity(capacity, capacity * 10);
952                    for (len, index) in chunks {
953                        match index {
954                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
955                                for val in index.$values_iter() {
956                                    b.append_option(val.map(|x| x.as_ref()));
957                                }
958                            }
959                            _ => b.append_nulls(len),
960                        }
961                    }
962                    Ok(Arc::new(b.finish()))
963                },
964                DataType::Utf8 => {
965                    let mut b = StringBuilder::with_capacity(capacity, capacity * 10);
966                    for (len, index) in chunks {
967                        match index {
968                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
969                                for val in index.$values_iter() {
970                                    match val {
971                                        Some(x) => match std::str::from_utf8(x.as_ref()) {
972                                            Ok(s) => b.append_value(s),
973                                            _ => b.append_null(),
974                                        }
975                                        None => b.append_null(),
976                                    }
977                                }
978                            }
979                            _ => b.append_nulls(len),
980                        }
981                    }
982                    Ok(Arc::new(b.finish()))
983                },
984                DataType::LargeUtf8 => {
985                    let mut b = LargeStringBuilder::with_capacity(capacity, capacity * 10);
986                    for (len, index) in chunks {
987                        match index {
988                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
989                                for val in index.$values_iter() {
990                                    match val {
991                                        Some(x) => match std::str::from_utf8(x.as_ref()) {
992                                            Ok(s) => b.append_value(s),
993                                            _ => b.append_null(),
994                                        }
995                                        None => b.append_null(),
996                                    }
997                                }
998                            }
999                            _ => b.append_nulls(len),
1000                        }
1001                    }
1002                    Ok(Arc::new(b.finish()))
1003                },
1004                DataType::Dictionary(_, value_type) => {
1005                    $page_statistics(value_type, chunks.into_iter(), $physical_type)
1006                },
1007                DataType::Timestamp(unit, timezone) => {
1008                    match unit {
1009                        TimeUnit::Second => {
1010                            let mut b = TimestampSecondBuilder::with_capacity(capacity);
1011                            for (len, index) in chunks {
1012                                match index {
1013                                    Some(ColumnIndexMetaData::INT64(index)) => {
1014                                        b.extend_from_iter_option(
1015                                            index.$values_iter()
1016                                                .map(|val| val.copied()),
1017                                        );
1018                                    }
1019                                    Some(ColumnIndexMetaData::INT96(index)) => {
1020                                        b.extend_from_iter_option(
1021                                            index.min_values_iter().zip(index.max_values_iter())
1022                                                .map(|(min, max)| {
1023                                                    let (min, max) = int96_statistics(min?, max?, unit)?;
1024                                                    Some(if $is_min { min } else { max })
1025                                                }),
1026                                        );
1027                                    }
1028                                    _ => b.append_nulls(len),
1029                                }
1030                            }
1031                            Ok(Arc::new(b.finish().with_timezone_opt(timezone.clone())))
1032                        }
1033                        TimeUnit::Millisecond => {
1034                            let mut b = TimestampMillisecondBuilder::with_capacity(capacity);
1035                            for (len, index) in chunks {
1036                                match index {
1037                                    Some(ColumnIndexMetaData::INT64(index)) => {
1038                                        b.extend_from_iter_option(
1039                                            index.$values_iter()
1040                                                .map(|val| val.copied()),
1041                                        );
1042                                    }
1043                                    Some(ColumnIndexMetaData::INT96(index)) => {
1044                                        b.extend_from_iter_option(
1045                                            index.min_values_iter().zip(index.max_values_iter())
1046                                                .map(|(min, max)| {
1047                                                    let (min, max) = int96_statistics(min?, max?, unit)?;
1048                                                    Some(if $is_min { min } else { max })
1049                                                }),
1050                                        );
1051                                    }
1052                                    _ => b.append_nulls(len),
1053                                }
1054                            }
1055                            Ok(Arc::new(b.finish().with_timezone_opt(timezone.clone())))
1056                        }
1057                        TimeUnit::Microsecond => {
1058                            let mut b = TimestampMicrosecondBuilder::with_capacity(capacity);
1059                            for (len, index) in chunks {
1060                                match index {
1061                                    Some(ColumnIndexMetaData::INT64(index)) => {
1062                                        b.extend_from_iter_option(
1063                                            index.$values_iter()
1064                                                .map(|val| val.copied()),
1065                                        );
1066                                    }
1067                                    Some(ColumnIndexMetaData::INT96(index)) => {
1068                                        b.extend_from_iter_option(
1069                                            index.min_values_iter().zip(index.max_values_iter())
1070                                                .map(|(min, max)| {
1071                                                    let (min, max) = int96_statistics(min?, max?, unit)?;
1072                                                    Some(if $is_min { min } else { max })
1073                                                }),
1074                                        );
1075                                    }
1076                                    _ => b.append_nulls(len),
1077                                }
1078                            }
1079                            Ok(Arc::new(b.finish().with_timezone_opt(timezone.clone())))
1080                        }
1081                        TimeUnit::Nanosecond => {
1082                            let mut b = TimestampNanosecondBuilder::with_capacity(capacity);
1083                            for (len, index) in chunks {
1084                                match index {
1085                                    Some(ColumnIndexMetaData::INT64(index)) => {
1086                                        b.extend_from_iter_option(
1087                                            index.$values_iter()
1088                                                .map(|val| val.copied()),
1089                                        );
1090                                    }
1091                                    Some(ColumnIndexMetaData::INT96(index)) => {
1092                                        b.extend_from_iter_option(
1093                                            index.min_values_iter().zip(index.max_values_iter())
1094                                                .map(|(min, max)| {
1095                                                    let (min, max) = int96_statistics(min?, max?, unit)?;
1096                                                    Some(if $is_min { min } else { max })
1097                                                }),
1098                                        );
1099                                    }
1100                                    _ => b.append_nulls(len),
1101                                }
1102                            }
1103                            Ok(Arc::new(b.finish().with_timezone_opt(timezone.clone())))
1104                        }
1105                    }
1106                },
1107                DataType::Date32 => {
1108                    let mut b = Date32Builder::with_capacity(capacity);
1109                    for (len, index) in chunks {
1110                        match index {
1111                            Some(ColumnIndexMetaData::INT32(index)) => {
1112                                b.extend_from_iter_option(
1113                                    index.$values_iter()
1114                                        .map(|val| val.copied()),
1115                                );
1116                            }
1117                            _ => b.append_nulls(len),
1118                        }
1119                    }
1120                    Ok(Arc::new(b.finish()))
1121                },
1122                DataType::Date64 if $physical_type == Some(PhysicalType::INT32)=> {
1123                    let mut b = Date64Builder::with_capacity(capacity);
1124                    for (len, index) in chunks {
1125                        match index {
1126                            Some(ColumnIndexMetaData::INT32(index)) => {
1127                                b.extend_from_iter_option(
1128                                    index.$values_iter()
1129                                        .map(|val| val.map(|&x| (x as i64) * 24 * 60 * 60 * 1000)),
1130                                );
1131                            }
1132                            _ => b.append_nulls(len),
1133                        }
1134                    }
1135                    Ok(Arc::new(b.finish()))
1136                },
1137                DataType::Date64 if $physical_type == Some(PhysicalType::INT64) => {
1138                    let mut b = Date64Builder::with_capacity(capacity);
1139                    for (len, index) in chunks {
1140                        match index {
1141                            Some(ColumnIndexMetaData::INT64(index)) => {
1142                                b.extend_from_iter_option(
1143                                    index.$values_iter()
1144                                        .map(|val| val.copied()),
1145                                );
1146                            }
1147                            _ => b.append_nulls(len),
1148                        }
1149                    }
1150                    Ok(Arc::new(b.finish()))
1151                },
1152                DataType::Decimal32(precision, scale) => {
1153                    let mut b = Decimal32Builder::with_capacity(capacity);
1154                    for (len, index) in chunks {
1155                        match index {
1156                            Some(ColumnIndexMetaData::INT32(index)) => {
1157                                b.extend_from_iter_option(
1158                                    index.$values_iter()
1159                                        .map(|val| val.copied()),
1160                                );
1161                            }
1162                            Some(ColumnIndexMetaData::INT64(index)) => {
1163                                b.extend_from_iter_option(
1164                                    index.$values_iter()
1165                                        .map(|val| val.and_then(|&x| i32::try_from(x).ok())),
1166                                );
1167                            }
1168                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1169                                b.extend_from_iter_option(
1170                                    index.$values_iter()
1171                                        .map(|val| val.map(|x| from_bytes_to_i32(x.as_ref()))),
1172                                );
1173                            }
1174                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
1175                                b.extend_from_iter_option(
1176                                    index.$values_iter()
1177                                        .map(|val| val.map(|x| from_bytes_to_i32(x.as_ref()))),
1178                                );
1179                            }
1180                            _ => b.append_nulls(len),
1181                        }
1182                    }
1183                    Ok(Arc::new(b.with_precision_and_scale(*precision, *scale)?.finish()))
1184                },
1185                DataType::Decimal64(precision, scale) => {
1186                    let mut b = Decimal64Builder::with_capacity(capacity);
1187                    for (len, index) in chunks {
1188                        match index {
1189                            Some(ColumnIndexMetaData::INT32(index)) => {
1190                                b.extend_from_iter_option(
1191                                    index.$values_iter()
1192                                        .map(|val| val.map(|x| *x as i64)),
1193                                );
1194                            }
1195                            Some(ColumnIndexMetaData::INT64(index)) => {
1196                                b.extend_from_iter_option(
1197                                    index.$values_iter()
1198                                        .map(|val| val.copied()),
1199                                );
1200                            }
1201                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1202                                b.extend_from_iter_option(
1203                                    index.$values_iter()
1204                                        .map(|val| val.map(|x| from_bytes_to_i64(x.as_ref()))),
1205                                );
1206                            }
1207                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
1208                                b.extend_from_iter_option(
1209                                    index.$values_iter()
1210                                        .map(|val| val.map(|x| from_bytes_to_i64(x.as_ref()))),
1211                                );
1212                            }
1213                            _ => b.append_nulls(len),
1214                        }
1215                    }
1216                    Ok(Arc::new(b.with_precision_and_scale(*precision, *scale)?.finish()))
1217                },
1218                DataType::Decimal128(precision, scale) => {
1219                    let mut b = Decimal128Array::builder(capacity);
1220                    for (len, index) in chunks {
1221                        match index {
1222                            Some(ColumnIndexMetaData::INT32(index)) => {
1223                                b.extend_from_iter_option(
1224                                    index.$values_iter()
1225                                        .map(|val| val.map(|x| *x as i128)),
1226                                );
1227                            }
1228                            Some(ColumnIndexMetaData::INT64(index)) => {
1229                                b.extend_from_iter_option(
1230                                    index.$values_iter()
1231                                        .map(|val| val.map(|x| *x as i128)),
1232                                );
1233                            }
1234                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1235                                b.extend_from_iter_option(
1236                                    index.$values_iter()
1237                                        .map(|val| val.map(|x| from_bytes_to_i128(x.as_ref()))),
1238                                );
1239                            }
1240                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
1241                                b.extend_from_iter_option(
1242                                    index.$values_iter()
1243                                        .map(|val| val.map(|x| from_bytes_to_i128(x.as_ref()))),
1244                                );
1245                            }
1246                            _ => b.append_nulls(len),
1247                        }
1248                    }
1249                    Ok(Arc::new(b.with_precision_and_scale(*precision, *scale)?.finish()))
1250                },
1251                DataType::Decimal256(precision, scale) => {
1252                    let mut b = Decimal256Array::builder(capacity);
1253                    for (len, index) in chunks {
1254                        match index {
1255                            Some(ColumnIndexMetaData::INT32(index)) => {
1256                                b.extend_from_iter_option(
1257                                    index.$values_iter()
1258                                        .map(|val| val.map(|x| i256::from_i128(*x as i128))),
1259                                );
1260                            }
1261                            Some(ColumnIndexMetaData::INT64(index)) => {
1262                                b.extend_from_iter_option(
1263                                    index.$values_iter()
1264                                        .map(|val| val.map(|x| i256::from_i128(*x as i128))),
1265                                );
1266                            }
1267                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1268                                b.extend_from_iter_option(
1269                                    index.$values_iter()
1270                                        .map(|val| val.map(|x| from_bytes_to_i256(x.as_ref()))),
1271                                );
1272                            }
1273                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
1274                                b.extend_from_iter_option(
1275                                    index.$values_iter()
1276                                        .map(|val| val.map(|x| from_bytes_to_i256(x.as_ref()))),
1277                                );
1278                            }
1279                            _ => b.append_nulls(len),
1280                        }
1281                    }
1282                    Ok(Arc::new(b.with_precision_and_scale(*precision, *scale)?.finish()))
1283                },
1284                DataType::Time32(unit) => {
1285                    match unit {
1286                        TimeUnit::Second => {
1287                            let mut b = Time32SecondBuilder::with_capacity(capacity);
1288                            for (len, index) in chunks {
1289                                match index {
1290                                    Some(ColumnIndexMetaData::INT32(index)) => {
1291                                        b.extend_from_iter_option(
1292                                            index.$values_iter()
1293                                                .map(|val| val.copied()),
1294                                        );
1295                                    }
1296                                    _ => b.append_nulls(len),
1297                                }
1298                            }
1299                            Ok(Arc::new(b.finish()))
1300                        }
1301                        TimeUnit::Millisecond => {
1302                            let mut b = Time32MillisecondBuilder::with_capacity(capacity);
1303                            for (len, index) in chunks {
1304                                match index {
1305                                    Some(ColumnIndexMetaData::INT32(index)) => {
1306                                        b.extend_from_iter_option(
1307                                            index.$values_iter()
1308                                                .map(|val| val.copied()),
1309                                        );
1310                                    }
1311                                    _ => b.append_nulls(len),
1312                                }
1313                            }
1314                            Ok(Arc::new(b.finish()))
1315                        }
1316                        _ => {
1317                            Ok(new_null_array($data_type, capacity))
1318                        }
1319                    }
1320                }
1321                DataType::Time64(unit) => {
1322                    match unit {
1323                        TimeUnit::Microsecond => {
1324                            let mut b = Time64MicrosecondBuilder::with_capacity(capacity);
1325                            for (len, index) in chunks {
1326                                match index {
1327                                    Some(ColumnIndexMetaData::INT64(index)) => {
1328                                        b.extend_from_iter_option(
1329                                            index.$values_iter()
1330                                                .map(|val| val.copied()),
1331                                        );
1332                                    }
1333                                    _ => b.append_nulls(len),
1334                                }
1335                            }
1336                            Ok(Arc::new(b.finish()))
1337                        }
1338                        TimeUnit::Nanosecond => {
1339                            let mut b = Time64NanosecondBuilder::with_capacity(capacity);
1340                            for (len, index) in chunks {
1341                                match index {
1342                                    Some(ColumnIndexMetaData::INT64(index)) => {
1343                                        b.extend_from_iter_option(
1344                                            index.$values_iter()
1345                                                .map(|val| val.copied()),
1346                                        );
1347                                    }
1348                                    _ => b.append_nulls(len),
1349                                }
1350                            }
1351                            Ok(Arc::new(b.finish()))
1352                        }
1353                        _ => {
1354                            Ok(new_null_array($data_type, capacity))
1355                        }
1356                    }
1357                },
1358                DataType::FixedSizeBinary(size) => {
1359                    let mut b = FixedSizeBinaryBuilder::with_capacity(capacity, *size);
1360                    for (len, index) in chunks {
1361                        match index {
1362                            Some(ColumnIndexMetaData::FIXED_LEN_BYTE_ARRAY(index)) => {
1363                                for val in index.$values_iter() {
1364                                    match val {
1365                                        Some(v) => {
1366                                           if v.len() == *size as usize {
1367                                               let _ = b.append_value(v.as_ref())?;
1368                                           } else {
1369                                               b.append_null();
1370                                           }
1371                                       }
1372                                        None => b.append_null(),
1373                                    }
1374                                }
1375                            }
1376                            _ => b.append_nulls(len),
1377                        }
1378                    }
1379                    Ok(Arc::new(b.finish()))
1380                },
1381                DataType::Utf8View => {
1382                    let mut b = StringViewBuilder::with_capacity(capacity);
1383                    for (len, index) in chunks {
1384                        match index {
1385                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1386                                for val in index.$values_iter() {
1387                                    match val {
1388                                        Some(x) => match std::str::from_utf8(x.as_ref()) {
1389                                            Ok(s) => b.append_value(s),
1390                                            _ => b.append_null(),
1391                                        }
1392                                        None => b.append_null(),
1393                                    }
1394                                }
1395                            }
1396                            _ => {
1397                                for _ in 0..len { b.append_null(); }
1398                            }
1399                        }
1400                    }
1401                    Ok(Arc::new(b.finish()))
1402                },
1403                DataType::BinaryView => {
1404                    let mut b = BinaryViewBuilder::with_capacity(capacity);
1405                    for (len, index) in chunks {
1406                        match index {
1407                            Some(ColumnIndexMetaData::BYTE_ARRAY(index)) => {
1408                                for val in index.$values_iter() {
1409                                    match val {
1410                                        Some(v) => b.append_value(v.as_ref()),
1411                                        None => b.append_null(),
1412                                    }
1413                                }
1414                            }
1415                            _ => {
1416                                for _ in 0..len { b.append_null(); }
1417                            }
1418                        }
1419                    }
1420                    Ok(Arc::new(b.finish()))
1421                },
1422                DataType::Date64 |  // required to cover $physical_type match guard
1423                DataType::Null |
1424                DataType::Duration(_) |
1425                DataType::Interval(_) |
1426                DataType::List(_) |
1427                DataType::ListView(_) |
1428                DataType::FixedSizeList(_, _) |
1429                DataType::LargeList(_) |
1430                DataType::LargeListView(_) |
1431                DataType::Struct(_) |
1432                DataType::Union(_, _) |
1433                DataType::Map(_, _) |
1434                DataType::RunEndEncoded(_, _) => {
1435                    // don't know how to extract statistics, so return a null array
1436                    Ok(new_null_array($data_type, capacity))
1437                },
1438            }
1439        }
1440    };
1441}
1442/// Extracts the min statistics from an iterator of [`ParquetStatistics`] to an
1443/// [`ArrayRef`]
1444///
1445/// This is an internal helper -- see [`StatisticsConverter`] for public API
1446fn min_statistics<'a, I: Iterator<Item = Option<&'a ParquetStatistics>>>(
1447    data_type: &DataType,
1448    iterator: I,
1449    physical_type: Option<PhysicalType>,
1450) -> Result<ArrayRef> {
1451    get_statistics!(Min, data_type, iterator, physical_type)
1452}
1453
1454/// Extracts the max statistics from an iterator of [`ParquetStatistics`] to an [`ArrayRef`]
1455///
1456/// This is an internal helper -- see [`StatisticsConverter`] for public API
1457fn max_statistics<'a, I: Iterator<Item = Option<&'a ParquetStatistics>>>(
1458    data_type: &DataType,
1459    iterator: I,
1460    physical_type: Option<PhysicalType>,
1461) -> Result<ArrayRef> {
1462    get_statistics!(Max, data_type, iterator, physical_type)
1463}
1464
1465/// Extracts the min statistics from an iterator
1466/// of parquet page [`ColumnIndexMetaData`]'s to an [`ArrayRef`]
1467pub(crate) fn min_page_statistics<'a, I>(
1468    data_type: &DataType,
1469    iterator: I,
1470    physical_type: Option<PhysicalType>,
1471) -> Result<ArrayRef>
1472where
1473    I: Iterator<Item = (usize, Option<&'a ColumnIndexMetaData>)>,
1474{
1475    get_data_page_statistics!(Min, data_type, iterator, physical_type)
1476}
1477
1478/// Extracts the max statistics from an iterator
1479/// of parquet page [`ColumnIndexMetaData`]'s to an [`ArrayRef`]
1480pub(crate) fn max_page_statistics<'a, I>(
1481    data_type: &DataType,
1482    iterator: I,
1483    physical_type: Option<PhysicalType>,
1484) -> Result<ArrayRef>
1485where
1486    I: Iterator<Item = (usize, Option<&'a ColumnIndexMetaData>)>,
1487{
1488    get_data_page_statistics!(Max, data_type, iterator, physical_type)
1489}
1490
1491/// Extracts the null count statistics from an iterator
1492/// of parquet page [`ColumnIndexMetaData`]'s to an [`ArrayRef`]
1493///
1494/// The returned Array is an [`UInt64Array`]
1495pub(crate) fn null_counts_page_statistics<'a, I>(iterator: I) -> Result<UInt64Array>
1496where
1497    I: Iterator<Item = (usize, Option<&'a ColumnIndexMetaData>)>,
1498{
1499    let chunks: Vec<_> = iterator.collect();
1500    let total_capacity: usize = chunks.iter().map(|(len, _)| *len).sum();
1501    let mut values = Vec::with_capacity(total_capacity);
1502    let mut nulls = NullBufferBuilder::new(total_capacity);
1503    for (len, index) in chunks {
1504        match index {
1505            Some(index) if index.null_counts().is_some() => {
1506                values.extend(index.null_counts().unwrap().iter().map(|&x| x as u64));
1507                nulls.append_n_non_nulls(len);
1508            }
1509            _ => {
1510                values.resize(values.len() + len, 0);
1511                nulls.append_n_nulls(len);
1512            }
1513        }
1514    }
1515    let null_buffer = nulls.build();
1516    let array = UInt64Array::new(values.into(), null_buffer);
1517    Ok(array)
1518}
1519
1520/// Extracts the NaN count statistics from an iterator
1521/// of parquet page [`ColumnIndexMetaData`]'s to an [`ArrayRef`]
1522///
1523/// The returned Array is an [`UInt64Array`]
1524pub(crate) fn nan_counts_page_statistics<'a, I>(iterator: I) -> Result<UInt64Array>
1525where
1526    I: Iterator<Item = (usize, Option<&'a ColumnIndexMetaData>)>,
1527{
1528    let chunks: Vec<_> = iterator.collect();
1529    let total_capacity: usize = chunks.iter().map(|(len, _)| *len).sum();
1530    let mut values = Vec::with_capacity(total_capacity);
1531    let mut nulls = NullBufferBuilder::new(total_capacity);
1532    for (len, index) in chunks {
1533        match index {
1534            Some(index) if index.nan_counts().is_some() => {
1535                values.extend(index.nan_counts().unwrap().iter().map(|&x| x as u64));
1536                nulls.append_n_non_nulls(len);
1537            }
1538            _ => {
1539                values.resize(values.len() + len, 0);
1540                nulls.append_n_nulls(len);
1541            }
1542        }
1543    }
1544    let null_buffer = nulls.build();
1545    let array = UInt64Array::new(values.into(), null_buffer);
1546    Ok(array)
1547}
1548
1549/// Extracts Parquet statistics as Arrow arrays
1550///
1551/// This is used to convert Parquet statistics to Arrow [`ArrayRef`], with
1552/// proper type conversions. This information can be used for pruning Parquet
1553/// files, row groups, and data pages based on the statistics embedded in
1554/// Parquet metadata.
1555///
1556/// # Statistics ordering
1557///
1558/// INT96 timestamp bounds are returned only when [`Self::with_column_orders`]
1559/// supplies [`ColumnOrder::INT96_TIMESTAMP_ORDER`] for the selected Parquet leaf
1560/// column. Without this order, INT96 minimum and maximum values are null, since
1561/// legacy bounds may use a different order and are not safe for timestamp pruning.
1562/// Null counts are unaffected.
1563///
1564/// # Schemas
1565///
1566/// The converter uses the schema of the Parquet file and the Arrow schema to
1567/// convert the underlying statistics value (stored as a parquet value) into the
1568/// corresponding Arrow value. For example, Decimals are stored as binary in
1569/// parquet files and this structure handles mapping them to the `i128`
1570/// representation used in Arrow.
1571///
1572/// Note: The Parquet schema and Arrow schema do not have to be identical (for
1573/// example, the columns may be in different orders and one or the other schemas
1574/// may have additional columns). The function [`parquet_column`] is used to
1575/// match the column in the Parquet schema to the column in the Arrow schema
1576/// when using [`Self::try_new`]. For nested fields (e.g., struct fields),
1577/// where `parquet_column` does not support schema resolution, use
1578/// [`Self::from_column_index`] instead with a pre-resolved leaf column index.
1579#[derive(Debug)]
1580pub struct StatisticsConverter<'a> {
1581    /// the index of the matched column in the Parquet schema
1582    parquet_column_index: Option<usize>,
1583    /// The field (with data type) of the column in the Arrow schema
1584    arrow_field: &'a Field,
1585    /// treat missing null_counts as 0 nulls
1586    missing_null_counts_as_zero: bool,
1587    /// The physical type of the matched column in the Parquet schema
1588    physical_type: Option<PhysicalType>,
1589    /// The column order of the matched Parquet leaf column, if provided
1590    column_order: Option<ColumnOrder>,
1591}
1592
1593impl<'a> StatisticsConverter<'a> {
1594    /// Return the index of the column in the Parquet schema, if any
1595    ///
1596    /// Returns `None` if the column is was present in the Arrow schema, but not
1597    /// present in the parquet file
1598    pub fn parquet_column_index(&self) -> Option<usize> {
1599        self.parquet_column_index
1600    }
1601
1602    /// Return the arrow schema's [`Field]` of the column in the Arrow schema
1603    pub fn arrow_field(&self) -> &'a Field {
1604        self.arrow_field
1605    }
1606
1607    /// Set the statistics converter to treat missing null counts as missing
1608    ///
1609    /// By default, the converter will treat missing null counts as though
1610    /// the null count is known to be `0`.
1611    ///
1612    /// Note that parquet files written by parquet-rs currently do not store
1613    /// null counts even when it is known there are zero nulls, and the reader
1614    /// will return 0 for the null counts in that instance. This behavior may
1615    /// change in a future release.
1616    ///
1617    /// Both parquet-java and parquet-cpp store null counts as 0 when there are
1618    /// no nulls, and don't write unknown values to the null count field.
1619    pub fn with_missing_null_counts_as_zero(mut self, missing_null_counts_as_zero: bool) -> Self {
1620        self.missing_null_counts_as_zero = missing_null_counts_as_zero;
1621        self
1622    }
1623
1624    /// Set the file's column orders, as returned by
1625    /// [`FileMetaData::column_orders`](crate::file::metadata::FileMetaData::column_orders).
1626    ///
1627    /// The orders must correspond to the Parquet schema's leaf columns. This
1628    /// converter selects the order using its resolved Parquet column index.
1629    /// INT96 min/max statistics are returned only when that order is
1630    /// [`ColumnOrder::INT96_TIMESTAMP_ORDER`]. Missing or other orders leave
1631    /// INT96 bounds unknown. Statistics for other physical types and null counts
1632    /// are unaffected.
1633    ///
1634    /// Only the selected order is copied, so the slice need not outlive the converter.
1635    pub fn with_column_orders(mut self, column_orders: Option<&[ColumnOrder]>) -> Self {
1636        self.column_order = self
1637            .parquet_column_index
1638            .and_then(|index| column_orders?.get(index).copied());
1639        self
1640    }
1641
1642    /// Returns a [`UInt64Array`] with row counts for each row group
1643    ///
1644    /// # Return Value
1645    ///
1646    /// The returned array has no nulls, and has one value for each row group.
1647    /// Each value is the number of rows in the row group.
1648    ///
1649    /// # Example
1650    /// ```no_run
1651    /// # use arrow::datatypes::Schema;
1652    /// # use arrow_array::{ArrayRef, UInt64Array};
1653    /// # use parquet::arrow::arrow_reader::statistics::StatisticsConverter;
1654    /// # use parquet::file::metadata::ParquetMetaData;
1655    /// # fn get_parquet_metadata() -> ParquetMetaData { unimplemented!() }
1656    /// # fn get_arrow_schema() -> Schema { unimplemented!() }
1657    /// // Given the metadata for a parquet file and the arrow schema
1658    /// let metadata: ParquetMetaData = get_parquet_metadata();
1659    /// let arrow_schema: Schema = get_arrow_schema();
1660    /// let parquet_schema = metadata.file_metadata().schema_descr();
1661    /// // create a converter
1662    /// let converter = StatisticsConverter::try_new("foo", &arrow_schema, parquet_schema)
1663    ///   .unwrap();
1664    /// // get the row counts for each row group
1665    /// let row_counts = converter.row_group_row_counts(metadata
1666    ///   .row_groups()
1667    ///   .iter()
1668    /// ).unwrap();
1669    /// // file had 2 row groups, with 1024 and 23 rows respectively
1670    /// assert_eq!(row_counts, Some(UInt64Array::from(vec![1024, 23])));
1671    /// ```
1672    pub fn row_group_row_counts<I>(&self, metadatas: I) -> Result<Option<UInt64Array>>
1673    where
1674        I: IntoIterator<Item = &'a RowGroupMetaData>,
1675    {
1676        let Some(_) = self.parquet_column_index else {
1677            return Ok(None);
1678        };
1679
1680        let mut builder = UInt64Array::builder(10);
1681        for metadata in metadatas {
1682            let row_count = metadata.num_rows();
1683            let row_count: u64 = row_count.try_into().map_err(|e| {
1684                arrow_err!(format!(
1685                    "Parquet row count {row_count} too large to convert to u64: {e}"
1686                ))
1687            })?;
1688            builder.append_value(row_count);
1689        }
1690        Ok(Some(builder.finish()))
1691    }
1692
1693    /// Create a new `StatisticsConverter` to extract statistics for a column
1694    ///
1695    /// Note if there is no corresponding column in the parquet file, the returned
1696    /// arrays will be null. This can happen if the column is in the arrow
1697    /// schema but not in the parquet schema due to schema evolution.
1698    ///
1699    /// This constructor only supports top-level, non-nested columns. For nested
1700    /// fields (e.g., fields within a struct), use [`Self::from_column_index`].
1701    ///
1702    /// See example on [`Self::row_group_mins`] for usage
1703    ///
1704    /// # Errors
1705    ///
1706    /// * If the column is not found in the arrow schema
1707    pub fn try_new<'b>(
1708        column_name: &'b str,
1709        arrow_schema: &'a Schema,
1710        parquet_schema: &'a SchemaDescriptor,
1711    ) -> Result<Self> {
1712        // ensure the requested column is in the arrow schema
1713        let Some((_idx, arrow_field)) = arrow_schema.column_with_name(column_name) else {
1714            return Err(arrow_err!(format!(
1715                "Column '{}' not found in schema for statistics conversion",
1716                column_name
1717            )));
1718        };
1719
1720        // find the column in the parquet schema, if not, return a null array
1721        let parquet_index = match parquet_column(parquet_schema, arrow_schema, column_name) {
1722            Some((parquet_idx, matched_field)) => {
1723                // sanity check that matching field matches the arrow field
1724                if matched_field.as_ref() != arrow_field {
1725                    return Err(arrow_err!(format!(
1726                        "Matched column '{:?}' does not match original matched column '{:?}'",
1727                        matched_field, arrow_field
1728                    )));
1729                }
1730                Some(parquet_idx)
1731            }
1732            None => None,
1733        };
1734
1735        Ok(Self {
1736            parquet_column_index: parquet_index,
1737            arrow_field,
1738            missing_null_counts_as_zero: true,
1739            physical_type: parquet_index.map(|idx| parquet_schema.column(idx).physical_type()),
1740            column_order: None,
1741        })
1742    }
1743
1744    /// Create a new `StatisticsConverter` from a Parquet leaf column index directly.
1745    ///
1746    /// Unlike [`Self::try_new`], this constructor bypasses schema resolution and
1747    /// accepts a Parquet column index directly. This is useful for nested fields
1748    /// (e.g., struct fields) where the caller has already resolved the mapping
1749    /// from the Arrow field to the Parquet leaf column.
1750    ///
1751    /// # Arguments
1752    ///
1753    /// * `parquet_column_index` - The index of the leaf column in the Parquet schema
1754    /// * `arrow_field` - The Arrow field describing the column's data type
1755    /// * `parquet_schema` - The Parquet schema descriptor (used to look up the physical type)
1756    ///
1757    /// The caller must ensure that `arrow_field` describes the same leaf column as
1758    /// `parquet_column_index`. This mapping is not validated by the converter; if
1759    /// the Arrow type does not match the Parquet column statistics, extraction
1760    /// returns null statistics values rather than an error.
1761    ///
1762    /// # Errors
1763    ///
1764    /// * If the `parquet_column_index` is out of bounds
1765    pub fn from_column_index(
1766        parquet_column_index: usize,
1767        arrow_field: &'a Field,
1768        parquet_schema: &'a SchemaDescriptor,
1769    ) -> Result<Self> {
1770        if parquet_column_index >= parquet_schema.columns().len() {
1771            return Err(arrow_err!(format!(
1772                "Parquet column index {} out of bounds, column count {}",
1773                parquet_column_index,
1774                parquet_schema.columns().len()
1775            )));
1776        }
1777
1778        let physical_type = parquet_schema.column(parquet_column_index).physical_type();
1779
1780        Ok(Self {
1781            parquet_column_index: Some(parquet_column_index),
1782            arrow_field,
1783            missing_null_counts_as_zero: true,
1784            physical_type: Some(physical_type),
1785            column_order: None,
1786        })
1787    }
1788
1789    /// Extract the minimum values from row group statistics in [`RowGroupMetaData`]
1790    ///
1791    /// # Return Value
1792    ///
1793    /// The returned array contains 1 value for each row group, in the same order as `metadatas`
1794    ///
1795    /// Each value is either
1796    /// * the minimum value for the column
1797    /// * a null value, if the statistics can not be extracted
1798    ///
1799    /// Note that a null value does NOT mean the min value was actually
1800    /// `null` it means it the requested statistic is unknown
1801    ///
1802    /// # Errors
1803    ///
1804    /// Reasons for not being able to extract the statistics include:
1805    /// * the column is not present in the parquet file
1806    /// * statistics for the column are not present in the row group
1807    /// * the stored statistic value can not be converted to the requested type
1808    ///
1809    /// # Example
1810    /// ```no_run
1811    /// # use std::sync::Arc;
1812    /// # use arrow::datatypes::Schema;
1813    /// # use arrow_array::{ArrayRef, Float64Array};
1814    /// # use parquet::arrow::arrow_reader::statistics::StatisticsConverter;
1815    /// # use parquet::file::metadata::ParquetMetaData;
1816    /// # fn get_parquet_metadata() -> ParquetMetaData { unimplemented!() }
1817    /// # fn get_arrow_schema() -> Schema { unimplemented!() }
1818    /// // Given the metadata for a parquet file and the arrow schema
1819    /// let metadata: ParquetMetaData = get_parquet_metadata();
1820    /// let arrow_schema: Schema = get_arrow_schema();
1821    /// let parquet_schema = metadata.file_metadata().schema_descr();
1822    /// // create a converter
1823    /// let converter = StatisticsConverter::try_new("foo", &arrow_schema, parquet_schema)
1824    ///   .unwrap();
1825    /// // get the minimum value for the column "foo" in the parquet file
1826    /// let min_values: ArrayRef = converter
1827    ///   .row_group_mins(metadata.row_groups().iter())
1828    ///   .unwrap();
1829    /// // if "foo" is a Float64 value, the returned array will contain Float64 values
1830    /// assert_eq!(min_values, Arc::new(Float64Array::from(vec![Some(1.0), Some(2.0)])) as _);
1831    /// ```
1832    pub fn row_group_mins<I>(&self, metadatas: I) -> Result<ArrayRef>
1833    where
1834        I: IntoIterator<Item = &'a RowGroupMetaData>,
1835    {
1836        let data_type = self.arrow_field.data_type();
1837
1838        let Some(parquet_index) = self.parquet_column_index else {
1839            return Ok(self.make_null_array(data_type, metadatas));
1840        };
1841
1842        let iter = metadatas.into_iter().map(|x| {
1843            x.column(parquet_index)
1844                .statistics()
1845                .filter(|_| self.is_min_max_order_supported())
1846        });
1847        min_statistics(data_type, iter, self.physical_type)
1848    }
1849
1850    /// Extract the maximum values from row group statistics in [`RowGroupMetaData`]
1851    ///
1852    /// See docs on [`Self::row_group_mins`] for details
1853    pub fn row_group_maxes<I>(&self, metadatas: I) -> Result<ArrayRef>
1854    where
1855        I: IntoIterator<Item = &'a RowGroupMetaData>,
1856    {
1857        let data_type = self.arrow_field.data_type();
1858
1859        let Some(parquet_index) = self.parquet_column_index else {
1860            return Ok(self.make_null_array(data_type, metadatas));
1861        };
1862
1863        let iter = metadatas.into_iter().map(|x| {
1864            x.column(parquet_index)
1865                .statistics()
1866                .filter(|_| self.is_min_max_order_supported())
1867        });
1868        max_statistics(data_type, iter, self.physical_type)
1869    }
1870
1871    /// Extract the `is_max_value_exact` flags from row group statistics in [`RowGroupMetaData`]
1872    ///
1873    /// See docs on [`Self::row_group_maxes`] for details
1874    pub fn row_group_is_max_value_exact<I>(&self, metadatas: I) -> Result<BooleanArray>
1875    where
1876        I: IntoIterator<Item = &'a RowGroupMetaData>,
1877    {
1878        let Some(parquet_index) = self.parquet_column_index else {
1879            let num_row_groups = metadatas.into_iter().count();
1880            return Ok(BooleanArray::from_iter(std::iter::repeat_n(
1881                None,
1882                num_row_groups,
1883            )));
1884        };
1885
1886        let is_max_value_exact = metadatas
1887            .into_iter()
1888            .map(|x| x.column(parquet_index).statistics())
1889            .map(|s| s.map(|s| s.max_is_exact()));
1890        Ok(BooleanArray::from_iter(is_max_value_exact))
1891    }
1892
1893    /// Extract the `is_min_value_exact` flags from row group statistics in [`RowGroupMetaData`]
1894    ///
1895    /// See docs on [`Self::row_group_mins`] for details
1896    pub fn row_group_is_min_value_exact<I>(&self, metadatas: I) -> Result<BooleanArray>
1897    where
1898        I: IntoIterator<Item = &'a RowGroupMetaData>,
1899    {
1900        let Some(parquet_index) = self.parquet_column_index else {
1901            let num_row_groups = metadatas.into_iter().count();
1902            return Ok(BooleanArray::from_iter(std::iter::repeat_n(
1903                None,
1904                num_row_groups,
1905            )));
1906        };
1907
1908        let is_min_value_exact = metadatas
1909            .into_iter()
1910            .map(|x| x.column(parquet_index).statistics())
1911            .map(|s| s.map(|s| s.min_is_exact()));
1912        Ok(BooleanArray::from_iter(is_min_value_exact))
1913    }
1914
1915    /// Extract the null counts from row group statistics in [`RowGroupMetaData`]
1916    ///
1917    /// See docs on [`Self::row_group_mins`] for details
1918    pub fn row_group_null_counts<I>(&self, metadatas: I) -> Result<UInt64Array>
1919    where
1920        I: IntoIterator<Item = &'a RowGroupMetaData>,
1921    {
1922        let Some(parquet_index) = self.parquet_column_index else {
1923            let num_row_groups = metadatas.into_iter().count();
1924            return Ok(UInt64Array::from_iter(std::iter::repeat_n(
1925                None,
1926                num_row_groups,
1927            )));
1928        };
1929
1930        let null_counts = metadatas
1931            .into_iter()
1932            .map(|x| x.column(parquet_index).statistics())
1933            .map(|s| {
1934                let s = s?;
1935                if self.missing_null_counts_as_zero {
1936                    Some(s.null_count_opt().unwrap_or(0))
1937                } else {
1938                    s.null_count_opt()
1939                }
1940            });
1941        Ok(UInt64Array::from_iter(null_counts))
1942    }
1943
1944    /// Extract the NaN counts from row group statistics in [`RowGroupMetaData`]
1945    ///
1946    /// See docs on [`Self::row_group_mins`] for details
1947    pub fn row_group_nan_counts<I>(&self, metadatas: I) -> Result<UInt64Array>
1948    where
1949        I: IntoIterator<Item = &'a RowGroupMetaData>,
1950    {
1951        let Some(parquet_index) = self.parquet_column_index else {
1952            let num_row_groups = metadatas.into_iter().count();
1953            return Ok(UInt64Array::from_iter(std::iter::repeat_n(
1954                None,
1955                num_row_groups,
1956            )));
1957        };
1958
1959        let nan_counts = metadatas
1960            .into_iter()
1961            .map(|x| x.column(parquet_index).statistics())
1962            .map(|s| s?.nan_count_opt());
1963        Ok(UInt64Array::from_iter(nan_counts))
1964    }
1965
1966    /// Extract the distinct counts from row group statistics in [`RowGroupMetaData`]
1967    ///
1968    /// See docs on [`Self::row_group_mins`] for details
1969    pub fn row_group_distinct_counts<I>(&self, metadatas: I) -> Result<UInt64Array>
1970    where
1971        I: IntoIterator<Item = &'a RowGroupMetaData>,
1972    {
1973        let Some(parquet_index) = self.parquet_column_index else {
1974            let num_row_groups = metadatas.into_iter().count();
1975            return Ok(UInt64Array::from_iter(std::iter::repeat_n(
1976                None,
1977                num_row_groups,
1978            )));
1979        };
1980
1981        let distinct_counts = metadatas
1982            .into_iter()
1983            .map(|x| x.column(parquet_index).statistics())
1984            .map(|s| s?.distinct_count_opt());
1985        Ok(UInt64Array::from_iter(distinct_counts))
1986    }
1987
1988    /// Extract the minimum values from Data Page statistics.
1989    ///
1990    /// In Parquet files, in addition to the Column Chunk level statistics
1991    /// (stored for each column for each row group) there are also
1992    /// optional statistics stored for each data page, as part of
1993    /// the [`PageIndex`].
1994    ///
1995    /// Since a single Column Chunk is stored as one or more pages,
1996    /// page level statistics can prune at a finer granularity.
1997    ///
1998    /// However since they are stored in a separate metadata
1999    /// structure ([`ColumnIndexMetaData`]) there is different code to extract them as
2000    /// compared to arrow statistics.
2001    ///
2002    /// # Parameters:
2003    ///
2004    /// * `page_index`: The parquet page indices, read from `ParquetMetaData`
2005    ///
2006    /// * `row_group_indices`: The indices of the row groups, that are used to
2007    ///   extract the column page index and offset index on a per row group
2008    ///   per column basis.
2009    ///
2010    /// # Return Value
2011    ///
2012    /// The returned array contains 1 value for each `NativeIndex`
2013    /// in the underlying `Index`es, in the same order as they appear
2014    /// in `metadatas`.
2015    ///
2016    /// For example, if there are two `Index`es in `metadatas`:
2017    /// 1. the first having `3` `PageIndex` entries
2018    /// 2. the second having `2` `PageIndex` entries
2019    ///
2020    /// The returned array would have 5 rows.
2021    ///
2022    /// Each value is either:
2023    /// * the minimum value for the page
2024    /// * a null value, if the statistics can not be extracted
2025    ///
2026    /// Note that a null value does NOT mean the min value was actually
2027    /// `null` it means it the requested statistic is unknown
2028    ///
2029    /// # Errors
2030    ///
2031    /// Reasons for not being able to extract the statistics include:
2032    /// * the column is not present in the parquet file
2033    /// * statistics for the pages are not present in the row group
2034    /// * the stored statistic value can not be converted to the requested type
2035    ///
2036    /// [`PageIndex`]: crate::file::metadata::page_index::PageIndex
2037    pub fn data_page_mins<I>(
2038        &self,
2039        page_index: &dyn PageIndexProvider,
2040        row_group_indices: I,
2041    ) -> Result<ArrayRef>
2042    where
2043        I: IntoIterator<Item = &'a usize>,
2044    {
2045        let data_type = self.arrow_field.data_type();
2046
2047        let Some(parquet_index) = self.parquet_column_index else {
2048            return Ok(self.make_null_array(data_type, row_group_indices));
2049        };
2050
2051        let iter = row_group_indices.into_iter().map(|rg_index| {
2052            let column_page_index_per_row_group_per_column = page_index
2053                .column_index(*rg_index, parquet_index)
2054                .filter(|_| self.is_min_max_order_supported());
2055            let num_data_pages = page_index
2056                .num_data_pages(*rg_index, parquet_index)
2057                .unwrap_or(0);
2058
2059            (num_data_pages, column_page_index_per_row_group_per_column)
2060        });
2061
2062        min_page_statistics(data_type, iter, self.physical_type)
2063    }
2064
2065    /// Extract the maximum values from Data Page statistics.
2066    ///
2067    /// See docs on [`Self::data_page_mins`] for details.
2068    pub fn data_page_maxes<I>(
2069        &self,
2070        page_index: &dyn PageIndexProvider,
2071        row_group_indices: I,
2072    ) -> Result<ArrayRef>
2073    where
2074        I: IntoIterator<Item = &'a usize>,
2075    {
2076        let data_type = self.arrow_field.data_type();
2077
2078        let Some(parquet_index) = self.parquet_column_index else {
2079            return Ok(self.make_null_array(data_type, row_group_indices));
2080        };
2081
2082        let iter = row_group_indices.into_iter().map(|rg_index| {
2083            let column_page_index_per_row_group_per_column = page_index
2084                .column_index(*rg_index, parquet_index)
2085                .filter(|_| self.is_min_max_order_supported());
2086            let num_data_pages = page_index
2087                .num_data_pages(*rg_index, parquet_index)
2088                .unwrap_or(0);
2089
2090            (num_data_pages, column_page_index_per_row_group_per_column)
2091        });
2092
2093        max_page_statistics(data_type, iter, self.physical_type)
2094    }
2095
2096    /// Returns a [`UInt64Array`] with null counts for each data page.
2097    ///
2098    /// See docs on [`Self::data_page_mins`] for details.
2099    pub fn data_page_null_counts<I>(
2100        &self,
2101        page_index: &dyn PageIndexProvider,
2102        row_group_indices: I,
2103    ) -> Result<UInt64Array>
2104    where
2105        I: IntoIterator<Item = &'a usize>,
2106    {
2107        let Some(parquet_index) = self.parquet_column_index else {
2108            let num_row_groups = row_group_indices.into_iter().count();
2109            return Ok(UInt64Array::new_null(num_row_groups));
2110        };
2111
2112        let iter = row_group_indices.into_iter().map(|rg_index| {
2113            let column_page_index_per_row_group_per_column =
2114                page_index.column_index(*rg_index, parquet_index);
2115            let num_data_pages = page_index
2116                .num_data_pages(*rg_index, parquet_index)
2117                .unwrap_or(0);
2118
2119            (num_data_pages, column_page_index_per_row_group_per_column)
2120        });
2121        null_counts_page_statistics(iter)
2122    }
2123
2124    /// Returns a [`UInt64Array`] with NaN counts for each data page.
2125    ///
2126    /// See docs on [`Self::data_page_mins`] for details.
2127    pub fn data_page_nan_counts<I>(
2128        &self,
2129        page_index: &dyn PageIndexProvider,
2130        row_group_indices: I,
2131    ) -> Result<UInt64Array>
2132    where
2133        I: IntoIterator<Item = &'a usize>,
2134    {
2135        let Some(parquet_index) = self.parquet_column_index else {
2136            let num_row_groups = row_group_indices.into_iter().count();
2137            return Ok(UInt64Array::new_null(num_row_groups));
2138        };
2139
2140        let iter = row_group_indices.into_iter().map(|rg_index| {
2141            let column_page_index_per_row_group_per_column =
2142                page_index.column_index(*rg_index, parquet_index);
2143            let num_data_pages = page_index
2144                .num_data_pages(*rg_index, parquet_index)
2145                .unwrap_or(0);
2146
2147            (num_data_pages, column_page_index_per_row_group_per_column)
2148        });
2149        nan_counts_page_statistics(iter)
2150    }
2151
2152    /// Returns a [`UInt64Array`] with row counts for each data page.
2153    ///
2154    /// This function iterates over the given row group indexes and computes
2155    /// the row count for each page in the specified column.
2156    ///
2157    /// # Parameters:
2158    ///
2159    /// * `column_offset_index`: The parquet column offset indices, read from
2160    ///   `ParquetMetaData` offset_index
2161    ///
2162    /// * `row_group_metadatas`: The metadata slice of the row groups, read
2163    ///   from `ParquetMetaData` row_groups
2164    ///
2165    /// * `row_group_indices`: The indices of the row groups, that are used to
2166    ///   extract the column offset index on a per row group per column basis.
2167    ///
2168    /// See docs on [`Self::data_page_mins`] for details.
2169    pub fn data_page_row_counts<I>(
2170        &self,
2171        page_index: &dyn PageIndexProvider,
2172        row_group_metadatas: &'a [RowGroupMetaData],
2173        row_group_indices: I,
2174    ) -> Result<Option<UInt64Array>>
2175    where
2176        I: IntoIterator<Item = &'a usize>,
2177    {
2178        let Some(parquet_index) = self.parquet_column_index else {
2179            // no matching column found in parquet_index;
2180            // thus we cannot extract page_locations in order to determine
2181            // the row count on a per DataPage basis.
2182            return Ok(None);
2183        };
2184
2185        let mut row_counts = Vec::new();
2186        let mut nulls = NullBufferBuilder::new(0);
2187        for rg_idx in row_group_indices {
2188            let Some(offset_index) = page_index.offset_index(*rg_idx, parquet_index) else {
2189                // A partial page index may contain the ColumnIndex but not the OffsetIndex.
2190                // Preserve page-wise alignment with data_page_{mins,maxes,null_counts} by
2191                // emitting one null row count for every page known from the ColumnIndex.
2192                let num_pages = page_index
2193                    .column_index(*rg_idx, parquet_index)
2194                    .map(|index| index.num_pages() as usize)
2195                    .unwrap_or(0);
2196                row_counts.resize(row_counts.len() + num_pages, 0);
2197                nulls.append_n_nulls(num_pages);
2198                continue;
2199            };
2200            let page_locations = offset_index.page_locations();
2201
2202            let row_count_per_page = page_locations
2203                .windows(2)
2204                .map(|loc| Some(loc[1].first_row_index as u64 - loc[0].first_row_index as u64));
2205
2206            // append the last page row count
2207            let num_rows_in_row_group = &row_group_metadatas[*rg_idx].num_rows();
2208            let row_count_per_page = row_count_per_page.chain(std::iter::once(Some(
2209                *num_rows_in_row_group as u64
2210                    - page_locations.last().unwrap().first_row_index as u64,
2211            )));
2212
2213            row_counts.extend(row_count_per_page.clone().map(|x| x.unwrap_or(0)));
2214            for val in row_count_per_page {
2215                if val.is_some() {
2216                    nulls.append_non_null();
2217                } else {
2218                    nulls.append_null();
2219                }
2220            }
2221        }
2222
2223        Ok(Some(UInt64Array::new(row_counts.into(), nulls.build())))
2224    }
2225
2226    fn is_min_max_order_supported(&self) -> bool {
2227        self.physical_type != Some(PhysicalType::INT96)
2228            || self.column_order == Some(ColumnOrder::INT96_TIMESTAMP_ORDER)
2229    }
2230
2231    /// Returns a null array of data_type with one element per row group
2232    fn make_null_array<I, A>(&self, data_type: &DataType, metadatas: I) -> ArrayRef
2233    where
2234        I: IntoIterator<Item = A>,
2235    {
2236        // column was in the arrow schema but not in the parquet schema, so return a null array
2237        let num_row_groups = metadatas.into_iter().count();
2238        new_null_array(data_type, num_row_groups)
2239    }
2240}
2241
2242// See tests in parquet/tests/arrow_reader/statistics.rs