1use std::sync::Arc;
24
25use crate::errors::ParquetError;
26use crate::file::metadata::page_index::{PageIndexBuilder, PageIndexProvider};
27use crate::file::metadata::thrift::parquet_metadata_from_bytes;
28use crate::file::metadata::{
29 ColumnChunkMetaData, PageIndexPolicy, ParquetMetaData, ParquetMetaDataOptions,
30};
31
32use crate::file::page_index::column_index::ColumnIndexMetaData;
33use crate::file::page_index::index_reader::{decode_column_index, decode_offset_index};
34use crate::file::page_index::offset_index::OffsetIndexMetaData;
35use bytes::Bytes;
36
37pub(crate) use inner::MetadataParser;
45
46#[cfg(feature = "encryption")]
47mod inner {
48 use std::sync::Arc;
49
50 use super::*;
51 use crate::encryption::decrypt::FileDecryptionProperties;
52 use crate::errors::Result;
53
54 #[derive(Debug, Default)]
56 pub(crate) struct MetadataParser {
57 file_decryption_properties: Option<Arc<FileDecryptionProperties>>,
59 metadata_options: Option<Arc<ParquetMetaDataOptions>>,
61 }
62
63 impl MetadataParser {
64 pub(crate) fn new() -> Self {
65 MetadataParser::default()
66 }
67
68 pub(crate) fn with_file_decryption_properties(
69 mut self,
70 file_decryption_properties: Option<Arc<FileDecryptionProperties>>,
71 ) -> Self {
72 self.file_decryption_properties = file_decryption_properties;
73 self
74 }
75
76 pub(crate) fn with_metadata_options(
77 self,
78 options: Option<Arc<ParquetMetaDataOptions>>,
79 ) -> Self {
80 Self {
81 metadata_options: options,
82 ..self
83 }
84 }
85
86 pub(crate) fn decode_metadata(
87 &self,
88 buf: &[u8],
89 encrypted_footer: bool,
90 ) -> Result<ParquetMetaData> {
91 if encrypted_footer || self.file_decryption_properties.is_some() {
92 crate::file::metadata::thrift::encryption::parquet_metadata_with_encryption(
93 self.file_decryption_properties.as_ref(),
94 encrypted_footer,
95 buf,
96 self.metadata_options.as_deref(),
97 )
98 } else {
99 decode_metadata(buf, self.metadata_options.as_deref())
100 }
101 }
102 }
103
104 pub(super) fn parse_single_column_index(
105 bytes: &[u8],
106 metadata: &ParquetMetaData,
107 column: &ColumnChunkMetaData,
108 row_group_index: usize,
109 col_index: usize,
110 ) -> crate::errors::Result<ColumnIndexMetaData> {
111 use crate::encryption::decrypt::CryptoContext;
112 match &column.column_crypto_metadata {
113 Some(crypto_metadata) => {
114 let file_decryptor = metadata.file_decryptor.as_ref().ok_or_else(|| {
115 general_err!("Cannot decrypt column index, no file decryptor set")
116 })?;
117 let crypto_context = CryptoContext::for_column(
118 file_decryptor,
119 crypto_metadata,
120 row_group_index,
121 col_index,
122 )?;
123 let column_decryptor = crypto_context.metadata_decryptor();
124 let aad = crypto_context.create_column_index_aad()?;
125 let plaintext = column_decryptor.decrypt(bytes, &aad)?;
126 decode_column_index(&plaintext, column.column_type())
127 }
128 None => decode_column_index(bytes, column.column_type()),
129 }
130 }
131
132 pub(super) fn parse_single_offset_index(
133 bytes: &[u8],
134 metadata: &ParquetMetaData,
135 column: &ColumnChunkMetaData,
136 row_group_index: usize,
137 col_index: usize,
138 ) -> crate::errors::Result<OffsetIndexMetaData> {
139 use crate::encryption::decrypt::CryptoContext;
140 match &column.column_crypto_metadata {
141 Some(crypto_metadata) => {
142 let file_decryptor = metadata.file_decryptor.as_ref().ok_or_else(|| {
143 general_err!("Cannot decrypt offset index, no file decryptor set")
144 })?;
145 let crypto_context = CryptoContext::for_column(
146 file_decryptor,
147 crypto_metadata,
148 row_group_index,
149 col_index,
150 )?;
151 let column_decryptor = crypto_context.metadata_decryptor();
152 let aad = crypto_context.create_offset_index_aad()?;
153 let plaintext = column_decryptor.decrypt(bytes, &aad)?;
154 decode_offset_index(&plaintext)
155 }
156 None => decode_offset_index(bytes),
157 }
158 }
159}
160
161#[cfg(not(feature = "encryption"))]
162mod inner {
163 use super::*;
164 use crate::errors::Result;
165 use std::sync::Arc;
166 #[derive(Debug, Default)]
170 pub(crate) struct MetadataParser {
171 metadata_options: Option<Arc<ParquetMetaDataOptions>>,
173 }
174
175 impl MetadataParser {
176 pub(crate) fn new() -> Self {
177 MetadataParser::default()
178 }
179
180 pub(crate) fn with_metadata_options(
181 self,
182 options: Option<Arc<ParquetMetaDataOptions>>,
183 ) -> Self {
184 Self {
185 metadata_options: options,
186 }
187 }
188
189 pub(crate) fn decode_metadata(
190 &self,
191 buf: &[u8],
192 encrypted_footer: bool,
193 ) -> Result<ParquetMetaData> {
194 if encrypted_footer {
195 Err(general_err!(
196 "Parquet file has an encrypted footer but the encryption feature is disabled"
197 ))
198 } else {
199 decode_metadata(buf, self.metadata_options.as_deref())
200 }
201 }
202 }
203
204 pub(super) fn parse_single_column_index(
205 bytes: &[u8],
206 _metadata: &ParquetMetaData,
207 column: &ColumnChunkMetaData,
208 _row_group_index: usize,
209 _col_index: usize,
210 ) -> crate::errors::Result<ColumnIndexMetaData> {
211 decode_column_index(bytes, column.column_type())
212 }
213
214 pub(super) fn parse_single_offset_index(
215 bytes: &[u8],
216 _metadata: &ParquetMetaData,
217 _column: &ColumnChunkMetaData,
218 _row_group_index: usize,
219 _col_index: usize,
220 ) -> crate::errors::Result<OffsetIndexMetaData> {
221 decode_offset_index(bytes)
222 }
223}
224
225pub(crate) fn decode_metadata(
233 buf: &[u8],
234 options: Option<&ParquetMetaDataOptions>,
235) -> crate::errors::Result<ParquetMetaData> {
236 parquet_metadata_from_bytes(buf, options)
237}
238
239pub(crate) fn parse_page_index(
250 metadata: &mut ParquetMetaData,
251 column_index_policy: PageIndexPolicy,
252 offset_index_policy: PageIndexPolicy,
253 bytes: &Bytes,
254 start_offset: u64,
255) -> crate::errors::Result<()> {
256 if column_index_policy == PageIndexPolicy::Skip && offset_index_policy == PageIndexPolicy::Skip
257 {
258 return Ok(());
259 }
260 let num_row_groups = metadata.num_row_groups();
261 let num_columns = metadata.file_metadata().schema_descr().num_columns();
262 let mut builder = PageIndexBuilder::default();
263 if column_index_policy != PageIndexPolicy::Skip {
264 builder.allocate_column_indexes(num_row_groups, num_columns);
265 parse_column_index(
266 metadata,
267 column_index_policy,
268 &mut builder,
269 bytes,
270 start_offset,
271 )?;
272 }
273 if offset_index_policy != PageIndexPolicy::Skip {
274 builder.allocate_offset_indexes(num_row_groups, num_columns);
275 parse_offset_index(
276 metadata,
277 offset_index_policy,
278 &mut builder,
279 bytes,
280 start_offset,
281 )?;
282 }
283
284 let page_index = builder.build();
285 if !page_index.has_column_indexes() && !page_index.has_offset_indexes() {
287 return Ok(());
288 }
289 metadata.set_page_index(Some(Arc::new(page_index)));
290 Ok(())
291}
292
293fn parse_column_index(
294 metadata: &ParquetMetaData,
295 column_index_policy: PageIndexPolicy,
296 page_index_builder: &mut PageIndexBuilder,
297 bytes: &Bytes,
298 start_offset: u64,
299) -> crate::errors::Result<()> {
300 if column_index_policy == PageIndexPolicy::Skip {
301 return Ok(());
302 }
303 for rg_idx in 0..metadata.num_row_groups() {
304 let rg = metadata.row_group(rg_idx);
305 for col_idx in 0..rg.num_columns() {
306 let col = rg.column(col_idx);
307 if let Some(r) = col.column_index_range() {
308 let r_start = usize::try_from(r.start - start_offset)?;
309 let r_end = usize::try_from(r.end - start_offset)?;
310 let idx = inner::parse_single_column_index(
311 &bytes[r_start..r_end],
312 metadata,
313 col,
314 rg_idx,
315 col_idx,
316 )?;
317 page_index_builder.put_column_index(idx, rg_idx, col_idx);
318 }
319 }
320 }
321
322 Ok(())
323}
324
325fn parse_offset_index(
326 metadata: &ParquetMetaData,
327 offset_index_policy: PageIndexPolicy,
328 page_index_builder: &mut PageIndexBuilder,
329 bytes: &Bytes,
330 start_offset: u64,
331) -> crate::errors::Result<()> {
332 if offset_index_policy == PageIndexPolicy::Skip {
333 return Ok(());
334 }
335 for rg_idx in 0..metadata.num_row_groups() {
336 let rg = metadata.row_group(rg_idx);
337 for col_idx in 0..rg.num_columns() {
338 let col = rg.column(col_idx);
339 if let Some(r) = col.offset_index_range() {
340 let r_start = usize::try_from(r.start - start_offset)?;
341 let r_end = usize::try_from(r.end - start_offset)?;
342 let idx = inner::parse_single_offset_index(
343 &bytes[r_start..r_end],
344 metadata,
345 col,
346 rg_idx,
347 col_idx,
348 )?;
349 page_index_builder.put_offset_index(idx, rg_idx, col_idx);
350 } else if offset_index_policy == PageIndexPolicy::Required {
351 return Err(general_err!("missing offset index"));
352 }
353 }
354 }
355
356 Ok(())
357}