Skip to main content

parquet/arrow/array_reader/
mod.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//! Logic for reading into arrow arrays: [`ArrayReader`] and [`RowGroups`]
6
7use crate::errors::Result;
8use arrow_array::ArrayRef;
9use arrow_schema::DataType as ArrowType;
10use std::any::Any;
11use std::sync::Arc;
12
13use crate::arrow::record_reader::GenericRecordReader;
14use crate::arrow::record_reader::buffer::ValuesBuffer;
15use crate::column::page::PageIterator;
16use crate::column::reader::decoder::ColumnValueDecoder;
17use crate::file::metadata::ParquetMetaData;
18use crate::file::reader::{FilePageIterator, FileReader};
19
20mod builder;
21mod byte_array;
22mod byte_array_dictionary;
23mod byte_view_array;
24mod cached_array_reader;
25mod empty_array;
26mod fixed_len_byte_array;
27mod fixed_size_list_array;
28mod list_array;
29mod list_view_array;
30mod map_array;
31mod null_array;
32mod primitive_array;
33mod row_group_cache;
34mod row_group_index;
35mod row_number;
36mod struct_array;
37
38#[cfg(test)]
39pub(crate) mod test_util;
40
41// Note that this crate is public under the `experimental` feature flag.
42use crate::file::metadata::RowGroupMetaData;
43pub use builder::{ArrayReaderBuilder, CacheOptions, CacheOptionsBuilder};
44pub use byte_array::make_byte_array_reader;
45// Re-exported (beyond the `experimental` feature) so `file::metadata::dictionary`
46// can PLAIN-decode a raw dictionary page without duplicating this logic.
47pub(crate) use byte_array::ByteArrayDecoderPlain;
48pub use byte_array_dictionary::make_byte_array_dictionary_reader;
49#[cfg_attr(not(feature = "experimental"), expect(unused_imports))]
50pub use byte_view_array::make_byte_view_array_reader;
51#[cfg_attr(not(feature = "experimental"), expect(unused_imports))]
52pub use fixed_len_byte_array::make_fixed_len_byte_array_reader;
53pub use fixed_size_list_array::FixedSizeListArrayReader;
54pub use list_array::ListArrayReader;
55pub use list_view_array::ListViewArrayReader;
56pub use map_array::MapArrayReader;
57pub use null_array::NullArrayReader;
58pub use primitive_array::PrimitiveArrayReader;
59pub use row_group_cache::RowGroupCache;
60pub use struct_array::StructArrayReader;
61
62/// Reads Parquet data into Arrow Arrays.
63///
64/// This is an internal implementation detail of the Parquet reader, and is not
65/// intended for public use.
66///
67/// This is the core trait for reading encoded Parquet data directly into Arrow
68/// Arrays efficiently. There are various specializations of this trait for
69/// different combinations of encodings and arrays, such as
70/// [`PrimitiveArrayReader`], [`ListArrayReader`], etc.
71///
72/// Each `ArrayReader` logically contains the following state
73/// 1. A handle to the encoded Parquet data
74/// 2. An in progress buffered Array
75///
76/// Data can either be read in batches using [`ArrayReader::next_batch`] or
77/// incrementally using [`ArrayReader::read_records`] and [`ArrayReader::skip_records`].
78pub trait ArrayReader: Send {
79    // TODO: this function is never used, and the trait is not public. Perhaps this should be
80    // removed.
81    fn as_any(&self) -> &dyn Any;
82
83    /// Returns the arrow type of this array reader.
84    fn get_data_type(&self) -> &ArrowType;
85
86    /// Reads at most `batch_size` records into an arrow array and return it.
87    #[cfg(any(feature = "experimental", test))]
88    fn next_batch(&mut self, batch_size: usize) -> Result<ArrayRef> {
89        self.read_records(batch_size)?;
90        self.consume_batch()
91    }
92
93    /// Reads at most `batch_size` records' bytes into buffer
94    ///
95    /// Returns the number of records read, which can be less than `batch_size` if
96    /// pages is exhausted.
97    fn read_records(&mut self, batch_size: usize) -> Result<usize>;
98
99    /// Consume all currently stored buffer data
100    /// into an arrow array and return it.
101    fn consume_batch(&mut self) -> Result<ArrayRef>;
102
103    /// Skips over `num_records` records, returning the number of rows skipped
104    ///
105    /// Note that calling `skip_records` with large values of `num_records` is
106    /// efficient as it avoids decoding data into the the in-progress array.
107    /// However, there is overhead to calling this function, so for small values of
108    /// `num_records`, it can be more efficient to call read_records and apply
109    /// a filter to the resulting array.
110    fn skip_records(&mut self, num_records: usize) -> Result<usize>;
111
112    /// If this array has a non-zero definition level, i.e. has a nullable parent
113    /// array, returns the definition levels of data from the last call of `next_batch`
114    ///
115    /// Otherwise returns None
116    ///
117    /// This is used by parent [`ArrayReader`] to compute their null bitmaps
118    fn get_def_levels(&self) -> Option<&[i16]>;
119
120    /// If this array has a non-zero repetition level, i.e. has a repeated parent
121    /// array, returns the repetition levels of data from the last call of `next_batch`
122    ///
123    /// Otherwise returns None
124    ///
125    /// This is used by parent [`ArrayReader`] to compute their array offsets
126    fn get_rep_levels(&self) -> Option<&[i16]>;
127
128    /// Returns the maximum definition level for this reader as defined by
129    /// the Parquet schema. For leaf readers this is the column's max def level;
130    /// for composite readers it is the level at which the composite itself is
131    /// fully defined.
132    ///
133    /// The default panics. Synthetic readers that are not backed by a Parquet
134    /// column (e.g. row-number or empty-struct readers) may rely on this
135    /// default because they are never used as children of schema-driven
136    /// composite readers (list, map, struct), which are the only callers.
137    fn max_def_level(&self) -> i16 {
138        panic!("max_def_level called on a reader that does not track definition levels")
139    }
140}
141
142/// Interface for reading data pages from the columns of one or more RowGroups.
143pub trait RowGroups {
144    /// Get the number of rows in this collection
145    fn num_rows(&self) -> usize;
146
147    /// Returns a [`PageIterator`] for all pages in the specified column chunk
148    /// across all row groups in this collection.
149    fn column_chunks(&self, i: usize) -> Result<Box<dyn PageIterator>>;
150
151    /// Returns an iterator over the row groups in this collection
152    ///
153    /// Note this may not include all row groups in [`Self::metadata`].
154    fn row_groups(&self) -> Box<dyn Iterator<Item = &RowGroupMetaData> + '_>;
155
156    /// Returns the parquet metadata
157    fn metadata(&self) -> &ParquetMetaData;
158}
159
160impl RowGroups for Arc<dyn FileReader> {
161    fn num_rows(&self) -> usize {
162        FileReader::metadata(self.as_ref())
163            .file_metadata()
164            .num_rows() as usize
165    }
166
167    fn column_chunks(&self, column_index: usize) -> Result<Box<dyn PageIterator>> {
168        let iterator = FilePageIterator::new(column_index, Arc::clone(self))?;
169        Ok(Box::new(iterator))
170    }
171
172    fn row_groups(&self) -> Box<dyn Iterator<Item = &RowGroupMetaData> + '_> {
173        Box::new(FileReader::metadata(self.as_ref()).row_groups().iter())
174    }
175
176    fn metadata(&self) -> &ParquetMetaData {
177        FileReader::metadata(self.as_ref())
178    }
179}
180
181/// Uses `record_reader` to read up to `batch_size` records from `pages`
182///
183/// Returns the number of records read, which can be less than `batch_size` if
184/// pages is exhausted.
185fn read_records<V, CV>(
186    record_reader: &mut GenericRecordReader<V, CV>,
187    pages: &mut dyn PageIterator,
188    batch_size: usize,
189) -> Result<usize>
190where
191    V: ValuesBuffer,
192    CV: ColumnValueDecoder<Buffer = V>,
193{
194    let mut records_read = 0usize;
195    while records_read < batch_size {
196        let records_to_read = batch_size - records_read;
197
198        let records_read_once = record_reader.read_records(records_to_read)?;
199        records_read += records_read_once;
200
201        // Record reader exhausted
202        if records_read_once < records_to_read {
203            if let Some(page_reader) = pages.next() {
204                // Read from new page reader (i.e. column chunk)
205                record_reader.set_page_reader(page_reader?)?;
206            } else {
207                // Page reader also exhausted
208                break;
209            }
210        }
211    }
212    Ok(records_read)
213}
214
215/// Uses `record_reader` to skip up to `batch_size` records from `pages`
216///
217/// Returns the number of records skipped, which can be less than `batch_size` if
218/// pages is exhausted
219fn skip_records<V, CV>(
220    record_reader: &mut GenericRecordReader<V, CV>,
221    pages: &mut dyn PageIterator,
222    batch_size: usize,
223) -> Result<usize>
224where
225    V: ValuesBuffer,
226    CV: ColumnValueDecoder<Buffer = V>,
227{
228    let mut records_skipped = 0usize;
229    while records_skipped < batch_size {
230        let records_to_read = batch_size - records_skipped;
231
232        let records_skipped_once = record_reader.skip_records(records_to_read)?;
233        records_skipped += records_skipped_once;
234
235        // Record reader exhausted
236        if records_skipped_once < records_to_read {
237            if let Some(page_reader) = pages.next() {
238                // Read from new page reader (i.e. column chunk)
239                record_reader.set_page_reader(page_reader?)?;
240            } else {
241                // Page reader also exhausted
242                break;
243            }
244        }
245    }
246    Ok(records_skipped)
247}