1pub(crate) mod apply_schema;
4
5use std::borrow::Cow;
6use std::collections::{HashMap, HashSet};
7use std::ops::Range;
8use std::sync::{Arc, OnceLock};
9
10use delta_kernel_derive::internal_api;
11use itertools::Itertools;
12use tracing::debug;
13
14use self::apply_schema::apply_schema_to_struct;
15use crate::arrow::array::cast::AsArray;
16use crate::arrow::array::{
17 make_array, new_null_array, Array as ArrowArray, ArrayRef as ArrowArrayRef, GenericListArray,
18 MapArray, OffsetSizeTrait, PrimitiveArray, RecordBatch, RecordBatchOptions, StringArray,
19 StructArray,
20};
21use crate::arrow::buffer::NullBuffer;
22use crate::arrow::compute::{cast_with_options, CastOptions};
23use crate::arrow::datatypes::{
24 DataType as ArrowDataType, Field as ArrowField, FieldRef as ArrowFieldRef,
25 Fields as ArrowFields, Int64Type, Schema as ArrowSchema, SchemaRef as ArrowSchemaRef,
26};
27use crate::arrow::json::writer::{make_encoder, LineDelimited, NullableEncoder};
28use crate::arrow::json::{Encoder, EncoderFactory, EncoderOptions, ReaderBuilder, WriterBuilder};
29use crate::engine::arrow_conversion::{TryFromKernel as _, TryIntoArrow as _};
30use crate::engine::arrow_data::ArrowEngineData;
31use crate::engine::ensure_data_types::DataTypeCompat;
32use crate::engine_data::FilteredEngineData;
33use crate::parquet::arrow::{ProjectionMask, PARQUET_FIELD_ID_META_KEY};
34use crate::parquet::file::metadata::RowGroupMetaData;
35use crate::parquet::schema::types::SchemaDescriptor;
36use crate::schema::{
37 ArrayType, ColumnMetadataKey, DataType, MapType, MetadataColumnSpec, MetadataValue,
38 PrimitiveType, Schema, SchemaRef, StructField, StructType,
39};
40use crate::transforms::{transform_output_type, SchemaTransform};
41use crate::utils::require;
42use crate::{DeltaResult, EngineData, Error};
43
44macro_rules! prim_array_cmp {
45 ( $left_arr: ident, $right_arr: ident, $(($data_ty: pat, $prim_ty: ty)),+ ) => {
46
47 return match $left_arr.data_type() {
48 $(
49 $data_ty => {
50 let prim_array = $left_arr.as_primitive_opt::<$prim_ty>()
51 .ok_or(Error::invalid_expression(
52 format!("Cannot cast to primitive array: {}", $left_arr.data_type()))
53 )?;
54 let list_array = $right_arr.as_list_opt::<i32>()
55 .ok_or(Error::invalid_expression(
56 format!("Cannot cast to list array: {}", $right_arr.data_type()))
57 )?;
58 crate::arrow::compute::kernels::comparison::in_list(prim_array, list_array)
59 }
60 )+
61 _ => Err(ArrowError::CastError(
62 format!("Bad Comparison between: {:?} and {:?}",
63 $left_arr.data_type(),
64 $right_arr.data_type())
65 )
66 )
67 }.map_err(Error::generic_err);
68 };
69}
70
71pub(crate) use prim_array_cmp;
72
73type FieldIndex = usize;
74type FlattenedRangeIterator<T> = std::iter::Flatten<std::vec::IntoIter<Range<T>>>;
75
76struct KernelFieldInfo<'k> {
81 parquet_index: FieldIndex,
83 field: &'k StructField,
85}
86
87struct MatchedParquetField<'p, 'k> {
94 parquet_index: FieldIndex,
96 parquet_field: &'p ArrowField,
98 kernel_field_info: Option<KernelFieldInfo<'k>>,
100}
101
102#[internal_api]
104pub(crate) fn make_arrow_error(s: impl Into<String>) -> Error {
105 Error::Arrow(crate::arrow::error::ArrowError::InvalidArgumentError(
106 s.into(),
107 ))
108 .with_backtrace()
109}
110
111#[internal_api]
113pub(crate) struct RowIndexBuilder {
114 row_group_row_index_ranges: Vec<Range<i64>>,
115 row_group_ordinals: Option<Vec<usize>>,
116}
117
118impl RowIndexBuilder {
119 #[internal_api]
120 pub(crate) fn new(row_groups: &[RowGroupMetaData]) -> Self {
121 let mut row_group_row_index_ranges = Vec::with_capacity(row_groups.len());
122 let mut offset = 0;
123 for row_group in row_groups {
124 let num_rows = row_group.num_rows();
125 row_group_row_index_ranges.push(offset..offset + num_rows);
126 offset += num_rows;
127 }
128 Self {
129 row_group_row_index_ranges,
130 row_group_ordinals: None,
131 }
132 }
133
134 #[internal_api]
137 pub(crate) fn select_row_groups(&mut self, ordinals: &[usize]) {
138 self.row_group_ordinals = Some(ordinals.to_vec())
141 }
142
143 #[internal_api]
149 pub(crate) fn build(self) -> DeltaResult<FlattenedRangeIterator<i64>> {
150 let starting_offsets = match self.row_group_ordinals {
151 Some(ordinals) => {
152 let mut seen_ordinals = HashSet::with_capacity(ordinals.len());
153 ordinals
154 .iter()
155 .map(|&i| {
156 if !seen_ordinals.insert(i) {
158 return Err(Error::generic("Found duplicate row group ordinal"));
159 }
160 self.row_group_row_index_ranges
163 .get(i)
164 .cloned()
165 .ok_or_else(|| {
166 Error::generic(format!("Row group ordinal {i} is out of bounds"))
167 })
168 })
169 .try_collect()?
170 }
171 None => self.row_group_row_index_ranges,
172 };
173 Ok(starting_offsets.into_iter().flatten())
174 }
175}
176
177#[internal_api]
206pub(crate) fn fixup_parquet_read(
207 batch: RecordBatch,
208 requested_ordering: &[ReorderIndex],
209 row_indexes: Option<&mut FlattenedRangeIterator<i64>>,
210 file_location: Option<&str>,
211 target_schema: Option<&SchemaRef>,
212) -> DeltaResult<ArrowEngineData> {
213 let data = reorder_struct_array(batch.into(), requested_ordering, row_indexes, file_location)?;
214 let data = fix_nested_null_masks(data);
215 let data = if let Some(schema) = target_schema {
216 apply_schema_to_struct(&data, schema)?
217 } else {
218 data
219 };
220 Ok(data.into())
221}
222
223#[derive(Debug, PartialEq)]
331#[internal_api]
332pub(crate) struct ReorderIndex {
333 pub index: usize,
334 transform: ReorderIndexTransform,
335}
336
337#[derive(Debug, PartialEq)]
338#[internal_api]
339pub(crate) enum ReorderIndexTransform {
340 Cast(ArrowDataType),
342 Nested(Vec<ReorderIndex>),
344 Identity,
346 Missing(ArrowFieldRef),
348 RowIndex(ArrowFieldRef),
350 FilePath(ArrowFieldRef),
352}
353
354impl ReorderIndex {
355 fn new(index: usize, transform: ReorderIndexTransform) -> Self {
356 ReorderIndex { index, transform }
357 }
358
359 fn cast(index: usize, target: ArrowDataType) -> Self {
360 ReorderIndex::new(index, ReorderIndexTransform::Cast(target))
361 }
362
363 fn nested(index: usize, children: Vec<ReorderIndex>) -> Self {
364 ReorderIndex::new(index, ReorderIndexTransform::Nested(children))
365 }
366
367 fn identity(index: usize) -> Self {
368 ReorderIndex::new(index, ReorderIndexTransform::Identity)
369 }
370
371 fn missing(index: usize, field: ArrowFieldRef) -> Self {
372 ReorderIndex::new(index, ReorderIndexTransform::Missing(field))
373 }
374
375 fn row_index(index: usize, field: ArrowFieldRef) -> Self {
376 ReorderIndex::new(index, ReorderIndexTransform::RowIndex(field))
377 }
378
379 fn file_path(index: usize, field: ArrowFieldRef) -> Self {
380 ReorderIndex::new(index, ReorderIndexTransform::FilePath(field))
381 }
382
383 fn needs_transform(&self) -> bool {
386 match self.transform {
387 ReorderIndexTransform::Cast(_)
390 | ReorderIndexTransform::Missing(_)
391 | ReorderIndexTransform::RowIndex(_)
392 | ReorderIndexTransform::FilePath(_) => true,
393 ReorderIndexTransform::Nested(ref children) => ordering_needs_transform(children),
395 ReorderIndexTransform::Identity => false,
397 }
398 }
399}
400
401fn count_cols(field: &ArrowField) -> usize {
403 _count_cols(field.data_type())
404}
405
406fn _count_cols(dt: &ArrowDataType) -> usize {
407 match dt {
408 ArrowDataType::Struct(fields) => fields.iter().map(|f| count_cols(f)).sum(),
409 ArrowDataType::Union(fields, _) => fields.iter().map(|(_, f)| count_cols(f)).sum(),
410 ArrowDataType::List(field)
411 | ArrowDataType::LargeList(field)
412 | ArrowDataType::FixedSizeList(field, _)
413 | ArrowDataType::Map(field, _) => count_cols(field),
414 ArrowDataType::Dictionary(_, value_field) => _count_cols(value_field.as_ref()),
415 _ => 1, }
417}
418
419fn validate_parquet_variant(field: &ArrowField) -> DeltaResult<()> {
424 fn variant_parquet_error(field_name: &String) -> Error {
425 Error::Generic(format!(
426 "The field {field_name} presumed to be of Variant type might be \
427 shredded in the parquet file. The default engine does not support \
428 shredded reads yet."
429 ))
430 }
431 match field.data_type() {
432 ArrowDataType::Struct(fields) => {
433 if fields.len() != 2 {
434 return Err(variant_parquet_error(field.name()));
435 }
436 if !matches!(
437 (fields[0].name().as_str(), fields[1].name().as_str()),
438 ("value", "metadata") | ("metadata", "value")
439 ) {
440 return Err(variant_parquet_error(field.name()));
441 }
442 Ok(())
443 }
444 _ => Err(variant_parquet_error(field.name())),
445 }
446}
447
448fn get_indices(
453 start_parquet_offset: usize,
454 requested_schema: &Schema,
455 fields: &ArrowFields,
456 mask_indices: &mut Vec<usize>,
457) -> DeltaResult<(usize, Vec<ReorderIndex>)> {
458 let mut found_fields = HashSet::with_capacity(requested_schema.num_fields());
459 let mut reorder_indices = Vec::with_capacity(requested_schema.num_fields());
460 let mut deferred_missing = Vec::new();
464 let mut parquet_offset = start_parquet_offset;
465 let matched_parquet_fields = match_parquet_fields(requested_schema, fields);
469 for MatchedParquetField {
470 parquet_index,
471 parquet_field: field,
472 kernel_field_info,
473 } in matched_parquet_fields
474 {
475 debug!(
476 "Getting indices for field {} with offset {parquet_offset}, with index {parquet_index}",
477 field.name()
478 );
479 if let Some(KernelFieldInfo {
480 parquet_index: index,
481 field: requested_field,
482 ..
483 }) = kernel_field_info
484 {
485 if requested_field.data_type == DataType::unshredded_variant() {
489 validate_parquet_variant(field)?;
490 }
491 match field.data_type() {
492 ArrowDataType::Struct(fields) => {
493 if let DataType::Struct(ref requested_schema)
494 | DataType::Variant(ref requested_schema) = requested_field.data_type
495 {
496 let mask_before = mask_indices.len();
497 let (parquet_advance, children) = get_indices(
498 parquet_index + parquet_offset,
499 requested_schema.as_ref(),
500 fields,
501 mask_indices,
502 )?;
503 parquet_offset += parquet_advance.saturating_sub(1);
507 found_fields.insert(requested_field.name());
513 if mask_indices.len() > mask_before {
514 reorder_indices.push(ReorderIndex::nested(index, children));
515 } else {
516 debug_assert_eq!(children.len(), requested_schema.num_fields());
521 deferred_missing.push(ReorderIndex::missing(
522 index,
523 Arc::new(requested_field.try_into_arrow()?),
524 ));
525 }
526 } else {
527 return Err(Error::unexpected_column_type(field.name()));
528 }
529 }
530 ArrowDataType::List(list_field)
531 | ArrowDataType::LargeList(list_field)
532 | ArrowDataType::ListView(list_field) => {
533 if let DataType::Array(array_type) = requested_field.data_type() {
536 let requested_schema = StructType::new_unchecked([StructField::new(
537 list_field.name().clone(), array_type.element_type.clone(),
539 array_type.contains_null,
540 )]);
541 let mask_before = mask_indices.len();
542 let (parquet_advance, mut children) = get_indices(
543 parquet_index + parquet_offset,
544 &requested_schema,
545 &[list_field.clone()].into(),
546 mask_indices,
547 )?;
548 parquet_offset += parquet_advance - 1;
550 found_fields.insert(requested_field.name());
551 if mask_indices.len() <= mask_before {
552 deferred_missing.push(ReorderIndex::missing(
554 index,
555 Arc::new(requested_field.try_into_arrow()?),
556 ));
557 } else if children.len() != 1 {
558 return Err(Error::generic(
559 "List call should not have generated more than one reorder index",
560 ));
561 } else {
562 let mut children = children.swap_remove(0);
564 children.index = index;
567 reorder_indices.push(children);
568 }
569 } else {
570 return Err(Error::unexpected_column_type(list_field.name()));
571 }
572 }
573 ArrowDataType::Map(key_val_field, _) => {
574 match (key_val_field.data_type(), requested_field.data_type()) {
575 (ArrowDataType::Struct(inner_fields), DataType::Map(map_type)) => {
576 let mut key_val_names =
577 inner_fields.iter().map(|f| f.name().to_string());
578 let key_name = key_val_names.next().ok_or_else(|| {
579 Error::generic("map fields didn't include a key col")
580 })?;
581 let val_name = key_val_names.next().ok_or_else(|| {
582 Error::generic("map fields didn't include a val col")
583 })?;
584 if key_val_names.next().is_some() {
585 return Err(Error::generic("map fields had more than 2 members"));
586 }
587 let inner_schema = map_type.as_struct_schema(key_name, val_name);
588 let mask_before = mask_indices.len();
589 let (parquet_advance, mut children) = get_indices(
590 parquet_index + parquet_offset,
591 &inner_schema,
592 inner_fields,
593 mask_indices,
594 )?;
595
596 parquet_offset += parquet_advance - 1;
600 found_fields.insert(requested_field.name());
601 if mask_indices.len() <= mask_before {
602 deferred_missing.push(ReorderIndex::missing(
604 index,
605 Arc::new(requested_field.try_into_arrow()?),
606 ));
607 } else if children.len() != 2 {
608 return Err(Error::generic(
609 "Map call should have generated exactly two reorder indices",
610 ));
611 } else {
612 let mut num_identity_transforms = 0;
614 if !children[0].needs_transform() {
615 children[0] = ReorderIndex::identity(0);
616 num_identity_transforms += 1;
617 }
618 if !children[1].needs_transform() {
619 children[1] = ReorderIndex::identity(1);
620 num_identity_transforms += 1;
621 }
622 let transform = match num_identity_transforms {
623 2 => ReorderIndex::identity(index),
624 _ => ReorderIndex::nested(index, children),
625 };
626 reorder_indices.push(transform);
627 }
628 }
629 _ => {
630 return Err(Error::unexpected_column_type(field.name()));
631 }
632 }
633 }
634 _ => {
635 match super::ensure_data_types::ensure_data_types(
639 &requested_field.data_type,
640 field.data_type(),
641 super::ensure_data_types::ValidationMode::TypesAndNames,
642 )? {
643 DataTypeCompat::Identical => {
644 reorder_indices.push(ReorderIndex::identity(index))
645 }
646 DataTypeCompat::NeedsCast(target) => {
647 reorder_indices.push(ReorderIndex::cast(index, target))
648 }
649 DataTypeCompat::Nested => {
650 return Err(Error::internal_error(
651 "Comparing nested types in get_indices",
652 ))
653 }
654 }
655 found_fields.insert(requested_field.name());
656 mask_indices.push(parquet_offset + parquet_index);
657 }
658 }
659 } else {
660 debug!("Skipping over un-selected field: {}", field.name());
663 parquet_offset += count_cols(field).saturating_sub(1);
666 }
667 }
668
669 reorder_indices.extend(deferred_missing);
671
672 if found_fields.len() != requested_schema.num_fields() {
673 for (requested_position, field) in requested_schema.fields().enumerate() {
676 if !found_fields.contains(field.name()) {
677 match field.get_metadata_column_spec() {
678 Some(MetadataColumnSpec::RowIndex) => {
679 debug!("Inserting a row index column: {}", field.name());
680 reorder_indices.push(ReorderIndex::row_index(
681 requested_position,
682 Arc::new(field.try_into_arrow()?),
683 ));
684 }
685 Some(MetadataColumnSpec::FilePath) => {
686 debug!("Inserting a file path column: {}", field.name());
687 reorder_indices.push(ReorderIndex::file_path(
688 requested_position,
689 Arc::new(field.try_into_arrow()?),
690 ));
691 }
692 Some(metadata_spec) => {
693 return Err(Error::Generic(format!(
694 "Metadata column {metadata_spec:?} is not supported by the default parquet reader"
695 )));
696 }
697 None if field.nullable => {
698 debug!("Inserting missing and nullable field: {}", field.name());
699 reorder_indices.push(ReorderIndex::missing(
700 requested_position,
701 Arc::new(field.try_into_arrow()?),
702 ));
703 }
704 None => {
705 return Err(Error::Generic(format!(
706 "Requested field not found in parquet schema, and field is not nullable: {}",
707 field.name()
708 )));
709 }
710 }
711 }
712 }
713 }
714 Ok((
715 parquet_offset + fields.len() - start_parquet_offset,
716 reorder_indices,
717 ))
718}
719
720fn match_parquet_fields<'k, 'p>(
725 kernel_schema: &'k StructType,
726 parquet_fields: &'p ArrowFields,
727) -> impl Iterator<Item = MatchedParquetField<'p, 'k>> {
728 type FieldId = i64;
729
730 let field_id_to_name: OnceLock<HashMap<FieldId, &String>> = OnceLock::new();
732 let init_field_map = || {
733 kernel_schema
734 .fields()
735 .filter_map(
736 |field| match field.get_config_value(&ColumnMetadataKey::ParquetFieldId) {
737 Some(MetadataValue::Number(fid)) => Some((*fid, field.name())),
738 _ => None,
739 },
740 )
741 .collect()
742 };
743
744 parquet_fields
745 .iter()
746 .enumerate()
747 .map(move |(parquet_index, parquet_field)| {
750 let parquet_field_id = parquet_field
752 .metadata()
753 .get(PARQUET_FIELD_ID_META_KEY)
754 .and_then(|x| x.parse::<FieldId>().ok());
755
756 let field_name = parquet_field_id
759 .and_then(|field_id| {
760 field_id_to_name
763 .get_or_init(init_field_map)
764 .get(&field_id)
765 .copied()
766 })
767 .unwrap_or_else(|| parquet_field.name());
768
769 let kernel_field_info =
771 kernel_schema
772 .field_with_index(field_name)
773 .and_then(|(idx, field)| {
774 (!field.is_metadata_column()).then_some(KernelFieldInfo {
775 parquet_index: idx,
776 field,
777 })
778 });
779
780 MatchedParquetField {
781 parquet_index,
782 parquet_field,
783 kernel_field_info,
784 }
785 })
786}
787
788#[internal_api]
796pub(crate) fn get_requested_indices(
797 requested_schema: &SchemaRef,
798 parquet_schema: &ArrowSchemaRef,
799) -> DeltaResult<(Vec<usize>, Vec<ReorderIndex>)> {
800 let mut mask_indices = vec![];
801 let (_, reorder_indexes) = get_indices(
802 0,
803 requested_schema,
804 parquet_schema.fields(),
805 &mut mask_indices,
806 )?;
807 Ok((mask_indices, reorder_indexes))
808}
809
810#[internal_api]
813pub(crate) fn generate_mask(
814 _requested_schema: &SchemaRef,
815 _parquet_schema: &ArrowSchemaRef,
816 parquet_physical_schema: &SchemaDescriptor,
817 indices: &[usize],
818) -> Option<ProjectionMask> {
819 Some(ProjectionMask::leaves(
822 parquet_physical_schema,
823 indices.to_owned(),
824 ))
825}
826
827fn ordering_needs_transform(requested_ordering: &[ReorderIndex]) -> bool {
833 if requested_ordering.is_empty() {
834 return false;
835 }
836 if requested_ordering[0].needs_transform() {
838 return true;
839 }
840 requested_ordering
843 .windows(2)
844 .any(|ri| (ri[0].index >= ri[1].index) || ri[1].needs_transform())
845}
846
847#[internal_api]
852pub(crate) fn ordering_needs_row_indexes(requested_ordering: &[ReorderIndex]) -> bool {
853 requested_ordering
854 .iter()
855 .any(|reorder_index| matches!(&reorder_index.transform, ReorderIndexTransform::RowIndex(_)))
856}
857
858type FieldArrayOpt = Option<(Arc<ArrowField>, Arc<dyn ArrowArray>)>;
861
862fn new_missing_array(field: &ArrowField, num_rows: usize) -> Arc<dyn ArrowArray> {
866 match (field.is_nullable(), field.data_type()) {
867 (false, ArrowDataType::Struct(child_fields)) => {
868 let child_arrays: Vec<Arc<dyn ArrowArray>> = child_fields
869 .iter()
870 .map(|f| new_missing_array(f, num_rows))
871 .collect();
872 Arc::new(StructArray::new(child_fields.clone(), child_arrays, None))
873 }
874 _ => new_null_array(field.data_type(), num_rows),
875 }
876}
877
878pub(crate) fn reorder_struct_array(
886 input_data: StructArray,
887 requested_ordering: &[ReorderIndex],
888 mut row_indexes: Option<&mut FlattenedRangeIterator<i64>>,
889 file_location: Option<&str>,
890) -> DeltaResult<StructArray> {
891 debug!("Reordering {input_data:?} with ordering: {requested_ordering:?}");
892 if !ordering_needs_transform(requested_ordering) {
893 Ok(input_data)
896 } else {
897 debug!("Have requested reorder {requested_ordering:#?} on {input_data:?}");
899 let num_rows = input_data.len();
900 let num_cols = requested_ordering.len();
901 let (input_fields, input_cols, null_buffer) = input_data.into_parts();
902 let mut final_fields_cols: Vec<FieldArrayOpt> = vec![None; num_cols];
903 for (parquet_position, reorder_index) in requested_ordering.iter().enumerate() {
904 match &reorder_index.transform {
907 ReorderIndexTransform::Cast(target) => {
908 let col = input_cols[parquet_position].as_ref();
909 let col = Arc::new(crate::arrow::compute::cast(col, target)?);
910 let new_field = Arc::new(
911 input_fields[parquet_position]
912 .as_ref()
913 .clone()
914 .with_data_type(col.data_type().clone()),
915 );
916 final_fields_cols[reorder_index.index] = Some((new_field, col));
917 }
918 ReorderIndexTransform::Nested(children) => {
919 let input_field_name = input_fields[parquet_position].name();
920 match input_cols[parquet_position].data_type() {
921 ArrowDataType::Struct(_) => {
922 let struct_array = input_cols[parquet_position].as_struct().clone();
923 let result_array = Arc::new(reorder_struct_array(
924 struct_array,
925 children,
926 None, None, )?);
931 let new_field = Arc::new(ArrowField::new_struct(
933 input_field_name,
934 result_array.fields().clone(),
935 input_fields[parquet_position].is_nullable(),
936 ));
937 final_fields_cols[reorder_index.index] =
938 Some((new_field, result_array));
939 }
940 ArrowDataType::List(_) => {
941 let list_array = input_cols[parquet_position].as_list::<i32>().clone();
942 final_fields_cols[reorder_index.index] =
943 reorder_list(list_array, input_field_name, children)?;
944 }
945 ArrowDataType::LargeList(_) => {
946 let list_array = input_cols[parquet_position].as_list::<i64>().clone();
947 final_fields_cols[reorder_index.index] =
948 reorder_list(list_array, input_field_name, children)?;
949 }
950 ArrowDataType::Map(_, _) => {
951 let map_array = input_cols[parquet_position].as_map().clone();
952 final_fields_cols[reorder_index.index] =
953 reorder_map(map_array, input_field_name, children)?;
954 }
955 _ => {
956 return Err(Error::internal_error(
957 "Nested reorder can only apply to struct/list/map.",
958 ));
959 }
960 }
961 }
962 ReorderIndexTransform::Identity => {
963 final_fields_cols[reorder_index.index] = Some((
964 input_fields[parquet_position].clone(), input_cols[parquet_position].clone(), ));
967 }
968 ReorderIndexTransform::Missing(field) => {
969 let array = new_missing_array(field, num_rows);
970 final_fields_cols[reorder_index.index] = Some((field.clone(), array));
971 }
972 ReorderIndexTransform::RowIndex(field) => {
973 let Some(ref mut row_index_iter) = row_indexes else {
974 return Err(Error::generic(
975 "Row index column requested but row index iterator not provided",
976 ));
977 };
978 let row_index_array: PrimitiveArray<Int64Type> =
979 row_index_iter.take(num_rows).collect();
980 require!(
981 row_index_array.len() == num_rows,
982 Error::internal_error(
983 "Row index iterator exhausted before reaching the end of the file"
984 )
985 );
986 final_fields_cols[reorder_index.index] =
987 Some((Arc::clone(field), Arc::new(row_index_array)));
988 }
989 ReorderIndexTransform::FilePath(field) => {
990 let Some(file_path) = file_location else {
991 return Err(Error::generic(
992 "File path column requested but file location not provided",
993 ));
994 };
995 let file_path_array = StringArray::from(vec![file_path; num_rows]);
996 final_fields_cols[reorder_index.index] =
997 Some((Arc::clone(field), Arc::new(file_path_array)));
998 }
999 }
1000 }
1001 let num_cols = final_fields_cols.len();
1002 let (field_vec, reordered_columns): (Vec<Arc<ArrowField>>, _) =
1003 final_fields_cols.into_iter().flatten().unzip();
1004 if field_vec.len() != num_cols {
1005 Err(Error::internal_error("Found a None in final_fields_cols."))
1006 } else {
1007 Ok(StructArray::try_new(
1008 field_vec.into(),
1009 reordered_columns,
1010 null_buffer,
1011 )?)
1012 }
1013 }
1014}
1015
1016fn reorder_list<O: OffsetSizeTrait>(
1017 list_array: GenericListArray<O>,
1018 input_field_name: &str,
1019 children: &[ReorderIndex],
1020) -> DeltaResult<FieldArrayOpt> {
1021 let (list_field, offset_buffer, maybe_sa, null_buf) = list_array.into_parts();
1022 if let Some(struct_array) = maybe_sa.as_struct_opt() {
1023 let struct_array = struct_array.clone();
1024 let result_array = Arc::new(reorder_struct_array(
1025 struct_array,
1026 children,
1027 None, None, )?);
1031 let new_list_field = Arc::new(ArrowField::new_struct(
1032 list_field.name(),
1033 result_array.fields().clone(),
1034 result_array.is_nullable(),
1035 ));
1036 let new_field = Arc::new(ArrowField::new_list(
1037 input_field_name,
1038 new_list_field.clone(),
1039 list_field.is_nullable(),
1040 ));
1041 let list = Arc::new(GenericListArray::try_new(
1042 new_list_field,
1043 offset_buffer,
1044 result_array,
1045 null_buf,
1046 )?);
1047 Ok(Some((new_field, list)))
1048 } else {
1049 Err(Error::internal_error(
1050 "Nested reorder of list should have had struct child.",
1051 ))
1052 }
1053}
1054
1055fn reorder_map(
1056 map_array: MapArray,
1057 input_field_name: &str,
1058 children: &[ReorderIndex],
1059) -> DeltaResult<FieldArrayOpt> {
1060 let (map_field, offset_buffer, struct_array, null_buf, ordered) = map_array.into_parts();
1061 let result_array = reorder_struct_array(
1062 struct_array,
1063 children,
1064 None, None, )?;
1067 let result_fields = result_array.fields();
1068 let new_map_field = Arc::new(ArrowField::new_struct(
1069 map_field.name(),
1070 result_fields.clone(),
1071 result_array.is_nullable(),
1072 ));
1073 let key_field = result_fields[0].clone();
1074 let val_field = result_fields[1].clone();
1075 let new_field = Arc::new(ArrowField::new_map(
1076 input_field_name,
1077 map_field.name(),
1078 key_field,
1079 val_field,
1080 ordered,
1081 map_field.is_nullable(),
1082 ));
1083 let map = Arc::new(MapArray::try_new(
1084 new_map_field,
1085 offset_buffer,
1086 result_array,
1087 null_buf,
1088 ordered,
1089 )?);
1090 Ok(Some((new_field, map)))
1091}
1092
1093pub fn fix_nested_null_masks(batch: StructArray) -> StructArray {
1100 compute_nested_null_masks(batch, None)
1101}
1102
1103fn compute_nested_null_masks(sa: StructArray, parent_nulls: Option<&NullBuffer>) -> StructArray {
1106 let (fields, columns, nulls) = sa.into_parts();
1107 let nulls = NullBuffer::union(parent_nulls, nulls.as_ref());
1108 let columns = columns
1109 .into_iter()
1110 .map(|column| match column.data_type() {
1111 ArrowDataType::Null => column,
1114 ArrowDataType::Struct(_) => {
1115 let sa = column.as_struct();
1116 Arc::new(compute_nested_null_masks(sa.clone(), nulls.as_ref())) as _
1117 }
1118 _ => {
1119 let data = column.to_data();
1120 let nulls = NullBuffer::union(nulls.as_ref(), data.nulls());
1121 let builder = data.into_builder().nulls(nulls);
1122 let data = unsafe { builder.build_unchecked() };
1133 make_array(data)
1134 }
1135 })
1136 .collect();
1137
1138 unsafe { StructArray::new_unchecked(fields, columns, nulls) }
1144}
1145
1146#[internal_api]
1157pub(crate) fn parse_json(
1158 json_strings: Box<dyn EngineData>,
1159 schema: SchemaRef,
1160) -> DeltaResult<Box<dyn EngineData>> {
1161 let json_strings: RecordBatch = ArrowEngineData::try_from_engine_data(json_strings)?.into();
1162 let result = parse_json_impl(json_strings.column(0).as_ref(), schema)?;
1163 Ok(Box::new(ArrowEngineData::new(result)))
1164}
1165
1166pub(crate) fn parse_json_impl(
1171 json_strings: &dyn ArrowArray,
1172 schema: SchemaRef,
1173) -> DeltaResult<RecordBatch> {
1174 let num_rows = json_strings.len();
1175 match json_strings.data_type() {
1176 ArrowDataType::Utf8 => {
1177 parse_json_inner(json_strings.as_string::<i32>().iter(), num_rows, schema)
1178 }
1179 ArrowDataType::LargeUtf8 => {
1180 parse_json_inner(json_strings.as_string::<i64>().iter(), num_rows, schema)
1181 }
1182 ArrowDataType::Utf8View => {
1183 parse_json_inner(json_strings.as_string_view().iter(), num_rows, schema)
1184 }
1185 dt => Err(Error::generic(format!(
1186 "Expected string array for JSON parsing, got {dt}"
1187 ))),
1188 }
1189}
1190
1191fn parse_json_inner<'a>(
1192 json_strings: impl Iterator<Item = Option<&'a str>>,
1193 num_rows: usize,
1194 schema: SchemaRef,
1195) -> DeltaResult<RecordBatch> {
1196 match StringifyFailureProneLeaves.transform_struct(schema.as_ref()) {
1200 Cow::Borrowed(_) => {
1201 let arrow_target = Arc::new(ArrowSchema::try_from_kernel(schema.as_ref())?);
1202 decode_with_arrow_json(json_strings, num_rows, arrow_target)
1203 }
1204 Cow::Owned(relaxed) => {
1205 let arrow_target = Arc::new(ArrowSchema::try_from_kernel(schema.as_ref())?);
1206 let arrow_relaxed = Arc::new(ArrowSchema::try_from_kernel(&relaxed)?);
1207 let decoded = decode_with_arrow_json(json_strings, num_rows, arrow_relaxed)?;
1208 safe_cast_back(decoded, &arrow_target)
1209 }
1210 }
1211}
1212
1213fn decode_with_arrow_json<'a>(
1217 json_strings: impl Iterator<Item = Option<&'a str>>,
1218 num_rows: usize,
1219 schema: ArrowSchemaRef,
1220) -> DeltaResult<RecordBatch> {
1221 if num_rows == 0 {
1222 return Ok(RecordBatch::new_empty(schema));
1223 }
1224
1225 let mut decoder = ReaderBuilder::new(schema)
1226 .with_batch_size(num_rows)
1227 .with_coerce_primitive(true)
1228 .build_decoder()?;
1229
1230 for (json, row_number) in json_strings.zip(1..) {
1231 let line = json.unwrap_or("{}");
1232 let consumed = decoder.decode(line.as_bytes())?;
1233 if consumed != line.len() || decoder.has_partial_record() {
1235 return Err(Error::Generic(format!(
1236 "Malformed JSON: Multiple, partial, or 0 JSON objects on row {row_number}"
1237 )));
1238 }
1239 if decoder.len() != row_number {
1241 return Err(Error::Generic(format!(
1242 "Malformed JSON: Multiple, partial, or 0 JSON objects on row {row_number}"
1243 )));
1244 }
1245 }
1246 if let Some(batch) = decoder.flush()? {
1248 if batch.num_rows() != num_rows {
1249 return Err(Error::Generic(format!(
1250 "Unexpected number of rows decoded. Got {}, expected{}",
1251 batch.num_rows(),
1252 num_rows
1253 )));
1254 }
1255 return Ok(batch);
1256 }
1257 Err(Error::generic(
1258 "Malformed JSON: exited parse_json_impl without deserializing anything useful",
1259 ))
1260}
1261
1262struct StringifyFailureProneLeaves;
1268
1269impl<'a> SchemaTransform<'a> for StringifyFailureProneLeaves {
1270 transform_output_type!(|'a, T| Cow<'a, T>);
1271
1272 fn transform_primitive(&mut self, ptype: &'a PrimitiveType) -> Cow<'a, PrimitiveType> {
1273 use PrimitiveType::*;
1274 match ptype {
1275 Timestamp | TimestampNtz | Date | Decimal(_) => Cow::Owned(String),
1276 _ => Cow::Borrowed(ptype),
1277 }
1278 }
1279
1280 fn transform_array(&mut self, atype: &'a ArrayType) -> Cow<'a, ArrayType> {
1281 Cow::Borrowed(atype)
1282 }
1283
1284 fn transform_map(&mut self, mtype: &'a MapType) -> Cow<'a, MapType> {
1285 Cow::Borrowed(mtype)
1286 }
1287
1288 fn transform_variant(&mut self, stype: &'a StructType) -> Cow<'a, StructType> {
1289 Cow::Borrowed(stype)
1290 }
1291}
1292
1293fn safe_cast_back(decoded: RecordBatch, target: &ArrowSchemaRef) -> DeltaResult<RecordBatch> {
1296 let opts = CastOptions {
1297 safe: true,
1298 ..Default::default()
1299 };
1300 let (_, columns, row_count) = decoded.into_parts();
1301 let columns = columns
1302 .into_iter()
1303 .zip(target.fields().iter())
1304 .map(|(arr, field)| cast_array_to_type(arr, field.data_type(), &opts))
1305 .collect::<DeltaResult<Vec<_>>>()?;
1306 Ok(RecordBatch::try_new_with_options(
1307 target.clone(),
1308 columns,
1309 &RecordBatchOptions::new().with_row_count(Some(row_count)),
1310 )?)
1311}
1312
1313#[cfg(test)]
1344pub(crate) fn coerce_columns_to_schema(
1345 columns: Vec<ArrowArrayRef>,
1346 target: &ArrowSchemaRef,
1347) -> DeltaResult<Vec<ArrowArrayRef>> {
1348 let opts = CastOptions {
1349 safe: false,
1350 ..Default::default()
1351 };
1352 columns
1353 .into_iter()
1354 .zip(target.fields().iter())
1355 .map(|(arr, field)| cast_array_to_type(arr, field.data_type(), &opts))
1356 .collect()
1357}
1358
1359fn cast_array_to_type(
1369 array: ArrowArrayRef,
1370 target: &ArrowDataType,
1371 opts: &CastOptions<'_>,
1372) -> DeltaResult<ArrowArrayRef> {
1373 if array.data_type() == target {
1374 return Ok(array);
1375 }
1376 match target {
1377 ArrowDataType::Struct(target_fields) => {
1378 let s = array.as_struct_opt().ok_or_else(|| {
1379 Error::generic(format!(
1380 "cannot cast {} to a struct target",
1381 array.data_type()
1382 ))
1383 })?;
1384 let nulls = s.nulls().cloned();
1385 require!(
1386 s.columns().len() == target_fields.len(),
1387 Error::generic(format!(
1388 "cannot cast struct with {} children to target with {} fields",
1389 s.columns().len(),
1390 target_fields.len()
1391 ))
1392 );
1393 let new_children = s
1394 .columns()
1395 .iter()
1396 .zip(target_fields.iter())
1397 .map(|(c, f)| cast_array_to_type(c.clone(), f.data_type(), opts))
1398 .collect::<DeltaResult<Vec<_>>>()?;
1399 Ok(Arc::new(StructArray::try_new(
1400 target_fields.clone(),
1401 new_children,
1402 nulls,
1403 )?))
1404 }
1405 _ => Ok(cast_with_options(&array, target, opts)?),
1406 }
1407}
1408
1409pub(crate) fn filter_to_record_batch(
1410 filtered_data: FilteredEngineData,
1411) -> DeltaResult<RecordBatch> {
1412 let filtered = filtered_data.apply_selection_vector()?;
1413 let arrow_data = ArrowEngineData::try_from_engine_data(filtered)?;
1414 Ok((*arrow_data).into())
1415}
1416
1417struct NullValueMapEncoder<'a> {
1421 field: &'a ArrowFieldRef,
1422 array: &'a MapArray,
1423}
1424
1425impl<'a> Encoder for NullValueMapEncoder<'a> {
1426 fn encode(&mut self, idx: usize, out: &mut Vec<u8>) {
1427 let options = EncoderOptions::default().with_explicit_nulls(true);
1428 #[allow(clippy::unwrap_used)]
1432 let mut encoder = make_encoder(self.field, self.array, &options).unwrap();
1433 encoder.encode(idx, out);
1434 }
1435}
1436
1437#[derive(Debug)]
1441struct NullValueMapEncoderFactory;
1442
1443impl EncoderFactory for NullValueMapEncoderFactory {
1444 fn make_default_encoder<'a>(
1445 &self,
1446 field: &'a ArrowFieldRef,
1447 array: &'a dyn ArrowArray,
1448 _options: &'a EncoderOptions,
1449 ) -> Result<Option<NullableEncoder<'a>>, crate::arrow::error::ArrowError> {
1450 match array.data_type() {
1457 ArrowDataType::Map(_, _) => {
1458 let array = array.as_map();
1459 let encoder = NullValueMapEncoder { field, array };
1460 let array_encoder = Box::new(encoder) as Box<dyn Encoder + 'a>;
1461 let nulls = array.nulls().cloned();
1462 Ok(Some(NullableEncoder::new(array_encoder, nulls)))
1463 }
1464 _ => Ok(None),
1465 }
1466 }
1467}
1468
1469#[internal_api]
1472pub(crate) fn to_json_bytes(
1473 data: impl Iterator<Item = DeltaResult<FilteredEngineData>> + Send,
1474) -> DeltaResult<Vec<u8>> {
1475 let builder = WriterBuilder::new().with_encoder_factory(Arc::new(NullValueMapEncoderFactory));
1476 let mut writer = builder.build::<_, LineDelimited>(Vec::new());
1477 for chunk in data {
1478 let batch = filter_to_record_batch(chunk?)?;
1479 writer.write(&batch)?;
1480 }
1481 writer.finish()?;
1482 Ok(writer.into_inner())
1483}
1484
1485#[internal_api]
1491pub(crate) fn fixup_json_read(
1492 batch: RecordBatch,
1493 reorder_indices: &[ReorderIndex],
1494 file_location: &str,
1495) -> DeltaResult<ArrowEngineData> {
1496 let data = reorder_struct_array(batch.into(), reorder_indices, None, Some(file_location))?;
1497 Ok(data.into())
1498}
1499
1500#[internal_api]
1515pub(crate) fn build_json_reorder_indices(schema: &StructType) -> DeltaResult<Vec<ReorderIndex>> {
1516 let mut reorder_indices = Vec::with_capacity(schema.num_fields());
1519 let mut metadata_entries = Vec::new();
1524
1525 for (output_pos, field) in schema.fields().enumerate() {
1526 match field.get_metadata_column_spec() {
1527 None => reorder_indices.push(ReorderIndex::identity(output_pos)),
1528 Some(spec) => metadata_entries.push((output_pos, field, spec)),
1529 }
1530 }
1531
1532 for (output_pos, field, spec) in metadata_entries {
1533 let field = Arc::new(field.try_into_arrow()?);
1534 let rindex = match spec {
1535 MetadataColumnSpec::FilePath => ReorderIndex::file_path(output_pos, field),
1536 _ => ReorderIndex::missing(output_pos, field),
1537 };
1538 reorder_indices.push(rindex);
1539 }
1540
1541 Ok(reorder_indices)
1542}
1543
1544#[internal_api]
1551pub(crate) fn json_arrow_schema(schema: &StructType) -> DeltaResult<ArrowSchema> {
1552 let json_fields = schema.with_fields_filtered(|f| f.get_metadata_column_spec().is_none())?;
1553 Ok(ArrowSchema::try_from_kernel(&json_fields)?)
1554}
1555
1556#[cfg(test)]
1557mod tests {
1558 use std::sync::Arc;
1559
1560 use rstest::rstest;
1561
1562 use super::*;
1563 use crate::arrow::array::{
1564 Array, ArrayRef as ArrowArrayRef, AsArray, BooleanArray, GenericListArray, Int32Array,
1565 Int32Builder, Int64Array, ListArray, MapArray, MapBuilder, MapFieldNames, StringArray,
1566 StringBuilder, StructArray, StructBuilder,
1567 };
1568 use crate::arrow::buffer::{OffsetBuffer, ScalarBuffer};
1569 use crate::arrow::datatypes::{
1570 DataType as ArrowDataType, Field as ArrowField, Fields as ArrowFields, Int32Type,
1571 Schema as ArrowSchema, SchemaRef as ArrowSchemaRef,
1572 };
1573 use crate::engine::arrow_conversion::TryIntoArrow;
1574 use crate::schema::{
1575 schema, schema_ref, ArrayType, ColumnMetadataKey, DataType, MapType, MetadataColumnSpec,
1576 MetadataValue, StructField, StructType,
1577 };
1578 use crate::table_features::ColumnMappingMode;
1579 use crate::unit_test_utils::assert_result_error_with_message;
1580
1581 fn column_mapping_cases() -> [ColumnMappingMode; 3] {
1582 [
1583 ColumnMappingMode::Id,
1584 ColumnMappingMode::Name,
1585 ColumnMappingMode::None,
1586 ]
1587 }
1588
1589 fn logical_name(field_id: i64) -> String {
1592 format!("logical-{field_id}")
1593 }
1594
1595 fn physical_name(field_id: i64) -> String {
1598 format!("physical-{field_id}")
1599 }
1600
1601 fn parquet_name(field_id: i64, mode: ColumnMappingMode) -> String {
1604 match mode {
1605 ColumnMappingMode::Id | ColumnMappingMode::Name => physical_name(field_id),
1606 ColumnMappingMode::None => logical_name(field_id),
1607 }
1608 }
1609
1610 fn column_mapping_metadata(
1613 field_id: i64,
1614 mode: ColumnMappingMode,
1615 ) -> HashMap<String, MetadataValue> {
1616 match mode {
1617 ColumnMappingMode::None => HashMap::new(),
1618 _ => kernel_fid_and_name(field_id, physical_name(field_id)),
1619 }
1620 }
1621
1622 fn arrow_fid(field_id: i64) -> HashMap<String, String> {
1624 HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), field_id.to_string())])
1625 }
1626
1627 fn kernel_fid_and_name(field_id: i64, name: impl AsRef<str>) -> HashMap<String, MetadataValue> {
1630 HashMap::from([
1631 (
1632 ColumnMetadataKey::ColumnMappingId.as_ref().to_string(),
1633 field_id.into(),
1634 ),
1635 (
1636 ColumnMetadataKey::ColumnMappingPhysicalName
1637 .as_ref()
1638 .to_string(),
1639 name.as_ref().to_string().into(),
1640 ),
1641 ])
1642 }
1643
1644 fn create_mock_row_group(num_rows: i64) -> RowGroupMetaData {
1646 use crate::parquet::basic::{Encoding, Type as PhysicalType};
1647 use crate::parquet::file::metadata::ColumnChunkMetaData;
1648 use crate::parquet::schema::types::Type;
1649
1650 let schema = Arc::new(SchemaDescriptor::new(Arc::new(
1652 Type::group_type_builder("schema")
1653 .with_fields(vec![Arc::new(
1654 Type::primitive_type_builder("test_col", PhysicalType::INT32)
1655 .build()
1656 .unwrap(),
1657 )])
1658 .build()
1659 .unwrap(),
1660 )));
1661
1662 let column_chunk = ColumnChunkMetaData::builder(schema.column(0))
1664 .set_encodings(vec![Encoding::PLAIN])
1665 .set_total_compressed_size(100)
1666 .set_total_uncompressed_size(100)
1667 .set_num_values(num_rows)
1668 .build()
1669 .unwrap();
1670
1671 RowGroupMetaData::builder(schema)
1672 .set_num_rows(num_rows)
1673 .set_total_byte_size(100)
1674 .set_column_metadata(vec![column_chunk])
1675 .build()
1676 .unwrap()
1677 }
1678
1679 #[test]
1680 fn test_json_parsing() {
1681 static EXPECTED_JSON_ERR_STR: &str = "Generic delta kernel error: Malformed JSON: Multiple, partial, or 0 JSON objects on row";
1682 fn check_parse_fails(input: Vec<Option<&str>>, schema: SchemaRef, expected_start: &str) {
1683 let result = parse_json_impl(&StringArray::from(input), schema);
1684 let err = result.expect_err("Expected an error");
1685 let msg = err.to_string();
1686 assert!(
1687 msg.starts_with(expected_start),
1688 "Error message was not what was expected"
1689 );
1690 }
1691
1692 let requested_schema = Arc::new(
1693 StructType::try_new(vec![
1694 StructField::nullable("a", DataType::INTEGER),
1695 StructField::nullable("b", DataType::STRING),
1696 StructField::nullable("c", DataType::INTEGER),
1697 ])
1698 .unwrap(),
1699 );
1700 let input: Vec<&str> = vec![];
1701 let result = parse_json_impl(&StringArray::from(input), requested_schema.clone()).unwrap();
1702 assert_eq!(result.num_rows(), 0);
1703
1704 for input in [
1705 vec![Some("")],
1706 vec![Some(" \n\t")],
1707 vec![Some(r#"{ "a": 1"#)],
1708 vec![Some("{}{}")],
1709 vec![Some(r#"{} { "a": 1"#)],
1710 vec![Some(r#"{} { "a": 1"#), Some("}")],
1711 vec![Some(r#"{ "a": 1"#), Some(r#", "b": "b"}"#)],
1712 ] {
1713 check_parse_fails(input, requested_schema.clone(), EXPECTED_JSON_ERR_STR);
1714 }
1715
1716 check_parse_fails(
1718 vec![Some(r#""a""#)],
1719 requested_schema.clone(),
1720 "Json error: expected { got \"a\"",
1721 );
1722
1723 let input: Vec<Option<&str>> = vec![None, Some(r#"{"a": 1, "b": "2", "c": 3}"#), None];
1724 let result = parse_json_impl(&StringArray::from(input), requested_schema).unwrap();
1725 assert_eq!(result.num_rows(), 3);
1726 assert_eq!(result.column(0).null_count(), 2);
1727 assert_eq!(result.column(1).null_count(), 2);
1728 assert_eq!(result.column(2).null_count(), 2);
1729 }
1730
1731 #[test]
1732 fn test_parse_json_with_long_strings() {
1733 let schema = Arc::new(
1735 StructType::try_new(vec![StructField::nullable("long_val", DataType::STRING)]).unwrap(),
1736 );
1737 let long_string = "a".repeat(1_000_000); let json_string = format!(r#"{{"long_val": "{long_string}"}}"#);
1739 let input: Vec<Option<&str>> = vec![Some(&json_string)];
1740
1741 let batch = parse_json_impl(&StringArray::from(input), schema).unwrap();
1742 assert_eq!(batch.num_rows(), 1);
1743 let long_col = batch.column(0).as_string::<i32>();
1744 assert_eq!(long_col.value(0), long_string);
1745 }
1746
1747 #[test]
1748 fn test_parse_json_large_string_array() {
1749 use crate::arrow::array::LargeStringArray;
1751 use crate::engine::arrow_data::ArrowEngineData;
1752
1753 let large_strings = LargeStringArray::from(vec![
1754 Some(r#"{"a": 1, "b": "hello"}"#),
1755 None,
1756 Some(r#"{"a": 3, "b": "world"}"#),
1757 ]);
1758 let field = Arc::new(ArrowField::new("s", ArrowDataType::LargeUtf8, true));
1759 let schema = Arc::new(ArrowSchema::new(vec![field]));
1760 let batch =
1761 RecordBatch::try_new(schema, vec![Arc::new(large_strings) as ArrowArrayRef]).unwrap();
1762 let engine_data: Box<dyn crate::EngineData> = Box::new(ArrowEngineData::new(batch));
1763
1764 let output_schema: crate::schema::SchemaRef = Arc::new(StructType::new_unchecked(vec![
1765 StructField::nullable("a", DataType::INTEGER),
1766 StructField::nullable("b", DataType::STRING),
1767 ]));
1768 let result = parse_json(engine_data, output_schema).unwrap();
1769 let result = ArrowEngineData::try_from_engine_data(result).unwrap();
1770 let batch: RecordBatch = result.into();
1771 assert_eq!(batch.num_rows(), 3);
1772 assert_eq!(batch.column(0).null_count(), 1);
1773 assert_eq!(batch.column(1).null_count(), 1);
1774 }
1775
1776 #[test]
1777 fn test_parse_json_string_view_array() {
1778 use crate::arrow::array::StringViewArray;
1779 use crate::engine::arrow_data::ArrowEngineData;
1780
1781 let view_strings = StringViewArray::from(vec![
1782 Some(r#"{"a": 1, "b": "hello"}"#),
1783 None,
1784 Some(r#"{"a": 3, "b": "world"}"#),
1785 ]);
1786 let field = Arc::new(ArrowField::new("s", ArrowDataType::Utf8View, true));
1787 let schema = Arc::new(ArrowSchema::new(vec![field]));
1788 let batch =
1789 RecordBatch::try_new(schema, vec![Arc::new(view_strings) as ArrowArrayRef]).unwrap();
1790 let engine_data: Box<dyn crate::EngineData> = Box::new(ArrowEngineData::new(batch));
1791
1792 let output_schema: crate::schema::SchemaRef = Arc::new(StructType::new_unchecked(vec![
1793 StructField::nullable("a", DataType::INTEGER),
1794 StructField::nullable("b", DataType::STRING),
1795 ]));
1796 let result = parse_json(engine_data, output_schema).unwrap();
1797 let result = ArrowEngineData::try_from_engine_data(result).unwrap();
1798 let batch: RecordBatch = result.into();
1799 assert_eq!(batch.num_rows(), 3);
1800 assert_eq!(batch.column(0).null_count(), 1);
1801 assert_eq!(batch.column(1).null_count(), 1);
1802 }
1803
1804 #[test]
1805 fn test_parse_json_rejects_non_string_array() {
1806 use crate::engine::arrow_data::ArrowEngineData;
1807
1808 let int_array = Int32Array::from(vec![1, 2, 3]);
1809 let field = Arc::new(ArrowField::new("s", ArrowDataType::Int32, true));
1810 let schema = Arc::new(ArrowSchema::new(vec![field]));
1811 let batch =
1812 RecordBatch::try_new(schema, vec![Arc::new(int_array) as ArrowArrayRef]).unwrap();
1813 let engine_data: Box<dyn crate::EngineData> = Box::new(ArrowEngineData::new(batch));
1814
1815 let output_schema: crate::schema::SchemaRef = schema_ref! { nullable "a": INTEGER };
1816 let err = match parse_json(engine_data, output_schema) {
1817 Err(e) => e.to_string(),
1818 Ok(_) => panic!("Expected error for non-string array input"),
1819 };
1820 assert!(
1821 err.contains("Expected string array for JSON parsing"),
1822 "Unexpected error: {err}"
1823 );
1824 }
1825
1826 #[test]
1827 fn test_parse_json_impl_strict_leaf_errors_propagate() {
1828 let schema = Arc::new(
1831 StructType::try_new(vec![StructField::nullable("a", DataType::LONG)]).unwrap(),
1832 );
1833 let input: Vec<Option<&str>> = vec![Some(r#"{"a": "not_a_number"}"#)];
1834 assert!(parse_json_impl(&StringArray::from(input), schema).is_err());
1835 }
1836
1837 fn assert_per_cell_null_isolation(
1842 column_name: &str,
1843 leaf_type: DataType,
1844 inputs: &[&str],
1845 expected_null_rows: &[usize],
1846 ) {
1847 let schema = Arc::new(
1848 StructType::try_new(vec![StructField::nullable(column_name, leaf_type)]).unwrap(),
1849 );
1850 let inputs: Vec<Option<&str>> = inputs.iter().copied().map(Some).collect();
1851 let batch = parse_json_impl(&StringArray::from(inputs.clone()), schema)
1852 .expect("parse_json_impl should not error on failure-prone leaf parse failures");
1853 assert_eq!(batch.num_rows(), inputs.len());
1854 let col = batch.column(0);
1855 assert_eq!(
1856 col.null_count(),
1857 expected_null_rows.len(),
1858 "unexpected null count for column {column_name}",
1859 );
1860 for row in 0..inputs.len() {
1861 let want_null = expected_null_rows.contains(&row);
1862 assert_eq!(
1863 col.is_null(row),
1864 want_null,
1865 "row {row} of column {column_name}: expected is_null={want_null}",
1866 );
1867 }
1868 }
1869
1870 #[rstest]
1874 #[case::timestamp_extended_year(
1875 DataType::TIMESTAMP,
1876 &[
1877 r#"{"v": "2024-01-01T00:00:00Z"}"#,
1878 r#"{"v": "+48690-07-02T22:50:38.211Z"}"#,
1879 r#"{"v": "2025-06-01T00:00:00Z"}"#,
1880 ],
1881 )]
1882 #[case::timestamp_ntz_garbage(
1883 DataType::TIMESTAMP_NTZ,
1884 &[
1885 r#"{"v": "2024-01-01T00:00:00"}"#,
1886 r#"{"v": "not-a-timestamp"}"#,
1887 r#"{"v": "2025-06-01T00:00:00"}"#,
1888 ],
1889 )]
1890 #[case::date_out_of_range_month(
1891 DataType::DATE,
1892 &[
1893 r#"{"v": "2024-01-01"}"#,
1894 r#"{"v": "2024-13-01"}"#,
1895 r#"{"v": "2025-06-30"}"#,
1896 ],
1897 )]
1898 #[case::decimal_overflow(
1899 DataType::decimal(10, 2).unwrap(),
1900 &[
1901 r#"{"v": "10.50"}"#,
1902 r#"{"v": "99999999999.99"}"#,
1903 r#"{"v": "5.25"}"#,
1904 ],
1905 )]
1906 fn test_parse_json_safe_cast_per_cell_null(
1907 #[case] leaf_type: DataType,
1908 #[case] inputs: &[&str],
1909 ) {
1910 assert_per_cell_null_isolation("v", leaf_type, inputs, &[1]);
1911 }
1912
1913 #[test]
1914 fn test_parse_json_safe_cast_intermixed_struct_per_cell_isolation() {
1915 let schema = Arc::new(
1919 StructType::try_new(vec![
1920 StructField::nullable("EventTime", DataType::TIMESTAMP),
1921 StructField::nullable("IngestTime", DataType::TIMESTAMP),
1922 StructField::nullable("Price", DataType::decimal(10, 2).unwrap()),
1923 StructField::nullable("UserId", DataType::LONG),
1924 ])
1925 .unwrap(),
1926 );
1927 let inputs: Vec<Option<&str>> = vec![
1928 Some(
1929 r#"{"EventTime": "2024-01-01T00:00:00Z", "IngestTime": "2024-01-01T00:00:01Z", "Price": "10.50", "UserId": 1}"#,
1930 ),
1931 Some(
1932 r#"{"EventTime": "+48690-07-02T22:50:38.211Z", "IngestTime": "2024-06-01T00:00:01Z", "Price": "20.00", "UserId": 2}"#,
1933 ),
1934 Some(
1935 r#"{"EventTime": "2025-06-01T00:00:00Z", "IngestTime": "2025-06-01T00:00:01Z", "Price": "30.75", "UserId": 3}"#,
1936 ),
1937 ];
1938 let batch = parse_json_impl(&StringArray::from(inputs.clone()), schema).unwrap();
1939 assert_eq!(batch.num_rows(), 3);
1940
1941 let event_time = batch.column_by_name("EventTime").unwrap();
1942 let ingest_time = batch.column_by_name("IngestTime").unwrap();
1943 let price = batch.column_by_name("Price").unwrap();
1944 let user_id = batch.column_by_name("UserId").unwrap();
1945
1946 assert!(!event_time.is_null(0) && event_time.is_null(1) && !event_time.is_null(2));
1947 assert_eq!(event_time.null_count(), 1);
1948
1949 for col in [&ingest_time, &price, &user_id] {
1950 assert_eq!(
1951 col.null_count(),
1952 0,
1953 "row 1's bad EventTime should not contaminate other fields in the same row"
1954 );
1955 }
1956 }
1957
1958 #[test]
1959 fn test_parse_json_safe_cast_nested_stats_shape() {
1960 let null_count_struct = StructType::try_new(vec![
1966 StructField::nullable("EventTime", DataType::LONG),
1967 StructField::nullable("UserId", DataType::LONG),
1968 ])
1969 .unwrap();
1970 let min_max_struct = StructType::try_new(vec![
1971 StructField::nullable("EventTime", DataType::TIMESTAMP),
1972 StructField::nullable("UserId", DataType::LONG),
1973 ])
1974 .unwrap();
1975 let schema = Arc::new(
1976 StructType::try_new(vec![
1977 StructField::nullable("numRecords", DataType::LONG),
1978 StructField::nullable("nullCount", null_count_struct),
1979 StructField::nullable("minValues", min_max_struct.clone()),
1980 StructField::nullable("maxValues", min_max_struct),
1981 StructField::nullable("tightBounds", DataType::BOOLEAN),
1982 ])
1983 .unwrap(),
1984 );
1985
1986 let inputs: Vec<Option<&str>> = vec![
1987 Some(
1988 r#"{"numRecords": 10, "nullCount": {"EventTime": 0, "UserId": 0},
1989 "minValues": {"EventTime": "2024-01-01T00:00:00Z", "UserId": 1},
1990 "maxValues": {"EventTime": "2024-01-31T00:00:00Z", "UserId": 100},
1991 "tightBounds": true}"#,
1992 ),
1993 Some(
1994 r#"{"numRecords": 20, "nullCount": {"EventTime": 0, "UserId": 0},
1995 "minValues": {"EventTime": "+48690-07-02T22:50:38.211Z", "UserId": 5},
1996 "maxValues": {"EventTime": "+48690-07-02T22:50:38.211Z", "UserId": 200},
1997 "tightBounds": true}"#,
1998 ),
1999 Some(
2000 r#"{"numRecords": 30, "nullCount": {"EventTime": 0, "UserId": 0},
2001 "minValues": {"EventTime": "2025-01-01T00:00:00Z", "UserId": 10},
2002 "maxValues": {"EventTime": "2025-12-31T00:00:00Z", "UserId": 300},
2003 "tightBounds": true}"#,
2004 ),
2005 ];
2006 let batch = parse_json_impl(&StringArray::from(inputs), schema).unwrap();
2007 assert_eq!(batch.num_rows(), 3);
2008
2009 let num_records = batch.column_by_name("numRecords").unwrap();
2010 assert_eq!(num_records.null_count(), 0);
2011
2012 let min_values = batch.column_by_name("minValues").unwrap().as_struct();
2013 let max_values = batch.column_by_name("maxValues").unwrap().as_struct();
2014 let min_event = min_values.column_by_name("EventTime").unwrap();
2015 let max_event = max_values.column_by_name("EventTime").unwrap();
2016 let min_user = min_values.column_by_name("UserId").unwrap();
2017 let max_user = max_values.column_by_name("UserId").unwrap();
2018
2019 assert!(!min_event.is_null(0) && min_event.is_null(1) && !min_event.is_null(2));
2021 assert!(!max_event.is_null(0) && max_event.is_null(1) && !max_event.is_null(2));
2022 assert_eq!(min_user.null_count(), 0);
2024 assert_eq!(max_user.null_count(), 0);
2025 }
2026
2027 #[test]
2028 fn test_parse_json_safe_cast_all_null_input() {
2029 let schema = Arc::new(
2032 StructType::try_new(vec![
2033 StructField::nullable("ts", DataType::TIMESTAMP),
2034 StructField::nullable("n", DataType::LONG),
2035 ])
2036 .unwrap(),
2037 );
2038 let inputs: Vec<Option<&str>> = vec![None, None, None];
2039 let batch = parse_json_impl(&StringArray::from(inputs), schema).unwrap();
2040 assert_eq!(batch.num_rows(), 3);
2041 for col in batch.columns() {
2042 assert_eq!(col.null_count(), 3);
2043 }
2044 }
2045
2046 #[test]
2047 fn simple_mask_indices() {
2048 column_mapping_cases().into_iter().for_each(|mode| {
2049 let requested_schema = StructType::new_unchecked([
2050 StructField::not_null(logical_name(0), DataType::INTEGER)
2051 .with_metadata(column_mapping_metadata(0, mode)),
2052 StructField::nullable(logical_name(1), DataType::STRING)
2053 .with_metadata(column_mapping_metadata(1, mode)),
2054 StructField::nullable(logical_name(2), DataType::INTEGER)
2055 .with_metadata(column_mapping_metadata(2, mode)),
2056 ])
2057 .make_physical(mode)
2058 .unwrap()
2059 .into();
2060 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2061 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
2062 .with_metadata(arrow_fid(0)),
2063 ArrowField::new(parquet_name(1, mode), ArrowDataType::Utf8, true)
2064 .with_metadata(arrow_fid(1)),
2065 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, true)
2066 .with_metadata(arrow_fid(2)),
2067 ]));
2068 let (mask_indices, reorder_indices) =
2069 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2070 let expect_mask = vec![0, 1, 2];
2071 let expect_reorder = vec![
2072 ReorderIndex::identity(0),
2073 ReorderIndex::identity(1),
2074 ReorderIndex::identity(2),
2075 ];
2076 assert_eq!(mask_indices, expect_mask);
2077 assert_eq!(reorder_indices, expect_reorder);
2078 });
2079 }
2080
2081 #[test]
2082 fn test_variant_masks() {
2083 fn unshredded_variant_parquet_schema() -> ArrowField {
2084 ArrowField::new(
2085 "v",
2086 ArrowDataType::Struct(
2087 vec![
2088 ArrowField::new("metadata", ArrowDataType::Binary, false),
2089 ArrowField::new("value", ArrowDataType::Binary, false),
2090 ]
2091 .into(),
2092 ),
2093 true,
2094 )
2095 }
2096 fn shredded_variant_parquet_schema() -> ArrowField {
2097 ArrowField::new(
2098 "v",
2099 ArrowDataType::Struct(
2100 vec![
2101 ArrowField::new("metadata", ArrowDataType::Binary, false),
2102 ArrowField::new("value", ArrowDataType::Binary, true),
2103 ArrowField::new("typed_value", ArrowDataType::Int32, true),
2104 ]
2105 .into(),
2106 ),
2107 true,
2108 )
2109 }
2110 fn incorrect_variant_parquet_schema() -> ArrowField {
2111 ArrowField::new(
2112 "v",
2113 ArrowDataType::Struct(
2114 vec![
2115 ArrowField::new("field1", ArrowDataType::Binary, false),
2116 ArrowField::new("field2", ArrowDataType::Binary, false),
2117 ]
2118 .into(),
2119 ),
2120 true,
2121 )
2122 }
2123 fn scalar_variant_parquet_schema() -> ArrowField {
2124 ArrowField::new("v", ArrowDataType::Int16, true)
2125 }
2126 let requested_schema = schema_ref! { nullable "v": (DataType::unshredded_variant()) };
2128 let unshredded_parquet_schema =
2129 Arc::new(ArrowSchema::new(vec![unshredded_variant_parquet_schema()]));
2130 let shredded_parquet_schema =
2131 Arc::new(ArrowSchema::new(vec![shredded_variant_parquet_schema()]));
2132 let incorrect_parquet_schema =
2133 Arc::new(ArrowSchema::new(vec![incorrect_variant_parquet_schema()]));
2134 let scalar_parquet_schema =
2135 Arc::new(ArrowSchema::new(vec![scalar_variant_parquet_schema()]));
2136 let result_unshredded =
2137 get_requested_indices(&requested_schema, &unshredded_parquet_schema);
2138 assert!(result_unshredded.is_ok());
2139 let result_shredded = get_requested_indices(&requested_schema, &shredded_parquet_schema);
2140 assert!(matches!(result_shredded,
2141 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2142 let result_incorrect = get_requested_indices(&requested_schema, &incorrect_parquet_schema);
2143 assert!(matches!(result_incorrect,
2144 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2145 let result_scalar = get_requested_indices(&requested_schema, &scalar_parquet_schema);
2146 assert!(matches!(result_scalar,
2147 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2148
2149 let requested_schema = Arc::new(StructType::new_unchecked([StructField::nullable(
2151 "struct_v",
2152 StructType::new_unchecked([StructField::nullable("v", DataType::unshredded_variant())]),
2153 )]));
2154 let unshredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
2155 "struct_v",
2156 ArrowDataType::Struct(vec![unshredded_variant_parquet_schema()].into()),
2157 true,
2158 )]));
2159 let shredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
2160 "struct_v",
2161 ArrowDataType::Struct(vec![shredded_variant_parquet_schema()].into()),
2162 true,
2163 )]));
2164 let result_unshredded =
2165 get_requested_indices(&requested_schema, &unshredded_parquet_schema);
2166 let result_shredded = get_requested_indices(&requested_schema, &shredded_parquet_schema);
2167 assert!(result_unshredded.is_ok());
2168 assert!(matches!(result_shredded,
2169 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2170 let requested_schema = Arc::new(StructType::new_unchecked([StructField::nullable(
2172 "array_v",
2173 ArrayType::new(DataType::unshredded_variant(), true),
2174 )]));
2175 let unshredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
2176 "array_v",
2177 ArrowDataType::List(Arc::new(unshredded_variant_parquet_schema())),
2178 true,
2179 )]));
2180 let shredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
2181 "array_v",
2182 ArrowDataType::List(Arc::new(shredded_variant_parquet_schema())),
2183 true,
2184 )]));
2185 let result_unshredded =
2186 get_requested_indices(&requested_schema, &unshredded_parquet_schema);
2187 let result_shredded = get_requested_indices(&requested_schema, &shredded_parquet_schema);
2188 assert!(result_unshredded.is_ok());
2189 assert!(matches!(result_shredded,
2190 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2191
2192 let requested_schema = Arc::new(StructType::new_unchecked([StructField::nullable(
2194 "map_v",
2195 MapType::new(DataType::STRING, DataType::unshredded_variant(), true),
2196 )]));
2197 let unshredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new_map(
2198 "map_v",
2199 "struc_v",
2200 ArrowField::new("s", ArrowDataType::Utf8, false),
2201 unshredded_variant_parquet_schema(),
2202 false,
2203 false,
2204 )]));
2205 let shredded_parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new_map(
2206 "map_v",
2207 "struc_v",
2208 ArrowField::new("s", ArrowDataType::Utf8, false),
2209 shredded_variant_parquet_schema(),
2210 false,
2211 false,
2212 )]));
2213 let result_unshredded =
2214 get_requested_indices(&requested_schema, &unshredded_parquet_schema);
2215 let result_shredded = get_requested_indices(&requested_schema, &shredded_parquet_schema);
2216 assert!(result_unshredded.is_ok());
2217 assert!(matches!(result_shredded,
2218 Err(e) if e.to_string().contains("The default engine does not support shredded reads")));
2219 }
2220
2221 #[test]
2222 fn ensure_data_types_fails_correctly() {
2223 column_mapping_cases().into_iter().for_each(|mode| {
2224 let requested_schema = StructType::new_unchecked([
2225 StructField::not_null(logical_name(0), DataType::INTEGER)
2226 .with_metadata(column_mapping_metadata(0, mode)),
2227 StructField::nullable(logical_name(1), DataType::INTEGER)
2228 .with_metadata(column_mapping_metadata(1, mode)),
2229 ])
2230 .make_physical(mode)
2231 .unwrap()
2232 .into();
2233 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2234 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
2235 .with_metadata(arrow_fid(0)),
2236 ArrowField::new(parquet_name(1, mode), ArrowDataType::Utf8, true)
2237 .with_metadata(arrow_fid(1)),
2238 ]));
2239 let res = get_requested_indices(&requested_schema, &parquet_schema);
2240 assert_result_error_with_message(
2241 res,
2242 "Invalid argument error: Incorrect datatype. Expected integer, got Utf8",
2243 );
2244
2245 let requested_schema = StructType::new_unchecked([
2246 StructField::not_null(logical_name(0), DataType::INTEGER)
2247 .with_metadata(column_mapping_metadata(0, mode)),
2248 StructField::nullable(logical_name(1), DataType::STRING)
2249 .with_metadata(column_mapping_metadata(1, mode)),
2250 ])
2251 .make_physical(mode)
2252 .unwrap()
2253 .into();
2254 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2255 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false),
2256 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, true),
2257 ]));
2258 let res = get_requested_indices(&requested_schema, &parquet_schema);
2259 assert_result_error_with_message(
2260 res,
2261 "Invalid argument error: Incorrect datatype. Expected Utf8, got Int32",
2262 );
2263 })
2264 }
2265
2266 #[test]
2267 fn mask_with_map() {
2268 column_mapping_cases().into_iter().for_each(|mode| {
2269 let requested_schema = StructType::new_unchecked([StructField::not_null(
2270 logical_name(0),
2271 MapType::new(DataType::INTEGER, DataType::STRING, false),
2272 )
2273 .with_metadata(column_mapping_metadata(0, mode))])
2274 .make_physical(mode)
2275 .unwrap()
2276 .into();
2277
2278 let parquet_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new_map(
2280 parquet_name(0, mode),
2281 "entries",
2282 ArrowField::new("i", ArrowDataType::Int32, false),
2283 ArrowField::new("s", ArrowDataType::Utf8, false),
2284 false,
2285 false,
2286 )
2287 .with_metadata(arrow_fid(1))]));
2288 let (mask_indices, reorder_indices) =
2289 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2290 let expect_mask = vec![0, 1];
2291 let expect_reorder = vec![ReorderIndex::identity(0)];
2292 assert_eq!(mask_indices, expect_mask);
2293 assert_eq!(reorder_indices, expect_reorder);
2294 });
2295 }
2296
2297 #[test]
2298 fn simple_reorder_indices() {
2299 column_mapping_cases().into_iter().for_each(|mode| {
2300 let requested_schema = StructType::new_unchecked([
2301 StructField::not_null(logical_name(0), DataType::INTEGER)
2302 .with_metadata(column_mapping_metadata(0, mode)),
2303 StructField::nullable(logical_name(1), DataType::STRING)
2304 .with_metadata(column_mapping_metadata(1, mode)),
2305 StructField::nullable(logical_name(2), DataType::INTEGER)
2306 .with_metadata(column_mapping_metadata(2, mode)),
2307 ])
2308 .make_physical(mode)
2309 .unwrap()
2310 .into();
2311 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2312 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, true)
2313 .with_metadata(arrow_fid(2)),
2314 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
2315 .with_metadata(arrow_fid(0)),
2316 ArrowField::new(parquet_name(1, mode), ArrowDataType::Utf8, true)
2317 .with_metadata(arrow_fid(1)),
2318 ]));
2319 let (mask_indices, reorder_indices) =
2320 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2321 let expect_mask = vec![0, 1, 2];
2322 let expect_reorder = vec![
2323 ReorderIndex::identity(2),
2324 ReorderIndex::identity(0),
2325 ReorderIndex::identity(1),
2326 ];
2327 assert_eq!(mask_indices, expect_mask);
2328 assert_eq!(reorder_indices, expect_reorder);
2329 })
2330 }
2331
2332 #[test]
2333 fn simple_nullable_field_missing() {
2334 column_mapping_cases().into_iter().for_each(|mode| {
2335 let requested_schema = StructType::new_unchecked([
2336 StructField::not_null(logical_name(0), DataType::INTEGER)
2337 .with_metadata(column_mapping_metadata(0, mode)),
2338 StructField::nullable(logical_name(1), DataType::STRING)
2339 .with_metadata(column_mapping_metadata(1, mode)),
2340 StructField::nullable(logical_name(2), DataType::INTEGER)
2341 .with_metadata(column_mapping_metadata(2, mode)),
2342 ])
2343 .make_physical(mode)
2344 .unwrap()
2345 .into();
2346 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2347 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
2348 .with_metadata(arrow_fid(0)),
2349 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, true)
2350 .with_metadata(arrow_fid(2)),
2351 ]));
2352 let (mask_indices, reorder_indices) =
2353 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2354 let expect_mask = vec![0, 1];
2355 let expected_arrow_field = requested_schema
2356 .field(parquet_name(1, mode))
2357 .unwrap()
2358 .try_into_arrow()
2359 .unwrap();
2360 let expect_reorder = vec![
2361 ReorderIndex::identity(0),
2362 ReorderIndex::identity(2),
2363 ReorderIndex::missing(1, Arc::new(expected_arrow_field)),
2364 ];
2365 assert_eq!(mask_indices, expect_mask);
2366 assert_eq!(reorder_indices, expect_reorder);
2367 });
2368 }
2369
2370 #[test]
2371 fn get_requested_indices_by_id_only() {
2372 let requested_schema = StructType::new_unchecked([
2373 StructField::not_null("i_logical", DataType::INTEGER)
2374 .with_metadata(kernel_fid_and_name(1, "i_physical")),
2375 StructField::nullable("s_logical", DataType::STRING)
2376 .with_metadata(kernel_fid_and_name(2, "s_physical")),
2377 StructField::nullable("i2_logical", DataType::INTEGER)
2378 .with_metadata(kernel_fid_and_name(3, "i2_physical")),
2379 ])
2380 .make_physical(ColumnMappingMode::Id)
2381 .unwrap()
2382 .into();
2383 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2384 ArrowField::new("not-i", ArrowDataType::Int32, false).with_metadata(arrow_fid(1)),
2385 ArrowField::new("not-i2", ArrowDataType::Int32, true).with_metadata(arrow_fid(3)),
2386 ]));
2387 let (mask_indices, reorder_indices) =
2388 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2389 let expect_mask = vec![0, 1];
2390 let expected_arrow_field = requested_schema
2391 .field("s_physical")
2392 .unwrap()
2393 .try_into_arrow()
2394 .unwrap();
2395 let expect_reorder = vec![
2396 ReorderIndex::identity(0),
2397 ReorderIndex::identity(2),
2398 ReorderIndex::missing(1, Arc::new(expected_arrow_field)),
2399 ];
2400 assert_eq!(mask_indices, expect_mask);
2401 assert_eq!(reorder_indices, expect_reorder);
2402 }
2403
2404 #[test]
2405 fn get_requested_indices_by_id_falls_back_to_name() {
2406 let requested_schema = StructType::new_unchecked([
2407 StructField::not_null("i_logical", DataType::INTEGER)
2408 .with_metadata(kernel_fid_and_name(1, "i_physical")),
2409 StructField::nullable("s_logical", DataType::STRING)
2410 .with_metadata(kernel_fid_and_name(2, "s_physical")),
2411 StructField::nullable("i2_logical", DataType::INTEGER)
2412 .with_metadata(kernel_fid_and_name(3, "i2_physical")),
2413 ])
2414 .make_physical(ColumnMappingMode::Id)
2415 .unwrap()
2416 .into();
2417 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2418 ArrowField::new("i_logical", ArrowDataType::Int32, false).with_metadata(arrow_fid(1)),
2419 ArrowField::new("i2_physical", ArrowDataType::Int32, true).with_metadata(arrow_fid(3)),
2420 ]));
2421 let (mask_indices, reorder_indices) =
2422 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2423 let expect_mask = vec![0, 1];
2424 let expected_arrow_field = requested_schema
2425 .field("s_physical")
2426 .unwrap()
2427 .try_into_arrow()
2428 .unwrap();
2429 let expect_reorder = vec![
2430 ReorderIndex::identity(0),
2431 ReorderIndex::identity(2),
2432 ReorderIndex::missing(1, Arc::new(expected_arrow_field)),
2433 ];
2434 assert_eq!(mask_indices, expect_mask);
2435 assert_eq!(reorder_indices, expect_reorder);
2436 }
2437
2438 fn nested_parquet_schema(mode: ColumnMappingMode) -> ArrowSchemaRef {
2439 Arc::new(ArrowSchema::new(vec![
2440 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
2441 .with_metadata(arrow_fid(1)),
2442 ArrowField::new(
2443 parquet_name(3, mode),
2444 ArrowDataType::Struct(
2445 vec![
2446 ArrowField::new(parquet_name(4, mode), ArrowDataType::Int32, false)
2447 .with_metadata(arrow_fid(4)),
2448 ArrowField::new(parquet_name(5, mode), ArrowDataType::Utf8, false)
2449 .with_metadata(arrow_fid(5)),
2450 ]
2451 .into(),
2452 ),
2453 false,
2454 )
2455 .with_metadata(arrow_fid(3)),
2456 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, false)
2457 .with_metadata(arrow_fid(2)),
2458 ]))
2459 }
2460
2461 #[test]
2462 fn test_match_parquet_fields_filters_metadata_columns() {
2463 let kernel_schema = StructType::new_unchecked([
2464 StructField::not_null("regular_field", DataType::INTEGER),
2465 StructField::create_metadata_column("row_index", MetadataColumnSpec::RowIndex),
2466 StructField::nullable("another_field", DataType::STRING),
2467 ]);
2468
2469 let parquet_fields: ArrowFields = vec![
2470 ArrowField::new("regular_field", ArrowDataType::Int32, false),
2471 ArrowField::new("row_index", ArrowDataType::Int64, false),
2472 ArrowField::new("another_field", ArrowDataType::Utf8, true),
2473 ]
2474 .into();
2475
2476 let matched_fields: Vec<_> =
2477 match_parquet_fields(&kernel_schema, &parquet_fields).collect();
2478
2479 assert_eq!(matched_fields.len(), 3);
2480
2481 assert!(matched_fields[0].kernel_field_info.is_some());
2483 assert_eq!(matched_fields[0].parquet_field.name(), "regular_field");
2484
2485 assert!(matched_fields[1].kernel_field_info.is_none());
2487 assert_eq!(matched_fields[1].parquet_field.name(), "row_index");
2488
2489 assert!(matched_fields[2].kernel_field_info.is_some());
2491 assert_eq!(matched_fields[2].parquet_field.name(), "another_field");
2492 }
2493
2494 #[test]
2495 fn test_ordering_needs_row_indexes() {
2496 let ordering_no_row_index = vec![
2498 ReorderIndex::identity(0),
2499 ReorderIndex::cast(1, ArrowDataType::Int64),
2500 ReorderIndex::missing(
2501 2,
2502 Arc::new(ArrowField::new("missing", ArrowDataType::Utf8, true)),
2503 ),
2504 ];
2505 assert!(!ordering_needs_row_indexes(&ordering_no_row_index));
2506
2507 let ordering_with_row_index = vec![
2509 ReorderIndex::identity(0),
2510 ReorderIndex::row_index(
2511 1,
2512 Arc::new(ArrowField::new("row_idx", ArrowDataType::Int64, false)),
2513 ),
2514 ];
2515 assert!(ordering_needs_row_indexes(&ordering_with_row_index));
2516
2517 assert!(!ordering_needs_row_indexes(&[]));
2519 }
2520
2521 #[test]
2522 fn test_reorder_struct_array_missing_row_indexes() {
2523 let arry = make_struct_array();
2525 let reorder = vec![
2526 ReorderIndex::identity(0),
2527 ReorderIndex::row_index(
2528 1,
2529 Arc::new(ArrowField::new("row_idx", ArrowDataType::Int64, false)),
2530 ),
2531 ];
2532
2533 let result = reorder_struct_array(arry, &reorder, None, None);
2534 assert_result_error_with_message(
2535 result,
2536 "Row index column requested but row index iterator not provided",
2537 );
2538 }
2539
2540 #[test]
2541 fn test_reorder_struct_array_with_row_indexes() {
2542 let arry = make_struct_array();
2544 let reorder = vec![
2545 ReorderIndex::identity(0),
2546 ReorderIndex::row_index(
2547 1,
2548 Arc::new(ArrowField::new("row_idx", ArrowDataType::Int64, false)),
2549 ),
2550 ];
2551
2552 #[allow(clippy::single_range_in_vec_init)]
2554 let mut row_indexes = vec![(0..4)].into_iter().flatten();
2555
2556 let ordered = reorder_struct_array(arry, &reorder, Some(&mut row_indexes), None).unwrap();
2557 assert_eq!(ordered.column_names(), vec!["b", "row_idx"]);
2558
2559 let row_idx_col = ordered.column(1).as_primitive::<Int64Type>();
2561 assert_eq!(row_idx_col.values(), &[0, 1, 2, 3]);
2562 }
2563
2564 #[test]
2565 fn simple_row_index_field() {
2566 let requested_schema = Arc::new(StructType::new_unchecked([
2567 StructField::not_null("i", DataType::INTEGER),
2568 StructField::create_metadata_column("my_row_index", MetadataColumnSpec::RowIndex),
2569 StructField::nullable("i2", DataType::INTEGER),
2570 ]));
2571 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2572 ArrowField::new("i", ArrowDataType::Int32, false),
2573 ArrowField::new("i2", ArrowDataType::Int32, true),
2574 ]));
2575 let (mask_indices, reorder_indices) =
2576 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2577 let expect_mask = vec![0, 1];
2578 let mut arrow_row_index_field =
2579 ArrowField::new("my_row_index", ArrowDataType::Int64, false);
2580 arrow_row_index_field.set_metadata(HashMap::from([(
2581 "delta.metadataSpec".to_string(),
2582 "row_index".to_string(),
2583 )]));
2584 let expect_reorder = vec![
2585 ReorderIndex::identity(0),
2586 ReorderIndex::identity(2),
2587 ReorderIndex::row_index(1, Arc::new(arrow_row_index_field)),
2588 ];
2589 assert_eq!(mask_indices, expect_mask);
2590 assert_eq!(reorder_indices, expect_reorder);
2591 }
2592
2593 #[test]
2594 fn simple_file_path_field() {
2595 let requested_schema = Arc::new(StructType::new_unchecked([
2596 StructField::not_null("i", DataType::INTEGER),
2597 StructField::create_metadata_column("_file", MetadataColumnSpec::FilePath),
2598 StructField::nullable("i2", DataType::INTEGER),
2599 ]));
2600 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2601 ArrowField::new("i", ArrowDataType::Int32, false),
2602 ArrowField::new("i2", ArrowDataType::Int32, true),
2603 ]));
2604 let (mask_indices, reorder_indices) =
2605 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2606 let expect_mask = vec![0, 1];
2607 let mut arrow_file_path_field = ArrowField::new("_file", ArrowDataType::Utf8, false);
2608 arrow_file_path_field.set_metadata(HashMap::from([(
2609 "delta.metadataSpec".to_string(),
2610 "_file".to_string(),
2611 )]));
2612 let expect_reorder = vec![
2613 ReorderIndex::identity(0),
2614 ReorderIndex::identity(2),
2615 ReorderIndex::file_path(1, Arc::new(arrow_file_path_field)),
2616 ];
2617 assert_eq!(mask_indices, expect_mask);
2618 assert_eq!(reorder_indices, expect_reorder);
2619 }
2620
2621 #[test]
2622 fn test_reorder_struct_array_with_file_path() {
2623 let arry = make_struct_array();
2625 let reorder = vec![
2626 ReorderIndex::identity(0),
2627 ReorderIndex::file_path(
2628 1,
2629 Arc::new(ArrowField::new("_file", ArrowDataType::Utf8, false)),
2630 ),
2631 ];
2632
2633 let file_location = "s3://bucket/path/to/file.parquet";
2634 let ordered = reorder_struct_array(arry, &reorder, None, Some(file_location)).unwrap();
2635 assert_eq!(ordered.column_names(), vec!["b", "_file"]);
2636
2637 let file_path_col = ordered.column(1);
2639 let string_array = file_path_col
2640 .as_any()
2641 .downcast_ref::<StringArray>()
2642 .expect("Expected StringArray");
2643 assert_eq!(string_array.len(), 4);
2644 assert!(string_array.iter().all(|v| v == Some(file_location)));
2645 }
2646
2647 #[test]
2648 fn test_reorder_struct_array_missing_file_path() {
2649 let arry = make_struct_array();
2651 let reorder = vec![
2652 ReorderIndex::identity(0),
2653 ReorderIndex::file_path(
2654 1,
2655 Arc::new(ArrowField::new("_file", ArrowDataType::Utf8, false)),
2656 ),
2657 ];
2658
2659 let result = reorder_struct_array(arry, &reorder, None, None);
2660 assert_result_error_with_message(
2661 result,
2662 "File path column requested but file location not provided",
2663 );
2664 }
2665
2666 #[test]
2667 fn test_row_index_builder_no_skipping() {
2668 let row_groups = vec![
2669 create_mock_row_group(5), create_mock_row_group(3), create_mock_row_group(4), ];
2673
2674 let builder = RowIndexBuilder::new(&row_groups);
2675 let row_indexes: Vec<i64> = builder.build().unwrap().collect();
2676
2677 assert_eq!(row_indexes, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
2679 }
2680
2681 #[test]
2682 fn test_row_index_builder_with_skipping() {
2683 let row_groups = vec![
2684 create_mock_row_group(5), create_mock_row_group(3), create_mock_row_group(4), create_mock_row_group(2), ];
2689
2690 let mut builder = RowIndexBuilder::new(&row_groups);
2691 builder.select_row_groups(&[0, 2]);
2692
2693 let row_indexes: Vec<i64> = builder.build().unwrap().collect();
2694
2695 assert_eq!(row_indexes, vec![0, 1, 2, 3, 4, 8, 9, 10, 11]);
2697 }
2698
2699 #[test]
2700 fn test_row_index_builder_single_row_group() {
2701 let row_groups = vec![create_mock_row_group(7)];
2702
2703 let mut builder = RowIndexBuilder::new(&row_groups);
2704 builder.select_row_groups(&[0]);
2705
2706 let row_indexes: Vec<i64> = builder.build().unwrap().collect();
2707
2708 assert_eq!(row_indexes, vec![0, 1, 2, 3, 4, 5, 6]);
2709 }
2710
2711 #[test]
2712 fn test_row_index_builder_empty_selection() {
2713 let row_groups = vec![create_mock_row_group(3), create_mock_row_group(2)];
2714
2715 let mut builder = RowIndexBuilder::new(&row_groups);
2716 builder.select_row_groups(&[]);
2717
2718 let row_indexes: Vec<i64> = builder.build().unwrap().collect();
2719
2720 assert_eq!(row_indexes, Vec::<i64>::new());
2722 }
2723
2724 #[test]
2725 fn test_row_index_builder_out_of_order_selection() {
2726 let row_groups = vec![
2727 create_mock_row_group(2), create_mock_row_group(3), create_mock_row_group(1), ];
2731
2732 let mut builder = RowIndexBuilder::new(&row_groups);
2733 builder.select_row_groups(&[2, 0]);
2734
2735 let row_indexes: Vec<i64> = builder.build().unwrap().collect();
2736
2737 assert_eq!(row_indexes, vec![5, 0, 1]);
2739 }
2740
2741 #[test]
2742 fn test_row_index_builder_out_of_bounds_row_group_ordinals() {
2743 let row_groups = vec![create_mock_row_group(2)];
2744
2745 let mut builder = RowIndexBuilder::new(&row_groups);
2746 builder.select_row_groups(&[1]);
2747
2748 let result = builder.build();
2749 assert_result_error_with_message(result, "Row group ordinal 1 is out of bounds");
2750 }
2751
2752 #[test]
2753 fn test_row_index_builder_duplicate_row_group_ordinals() {
2754 let row_groups = vec![create_mock_row_group(2), create_mock_row_group(3)];
2755
2756 let mut builder = RowIndexBuilder::new(&row_groups);
2757 builder.select_row_groups(&[1, 1]);
2758
2759 let result = builder.build();
2760 assert_result_error_with_message(result, "Found duplicate row group ordinal");
2761 }
2762
2763 #[test]
2764 fn nested_indices() {
2765 column_mapping_cases().into_iter().for_each(|mode| {
2766 let requested_schema = StructType::new_unchecked([
2767 StructField::not_null(logical_name(1), DataType::INTEGER)
2768 .with_metadata(column_mapping_metadata(1, mode)),
2769 StructField::not_null(
2770 logical_name(3),
2771 StructType::new_unchecked([
2772 StructField::not_null(logical_name(4), DataType::INTEGER)
2773 .with_metadata(column_mapping_metadata(4, mode)),
2774 StructField::not_null(logical_name(5), DataType::STRING)
2775 .with_metadata(column_mapping_metadata(5, mode)),
2776 ]),
2777 )
2778 .with_metadata(column_mapping_metadata(3, mode)),
2779 StructField::not_null(logical_name(2), DataType::INTEGER)
2780 .with_metadata(column_mapping_metadata(2, mode)),
2781 ])
2782 .make_physical(mode)
2783 .unwrap()
2784 .into();
2785 let parquet_schema = nested_parquet_schema(mode);
2786 let (mask_indices, reorder_indices) =
2787 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2788 let expect_mask = vec![0, 1, 2, 3];
2789 let expect_reorder = vec![
2790 ReorderIndex::identity(0),
2791 ReorderIndex::nested(
2792 1,
2793 vec![ReorderIndex::identity(0), ReorderIndex::identity(1)],
2794 ),
2795 ReorderIndex::identity(2),
2796 ];
2797 assert_eq!(mask_indices, expect_mask);
2798 assert_eq!(reorder_indices, expect_reorder);
2799 });
2800 }
2801 #[test]
2802 fn nested_indices_reorder() {
2803 column_mapping_cases().into_iter().for_each(|mode| {
2804 let requested_schema = StructType::new_unchecked([
2805 StructField::not_null(
2806 logical_name(3),
2807 StructType::new_unchecked([
2808 StructField::not_null(logical_name(5), DataType::STRING)
2809 .with_metadata(column_mapping_metadata(5, mode)),
2810 StructField::not_null(logical_name(4), DataType::INTEGER)
2811 .with_metadata(column_mapping_metadata(4, mode)),
2812 ]),
2813 )
2814 .with_metadata(column_mapping_metadata(3, mode)),
2815 StructField::not_null(logical_name(2), DataType::INTEGER)
2816 .with_metadata(column_mapping_metadata(2, mode)),
2817 StructField::not_null(logical_name(1), DataType::INTEGER)
2818 .with_metadata(column_mapping_metadata(1, mode)),
2819 ])
2820 .make_physical(mode)
2821 .unwrap()
2822 .into();
2823 let parquet_schema = nested_parquet_schema(mode);
2824 let (mask_indices, reorder_indices) =
2825 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2826 let expect_mask = vec![0, 1, 2, 3];
2827 let expect_reorder = vec![
2828 ReorderIndex::identity(2),
2829 ReorderIndex::nested(
2830 0,
2831 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
2832 ),
2833 ReorderIndex::identity(1),
2834 ];
2835 assert_eq!(mask_indices, expect_mask);
2836 assert_eq!(reorder_indices, expect_reorder);
2837 });
2838 }
2839
2840 #[test]
2841 fn nested_indices_mask_inner() {
2842 column_mapping_cases().into_iter().for_each(|mode| {
2843 let requested_schema = StructType::new_unchecked([
2844 StructField::not_null(logical_name(1), DataType::INTEGER)
2845 .with_metadata(column_mapping_metadata(1, mode)),
2846 StructField::not_null(
2847 logical_name(3),
2848 StructType::new_unchecked([StructField::not_null(
2849 logical_name(4),
2850 DataType::INTEGER,
2851 )
2852 .with_metadata(column_mapping_metadata(4, mode))]),
2853 )
2854 .with_metadata(column_mapping_metadata(3, mode)),
2855 StructField::not_null(logical_name(2), DataType::INTEGER)
2856 .with_metadata(column_mapping_metadata(2, mode)),
2857 ])
2858 .make_physical(mode)
2859 .unwrap()
2860 .into();
2861 let parquet_schema = nested_parquet_schema(mode);
2862 let (mask_indices, reorder_indices) =
2863 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2864 let expect_mask = vec![0, 1, 3];
2865 let expect_reorder = vec![
2866 ReorderIndex::identity(0),
2867 ReorderIndex::nested(1, vec![ReorderIndex::identity(0)]),
2868 ReorderIndex::identity(2),
2869 ];
2870 assert_eq!(mask_indices, expect_mask);
2871 assert_eq!(reorder_indices, expect_reorder);
2872 })
2873 }
2874
2875 #[test]
2876 fn unmatched_struct_before_selected_leaf_ordering() {
2877 let requested_schema: SchemaRef = Arc::new(StructType::new_unchecked([
2881 StructField::nullable("a", DataType::LONG),
2882 StructField::nullable(
2883 "stats",
2884 StructType::new_unchecked([StructField::nullable("age", DataType::LONG)]),
2885 ),
2886 ]));
2887 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2889 ArrowField::new(
2890 "stats",
2891 ArrowDataType::Struct(
2892 vec![ArrowField::new("id", ArrowDataType::Int64, true)].into(),
2893 ),
2894 true,
2895 ),
2896 ArrowField::new("a", ArrowDataType::Int64, true),
2897 ]));
2898 let (mask_indices, reorder_indices) =
2899 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2900 assert_eq!(mask_indices, vec![1]);
2902 let expected_stats_field = Arc::new(
2903 requested_schema
2904 .field("stats")
2905 .unwrap()
2906 .try_into_arrow()
2907 .unwrap(),
2908 );
2909 assert_eq!(
2911 reorder_indices,
2912 vec![
2913 ReorderIndex::identity(0),
2914 ReorderIndex::missing(1, expected_stats_field),
2915 ]
2916 );
2917 }
2918
2919 #[test]
2920 fn simple_list_mask() {
2921 column_mapping_cases().into_iter().for_each(|mode| {
2922 let requested_schema = StructType::new_unchecked([
2923 StructField::not_null(logical_name(1), DataType::INTEGER)
2924 .with_metadata(column_mapping_metadata(1, mode)),
2925 StructField::not_null(logical_name(2), ArrayType::new(DataType::INTEGER, false))
2926 .with_metadata(column_mapping_metadata(2, mode)),
2927 StructField::not_null(logical_name(3), DataType::INTEGER)
2928 .with_metadata(column_mapping_metadata(3, mode)),
2929 ])
2930 .make_physical(mode)
2931 .unwrap()
2932 .into();
2933 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2934 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
2935 .with_metadata(arrow_fid(1)),
2936 ArrowField::new(
2937 parquet_name(2, mode),
2938 ArrowDataType::List(Arc::new(ArrowField::new(
2939 "nested",
2940 ArrowDataType::Int32,
2941 false,
2942 ))),
2943 false,
2944 )
2945 .with_metadata(arrow_fid(2)),
2946 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
2947 .with_metadata(arrow_fid(3)),
2948 ]));
2949 let (mask_indices, reorder_indices) =
2950 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2951 let expect_mask = vec![0, 1, 2];
2952 let expect_reorder = vec![
2953 ReorderIndex::identity(0),
2954 ReorderIndex::identity(1),
2955 ReorderIndex::identity(2),
2956 ];
2957 assert_eq!(mask_indices, expect_mask);
2958 assert_eq!(reorder_indices, expect_reorder);
2959 });
2960 }
2961
2962 #[test]
2963 fn list_skip_earlier_element() {
2964 column_mapping_cases().into_iter().for_each(|mode| {
2965 let requested_schema = StructType::new_unchecked([StructField::not_null(
2966 logical_name(1),
2967 ArrayType::new(DataType::INTEGER, false),
2968 )
2969 .with_metadata(column_mapping_metadata(1, mode))])
2970 .make_physical(mode)
2971 .unwrap()
2972 .into();
2973 let parquet_schema = Arc::new(ArrowSchema::new(vec![
2974 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
2975 .with_metadata(arrow_fid(0)),
2976 ArrowField::new(
2977 parquet_name(1, mode),
2978 ArrowDataType::List(Arc::new(
2979 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, false)
2980 .with_metadata(arrow_fid(2)),
2981 )),
2982 false,
2983 )
2984 .with_metadata(arrow_fid(1)),
2985 ]));
2986 let (mask_indices, reorder_indices) =
2987 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
2988 let expect_mask = vec![1];
2989 let expect_reorder = vec![ReorderIndex::identity(0)];
2990 assert_eq!(mask_indices, expect_mask);
2991 assert_eq!(reorder_indices, expect_reorder);
2992 });
2993 }
2994
2995 #[test]
2996 fn nested_indices_list() {
2997 column_mapping_cases().into_iter().for_each(|mode| {
2998 let requested_schema = StructType::new_unchecked([
2999 StructField::not_null(logical_name(0), DataType::INTEGER)
3000 .with_metadata(column_mapping_metadata(0, mode)),
3001 StructField::not_null(
3002 logical_name(1),
3003 ArrayType::new(
3004 StructType::new_unchecked([
3005 StructField::not_null(logical_name(3), DataType::INTEGER)
3006 .with_metadata(column_mapping_metadata(3, mode)),
3007 StructField::not_null(logical_name(4), DataType::STRING)
3008 .with_metadata(column_mapping_metadata(4, mode)),
3009 ]),
3010 false,
3011 ),
3012 )
3013 .with_metadata(column_mapping_metadata(1, mode)),
3014 StructField::not_null(logical_name(2), DataType::INTEGER)
3015 .with_metadata(column_mapping_metadata(2, mode)),
3016 ])
3017 .make_physical(mode)
3018 .unwrap()
3019 .into();
3020 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3021 ArrowField::new(parquet_name(0, mode), ArrowDataType::Int32, false)
3022 .with_metadata(arrow_fid(0)),
3023 ArrowField::new(
3024 parquet_name(1, mode),
3025 ArrowDataType::List(Arc::new(ArrowField::new(
3026 "nested",
3027 ArrowDataType::Struct(
3028 vec![
3029 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
3030 .with_metadata(arrow_fid(3)),
3031 ArrowField::new(parquet_name(4, mode), ArrowDataType::Utf8, false)
3032 .with_metadata(arrow_fid(4)),
3033 ]
3034 .into(),
3035 ),
3036 false,
3037 ))),
3038 false,
3039 )
3040 .with_metadata(arrow_fid(1)),
3041 ArrowField::new(parquet_name(2, mode), ArrowDataType::Int32, false)
3042 .with_metadata(arrow_fid(2)),
3043 ]));
3044 let (mask_indices, reorder_indices) =
3045 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3046 let expect_mask = vec![0, 1, 2, 3];
3047 let expect_reorder = vec![
3048 ReorderIndex::identity(0),
3049 ReorderIndex::nested(
3050 1,
3051 vec![ReorderIndex::identity(0), ReorderIndex::identity(1)],
3052 ),
3053 ReorderIndex::identity(2),
3054 ];
3055 assert_eq!(mask_indices, expect_mask);
3056 assert_eq!(reorder_indices, expect_reorder);
3057 });
3058 }
3059
3060 #[test]
3061 fn nested_indices_unselected_list() {
3062 column_mapping_cases().into_iter().for_each(|mode| {
3063 let requested_schema = StructType::new_unchecked([
3064 StructField::not_null(logical_name(1), DataType::INTEGER)
3065 .with_metadata(column_mapping_metadata(1, mode)),
3066 StructField::not_null(logical_name(3), DataType::INTEGER)
3067 .with_metadata(column_mapping_metadata(3, mode)),
3068 ])
3069 .make_physical(mode)
3070 .unwrap()
3071 .into();
3072 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3073 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
3074 .with_metadata(arrow_fid(1)),
3075 ArrowField::new(
3076 parquet_name(2, mode),
3077 ArrowDataType::List(Arc::new(ArrowField::new(
3078 "nested",
3079 ArrowDataType::Struct(
3080 vec![
3081 ArrowField::new(parquet_name(4, mode), ArrowDataType::Int32, false)
3082 .with_metadata(arrow_fid(4)),
3083 ArrowField::new(parquet_name(5, mode), ArrowDataType::Utf8, false)
3084 .with_metadata(arrow_fid(5)),
3085 ]
3086 .into(),
3087 ),
3088 false,
3089 ))),
3090 false,
3091 )
3092 .with_metadata(arrow_fid(2)),
3093 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
3094 .with_metadata(arrow_fid(3)),
3095 ]));
3096 let (mask_indices, reorder_indices) =
3097 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3098 let expect_mask = vec![0, 3];
3099 let expect_reorder = vec![ReorderIndex::identity(0), ReorderIndex::identity(1)];
3100 assert_eq!(mask_indices, expect_mask);
3101 assert_eq!(reorder_indices, expect_reorder);
3102 });
3103 }
3104
3105 #[test]
3106 fn nested_indices_list_mask_inner() {
3107 column_mapping_cases().into_iter().for_each(|mode| {
3108 let requested_schema = StructType::new_unchecked([
3109 StructField::not_null(logical_name(1), DataType::INTEGER)
3110 .with_metadata(column_mapping_metadata(1, mode)),
3111 StructField::not_null(
3112 logical_name(2),
3113 ArrayType::new(
3114 StructType::new_unchecked([StructField::not_null(
3115 logical_name(4),
3116 DataType::INTEGER,
3117 )
3118 .with_metadata(column_mapping_metadata(4, mode))]),
3119 false,
3120 ),
3121 )
3122 .with_metadata(column_mapping_metadata(2, mode)),
3123 StructField::not_null(logical_name(3), DataType::INTEGER)
3124 .with_metadata(column_mapping_metadata(3, mode)),
3125 ])
3126 .make_physical(mode)
3127 .unwrap()
3128 .into();
3129 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3130 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
3131 .with_metadata(arrow_fid(1)),
3132 ArrowField::new(
3133 parquet_name(2, mode),
3134 ArrowDataType::List(Arc::new(ArrowField::new(
3135 "nested",
3136 ArrowDataType::Struct(
3137 vec![
3138 ArrowField::new(parquet_name(4, mode), ArrowDataType::Int32, false)
3139 .with_metadata(arrow_fid(4)),
3140 ArrowField::new(parquet_name(5, mode), ArrowDataType::Utf8, false)
3141 .with_metadata(arrow_fid(5)),
3142 ]
3143 .into(),
3144 ),
3145 false,
3146 ))),
3147 false,
3148 )
3149 .with_metadata(arrow_fid(2)),
3150 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
3151 .with_metadata(arrow_fid(3)),
3152 ]));
3153 let (mask_indices, reorder_indices) =
3154 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3155 let expect_mask = vec![0, 1, 3];
3156 let expect_reorder = vec![
3157 ReorderIndex::identity(0),
3158 ReorderIndex::nested(1, vec![ReorderIndex::identity(0)]),
3159 ReorderIndex::identity(2),
3160 ];
3161 assert_eq!(mask_indices, expect_mask);
3162 assert_eq!(reorder_indices, expect_reorder);
3163 });
3164 }
3165
3166 #[test]
3167 fn nested_indices_list_mask_inner_reorder() {
3168 column_mapping_cases().into_iter().for_each(|mode| {
3169 let requested_schema = StructType::new_unchecked([
3170 StructField::not_null(logical_name(1), DataType::INTEGER)
3171 .with_metadata(column_mapping_metadata(1, mode)),
3172 StructField::not_null(
3173 logical_name(2),
3174 ArrayType::new(
3175 StructType::new_unchecked([
3176 StructField::not_null(logical_name(6), DataType::STRING)
3177 .with_metadata(column_mapping_metadata(6, mode)),
3178 StructField::not_null(logical_name(5), DataType::INTEGER)
3179 .with_metadata(column_mapping_metadata(5, mode)),
3180 ]),
3181 false,
3182 ),
3183 )
3184 .with_metadata(column_mapping_metadata(2, mode)),
3185 StructField::not_null(logical_name(3), DataType::INTEGER)
3186 .with_metadata(column_mapping_metadata(3, mode)),
3187 ])
3188 .make_physical(mode)
3189 .unwrap()
3190 .into();
3191 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3192 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
3193 .with_metadata(arrow_fid(1)),
3194 ArrowField::new(
3195 parquet_name(2, mode),
3196 ArrowDataType::List(Arc::new(ArrowField::new(
3197 "nested",
3198 ArrowDataType::Struct(
3199 vec![
3200 ArrowField::new(parquet_name(4, mode), ArrowDataType::Int32, false)
3201 .with_metadata(arrow_fid(4)),
3202 ArrowField::new(parquet_name(5, mode), ArrowDataType::Int32, false)
3203 .with_metadata(arrow_fid(5)),
3204 ArrowField::new(parquet_name(6, mode), ArrowDataType::Utf8, false)
3205 .with_metadata(arrow_fid(6)),
3206 ]
3207 .into(),
3208 ),
3209 false,
3210 ))),
3211 false,
3212 )
3213 .with_metadata(arrow_fid(2)),
3214 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
3215 .with_metadata(arrow_fid(3)),
3216 ]));
3217 let (mask_indices, reorder_indices) =
3218 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3219 let expect_mask = vec![0, 2, 3, 4];
3220 let expect_reorder = vec![
3221 ReorderIndex::identity(0),
3222 ReorderIndex::nested(
3223 1,
3224 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
3225 ),
3226 ReorderIndex::identity(2),
3227 ];
3228 assert_eq!(mask_indices, expect_mask);
3229 assert_eq!(reorder_indices, expect_reorder);
3230 });
3231 }
3232
3233 #[test]
3234 fn skipped_struct() {
3235 column_mapping_cases().into_iter().for_each(|mode| {
3236 let requested_schema = StructType::new_unchecked([
3237 StructField::not_null(logical_name(1), DataType::INTEGER)
3238 .with_metadata(column_mapping_metadata(1, mode)),
3239 StructField::not_null(
3240 logical_name(2),
3241 StructType::new_unchecked([
3242 StructField::not_null(logical_name(4), DataType::INTEGER)
3243 .with_metadata(column_mapping_metadata(4, mode)),
3244 StructField::not_null(logical_name(5), DataType::STRING)
3245 .with_metadata(column_mapping_metadata(5, mode)),
3246 ]),
3247 )
3248 .with_metadata(column_mapping_metadata(2, mode)),
3249 StructField::not_null(logical_name(3), DataType::INTEGER)
3250 .with_metadata(column_mapping_metadata(3, mode)),
3251 ])
3252 .make_physical(mode)
3253 .unwrap()
3254 .into();
3255 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3256 ArrowField::new(
3257 "skipped",
3258 ArrowDataType::Struct(
3259 vec![
3260 ArrowField::new(parquet_name(7, mode), ArrowDataType::Int32, false)
3261 .with_metadata(arrow_fid(7)),
3262 ArrowField::new(parquet_name(8, mode), ArrowDataType::Utf8, false)
3263 .with_metadata(arrow_fid(8)),
3264 ]
3265 .into(),
3266 ),
3267 false,
3268 )
3269 .with_metadata(arrow_fid(6)),
3270 ArrowField::new(parquet_name(3, mode), ArrowDataType::Int32, false)
3271 .with_metadata(arrow_fid(3)),
3272 ArrowField::new(
3273 parquet_name(2, mode),
3274 ArrowDataType::Struct(
3275 vec![
3276 ArrowField::new(parquet_name(4, mode), ArrowDataType::Int32, false)
3277 .with_metadata(arrow_fid(4)),
3278 ArrowField::new(parquet_name(5, mode), ArrowDataType::Utf8, false)
3279 .with_metadata(arrow_fid(5)),
3280 ]
3281 .into(),
3282 ),
3283 false,
3284 )
3285 .with_metadata(arrow_fid(2)),
3286 ArrowField::new(parquet_name(1, mode), ArrowDataType::Int32, false)
3287 .with_metadata(arrow_fid(1)),
3288 ]));
3289 let (mask_indices, reorder_indices) =
3290 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3291 let expect_mask = vec![2, 3, 4, 5];
3292 let expect_reorder = vec![
3293 ReorderIndex::identity(2),
3294 ReorderIndex::nested(
3295 1,
3296 vec![ReorderIndex::identity(0), ReorderIndex::identity(1)],
3297 ),
3298 ReorderIndex::identity(0),
3299 ];
3300 assert_eq!(mask_indices, expect_mask);
3301 assert_eq!(reorder_indices, expect_reorder);
3302 });
3303 }
3304
3305 #[test]
3306 fn reorder_map_with_structs() {
3307 let requested_schema = schema_ref! {
3308 not_null "i": INTEGER,
3309 not_null "map": {
3310 { not_null "k1": STRING, not_null "k2": STRING }
3311 => not_null { not_null "v2": STRING, not_null "v1": STRING }
3312 },
3313 };
3314 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3315 ArrowField::new("i", ArrowDataType::Int32, false),
3316 ArrowField::new_map(
3317 "map",
3318 "entries",
3319 ArrowField::new(
3320 "i",
3321 ArrowDataType::Struct(
3322 vec![
3323 ArrowField::new("k1", ArrowDataType::Utf8, false),
3324 ArrowField::new("k2", ArrowDataType::Utf8, false),
3325 ]
3326 .into(),
3327 ),
3328 false,
3329 ),
3330 ArrowField::new(
3331 "v",
3332 ArrowDataType::Struct(
3333 vec![
3334 ArrowField::new("v1", ArrowDataType::Utf8, false),
3335 ArrowField::new("v2", ArrowDataType::Utf8, false),
3336 ]
3337 .into(),
3338 ),
3339 false,
3340 ),
3341 false,
3342 false,
3343 ),
3344 ]));
3345 let (mask_indices, reorder_indices) =
3346 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3347 let expect_mask = vec![0, 1, 2, 3, 4];
3348 let expect_reorder = vec![
3349 ReorderIndex::identity(0),
3350 ReorderIndex::nested(
3351 1,
3352 vec![
3353 ReorderIndex::identity(0), ReorderIndex::nested(
3355 1,
3356 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
3357 ),
3358 ],
3359 ),
3360 ];
3361 assert_eq!(mask_indices, expect_mask);
3362 assert_eq!(reorder_indices, expect_reorder);
3363 }
3364
3365 fn make_struct_array() -> StructArray {
3366 let boolean = Arc::new(BooleanArray::from(vec![false, false, true, true]));
3367 let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31]));
3368 StructArray::from(vec![
3369 (
3370 Arc::new(ArrowField::new("b", ArrowDataType::Boolean, false)),
3371 boolean.clone() as ArrowArrayRef,
3372 ),
3373 (
3374 Arc::new(ArrowField::new("c", ArrowDataType::Int32, false)),
3375 int.clone() as ArrowArrayRef,
3376 ),
3377 ])
3378 }
3379
3380 #[test]
3381 fn simple_reorder_struct() {
3382 let arry = make_struct_array();
3383 let reorder = vec![ReorderIndex::identity(1), ReorderIndex::identity(0)];
3384 let ordered = reorder_struct_array(arry, &reorder, None, None).unwrap();
3385 assert_eq!(ordered.column_names(), vec!["c", "b"]);
3386 }
3387
3388 #[test]
3389 fn nested_reorder_struct() {
3390 let arry1 = Arc::new(make_struct_array());
3391 let arry2 = Arc::new(make_struct_array());
3392 let fields: ArrowFields = vec![
3393 Arc::new(ArrowField::new("b", ArrowDataType::Boolean, false)),
3394 Arc::new(ArrowField::new("c", ArrowDataType::Int32, false)),
3395 ]
3396 .into();
3397 let nested = StructArray::from(vec![
3398 (
3399 Arc::new(ArrowField::new(
3400 "struct1",
3401 ArrowDataType::Struct(fields.clone()),
3402 false,
3403 )),
3404 arry1 as ArrowArrayRef,
3405 ),
3406 (
3407 Arc::new(ArrowField::new(
3408 "struct2",
3409 ArrowDataType::Struct(fields),
3410 false,
3411 )),
3412 arry2 as ArrowArrayRef,
3413 ),
3414 ]);
3415 let reorder = vec![
3416 ReorderIndex::nested(
3417 1,
3418 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
3419 ),
3420 ReorderIndex::nested(
3421 0,
3422 vec![
3423 ReorderIndex::identity(0),
3424 ReorderIndex::identity(1),
3425 ReorderIndex::missing(
3426 2,
3427 Arc::new(ArrowField::new("s", ArrowDataType::Utf8, true)),
3428 ),
3429 ],
3430 ),
3431 ];
3432 let ordered = reorder_struct_array(nested, &reorder, None, None).unwrap();
3433 assert_eq!(ordered.column_names(), vec!["struct2", "struct1"]);
3434 let ordered_s2 = ordered.column(0).as_struct();
3435 assert_eq!(ordered_s2.column_names(), vec!["b", "c", "s"]);
3436 let ordered_s1 = ordered.column(1).as_struct();
3437 assert_eq!(ordered_s1.column_names(), vec!["c", "b"]);
3438 }
3439
3440 #[test]
3441 fn reorder_list_of_struct() {
3442 let boolean = Arc::new(BooleanArray::from(vec![
3443 false, false, true, true, false, true,
3444 ]));
3445 let int = Arc::new(Int32Array::from(vec![42, 28, 19, 31, 0, 3]));
3446 let list_sa = StructArray::from(vec![
3447 (
3448 Arc::new(ArrowField::new("b", ArrowDataType::Boolean, false)),
3449 boolean.clone() as ArrowArrayRef,
3450 ),
3451 (
3452 Arc::new(ArrowField::new("c", ArrowDataType::Int32, false)),
3453 int.clone() as ArrowArrayRef,
3454 ),
3455 ]);
3456 let offsets = OffsetBuffer::new(ScalarBuffer::from(vec![0, 3, 6]));
3457 let list_field = ArrowField::new("item", list_sa.data_type().clone(), false);
3458 let list = Arc::new(GenericListArray::new(
3459 Arc::new(list_field),
3460 offsets,
3461 Arc::new(list_sa),
3462 None,
3463 ));
3464 let fields: ArrowFields = vec![
3465 Arc::new(ArrowField::new("b", ArrowDataType::Boolean, false)),
3466 Arc::new(ArrowField::new("c", ArrowDataType::Int32, false)),
3467 ]
3468 .into();
3469 let list_dt = Arc::new(ArrowField::new(
3470 "list",
3471 ArrowDataType::new_list(ArrowDataType::Struct(fields), false),
3472 false,
3473 ));
3474 let struct_array = StructArray::from(vec![(list_dt, list as ArrowArrayRef)]);
3475 let reorder = vec![ReorderIndex::nested(
3476 0,
3477 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
3478 )];
3479 let ordered = reorder_struct_array(struct_array, &reorder, None, None).unwrap();
3480 let ordered_list_col = ordered.column(0).as_list::<i32>();
3481 for i in 0..ordered_list_col.len() {
3482 let array_item = ordered_list_col.value(i);
3483 let struct_item = array_item.as_struct();
3484 assert_eq!(struct_item.column_names(), vec!["c", "b"]);
3485 }
3486 }
3487
3488 fn build_arrow_map() -> MapArray {
3490 let key_struct_builder = StructBuilder::from_fields(
3491 ArrowFields::from(vec![
3492 ArrowField::new("k1", ArrowDataType::Int32, false),
3493 ArrowField::new("k2", ArrowDataType::Int32, false),
3494 ]),
3495 1,
3496 );
3497 let value_struct_builder = StructBuilder::from_fields(
3498 ArrowFields::from(vec![
3499 ArrowField::new("v1", ArrowDataType::Int32, false),
3500 ArrowField::new("v2", ArrowDataType::Int32, false),
3501 ]),
3502 1,
3503 );
3504 let mut map_builder = MapBuilder::new(None, key_struct_builder, value_struct_builder);
3505
3506 let (key_builder, value_builder) = map_builder.entries();
3507 let key_k1_builder = key_builder.field_builder::<Int32Builder>(0).unwrap();
3508 key_k1_builder.append_value(1);
3509 let key_k2_builder = key_builder.field_builder::<Int32Builder>(1).unwrap();
3510 key_k2_builder.append_value(2);
3511 key_builder.append(true);
3512
3513 let value_v1_builder = value_builder.field_builder::<Int32Builder>(0).unwrap();
3514 value_v1_builder.append_value(1);
3515 let value_v2_builder = value_builder.field_builder::<Int32Builder>(1).unwrap();
3516 value_v2_builder.append_value(2);
3517 value_builder.append(true);
3518 map_builder.append(true).unwrap();
3519 map_builder.finish()
3520 }
3521
3522 #[test]
3523 fn reorder_map_of_struct() {
3524 let int_array = Arc::new(Int32Array::from(vec![42]));
3525 let int_dt = Arc::new(ArrowField::new("i", int_array.data_type().clone(), false));
3526 let map_array = Arc::new(build_arrow_map());
3527 let map_dt = Arc::new(ArrowField::new("map", map_array.data_type().clone(), false));
3528 let struct_array = StructArray::from(vec![
3529 (int_dt, int_array as ArrowArrayRef),
3530 (map_dt, map_array as ArrowArrayRef),
3531 ]);
3532 let reorder = vec![
3533 ReorderIndex::identity(1),
3534 ReorderIndex::nested(
3535 0,
3536 vec![
3537 ReorderIndex::identity(0),
3538 ReorderIndex::nested(
3539 1,
3540 vec![ReorderIndex::identity(1), ReorderIndex::identity(0)],
3541 ),
3542 ],
3543 ),
3544 ];
3545 let ordered = reorder_struct_array(struct_array, &reorder, None, None).unwrap();
3546 assert_eq!(ordered.column_names(), vec!["map", "i"]);
3547 if let ArrowDataType::Map(field, _) = ordered.column(0).data_type() {
3548 if let ArrowDataType::Struct(fields) = field.data_type() {
3549 fn assert_col_order(field: &ArrowField, expected: Vec<&str>) {
3550 if let ArrowDataType::Struct(fields) = field.data_type() {
3551 let names: Vec<&str> =
3552 fields.iter().map(|field| field.name().as_str()).collect();
3553 assert_eq!(names, expected);
3554 } else {
3555 panic!("Expected struct field");
3556 }
3557 }
3558 assert_col_order(&fields[0], vec!["k1", "k2"]);
3559 assert_col_order(&fields[1], vec!["v2", "v1"]);
3560 } else {
3561 panic!("Inner field should have been a struct");
3562 }
3563 } else {
3564 panic!("Column 0 should have been a map");
3565 }
3566 }
3567
3568 #[test]
3569 fn no_matches() {
3570 column_mapping_cases().into_iter().for_each(|mode| {
3571 let requested_schema = StructType::new_unchecked([
3572 StructField::nullable(logical_name(1), DataType::STRING)
3573 .with_metadata(column_mapping_metadata(1, mode)),
3574 StructField::nullable(logical_name(2), DataType::INTEGER)
3575 .with_metadata(column_mapping_metadata(2, mode)),
3576 ])
3577 .make_physical(mode)
3578 .unwrap()
3579 .into();
3580 let nots_field =
3581 ArrowField::new("NOTs", ArrowDataType::Utf8, true).with_metadata(arrow_fid(3));
3582 let noti2_field =
3583 ArrowField::new("NOTi2", ArrowDataType::Int32, true).with_metadata(arrow_fid(4));
3584 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3585 nots_field.clone(),
3586 noti2_field.clone(),
3587 ]));
3588 let (mask_indices, reorder_indices) =
3589 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3590 let expect_mask: Vec<usize> = vec![];
3591
3592 let mut fields = requested_schema.fields();
3594 let expected_field1: Arc<ArrowField> =
3595 Arc::new(fields.next().unwrap().try_into_arrow().unwrap());
3596 let expected_field2: Arc<ArrowField> =
3597 Arc::new(fields.next().unwrap().try_into_arrow().unwrap());
3598
3599 let expect_reorder = vec![
3600 ReorderIndex::missing(0, expected_field1),
3601 ReorderIndex::missing(1, expected_field2),
3602 ];
3603 assert_eq!(mask_indices, expect_mask);
3604 assert_eq!(reorder_indices, expect_reorder);
3605 });
3606 }
3607
3608 #[test]
3609 fn empty_requested_schema() {
3610 let requested_schema = Arc::new(StructType::new_unchecked([]));
3611 let parquet_schema = Arc::new(ArrowSchema::new(vec![
3612 ArrowField::new("i", ArrowDataType::Int32, false),
3613 ArrowField::new("s", ArrowDataType::Utf8, true),
3614 ArrowField::new("i2", ArrowDataType::Int32, true),
3615 ]));
3616 let (mask_indices, reorder_indices) =
3617 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
3618 let expect_mask: Vec<usize> = vec![];
3619 let expect_reorder = vec![];
3620 assert_eq!(mask_indices, expect_mask);
3621 assert_eq!(reorder_indices, expect_reorder);
3622 }
3623
3624 #[test]
3625 fn test_write_json() -> DeltaResult<()> {
3626 let schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
3627 "string",
3628 ArrowDataType::Utf8,
3629 true,
3630 )]));
3631 let data = RecordBatch::try_new(
3632 schema.clone(),
3633 vec![Arc::new(StringArray::from(vec!["string1", "string2"]))],
3634 )?;
3635 let data: Box<dyn EngineData> = Box::new(ArrowEngineData::new(data));
3636 let filtered_data = FilteredEngineData::with_all_rows_selected(data);
3637 let json = to_json_bytes(Box::new(std::iter::once(Ok(filtered_data))))?;
3638 assert_eq!(
3639 json,
3640 "{\"string\":\"string1\"}\n{\"string\":\"string2\"}\n".as_bytes()
3641 );
3642 Ok(())
3643 }
3644
3645 #[test]
3646 fn test_to_json_bytes_filters_data() -> DeltaResult<()> {
3647 let schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
3649 "value",
3650 ArrowDataType::Utf8,
3651 true,
3652 )]));
3653 let record_batch = RecordBatch::try_new(
3654 schema.clone(),
3655 vec![Arc::new(StringArray::from(vec![
3656 "row0", "row1", "row2", "row3",
3657 ]))],
3658 )?;
3659
3660 let create_engine_data =
3662 || -> Box<dyn EngineData> { Box::new(ArrowEngineData::new(record_batch.clone())) };
3663
3664 let all_selected =
3666 FilteredEngineData::try_new(create_engine_data(), vec![true, true, true, true])?;
3667 let json_all = to_json_bytes(Box::new(std::iter::once(Ok(all_selected))))?;
3668 assert_eq!(
3669 json_all,
3670 "{\"value\":\"row0\"}\n{\"value\":\"row1\"}\n{\"value\":\"row2\"}\n{\"value\":\"row3\"}\n".as_bytes()
3671 );
3672
3673 let partial_selected =
3675 FilteredEngineData::try_new(create_engine_data(), vec![true, false, false, true])?;
3676 let json_partial = to_json_bytes(Box::new(std::iter::once(Ok(partial_selected))))?;
3677 assert_eq!(
3678 json_partial,
3679 "{\"value\":\"row0\"}\n{\"value\":\"row3\"}\n".as_bytes()
3680 );
3681
3682 let middle_selected =
3684 FilteredEngineData::try_new(create_engine_data(), vec![false, true, true, false])?;
3685 let json_middle = to_json_bytes(Box::new(std::iter::once(Ok(middle_selected))))?;
3686 assert_eq!(
3687 json_middle,
3688 "{\"value\":\"row1\"}\n{\"value\":\"row2\"}\n".as_bytes()
3689 );
3690
3691 let none_selected =
3693 FilteredEngineData::try_new(create_engine_data(), vec![false, false, false, false])?;
3694 let json_none = to_json_bytes(Box::new(std::iter::once(Ok(none_selected))))?;
3695 assert_eq!(json_none, "".as_bytes());
3696
3697 let one_selected =
3699 FilteredEngineData::try_new(create_engine_data(), vec![false, true, false, false])?;
3700 let json_one = to_json_bytes(Box::new(std::iter::once(Ok(one_selected))))?;
3701 assert_eq!(json_one, "{\"value\":\"row1\"}\n".as_bytes());
3702
3703 let one_selected =
3705 FilteredEngineData::try_new(create_engine_data(), vec![false, false, false])?;
3706 let json_one = to_json_bytes(Box::new(std::iter::once(Ok(one_selected))))?;
3707 assert_eq!(json_one, "{\"value\":\"row3\"}\n".as_bytes());
3708
3709 Ok(())
3710 }
3711
3712 #[test]
3713 fn test_arrow_broken_nested_null_masks() {
3714 use crate::arrow::datatypes::{DataType, Field, Schema};
3715 use crate::engine::arrow_utils::fix_nested_null_masks;
3716 use crate::parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
3717
3718 let schema = Arc::new(Schema::new(vec![Field::new(
3720 "outer",
3721 DataType::Struct(ArrowFields::from(vec![
3722 Field::new(
3723 "inner_nullable",
3724 DataType::Struct(ArrowFields::from(vec![
3725 Field::new("leaf_non_null", DataType::Int32, false),
3726 Field::new("leaf_nullable", DataType::Int32, true),
3727 ])),
3728 true,
3729 ),
3730 Field::new(
3731 "inner_non_null",
3732 DataType::Struct(ArrowFields::from(vec![
3733 Field::new("leaf_non_null", DataType::Int32, false),
3734 Field::new("leaf_nullable", DataType::Int32, true),
3735 ])),
3736 false,
3737 ),
3738 ])),
3739 true,
3740 )]));
3741 let json_string = r#"
3742{ }
3743{ "outer" : { "inner_non_null" : { "leaf_non_null" : 1 } } }
3744{ "outer" : { "inner_non_null" : { "leaf_non_null" : 2, "leaf_nullable" : 3 } } }
3745{ "outer" : { "inner_non_null" : { "leaf_non_null" : 4 }, "inner_nullable" : { "leaf_non_null" : 5 } } }
3746{ "outer" : { "inner_non_null" : { "leaf_non_null" : 6 }, "inner_nullable" : { "leaf_non_null" : 7, "leaf_nullable": 8 } } }
3747"#;
3748 let batch1 = crate::arrow::json::ReaderBuilder::new(schema.clone())
3749 .build(json_string.as_bytes())
3750 .unwrap()
3751 .next()
3752 .unwrap()
3753 .unwrap();
3754
3755 macro_rules! assert_nulls {
3756 ( $column: expr, $nulls: expr ) => {
3757 assert_eq!($column.nulls().unwrap(), &NullBuffer::from(&$nulls[..]));
3758 };
3759 }
3760
3761 let outer_1 = batch1.column(0).as_struct();
3765 assert_nulls!(outer_1, [false, true, true, true, true]);
3766 let inner_nullable_1 = outer_1.column(0).as_struct();
3767 assert_nulls!(inner_nullable_1, [false, false, false, true, true]);
3768 let nullable_leaf_non_null_1 = inner_nullable_1.column(0);
3769 assert_nulls!(nullable_leaf_non_null_1, [false, false, false, true, true]);
3770 let nullable_leaf_nullable_1 = inner_nullable_1.column(1);
3771 assert_nulls!(nullable_leaf_nullable_1, [false, false, false, false, true]);
3772 let inner_non_null_1 = outer_1.column(1).as_struct();
3773 assert_nulls!(inner_non_null_1, [false, true, true, true, true]);
3774 let non_null_leaf_non_null_1 = inner_non_null_1.column(0);
3775 assert_nulls!(non_null_leaf_non_null_1, [false, true, true, true, true]);
3776 let non_null_leaf_nullable_1 = inner_non_null_1.column(1);
3777 assert_nulls!(non_null_leaf_nullable_1, [false, false, true, false, false]);
3778
3779 let mut buffer = vec![];
3781 let mut writer =
3782 crate::parquet::arrow::ArrowWriter::try_new(&mut buffer, schema.clone(), None).unwrap();
3783 writer.write(&batch1).unwrap();
3784 writer.close().unwrap(); let batch2 = ParquetRecordBatchReaderBuilder::try_new(bytes::Bytes::from(buffer))
3786 .unwrap()
3787 .build()
3788 .unwrap()
3789 .next()
3790 .unwrap()
3791 .unwrap();
3792
3793 let batch2 = RecordBatch::from(fix_nested_null_masks(batch2.into()));
3795
3796 let outer_2 = batch2.column(0).as_struct();
3798 assert_eq!(outer_2, outer_1);
3799 let inner_nullable_2 = outer_2.column(0).as_struct();
3800 assert_eq!(inner_nullable_2, inner_nullable_1);
3801 let nullable_leaf_non_null_2 = inner_nullable_2.column(0);
3802 assert_eq!(nullable_leaf_non_null_2, nullable_leaf_non_null_1);
3803 let nullable_leaf_nullable_2 = inner_nullable_2.column(1);
3804 assert_eq!(nullable_leaf_nullable_2, nullable_leaf_nullable_1);
3805 let inner_non_null_2 = outer_2.column(1).as_struct();
3806 assert_eq!(inner_non_null_2, inner_non_null_1);
3807 let non_null_leaf_non_null_2 = inner_non_null_2.column(0);
3808 assert_eq!(non_null_leaf_non_null_2, non_null_leaf_non_null_1);
3809 let non_null_leaf_nullable_2 = inner_non_null_2.column(1);
3810 assert_eq!(non_null_leaf_nullable_2, non_null_leaf_nullable_1);
3811 }
3812
3813 const FILE_PATH: &str = "s3://bucket/test.json";
3816
3817 struct JsonInsertCase {
3818 schema: StructType,
3820 expected_json_names: &'static [&'static str],
3822 expected_output_names: &'static [&'static str],
3824 file_path_col: Option<usize>,
3826 }
3827
3828 #[rstest]
3831 #[case::no_file_path(JsonInsertCase {
3832 schema: StructType::new_unchecked([
3833 StructField::not_null("a", DataType::INTEGER),
3834 StructField::nullable("b", DataType::INTEGER),
3835 ]),
3836 expected_json_names: &["a", "b"],
3837 expected_output_names: &["a", "b"],
3838 file_path_col: None,
3839 })]
3840 #[case::file_path_at_start(JsonInsertCase {
3841 schema: StructType::new_unchecked([
3842 StructField::create_metadata_column("_file", MetadataColumnSpec::FilePath),
3843 StructField::not_null("a", DataType::INTEGER),
3844 StructField::nullable("b", DataType::INTEGER),
3845 ]),
3846 expected_json_names: &["a", "b"],
3847 expected_output_names: &["_file", "a", "b"],
3848 file_path_col: Some(0),
3849 })]
3850 #[case::file_path_in_middle(JsonInsertCase {
3851 schema: StructType::new_unchecked([
3852 StructField::not_null("a", DataType::INTEGER),
3853 StructField::create_metadata_column("_file", MetadataColumnSpec::FilePath),
3854 StructField::nullable("b", DataType::INTEGER),
3855 ]),
3856 expected_json_names: &["a", "b"],
3857 expected_output_names: &["a", "_file", "b"],
3858 file_path_col: Some(1),
3859 })]
3860 #[case::file_path_at_end(JsonInsertCase {
3861 schema: StructType::new_unchecked([
3862 StructField::not_null("a", DataType::INTEGER),
3863 StructField::nullable("b", DataType::INTEGER),
3864 StructField::create_metadata_column("_file", MetadataColumnSpec::FilePath),
3865 ]),
3866 expected_json_names: &["a", "b"],
3867 expected_output_names: &["a", "b", "_file"],
3868 file_path_col: Some(2),
3869 })]
3870 fn test_json_file_path_insertion(#[case] case: JsonInsertCase) {
3871 let json_schema = json_arrow_schema(&case.schema).unwrap();
3873 let json_names: Vec<_> = json_schema
3874 .fields()
3875 .iter()
3876 .map(|f| f.name().as_str())
3877 .collect();
3878 assert_eq!(json_names, case.expected_json_names);
3879
3880 let arrow_schema = Arc::new(json_schema);
3882 let cols: Vec<ArrowArrayRef> = (0..arrow_schema.fields().len())
3883 .map(|_| Arc::new(Int32Array::from(vec![1i32, 2, 3])) as _)
3884 .collect();
3885 let batch = RecordBatch::try_new(arrow_schema, cols).unwrap();
3886
3887 let indices = build_json_reorder_indices(&case.schema).unwrap();
3889 let result = RecordBatch::from(
3890 reorder_struct_array(batch.into(), &indices, None, Some(FILE_PATH)).unwrap(),
3891 );
3892
3893 let schema = result.schema();
3895 let output_names: Vec<_> = schema.fields().iter().map(|f| f.name().as_str()).collect();
3896 assert_eq!(output_names, case.expected_output_names);
3897 assert_eq!(result.num_rows(), 3);
3898
3899 if let Some(idx) = case.file_path_col {
3902 let arr = result
3903 .column(idx)
3904 .as_any()
3905 .downcast_ref::<StringArray>()
3906 .expect("_file column should be a StringArray");
3907 assert!(arr.iter().all(|v| v == Some(FILE_PATH)));
3908 } else {
3909 assert!(
3910 result.schema().fields().iter().all(|f| f.name() != "_file"),
3911 "_file should not appear when not declared in the schema"
3912 );
3913 }
3914 }
3915
3916 #[test]
3917 fn test_build_json_reorder_indices_unsupported_metadata_column_errors() {
3918 let schema = StructType::new_unchecked([
3922 StructField::not_null("a", DataType::INTEGER),
3923 StructField::create_metadata_column("row_index", MetadataColumnSpec::RowIndex),
3924 ]);
3925 let arrow_schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
3926 "a",
3927 ArrowDataType::Int32,
3928 false,
3929 )]));
3930 let batch = RecordBatch::try_new(
3931 arrow_schema,
3932 vec![Arc::new(Int32Array::from(vec![1, 2, 3]))],
3933 )
3934 .unwrap();
3935
3936 let indices = build_json_reorder_indices(&schema).unwrap();
3937 assert!(reorder_struct_array(batch.into(), &indices, None, None).is_err());
3938 }
3939
3940 #[test]
3941 fn ensure_we_encode_maps_with_null_values() {
3942 let schema = ArrowSchema::new(vec![
3943 ArrowField::new("str_col", ArrowDataType::Utf8, false),
3944 ArrowField::new(
3945 "map_col",
3946 ArrowDataType::Map(
3947 Arc::new(ArrowField::new(
3948 "entries",
3949 ArrowDataType::Struct(
3950 vec![
3951 ArrowField::new("keys", ArrowDataType::Utf8, false),
3952 ArrowField::new("values", ArrowDataType::Utf8, true),
3953 ]
3954 .into(),
3955 ),
3956 false,
3957 )),
3958 false, ),
3960 false,
3961 ),
3962 ]);
3963 let s_array = StringArray::from(vec!["foo"]);
3964
3965 let string_builder = StringBuilder::new();
3966 let string_builder2 = StringBuilder::new();
3967 let mut map_builder = MapBuilder::new(None, string_builder, string_builder2);
3968
3969 map_builder.keys().append_value("bar");
3971 map_builder.values().append_null();
3972 map_builder.append(true).unwrap(); let map_array: MapArray = map_builder.finish();
3975 let batch = RecordBatch::try_new(
3976 Arc::new(schema),
3977 vec![Arc::new(s_array), Arc::new(map_array)],
3978 )
3979 .unwrap();
3980
3981 let data: Box<dyn EngineData> = Box::new(ArrowEngineData::new(batch));
3982 let filtered_data = FilteredEngineData::with_all_rows_selected(data);
3983 let json = to_json_bytes(Box::new(std::iter::once(Ok(filtered_data)))).unwrap();
3984 assert_eq!(
3985 json,
3986 "{\"str_col\":\"foo\",\"map_col\":{\"bar\":null}}\n".as_bytes()
3987 );
3988 }
3989
3990 #[rstest]
3991 fn struct_with_all_nullable_children_unmatched_is_missing(
3992 #[values(true, false)] struct_nullable: bool,
3993 ) {
3994 let info_field = if struct_nullable {
3997 StructField::nullable(
3998 "info",
3999 StructType::new_unchecked([StructField::nullable("z", DataType::LONG)]),
4000 )
4001 } else {
4002 StructField::not_null(
4003 "info",
4004 StructType::new_unchecked([StructField::nullable("z", DataType::LONG)]),
4005 )
4006 };
4007 let requested_schema = Arc::new(StructType::new_unchecked([
4008 StructField::not_null("a", DataType::LONG),
4009 info_field,
4010 ]));
4011 let parquet_schema = Arc::new(ArrowSchema::new(vec![
4012 ArrowField::new("a", ArrowDataType::Int64, true),
4013 ArrowField::new(
4014 "info",
4015 ArrowDataType::Struct(
4016 vec![
4017 ArrowField::new("x", ArrowDataType::Int64, true),
4018 ArrowField::new("y", ArrowDataType::Utf8, true),
4019 ]
4020 .into(),
4021 ),
4022 !struct_nullable,
4023 ),
4024 ]));
4025 let (mask_indices, reorder_indices) =
4026 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
4027 assert_eq!(mask_indices, vec![0]);
4028 let expected_info_field = Arc::new(
4029 requested_schema
4030 .field("info")
4031 .unwrap()
4032 .try_into_arrow()
4033 .unwrap(),
4034 );
4035 assert_eq!(
4036 reorder_indices,
4037 vec![
4038 ReorderIndex::identity(0),
4039 ReorderIndex::missing(1, expected_info_field),
4040 ]
4041 );
4042 }
4043
4044 #[test]
4045 fn reorder_non_nullable_missing_struct_produces_non_null_struct() {
4046 let a_array: Arc<dyn ArrowArray> = Arc::new(Int64Array::from(vec![1, 2, 3]));
4049 let input = StructArray::from(vec![(
4050 Arc::new(ArrowField::new("a", ArrowDataType::Int64, false)),
4051 a_array,
4052 )]);
4053 let missing_field = Arc::new(ArrowField::new(
4054 "info",
4055 ArrowDataType::Struct(vec![ArrowField::new("z", ArrowDataType::Int64, true)].into()),
4056 false,
4057 ));
4058 let reorder = vec![
4059 ReorderIndex::identity(0),
4060 ReorderIndex::missing(1, missing_field),
4061 ];
4062 let ordered = reorder_struct_array(input, &reorder, None, None).unwrap();
4063 assert_eq!(ordered.column_names(), vec!["a", "info"]);
4064 let info = ordered.column(1).as_struct();
4065 assert_eq!(info.null_count(), 0);
4066 assert_eq!(info.column(0).null_count(), 3);
4067 }
4068
4069 #[test]
4070 fn reorder_nested_non_nullable_missing_struct_recurses() {
4071 let a_array: Arc<dyn ArrowArray> = Arc::new(Int64Array::from(vec![1, 2]));
4074 let input = StructArray::from(vec![(
4075 Arc::new(ArrowField::new("a", ArrowDataType::Int64, false)),
4076 a_array,
4077 )]);
4078 let inner_struct =
4079 ArrowDataType::Struct(vec![ArrowField::new("leaf", ArrowDataType::Int64, true)].into());
4080 let missing_field = Arc::new(ArrowField::new(
4081 "outer",
4082 ArrowDataType::Struct(vec![ArrowField::new("inner", inner_struct, false)].into()),
4083 false,
4084 ));
4085 let reorder = vec![
4086 ReorderIndex::identity(0),
4087 ReorderIndex::missing(1, missing_field),
4088 ];
4089 let ordered = reorder_struct_array(input, &reorder, None, None).unwrap();
4090 let outer = ordered.column(1).as_struct();
4091 assert_eq!(outer.null_count(), 0);
4092 let inner = outer.column(0).as_struct();
4093 assert_eq!(inner.null_count(), 0);
4094 assert_eq!(inner.column(0).null_count(), 2);
4095 }
4096
4097 #[test]
4098 fn empty_struct_is_matched() {
4099 let requested_schema = Arc::new(StructType::new_unchecked([
4102 StructField::not_null("a", DataType::LONG),
4103 StructField::not_null("empty", StructType::new_unchecked([])),
4104 ]));
4105 let parquet_schema = Arc::new(ArrowSchema::new(vec![
4106 ArrowField::new("a", ArrowDataType::Int64, true),
4107 ArrowField::new("empty", ArrowDataType::Struct(ArrowFields::empty()), false),
4108 ]));
4109 let (mask_indices, reorder_indices) =
4110 get_requested_indices(&requested_schema, &parquet_schema).unwrap();
4111 assert_eq!(mask_indices, vec![0]);
4112 let expected_empty_field = Arc::new(
4113 requested_schema
4114 .field("empty")
4115 .unwrap()
4116 .try_into_arrow()
4117 .unwrap(),
4118 );
4119 assert_eq!(
4120 reorder_indices,
4121 vec![
4122 ReorderIndex::identity(0),
4123 ReorderIndex::missing(1, expected_empty_field),
4124 ]
4125 );
4126 }
4127
4128 #[test]
4134 fn test_fixup_parquet_read_coerces_nullability_at_all_levels() {
4135 let src_list_elem = Arc::new(ArrowField::new("element", ArrowDataType::Int32, false));
4140 let src_list = ArrowField::new("lst", ArrowDataType::List(src_list_elem.clone()), false);
4141 let src_map_entries = Arc::new(ArrowField::new(
4142 "entries",
4143 ArrowDataType::Struct(ArrowFields::from(vec![
4144 ArrowField::new("key", ArrowDataType::Utf8, false),
4145 ArrowField::new("value", ArrowDataType::Int32, false),
4146 ])),
4147 false,
4148 ));
4149 let src_map = ArrowField::new(
4150 "mp",
4151 ArrowDataType::Map(src_map_entries.clone(), false),
4152 false,
4153 );
4154 let src_outer = ArrowField::new(
4155 "outer",
4156 ArrowDataType::Struct(ArrowFields::from(vec![src_list.clone(), src_map.clone()])),
4157 false,
4158 );
4159
4160 let offsets_1 = || OffsetBuffer::new(ScalarBuffer::from(vec![0i32, 1]));
4161 let list_col: ArrowArrayRef = Arc::new(
4162 GenericListArray::<i32>::try_new(
4163 src_list_elem,
4164 offsets_1(),
4165 Arc::new(Int32Array::from(vec![1])),
4166 None,
4167 )
4168 .unwrap(),
4169 );
4170 let entries_fields = match src_map_entries.data_type() {
4171 ArrowDataType::Struct(f) => f.clone(),
4172 _ => unreachable!(),
4173 };
4174 let entries = StructArray::try_new(
4175 entries_fields,
4176 vec![
4177 Arc::new(StringArray::from(vec!["a"])) as _,
4178 Arc::new(Int32Array::from(vec![10])) as _,
4179 ],
4180 None,
4181 )
4182 .unwrap();
4183 let map_col: ArrowArrayRef = Arc::new(
4184 MapArray::try_new(src_map_entries, offsets_1(), entries, None, false).unwrap(),
4185 );
4186 let outer_col: ArrowArrayRef = Arc::new(
4187 StructArray::try_new(
4188 ArrowFields::from(vec![src_list, src_map]),
4189 vec![list_col, map_col],
4190 None,
4191 )
4192 .unwrap(),
4193 );
4194
4195 let src_schema = Arc::new(ArrowSchema::new(vec![src_outer]));
4196 let batch = RecordBatch::try_new(src_schema, vec![outer_col]).unwrap();
4197
4198 let target_schema: SchemaRef =
4200 Arc::new(StructType::new_unchecked([StructField::nullable(
4201 "outer",
4202 StructType::new_unchecked([
4203 StructField::nullable("lst", ArrayType::new(DataType::INTEGER, true)),
4204 StructField::nullable(
4205 "mp",
4206 MapType::new(DataType::STRING, DataType::INTEGER, true),
4207 ),
4208 ]),
4209 )]));
4210
4211 let ordering = [ReorderIndex::identity(0)];
4212 let result =
4213 fixup_parquet_read(batch, &ordering, None, None, Some(&target_schema)).unwrap();
4214 let result_batch: RecordBatch = result.into();
4215
4216 let schema = result_batch.schema();
4217 let outer_field = schema.field(0);
4218 assert!(outer_field.is_nullable(), "outer should be nullable");
4219
4220 let outer_children = match outer_field.data_type() {
4221 ArrowDataType::Struct(f) => f,
4222 other => panic!("expected Struct, got {other:?}"),
4223 };
4224
4225 let lst_field = &outer_children[0];
4226 assert!(lst_field.is_nullable(), "lst should be nullable");
4227 let lst_element = match lst_field.data_type() {
4228 ArrowDataType::List(e) => e,
4229 other => panic!("expected List, got {other:?}"),
4230 };
4231 assert!(lst_element.is_nullable(), "list element should be nullable");
4232
4233 let mp_field = &outer_children[1];
4234 assert!(mp_field.is_nullable(), "mp should be nullable");
4235 let (mp_entries, mp_key, mp_value) = match mp_field.data_type() {
4236 ArrowDataType::Map(entries, _) => {
4237 let inner = match entries.data_type() {
4238 ArrowDataType::Struct(f) => f,
4239 other => panic!("expected Struct entries, got {other:?}"),
4240 };
4241 (entries.as_ref(), inner[0].clone(), inner[1].clone())
4242 }
4243 other => panic!("expected Map, got {other:?}"),
4244 };
4245 assert!(
4246 !mp_entries.is_nullable(),
4247 "map entries field must remain non-null per Arrow spec"
4248 );
4249 assert!(
4250 !mp_key.is_nullable(),
4251 "map key field must remain non-null per Arrow spec"
4252 );
4253 assert!(mp_value.is_nullable(), "map value should be nullable");
4254 }
4255
4256 #[test]
4261 fn test_fixup_parquet_read_renames_nested_struct_fields() {
4262 let src_inner_fields =
4264 ArrowFields::from(vec![ArrowField::new("src_z", ArrowDataType::Int32, false)]);
4265 let src_inner = ArrowField::new(
4266 "src_y",
4267 ArrowDataType::Struct(src_inner_fields.clone()),
4268 false,
4269 );
4270 let src_outer_fields = ArrowFields::from(vec![
4271 ArrowField::new("src_x", ArrowDataType::Int32, false),
4272 src_inner,
4273 ]);
4274 let src_outer = ArrowField::new(
4275 "outer",
4276 ArrowDataType::Struct(src_outer_fields.clone()),
4277 false,
4278 );
4279
4280 let inner_z: ArrowArrayRef = Arc::new(Int32Array::from(vec![42, 43, 44]));
4281 let inner_struct: ArrowArrayRef =
4282 Arc::new(StructArray::try_new(src_inner_fields, vec![inner_z], None).unwrap());
4283 let outer_x: ArrowArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
4284 let outer_col: ArrowArrayRef = Arc::new(
4285 StructArray::try_new(src_outer_fields, vec![outer_x, inner_struct], None).unwrap(),
4286 );
4287
4288 let src_schema = Arc::new(ArrowSchema::new(vec![src_outer]));
4289 let batch = RecordBatch::try_new(src_schema, vec![outer_col]).unwrap();
4290
4291 let target_schema: SchemaRef = schema_ref! {
4293 not_null "outer": {
4294 not_null "tgt_x": INTEGER,
4295 not_null "tgt_y": { not_null "tgt_z": INTEGER },
4296 },
4297 };
4298
4299 let ordering = [ReorderIndex::identity(0)];
4300 let result =
4301 fixup_parquet_read(batch, &ordering, None, None, Some(&target_schema)).unwrap();
4302 let result_batch: RecordBatch = result.into();
4303
4304 let schema = result_batch.schema();
4305 let outer_field = schema.field(0);
4306 assert_eq!(outer_field.name(), "outer");
4307
4308 let outer_children = match outer_field.data_type() {
4309 ArrowDataType::Struct(f) => f,
4310 other => panic!("expected Struct, got {other:?}"),
4311 };
4312 assert_eq!(outer_children[0].name(), "tgt_x");
4313 assert_eq!(outer_children[1].name(), "tgt_y");
4314
4315 let nested_children = match outer_children[1].data_type() {
4316 ArrowDataType::Struct(f) => f,
4317 other => panic!("expected nested Struct, got {other:?}"),
4318 };
4319 assert_eq!(nested_children[0].name(), "tgt_z");
4320 }
4321
4322 #[test]
4326 fn test_nested_null_masks_with_null_array_child() {
4327 use crate::arrow::array::NullArray;
4328 use crate::arrow::datatypes::{DataType, Field};
4329 use crate::engine::arrow_utils::fix_nested_null_masks;
4330
4331 let int_field = Field::new("val", DataType::Int32, true);
4333 let null_field = Field::new("void_col", DataType::Null, true);
4334 let fields = ArrowFields::from(vec![int_field, null_field]);
4335
4336 let int_col: ArrowArrayRef = Arc::new(Int32Array::from(vec![
4337 Some(10),
4338 Some(20),
4339 Some(30),
4340 Some(40),
4341 ]));
4342 let null_col: ArrowArrayRef = Arc::new(NullArray::new(4));
4343
4344 let parent_nulls = NullBuffer::from(&[false, true, true, true][..]);
4346 let sa = StructArray::new(fields.clone(), vec![int_col, null_col], Some(parent_nulls));
4347
4348 let outer_field = Field::new("outer", DataType::Struct(fields), true);
4350 let outer = StructArray::new(
4351 ArrowFields::from(vec![outer_field]),
4352 vec![Arc::new(sa)],
4353 None,
4354 );
4355
4356 let result = fix_nested_null_masks(outer);
4358
4359 let inner = result.column(0).as_struct();
4361 assert_eq!(inner.len(), 4);
4362
4363 let void_col = inner.column(1);
4364 assert_eq!(*void_col.data_type(), DataType::Null);
4365 assert_eq!(void_col.len(), 4);
4366 assert_eq!(void_col.null_count(), 0);
4369
4370 let val_col = inner.column(0);
4372 assert!(val_col.is_null(0), "row 0 should be null (parent null)");
4373 assert!(!val_col.is_null(1));
4374 assert!(!val_col.is_null(2));
4375 assert!(!val_col.is_null(3));
4376 }
4377
4378 fn kernel_target_schema(schema: StructType) -> ArrowSchemaRef {
4384 Arc::new((&schema).try_into_arrow().unwrap())
4385 }
4386
4387 fn map_string_string_with_entry_name(entry: &str) -> MapArray {
4390 let names = MapFieldNames {
4391 entry: entry.to_string(),
4392 key: "key".to_string(),
4393 value: "value".to_string(),
4394 };
4395 let mut builder = MapBuilder::new(Some(names), StringBuilder::new(), StringBuilder::new());
4396 builder.keys().append_value("k");
4397 builder.values().append_value("v");
4398 builder.append(true).unwrap();
4399 builder.finish()
4400 }
4401
4402 #[test]
4403 fn test_coerce_columns_renames_entries_map_to_key_value() {
4404 let map = map_string_string_with_entry_name("entries"); let ArrowDataType::Map(entry_field, _) = map.data_type() else {
4406 panic!("expected map");
4407 };
4408 assert_eq!(entry_field.name(), "entries");
4409
4410 let target = kernel_target_schema(schema! { nullable "m": { STRING => nullable STRING } });
4411 let coerced = coerce_columns_to_schema(vec![Arc::new(map)], &target).unwrap();
4412
4413 let ArrowDataType::Map(out_entry, _) = coerced[0].data_type() else {
4414 panic!("expected map");
4415 };
4416 assert_eq!(out_entry.name(), "key_value"); assert_eq!(coerced[0].data_type(), target.field(0).data_type());
4418 }
4419
4420 #[test]
4421 fn test_coerce_columns_renames_list_item_to_element() {
4422 let list: ListArray =
4424 ListArray::from_iter_primitive::<Int32Type, _, _>([Some(vec![Some(1), Some(2)])]);
4425 let ArrowDataType::List(elem_field) = list.data_type() else {
4426 panic!("expected list");
4427 };
4428 assert_eq!(elem_field.name(), "item"); let target = kernel_target_schema(schema! { nullable "l": [ nullable INTEGER ] });
4431 let coerced = coerce_columns_to_schema(vec![Arc::new(list)], &target).unwrap();
4432
4433 let ArrowDataType::List(out_elem) = coerced[0].data_type() else {
4434 panic!("expected list");
4435 };
4436 assert_eq!(out_elem.name(), "element"); assert_eq!(coerced[0].data_type(), target.field(0).data_type());
4438 }
4439
4440 #[test]
4441 fn test_coerce_columns_passes_through_matching_columns() {
4442 let col: ArrowArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
4443 let target = kernel_target_schema(schema! { nullable "i": INTEGER });
4444
4445 let coerced = coerce_columns_to_schema(vec![col.clone()], &target).unwrap();
4446
4447 assert!(Arc::ptr_eq(&col, &coerced[0]));
4449 }
4450
4451 #[test]
4454 fn test_coerce_columns_renames_map_nested_in_struct() {
4455 let map = map_string_string_with_entry_name("entries"); let config_field = ArrowField::new("config", map.data_type().clone(), true);
4457 let outer = StructArray::from(vec![(
4458 Arc::new(config_field),
4459 Arc::new(map) as ArrowArrayRef,
4460 )]);
4461
4462 let target = kernel_target_schema(schema! {
4463 nullable "meta": { nullable "config": { STRING => nullable STRING } },
4464 });
4465 let coerced = coerce_columns_to_schema(vec![Arc::new(outer)], &target).unwrap();
4466
4467 assert_eq!(coerced[0].data_type(), target.field(0).data_type());
4468 }
4469
4470 #[test]
4472 fn test_coerce_columns_casts_primitive_to_target_type() {
4473 let col: ArrowArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3])); let target = kernel_target_schema(schema! { nullable "n": LONG }); let coerced = coerce_columns_to_schema(vec![col], &target).unwrap();
4477
4478 assert_eq!(coerced[0].data_type(), &ArrowDataType::Int64);
4479 }
4480
4481 #[test]
4483 fn test_coerce_columns_preserves_struct_null_rows() {
4484 let child: ArrowArrayRef = Arc::new(Int32Array::from(vec![Some(1), Some(2)]));
4485 let fields: ArrowFields = vec![ArrowField::new("n", ArrowDataType::Int32, true)].into();
4486 let nulls = NullBuffer::from(vec![false, true]); let outer = StructArray::try_new(fields, vec![child], Some(nulls)).unwrap();
4488
4489 let target = kernel_target_schema(schema! { nullable "s": { nullable "n": LONG } });
4491 let coerced = coerce_columns_to_schema(vec![Arc::new(outer)], &target).unwrap();
4492
4493 let out = coerced[0].as_struct();
4494 assert!(out.is_null(0), "null struct row must survive the rebuild");
4495 assert!(!out.is_null(1));
4496 assert_eq!(out.column(0).data_type(), &ArrowDataType::Int64);
4497 }
4498
4499 #[test]
4500 fn test_coerce_columns_incompatible_leaf_type_errors() {
4501 let col: ArrowArrayRef = Arc::new(StringArray::from(vec!["not_a_date"]));
4502 let target = kernel_target_schema(schema! { nullable "d": DATE });
4503
4504 assert!(coerce_columns_to_schema(vec![col], &target).is_err());
4505 }
4506
4507 #[test]
4510 fn test_coerce_columns_non_struct_source_to_struct_target_errors() {
4511 let col: ArrowArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
4512 let target = kernel_target_schema(schema! { nullable "s": { nullable "n": INTEGER } });
4513
4514 let result = coerce_columns_to_schema(vec![col], &target);
4515 assert_result_error_with_message(result, "to a struct target");
4516 }
4517
4518 #[test]
4520 fn test_coerce_columns_struct_child_count_mismatch_errors() {
4521 let child: ArrowArrayRef = Arc::new(Int32Array::from(vec![1, 2]));
4522 let fields: ArrowFields = vec![ArrowField::new("a", ArrowDataType::Int32, true)].into();
4523 let source = StructArray::try_new(fields, vec![child], None).unwrap();
4524
4525 let target = kernel_target_schema(schema! {
4526 nullable "s": { nullable "a": INTEGER, nullable "b": INTEGER },
4527 });
4528
4529 let result = coerce_columns_to_schema(vec![Arc::new(source)], &target);
4530 assert_result_error_with_message(result, "cannot cast struct with 1 children");
4531 }
4532
4533 #[test]
4536 fn test_coerce_columns_recurses_through_struct_list_map() {
4537 let map = map_string_string_with_entry_name("entries"); let list_field = Arc::new(ArrowField::new("item", map.data_type().clone(), true));
4539 let list = ListArray::new(
4540 list_field,
4541 OffsetBuffer::from_lengths([1]),
4542 Arc::new(map),
4543 None,
4544 );
4545 let outer = StructArray::from(vec![(
4546 Arc::new(ArrowField::new("maps", list.data_type().clone(), true)),
4547 Arc::new(list) as ArrowArrayRef,
4548 )]);
4549
4550 let target = kernel_target_schema(schema! {
4551 nullable "s": { nullable "maps": [ nullable { STRING => nullable STRING } ] },
4552 });
4553 let coerced = coerce_columns_to_schema(vec![Arc::new(outer)], &target).unwrap();
4554
4555 assert_eq!(coerced[0].data_type(), target.field(0).data_type());
4556 }
4557}