1use std::collections::{HashMap, HashSet};
16
17use datatypes::extension::json::JSON2_REMAINDER_FIELD_NAME;
18use parquet::arrow::ProjectionMask;
19use parquet::basic::{ConvertedType, Type as PhysicalType};
20use parquet::schema::types::{ColumnDescriptor, SchemaDescriptor};
21
22pub type ParquetNestedPath = Vec<String>;
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct ParquetReadColumns {
28 root_indices: Vec<usize>,
34 cols: Vec<ParquetReadColumn>,
35 has_nested: bool,
36}
37
38impl ParquetReadColumns {
39 pub fn from_deduped(cols: Vec<ParquetReadColumn>) -> Self {
46 let has_nested = cols.iter().any(|col| !col.nested_paths.is_empty());
47 let root_indices = cols.iter().map(|col| col.root_index).collect();
48 Self {
49 root_indices,
50 cols,
51 has_nested,
52 }
53 }
54
55 pub fn from_deduped_root_indices(root_indices: impl IntoIterator<Item = usize>) -> Self {
60 let root_indices = root_indices.into_iter().collect::<Vec<_>>();
61 let cols = root_indices
62 .iter()
63 .copied()
64 .map(ParquetReadColumn::new)
65 .collect();
66 Self {
67 root_indices,
68 cols,
69 has_nested: false,
70 }
71 }
72
73 pub fn columns(&self) -> &[ParquetReadColumn] {
74 &self.cols
75 }
76
77 pub fn has_nested(&self) -> bool {
78 self.has_nested
79 }
80
81 pub fn root_indices_iter(&self) -> impl Iterator<Item = usize> + '_ {
82 self.root_indices.iter().copied()
83 }
84
85 pub fn root_indices(&self) -> &[usize] {
87 &self.root_indices
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct ParquetReadColumn {
104 root_index: usize,
106 nested_paths: Vec<ParquetNestedPath>,
113}
114
115impl ParquetReadColumn {
116 pub fn new(root_index: usize) -> Self {
117 Self {
118 root_index,
119 nested_paths: vec![],
120 }
121 }
122
123 pub fn with_nested_paths(self, nested_paths: Vec<ParquetNestedPath>) -> Self {
124 Self {
125 nested_paths,
126 ..self
127 }
128 }
129
130 pub fn merge_nested_paths(&mut self, nested_paths: Vec<ParquetNestedPath>) {
132 let reads_whole_root = self.nested_paths.is_empty() || nested_paths.is_empty();
133 if reads_whole_root {
134 self.nested_paths = vec![];
136 } else {
137 self.nested_paths.extend(nested_paths);
138 }
139 }
140
141 pub fn root_index(&self) -> usize {
142 self.root_index
143 }
144
145 pub fn nested_paths(&self) -> &[ParquetNestedPath] {
146 &self.nested_paths
147 }
148}
149
150#[derive(Clone)]
152pub struct ProjectionMaskPlan {
153 pub mask: ProjectionMask,
155 pub projected_root_presence: Vec<bool>,
165}
166
167pub(crate) fn build_projection_plan(
184 parquet_read_cols: &ParquetReadColumns,
185 parquet_schema_desc: &SchemaDescriptor,
186) -> ProjectionMaskPlan {
187 if !parquet_read_cols.has_nested() {
188 let mask =
189 ProjectionMask::roots(parquet_schema_desc, parquet_read_cols.root_indices_iter());
190 return ProjectionMaskPlan {
191 mask,
192 projected_root_presence: vec![true; parquet_read_cols.columns().len()],
193 };
194 }
195
196 let (matched_leaves, matched_roots) =
197 build_parquet_leaves_indices(parquet_schema_desc, parquet_read_cols);
198
199 let projected_root_presence = parquet_read_cols
200 .columns()
201 .iter()
202 .map(|col| matched_roots.contains(&col.root_index()))
203 .collect();
204
205 let mask = ProjectionMask::leaves(parquet_schema_desc, matched_leaves);
206 ProjectionMaskPlan {
207 mask,
208 projected_root_presence,
209 }
210}
211
212fn build_parquet_leaves_indices(
218 parquet_schema_desc: &SchemaDescriptor,
219 projection: &ParquetReadColumns,
220) -> (Vec<usize>, HashSet<usize>) {
221 let mut map = HashMap::with_capacity(projection.cols.len());
222 for col in &projection.cols {
223 map.insert(col.root_index, col);
224 }
225
226 let mut matched_leaves = HashSet::new();
227 let mut matched_roots = HashSet::with_capacity(projection.cols.len());
228
229 let mut prefix_matched = HashMap::<usize, Vec<bool>>::new();
231 for col in &projection.cols {
232 prefix_matched.insert(col.root_index, vec![false; col.nested_paths.len()]);
233 }
234
235 for (leaf_idx, leaf_col) in parquet_schema_desc.columns().iter().enumerate() {
237 let root_idx = parquet_schema_desc.get_column_root_idx(leaf_idx);
238 let Some(col) = map.get(&root_idx) else {
239 continue;
240 };
241 if col.nested_paths.is_empty() {
242 matched_leaves.insert(leaf_idx);
243 matched_roots.insert(root_idx);
244 continue;
245 }
246
247 let leaf_path = leaf_col.path().parts();
248 let mut matched = false;
249 for (path_idx, _) in col
250 .nested_paths
251 .iter()
252 .enumerate()
253 .filter(|(_, nested_path)| leaf_path.starts_with(nested_path))
254 {
255 prefix_matched.get_mut(&root_idx).unwrap()[path_idx] = true;
256 matched = true;
257 }
258
259 if matched {
260 matched_leaves.insert(leaf_idx);
261 matched_roots.insert(root_idx);
262 }
263 }
264
265 for col in &projection.cols {
270 let path_matches = &prefix_matched[&col.root_index];
271 let mut needs_remainder = false;
272 for (matched, nested_path) in path_matches.iter().zip(&col.nested_paths) {
273 if *matched {
274 if !needs_remainder {
275 needs_remainder =
276 path_points_to_struct(parquet_schema_desc, col.root_index, nested_path);
277 }
278 continue;
279 }
280
281 if let Some(leaf_idx) =
282 find_nearest_variant_parent(parquet_schema_desc, col.root_index, nested_path)
283 {
284 matched_leaves.insert(leaf_idx);
285 matched_roots.insert(col.root_index);
286 } else {
287 needs_remainder = true;
288 }
289 }
290
291 if needs_remainder {
292 let remainder_leaves = find_remainder_leaves(parquet_schema_desc, col.root_index);
293 if !remainder_leaves.is_empty() {
294 matched_leaves.extend(remainder_leaves);
295 matched_roots.insert(col.root_index);
296 }
297 }
298 }
299
300 let mut matched_leaves = matched_leaves.into_iter().collect::<Vec<_>>();
301 matched_leaves.sort_unstable();
302 (matched_leaves, matched_roots)
303}
304
305fn path_points_to_struct(
310 parquet_schema_desc: &SchemaDescriptor,
311 root_idx: usize,
312 path: &[String],
313) -> bool {
314 let Some(mut field) = parquet_schema_desc.root_schema().get_fields().get(root_idx) else {
315 return false;
316 };
317 for name in path.iter().skip(1) {
318 if !field.is_group() {
319 return false;
320 }
321 let Some(child) = field.get_fields().iter().find(|field| field.name() == name) else {
322 return false;
323 };
324 field = child;
325 }
326 field.is_group()
327}
328
329fn find_remainder_leaves(parquet_schema_desc: &SchemaDescriptor, root_idx: usize) -> Vec<usize> {
335 parquet_schema_desc
336 .columns()
337 .iter()
338 .enumerate()
339 .filter_map(|(i, column)| {
340 let path = column.path().parts();
341 (parquet_schema_desc.get_column_root_idx(i) == root_idx
342 && path.get(1).is_some_and(|x| x == JSON2_REMAINDER_FIELD_NAME))
343 .then_some(i)
344 })
345 .collect::<Vec<_>>()
346}
347
348fn find_nearest_variant_parent(
349 parquet_schema_desc: &SchemaDescriptor,
350 root_idx: usize,
351 nested_path: &[String],
352) -> Option<usize> {
353 if nested_path.len() <= 1 {
355 return None;
356 }
357
358 for parent_len in (2..nested_path.len()).rev() {
361 let parent_path = &nested_path[..parent_len];
362 for (leaf_idx, leaf_col) in parquet_schema_desc.columns().iter().enumerate() {
363 if parquet_schema_desc.get_column_root_idx(leaf_idx) != root_idx {
364 continue;
365 }
366 if leaf_col.path().parts() == parent_path && is_variant_leaf(leaf_col) {
367 return Some(leaf_idx);
368 }
369 }
370 }
371
372 None
373}
374
375fn is_variant_leaf(leaf_col: &ColumnDescriptor) -> bool {
376 matches!(
379 leaf_col.physical_type(),
380 PhysicalType::BYTE_ARRAY | PhysicalType::FIXED_LEN_BYTE_ARRAY
381 ) && leaf_col.logical_type_ref().is_none()
382 && leaf_col.converted_type() == ConvertedType::NONE
383}
384
385#[cfg(test)]
386mod tests {
387 use std::sync::Arc;
388
389 use parquet::basic::{ConvertedType, LogicalType, Repetition};
390 use parquet::errors::ParquetError;
391 use parquet::schema::types::Type;
392
393 use super::*;
394
395 #[test]
396 fn test_build_projection_mask_without_nested_paths() {
397 let parquet_schema_desc = build_test_nested_parquet_schema();
398 let projection = ParquetReadColumns::from_deduped_root_indices([0, 1]);
399
400 let plan = build_projection_plan(&projection, &parquet_schema_desc);
401
402 assert_eq!(vec![true, true], plan.projected_root_presence);
403 assert_eq!(
404 ProjectionMask::roots(&parquet_schema_desc, [0, 1]),
405 plan.mask
406 );
407 }
408
409 #[test]
410 fn test_reads_whole_root() {
411 let parquet_schema_desc = build_test_nested_parquet_schema();
412
413 let projection = ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0)]);
414
415 let (matched_leaves, matched_roots) =
416 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
417 assert_eq!(vec![0, 1, 2], matched_leaves);
418 assert_eq!(HashSet::from([0]), matched_roots);
419 }
420
421 #[test]
422 fn test_filters_nested_paths() {
423 let parquet_schema_desc = build_test_nested_parquet_schema();
424
425 let projection = ParquetReadColumns::from_deduped(vec![
426 ParquetReadColumn::new(0)
427 .with_nested_paths(vec![vec!["j".to_string(), "b".to_string()]]),
428 ParquetReadColumn::new(1),
429 ]);
430
431 let (matched_leaves, matched_roots) =
432 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
433 assert_eq!(vec![1, 2, 3], matched_leaves);
434 assert_eq!(HashSet::from([0, 1]), matched_roots);
435 }
436
437 #[test]
438 fn test_reads_middle_level_path() {
439 let parquet_schema_desc = build_test_nested_parquet_schema();
440
441 let projection = ParquetReadColumns::from_deduped(vec![
442 ParquetReadColumn::new(0)
443 .with_nested_paths(vec![vec!["j".to_string(), "b".to_string()]]),
444 ]);
445
446 let (matched_leaves, matched_roots) =
447 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
448 assert_eq!(vec![1, 2], matched_leaves);
449 assert_eq!(HashSet::from([0]), matched_roots);
450 }
451
452 #[test]
453 fn test_parent_path_covers_redundant_child_path() {
454 let parquet_schema_desc = build_test_nested_parquet_schema();
455 let nested_paths = vec![
456 vec!["j".to_string(), "b".to_string()],
457 vec!["j".to_string(), "b".to_string(), "c".to_string()],
458 ];
459
460 let read_column = ParquetReadColumn::new(0).with_nested_paths(nested_paths);
461 let projection = ParquetReadColumns::from_deduped(vec![read_column]);
462
463 let (matched_leaves, matched_roots) =
464 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
465 assert_eq!(vec![1, 2], matched_leaves);
466 assert_eq!(HashSet::from([0]), matched_roots);
467 }
468
469 #[test]
470 fn test_reads_leaf_level_path() {
471 let parquet_schema_desc = build_test_nested_parquet_schema();
472
473 let projection =
474 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
475 vec![vec!["j".to_string(), "b".to_string(), "c".to_string()]],
476 )]);
477
478 let (matched_leaves, matched_roots) =
479 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
480 assert_eq!(vec![1], matched_leaves);
481 assert_eq!(HashSet::from([0]), matched_roots);
482 }
483
484 #[test]
485 fn test_build_projection_mask_with_unmatched_roots() {
486 let parquet_schema_desc = build_test_nested_parquet_schema();
487
488 let projection = ParquetReadColumns::from_deduped(vec![
489 ParquetReadColumn::new(0)
490 .with_nested_paths(vec![vec!["j".to_string(), "missing".to_string()]]),
491 ParquetReadColumn::new(1),
492 ]);
493
494 let plan = build_projection_plan(&projection, &parquet_schema_desc);
495
496 assert_eq!(vec![false, true], plan.projected_root_presence);
497 assert_eq!(
498 ProjectionMask::leaves(&parquet_schema_desc, vec![3]),
499 plan.mask
500 );
501 }
502
503 #[test]
504 fn test_v2_routes_missing_path_to_remainder() -> Result<(), ParquetError> {
505 let parquet = build_test_v2_schema()?;
506 let projection =
507 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
508 vec![
509 vec!["j".to_string(), "cold".to_string()],
510 vec!["j".to_string(), "another".to_string()],
511 ],
512 )]);
513
514 let plan = build_projection_plan(&projection, &parquet);
515
516 assert_eq!(vec![true], plan.projected_root_presence);
517 assert_eq!(ProjectionMask::leaves(&parquet, [0, 1]), plan.mask);
518 Ok(())
519 }
520
521 #[test]
522 fn test_v2_explicit_path_does_not_read_remainder() -> Result<(), ParquetError> {
523 let parquet = build_test_v2_schema()?;
524 let projection = ParquetReadColumns::from_deduped(vec![
525 ParquetReadColumn::new(0)
526 .with_nested_paths(vec![vec!["j".to_string(), "hot".to_string()]]),
527 ]);
528
529 let plan = build_projection_plan(&projection, &parquet);
530
531 assert_eq!(vec![true], plan.projected_root_presence);
532 assert_eq!(ProjectionMask::leaves(&parquet, [3]), plan.mask);
533 Ok(())
534 }
535
536 #[test]
537 fn test_v2_container_path_reads_remainder() -> Result<(), ParquetError> {
538 let parquet = build_test_v2_schema()?;
539 let projection = ParquetReadColumns::from_deduped(vec![
540 ParquetReadColumn::new(0)
541 .with_nested_paths(vec![vec!["j".to_string(), "commit".to_string()]]),
542 ]);
543
544 let plan = build_projection_plan(&projection, &parquet);
545
546 assert_eq!(vec![true], plan.projected_root_presence);
547 assert_eq!(ProjectionMask::leaves(&parquet, [0, 1, 2]), plan.mask);
548 Ok(())
549 }
550
551 #[test]
555 fn test_v2_variant_parent_path_reads_parent() -> Result<(), ParquetError> {
556 let parquet = build_test_v2_schema()?;
557 let projection =
558 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
559 vec![vec![
560 "j".to_string(),
561 "opaque".to_string(),
562 "leaf".to_string(),
563 ]],
564 )]);
565
566 let plan = build_projection_plan(&projection, &parquet);
567
568 assert_eq!(vec![true], plan.projected_root_presence);
569 assert_eq!(ProjectionMask::leaves(&parquet, [4]), plan.mask);
570 Ok(())
571 }
572
573 #[test]
574 fn test_merges_mixed_paths() {
575 let parquet_schema_desc = build_test_nested_parquet_schema();
576
577 let projection =
578 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
579 vec![
580 vec!["j".to_string(), "a".to_string()],
581 vec!["j".to_string(), "b".to_string(), "d".to_string()],
582 ],
583 )]);
584
585 let (matched_leaves, matched_roots) =
586 build_parquet_leaves_indices(&parquet_schema_desc, &projection);
587 assert_eq!(vec![0, 2], matched_leaves);
588 assert_eq!(HashSet::from([0]), matched_roots);
589 }
590
591 #[test]
592 fn test_merge_nested_paths_extends_paths() {
593 let mut col = ParquetReadColumn::new(0)
594 .with_nested_paths(vec![vec!["j".to_string(), "a".to_string()]]);
595
596 col.merge_nested_paths(vec![vec!["j".to_string(), "b".to_string()]]);
597
598 assert_eq!(
599 &[
600 vec!["j".to_string(), "a".to_string()],
601 vec!["j".to_string(), "b".to_string()],
602 ],
603 col.nested_paths()
604 );
605 }
606
607 #[test]
608 fn test_merge_nested_paths_with_whole_root() {
609 let mut col = ParquetReadColumn::new(0)
610 .with_nested_paths(vec![vec!["j".to_string(), "a".to_string()]]);
611
612 col.merge_nested_paths(vec![]);
613
614 assert!(col.nested_paths().is_empty());
615 }
616
617 #[test]
618 fn test_fallback_to_nearest_variant_parent() {
619 let parquet_schema_desc = build_test_variant_parent_schema();
620 let projection =
621 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
622 vec![vec!["j".to_string(), "a".to_string(), "x".to_string()]],
623 )]);
624
625 let plan = build_projection_plan(&projection, &parquet_schema_desc);
626
627 assert_eq!(vec![true], plan.projected_root_presence);
628 assert_eq!(
629 ProjectionMask::leaves(&parquet_schema_desc, vec![0]),
630 plan.mask
631 );
632 }
633
634 #[test]
635 fn test_prefix_match_prevents_variant_parent_fallback() {
636 let parquet_schema_desc = build_test_variant_parent_schema();
637 let projection =
638 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
639 vec![vec!["j".to_string(), "b".to_string(), "x".to_string()]],
640 )]);
641
642 let plan = build_projection_plan(&projection, &parquet_schema_desc);
643
644 assert_eq!(vec![true], plan.projected_root_presence);
645 assert_eq!(
646 ProjectionMask::leaves(&parquet_schema_desc, vec![1]),
647 plan.mask
648 );
649 }
650
651 #[test]
652 fn test_mixed_prefix_and_fallback_paths() {
653 let parquet_schema_desc = build_test_variant_parent_schema();
654 let nested_paths = vec![
655 vec!["j".to_string(), "a".to_string(), "x".to_string()],
656 vec!["j".to_string(), "b".to_string(), "x".to_string()],
657 ];
658 let projection = ParquetReadColumns::from_deduped(vec![
659 ParquetReadColumn::new(0).with_nested_paths(nested_paths),
660 ]);
661
662 let plan = build_projection_plan(&projection, &parquet_schema_desc);
663
664 assert_eq!(vec![true], plan.projected_root_presence);
665 assert_eq!(
666 ProjectionMask::leaves(&parquet_schema_desc, vec![0, 1]),
667 plan.mask
668 );
669 }
670
671 #[test]
672 fn test_fallback_selects_multiple_variant_parents() {
673 let parquet_schema_desc = build_test_two_variant_parents_schema();
674 let nested_paths = vec![
675 vec!["j".to_string(), "a".to_string(), "y".to_string()],
676 vec!["j".to_string(), "b".to_string(), "d".to_string()],
677 vec!["j".to_string(), "a".to_string(), "x".to_string()],
678 ];
679 let projection = ParquetReadColumns::from_deduped(vec![
680 ParquetReadColumn::new(0).with_nested_paths(nested_paths),
681 ]);
682
683 let plan = build_projection_plan(&projection, &parquet_schema_desc);
684
685 assert_eq!(vec![true], plan.projected_root_presence);
686 assert_eq!(
687 ProjectionMask::leaves(&parquet_schema_desc, vec![0, 1]),
688 plan.mask
689 );
690 }
691
692 #[test]
693 fn test_nested_paths_fallback_to_variant_parent_by_default() {
694 let parquet_schema_desc = build_test_variant_parent_schema();
695 let projection =
696 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
697 vec![vec!["j".to_string(), "a".to_string(), "x".to_string()]],
698 )]);
699
700 let plan = build_projection_plan(&projection, &parquet_schema_desc);
701
702 assert_eq!(vec![true], plan.projected_root_presence);
703 assert_eq!(
704 ProjectionMask::leaves(&parquet_schema_desc, vec![0]),
705 plan.mask
706 );
707 }
708
709 #[test]
710 fn test_non_variant_parent_does_not_fallback() {
711 let parquet_schema_desc = build_test_nested_parquet_schema();
712 let projection =
713 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
714 vec![vec!["j".to_string(), "a".to_string(), "x".to_string()]],
715 )]);
716
717 let plan = build_projection_plan(&projection, &parquet_schema_desc);
718
719 assert_eq!(vec![false], plan.projected_root_presence);
720 }
721
722 #[test]
723 fn test_utf8_parent_does_not_fallback() {
724 let parquet_schema_desc = build_test_utf8_parent_schema();
725 let projection =
726 ParquetReadColumns::from_deduped(vec![ParquetReadColumn::new(0).with_nested_paths(
727 vec![vec!["j".to_string(), "a".to_string(), "x".to_string()]],
728 )]);
729
730 let plan = build_projection_plan(&projection, &parquet_schema_desc);
731
732 assert_eq!(vec![false], plan.projected_root_presence);
733 }
734
735 #[test]
736 fn test_root_variant_does_not_fallback() {
737 let parquet_schema_desc = build_test_root_variant_schema();
738 let projection = ParquetReadColumns::from_deduped(vec![
739 ParquetReadColumn::new(0)
740 .with_nested_paths(vec![vec!["j".to_string(), "a".to_string()]]),
741 ]);
742
743 let plan = build_projection_plan(&projection, &parquet_schema_desc);
744
745 assert_eq!(vec![false], plan.projected_root_presence);
746 }
747
748 fn build_test_nested_parquet_schema() -> SchemaDescriptor {
757 let leaf_a = Arc::new(
758 Type::primitive_type_builder("a", parquet::basic::Type::INT64)
759 .with_repetition(Repetition::REQUIRED)
760 .build()
761 .unwrap(),
762 );
763 let leaf_c = Arc::new(
764 Type::primitive_type_builder("c", parquet::basic::Type::INT64)
765 .with_repetition(Repetition::REQUIRED)
766 .build()
767 .unwrap(),
768 );
769 let leaf_d = Arc::new(
770 Type::primitive_type_builder("d", parquet::basic::Type::INT64)
771 .with_repetition(Repetition::REQUIRED)
772 .build()
773 .unwrap(),
774 );
775 let group_b = Arc::new(
776 Type::group_type_builder("b")
777 .with_repetition(Repetition::REQUIRED)
778 .with_fields(vec![leaf_c, leaf_d])
779 .build()
780 .unwrap(),
781 );
782 let root_j = Arc::new(
783 Type::group_type_builder("j")
784 .with_repetition(Repetition::REQUIRED)
785 .with_fields(vec![leaf_a, group_b])
786 .build()
787 .unwrap(),
788 );
789 let root_k = Arc::new(
790 Type::primitive_type_builder("k", parquet::basic::Type::INT64)
791 .with_repetition(Repetition::REQUIRED)
792 .build()
793 .unwrap(),
794 );
795 let schema = Arc::new(
796 Type::group_type_builder("schema")
797 .with_fields(vec![root_j, root_k])
798 .build()
799 .unwrap(),
800 );
801
802 SchemaDescriptor::new(schema)
803 }
804
805 fn build_test_v2_schema() -> Result<SchemaDescriptor, ParquetError> {
806 let metadata = Arc::new(
807 Type::primitive_type_builder("metadata", parquet::basic::Type::BYTE_ARRAY)
808 .with_repetition(Repetition::REQUIRED)
809 .build()?,
810 );
811 let value = Arc::new(
812 Type::primitive_type_builder("value", parquet::basic::Type::BYTE_ARRAY)
813 .with_repetition(Repetition::REQUIRED)
814 .build()?,
815 );
816 let remainder = Arc::new(
817 Type::group_type_builder(JSON2_REMAINDER_FIELD_NAME)
818 .with_repetition(Repetition::OPTIONAL)
819 .with_logical_type(Some(LogicalType::Variant {
820 specification_version: None,
821 }))
822 .with_fields(vec![metadata, value])
823 .build()?,
824 );
825 let operation = Arc::new(
826 Type::primitive_type_builder("operation", parquet::basic::Type::INT64)
827 .with_repetition(Repetition::OPTIONAL)
828 .build()?,
829 );
830 let commit = Arc::new(
831 Type::group_type_builder("commit")
832 .with_repetition(Repetition::OPTIONAL)
833 .with_fields(vec![operation])
834 .build()?,
835 );
836 let hot = Arc::new(
837 Type::primitive_type_builder("hot", parquet::basic::Type::INT64)
838 .with_repetition(Repetition::OPTIONAL)
839 .build()?,
840 );
841 let opaque = Arc::new(
845 Type::primitive_type_builder("opaque", parquet::basic::Type::BYTE_ARRAY)
846 .with_repetition(Repetition::OPTIONAL)
847 .build()?,
848 );
849 let root = Arc::new(
850 Type::group_type_builder("j")
851 .with_repetition(Repetition::OPTIONAL)
852 .with_fields(vec![remainder, commit, hot, opaque])
853 .build()?,
854 );
855 Ok(SchemaDescriptor::new(Arc::new(
856 Type::group_type_builder("schema")
857 .with_fields(vec![root])
858 .build()?,
859 )))
860 }
861
862 fn build_test_variant_parent_schema() -> SchemaDescriptor {
869 let leaf_a = Arc::new(
870 Type::primitive_type_builder("a", parquet::basic::Type::BYTE_ARRAY)
871 .with_repetition(Repetition::REQUIRED)
872 .build()
873 .unwrap(),
874 );
875 let leaf_x = Arc::new(
876 Type::primitive_type_builder("x", parquet::basic::Type::INT64)
877 .with_repetition(Repetition::REQUIRED)
878 .build()
879 .unwrap(),
880 );
881 let group_b = Arc::new(
882 Type::group_type_builder("b")
883 .with_repetition(Repetition::REQUIRED)
884 .with_fields(vec![leaf_x])
885 .build()
886 .unwrap(),
887 );
888 let root_j = Arc::new(
889 Type::group_type_builder("j")
890 .with_repetition(Repetition::REQUIRED)
891 .with_fields(vec![leaf_a, group_b])
892 .build()
893 .unwrap(),
894 );
895 let schema = Arc::new(
896 Type::group_type_builder("schema")
897 .with_fields(vec![root_j])
898 .build()
899 .unwrap(),
900 );
901
902 SchemaDescriptor::new(schema)
903 }
904
905 fn build_test_two_variant_parents_schema() -> SchemaDescriptor {
911 let leaf_a = Arc::new(
912 Type::primitive_type_builder("a", parquet::basic::Type::BYTE_ARRAY)
913 .with_repetition(Repetition::REQUIRED)
914 .build()
915 .unwrap(),
916 );
917 let leaf_b = Arc::new(
918 Type::primitive_type_builder("b", parquet::basic::Type::BYTE_ARRAY)
919 .with_repetition(Repetition::REQUIRED)
920 .build()
921 .unwrap(),
922 );
923 let root_j = Arc::new(
924 Type::group_type_builder("j")
925 .with_repetition(Repetition::REQUIRED)
926 .with_fields(vec![leaf_a, leaf_b])
927 .build()
928 .unwrap(),
929 );
930 let schema = Arc::new(
931 Type::group_type_builder("schema")
932 .with_fields(vec![root_j])
933 .build()
934 .unwrap(),
935 );
936
937 SchemaDescriptor::new(schema)
938 }
939
940 fn build_test_utf8_parent_schema() -> SchemaDescriptor {
941 let leaf_a = Arc::new(
942 Type::primitive_type_builder("a", parquet::basic::Type::BYTE_ARRAY)
943 .with_repetition(Repetition::REQUIRED)
944 .with_logical_type(Some(LogicalType::String))
945 .with_converted_type(ConvertedType::UTF8)
946 .build()
947 .unwrap(),
948 );
949 let root_j = Arc::new(
950 Type::group_type_builder("j")
951 .with_repetition(Repetition::REQUIRED)
952 .with_fields(vec![leaf_a])
953 .build()
954 .unwrap(),
955 );
956 let schema = Arc::new(
957 Type::group_type_builder("schema")
958 .with_fields(vec![root_j])
959 .build()
960 .unwrap(),
961 );
962
963 SchemaDescriptor::new(schema)
964 }
965
966 fn build_test_root_variant_schema() -> SchemaDescriptor {
970 let root_j = Arc::new(
971 Type::primitive_type_builder("j", parquet::basic::Type::BYTE_ARRAY)
972 .with_repetition(Repetition::REQUIRED)
973 .build()
974 .unwrap(),
975 );
976 let schema = Arc::new(
977 Type::group_type_builder("schema")
978 .with_fields(vec![root_j])
979 .build()
980 .unwrap(),
981 );
982
983 SchemaDescriptor::new(schema)
984 }
985}