1use crate::basic::Type as PhysicalType;
6use crate::column::reader::{ColumnReader, ColumnReaderImpl, get_typed_column_reader};
7use crate::data_type::*;
8use crate::errors::{ParquetError, Result};
9use crate::record::api::Field;
10use crate::schema::types::ColumnDescPtr;
11
12macro_rules! triplet_enum_func {
16 ($self:ident, $func:ident, $( $token:tt ),*) => ({
17 match *$self {
18 TripletIter::BoolTripletIter($($token)* typed) => typed.$func(),
19 TripletIter::Int32TripletIter($($token)* typed) => typed.$func(),
20 TripletIter::Int64TripletIter($($token)* typed) => typed.$func(),
21 TripletIter::Int96TripletIter($($token)* typed) => typed.$func(),
22 TripletIter::FloatTripletIter($($token)* typed) => typed.$func(),
23 TripletIter::DoubleTripletIter($($token)* typed) => typed.$func(),
24 TripletIter::ByteArrayTripletIter($($token)* typed) => typed.$func(),
25 TripletIter::FixedLenByteArrayTripletIter($($token)* typed) => typed.$func()
26 }
27 });
28}
29
30#[expect(clippy::enum_variant_names)]
33pub enum TripletIter {
34 BoolTripletIter(TypedTripletIter<BoolType>),
35 Int32TripletIter(TypedTripletIter<Int32Type>),
36 Int64TripletIter(TypedTripletIter<Int64Type>),
37 Int96TripletIter(TypedTripletIter<Int96Type>),
38 FloatTripletIter(TypedTripletIter<FloatType>),
39 DoubleTripletIter(TypedTripletIter<DoubleType>),
40 ByteArrayTripletIter(TypedTripletIter<ByteArrayType>),
41 FixedLenByteArrayTripletIter(TypedTripletIter<FixedLenByteArrayType>),
42}
43
44impl TripletIter {
45 pub fn new(descr: ColumnDescPtr, reader: ColumnReader, batch_size: usize) -> Self {
47 match descr.physical_type() {
48 PhysicalType::BOOLEAN => {
49 TripletIter::BoolTripletIter(TypedTripletIter::new(descr, batch_size, reader))
50 }
51 PhysicalType::INT32 => {
52 TripletIter::Int32TripletIter(TypedTripletIter::new(descr, batch_size, reader))
53 }
54 PhysicalType::INT64 => {
55 TripletIter::Int64TripletIter(TypedTripletIter::new(descr, batch_size, reader))
56 }
57 PhysicalType::INT96 => {
58 TripletIter::Int96TripletIter(TypedTripletIter::new(descr, batch_size, reader))
59 }
60 PhysicalType::FLOAT => {
61 TripletIter::FloatTripletIter(TypedTripletIter::new(descr, batch_size, reader))
62 }
63 PhysicalType::DOUBLE => {
64 TripletIter::DoubleTripletIter(TypedTripletIter::new(descr, batch_size, reader))
65 }
66 PhysicalType::BYTE_ARRAY => {
67 TripletIter::ByteArrayTripletIter(TypedTripletIter::new(descr, batch_size, reader))
68 }
69 PhysicalType::FIXED_LEN_BYTE_ARRAY => TripletIter::FixedLenByteArrayTripletIter(
70 TypedTripletIter::new(descr, batch_size, reader),
71 ),
72 }
73 }
74
75 #[inline]
78 pub fn read_next(&mut self) -> Result<bool> {
79 triplet_enum_func!(self, read_next, ref, mut)
80 }
81
82 #[inline]
87 pub fn has_next(&self) -> bool {
88 triplet_enum_func!(self, has_next, ref)
89 }
90
91 #[inline]
93 pub fn current_def_level(&self) -> i16 {
94 triplet_enum_func!(self, current_def_level, ref)
95 }
96
97 #[inline]
99 pub fn max_def_level(&self) -> i16 {
100 triplet_enum_func!(self, max_def_level, ref)
101 }
102
103 #[inline]
105 pub fn current_rep_level(&self) -> i16 {
106 triplet_enum_func!(self, current_rep_level, ref)
107 }
108
109 #[inline]
111 pub fn max_rep_level(&self) -> i16 {
112 triplet_enum_func!(self, max_rep_level, ref)
113 }
114
115 #[inline]
119 pub fn is_null(&self) -> bool {
120 self.current_def_level() < self.max_def_level()
121 }
122
123 pub fn current_value(&self) -> Result<Field> {
125 if self.is_null() {
126 return Ok(Field::Null);
127 }
128 let field = match *self {
129 TripletIter::BoolTripletIter(ref typed) => {
130 Field::convert_bool(typed.column_descr(), *typed.current_value())
131 }
132 TripletIter::Int32TripletIter(ref typed) => {
133 Field::convert_int32(typed.column_descr(), *typed.current_value())
134 }
135 TripletIter::Int64TripletIter(ref typed) => {
136 Field::convert_int64(typed.column_descr(), *typed.current_value())
137 }
138 TripletIter::Int96TripletIter(ref typed) => {
139 Field::convert_int96(typed.column_descr(), *typed.current_value())
140 }
141 TripletIter::FloatTripletIter(ref typed) => {
142 Field::convert_float(typed.column_descr(), *typed.current_value())
143 }
144 TripletIter::DoubleTripletIter(ref typed) => {
145 Field::convert_double(typed.column_descr(), *typed.current_value())
146 }
147 TripletIter::ByteArrayTripletIter(ref typed) => {
148 Field::convert_byte_array(typed.column_descr(), typed.current_value().clone())?
149 }
150 TripletIter::FixedLenByteArrayTripletIter(ref typed) => Field::convert_byte_array(
151 typed.column_descr(),
152 typed.current_value().clone().into(),
153 )?,
154 };
155 Ok(field)
156 }
157}
158
159pub struct TypedTripletIter<T: DataType> {
162 reader: ColumnReaderImpl<T>,
163 column_descr: ColumnDescPtr,
164 batch_size: usize,
165 max_def_level: i16,
167 max_rep_level: i16,
168 values: Vec<T::T>,
170 def_levels: Option<Vec<i16>>,
171 rep_levels: Option<Vec<i16>>,
172 curr_triplet_index: usize,
174 triplets_left: usize,
176 has_next: bool,
178}
179
180impl<T: DataType> TypedTripletIter<T> {
181 fn new(descr: ColumnDescPtr, batch_size: usize, column_reader: ColumnReader) -> Self {
184 assert!(
185 batch_size > 0,
186 "Expected positive batch size, found: {batch_size}"
187 );
188
189 let max_def_level = descr.max_def_level();
190 let max_rep_level = descr.max_rep_level();
191
192 let def_levels = if max_def_level == 0 {
193 None
194 } else {
195 Some(vec![0; batch_size])
196 };
197 let rep_levels = if max_rep_level == 0 {
198 None
199 } else {
200 Some(vec![0; batch_size])
201 };
202
203 Self {
204 reader: get_typed_column_reader(column_reader),
205 column_descr: descr,
206 batch_size,
207 max_def_level,
208 max_rep_level,
209 values: vec![T::T::default(); batch_size],
210 def_levels,
211 rep_levels,
212 curr_triplet_index: 0,
213 triplets_left: 0,
214 has_next: false,
215 }
216 }
217
218 #[inline]
220 pub fn column_descr(&self) -> &ColumnDescPtr {
221 &self.column_descr
222 }
223
224 #[inline]
226 fn max_def_level(&self) -> i16 {
227 self.max_def_level
228 }
229
230 #[inline]
232 fn max_rep_level(&self) -> i16 {
233 self.max_rep_level
234 }
235
236 #[inline]
239 fn current_value(&self) -> &T::T {
240 assert!(
241 self.current_def_level() == self.max_def_level(),
242 "Cannot extract value, max definition level: {}, current level: {}",
243 self.max_def_level(),
244 self.current_def_level()
245 );
246 &self.values[self.curr_triplet_index]
247 }
248
249 #[inline]
252 fn current_def_level(&self) -> i16 {
253 if !self.has_next {
254 return 0;
255 }
256 match self.def_levels {
257 Some(ref vec) => vec[self.curr_triplet_index],
258 None => self.max_def_level,
259 }
260 }
261
262 #[inline]
265 fn current_rep_level(&self) -> i16 {
266 if !self.has_next {
267 return 0;
268 }
269 match self.rep_levels {
270 Some(ref vec) => vec[self.curr_triplet_index],
271 None => self.max_rep_level,
272 }
273 }
274
275 #[inline]
278 fn has_next(&self) -> bool {
279 self.has_next
280 }
281
282 fn read_next(&mut self) -> Result<bool> {
285 self.curr_triplet_index += 1;
286
287 while self.curr_triplet_index >= self.triplets_left {
291 let (records_read, values_read, levels_read) = {
292 self.values.clear();
293 if let Some(x) = &mut self.def_levels {
294 x.clear()
295 }
296 if let Some(x) = &mut self.rep_levels {
297 x.clear()
298 }
299
300 self.reader.read_records(
302 self.batch_size,
303 self.def_levels.as_mut(),
304 self.rep_levels.as_mut(),
305 &mut self.values,
306 )?
307 };
308
309 if records_read == 0 && values_read == 0 && levels_read == 0 {
311 self.curr_triplet_index = 0;
312 self.has_next = false;
313 return Ok(false);
314 }
315
316 if levels_read == 0 || values_read == levels_read {
318 self.curr_triplet_index = 0;
321 self.triplets_left = values_read;
322 } else if values_read < levels_read {
323 let mut idx = values_read;
331 let def_levels = self.def_levels.as_ref().unwrap();
332 self.values.resize(levels_read, T::T::default());
333 for i in 0..levels_read {
334 if def_levels[levels_read - i - 1] == self.max_def_level {
335 idx -= 1; self.values.swap(levels_read - i - 1, idx);
337 }
338 }
339 self.curr_triplet_index = 0;
340 self.triplets_left = levels_read;
341 } else {
342 return Err(general_err!(
343 "Spacing of values/levels is wrong, values_read: {}, levels_read: {}",
344 values_read,
345 levels_read
346 ));
347 }
348 }
349
350 self.has_next = true;
351 Ok(true)
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 use crate::file::reader::{FileReader, SerializedFileReader};
360 use crate::schema::types::ColumnPath;
361 use crate::util::test_common::file_util::get_test_file;
362
363 #[test]
364 #[should_panic(expected = "Expected positive batch size, found: 0")]
365 fn test_triplet_zero_batch_size() {
366 let column_path = ColumnPath::from(vec!["b_struct".to_string(), "b_c_int".to_string()]);
367 test_column_in_file("nulls.snappy.parquet", 0, &column_path, &[], &[], &[]);
368 }
369
370 #[test]
371 fn test_triplet_null_column() {
372 let path = vec!["b_struct", "b_c_int"];
373 let values = vec![];
374 let def_levels = vec![1, 1, 1, 1, 1, 1, 1, 1];
375 let rep_levels = vec![0, 0, 0, 0, 0, 0, 0, 0];
376 test_triplet_iter(
377 "nulls.snappy.parquet",
378 path,
379 &values,
380 &def_levels,
381 &rep_levels,
382 );
383 }
384
385 #[test]
386 #[cfg_attr(miri, ignore)] fn test_triplet_required_column() {
388 let path = vec!["ID"];
389 let values = vec![Field::Long(8)];
390 let def_levels = vec![0];
391 let rep_levels = vec![0];
392 test_triplet_iter(
393 "nonnullable.impala.parquet",
394 path,
395 &values,
396 &def_levels,
397 &rep_levels,
398 );
399 }
400
401 #[test]
402 #[cfg_attr(miri, ignore)] fn test_triplet_optional_column() {
404 let path = vec!["nested_struct", "A"];
405 let values = vec![Field::Int(1), Field::Int(7)];
406 let def_levels = vec![2, 1, 1, 1, 1, 0, 2];
407 let rep_levels = vec![0, 0, 0, 0, 0, 0, 0];
408 test_triplet_iter(
409 "nullable.impala.parquet",
410 path,
411 &values,
412 &def_levels,
413 &rep_levels,
414 );
415 }
416
417 #[test]
418 #[cfg_attr(miri, ignore)] fn test_triplet_optional_list_column() {
420 let path = vec!["a", "list", "element", "list", "element", "list", "element"];
421 let values = vec![
422 Field::Str("a".to_string()),
423 Field::Str("b".to_string()),
424 Field::Str("c".to_string()),
425 Field::Str("d".to_string()),
426 Field::Str("a".to_string()),
427 Field::Str("b".to_string()),
428 Field::Str("c".to_string()),
429 Field::Str("d".to_string()),
430 Field::Str("e".to_string()),
431 Field::Str("a".to_string()),
432 Field::Str("b".to_string()),
433 Field::Str("c".to_string()),
434 Field::Str("d".to_string()),
435 Field::Str("e".to_string()),
436 Field::Str("f".to_string()),
437 ];
438 let def_levels = vec![7, 7, 7, 4, 7, 7, 7, 7, 7, 4, 7, 7, 7, 7, 7, 7, 4, 7];
439 let rep_levels = vec![0, 3, 2, 1, 2, 0, 3, 2, 3, 1, 2, 0, 3, 2, 3, 2, 1, 2];
440 test_triplet_iter(
441 "nested_lists.snappy.parquet",
442 path,
443 &values,
444 &def_levels,
445 &rep_levels,
446 );
447 }
448
449 #[test]
450 #[cfg_attr(miri, ignore)] fn test_triplet_optional_map_column() {
452 let path = vec!["a", "key_value", "value", "key_value", "key"];
453 let values = vec![
454 Field::Int(1),
455 Field::Int(2),
456 Field::Int(1),
457 Field::Int(1),
458 Field::Int(3),
459 Field::Int(4),
460 Field::Int(5),
461 ];
462 let def_levels = vec![4, 4, 4, 2, 3, 4, 4, 4, 4];
463 let rep_levels = vec![0, 2, 0, 0, 0, 0, 0, 2, 2];
464 test_triplet_iter(
465 "nested_maps.snappy.parquet",
466 path,
467 &values,
468 &def_levels,
469 &rep_levels,
470 );
471 }
472
473 fn test_triplet_iter(
475 file_name: &str,
476 column_path: Vec<&str>,
477 expected_values: &[Field],
478 expected_def_levels: &[i16],
479 expected_rep_levels: &[i16],
480 ) {
481 let path: Vec<String> = column_path.iter().map(|x| x.to_string()).collect();
483 let column_path = ColumnPath::from(path);
484
485 let batch_sizes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 128, 256];
486 for batch_size in batch_sizes {
487 test_column_in_file(
488 file_name,
489 batch_size,
490 &column_path,
491 expected_values,
492 expected_def_levels,
493 expected_rep_levels,
494 );
495 }
496 }
497
498 fn test_column_in_file(
500 file_name: &str,
501 batch_size: usize,
502 column_path: &ColumnPath,
503 expected_values: &[Field],
504 expected_def_levels: &[i16],
505 expected_rep_levels: &[i16],
506 ) {
507 let file = get_test_file(file_name);
508 let file_reader = SerializedFileReader::new(file).unwrap();
509 let metadata = file_reader.metadata();
510 let file_metadata = metadata.file_metadata();
512 let schema = file_metadata.schema_descr();
513 let row_group_reader = file_reader.get_row_group(0).unwrap();
515
516 for i in 0..schema.num_columns() {
517 let descr = schema.column(i);
518 if descr.path() == column_path {
519 let reader = row_group_reader.get_column_reader(i).unwrap();
520 test_triplet_column(
521 descr,
522 reader,
523 batch_size,
524 expected_values,
525 expected_def_levels,
526 expected_rep_levels,
527 );
528 }
529 }
530 }
531
532 fn test_triplet_column(
534 descr: ColumnDescPtr,
535 reader: ColumnReader,
536 batch_size: usize,
537 expected_values: &[Field],
538 expected_def_levels: &[i16],
539 expected_rep_levels: &[i16],
540 ) {
541 let mut iter = TripletIter::new(descr.clone(), reader, batch_size);
542 let mut values: Vec<Field> = Vec::new();
543 let mut def_levels: Vec<i16> = Vec::new();
544 let mut rep_levels: Vec<i16> = Vec::new();
545
546 assert_eq!(iter.max_def_level(), descr.max_def_level());
547 assert_eq!(iter.max_rep_level(), descr.max_rep_level());
548
549 while matches!(iter.read_next(), Ok(true)) {
550 assert!(iter.has_next());
551 if !iter.is_null() {
552 values.push(iter.current_value().unwrap());
553 }
554 def_levels.push(iter.current_def_level());
555 rep_levels.push(iter.current_rep_level());
556 }
557
558 assert_eq!(values, expected_values);
559 assert_eq!(def_levels, expected_def_levels);
560 assert_eq!(rep_levels, expected_rep_levels);
561 }
562
563 fn open_triplet_iter(file_name: &str, path: &[&str], batch_size: usize) -> TripletIter {
564 let column_path = ColumnPath::from(path.iter().map(|x| x.to_string()).collect::<Vec<_>>());
565 let file = get_test_file(file_name);
566 let file_reader = SerializedFileReader::new(file).unwrap();
567 let metadata = file_reader.metadata();
568 let schema = metadata.file_metadata().schema_descr();
569 let row_group_reader = file_reader.get_row_group(0).unwrap();
570 for i in 0..schema.num_columns() {
571 let descr = schema.column(i);
572 if descr.path() == &column_path {
573 let reader = row_group_reader.get_column_reader(i).unwrap();
574 return TripletIter::new(descr.clone(), reader, batch_size);
575 }
576 }
577 panic!("Column {column_path:?} not found in {file_name}");
578 }
579
580 #[test]
581 fn test_current_def_level_safe_after_exhaustion() {
582 let mut iter = open_triplet_iter("nulls.snappy.parquet", &["b_struct", "b_c_int"], 256);
583 while matches!(iter.read_next(), Ok(true)) {}
584 assert!(!iter.has_next());
585 assert_eq!(iter.current_def_level(), 0);
586 }
587
588 #[test]
589 fn test_current_rep_level_safe_after_exhaustion() {
590 let mut iter = open_triplet_iter(
591 "nested_lists.snappy.parquet",
592 &["a", "list", "element", "list", "element", "list", "element"],
593 256,
594 );
595 while matches!(iter.read_next(), Ok(true)) {}
596 assert!(!iter.has_next());
597 assert_eq!(iter.current_rep_level(), 0);
598 }
599}