1use std::collections::HashMap;
21use std::sync::Arc;
22
23use arrow_array::types::{Decimal128Type, validate_decimal_precision_and_scale};
24use arrow_array::{
25 BinaryArray, BooleanArray, Date32Array, Datum as ArrowDatum, Decimal128Array,
26 FixedSizeBinaryArray, Float32Array, Float64Array, Int32Array, Int64Array, Scalar, StringArray,
27 TimestampMicrosecondArray, TimestampNanosecondArray,
28};
29use arrow_schema::extension::{ExtensionType, Uuid as UuidExtensionType};
30use arrow_schema::{
31 ArrowError, DataType, Field, FieldRef, Fields, Schema as ArrowSchema, TimeUnit,
32};
33use parquet::arrow::PARQUET_FIELD_ID_META_KEY;
34use parquet::file::statistics::Statistics;
35use parquet_geospatial::{WkbEdges, WkbMetadata, WkbType, WkbTypeHint};
36use uuid::Uuid;
37
38use crate::error::{Result, invalid_data};
39use crate::spec::decimal_utils::i128_from_be_bytes;
40use crate::spec::geospatial::EdgeInterpolationAlgorithm;
41use crate::spec::{
42 Datum, FIRST_FIELD_ID, GeographyType, GeometryType, LIST_FIELD_NAME, ListType,
43 MAP_KEY_FIELD_NAME, MAP_VALUE_FIELD_NAME, MapType, NestedField, NestedFieldRef,
44 PrimitiveLiteral, PrimitiveType, Schema, SchemaVisitor, StructType, Type, VariantType,
45};
46use crate::{Error, ErrorKind};
47
48pub const DEFAULT_MAP_FIELD_NAME: &str = "key_value";
50pub const UTC_TIME_ZONE: &str = "+00:00";
52const UNSET_GEOSPATIAL_CRS: &str = "srid:0";
53
54#[derive(Debug, Clone, Copy, Default)]
63pub(crate) struct VariantExtensionType;
64
65impl ExtensionType for VariantExtensionType {
66 const NAME: &'static str = "arrow.parquet.variant";
67
68 type Metadata = ();
69
70 fn metadata(&self) -> &Self::Metadata {
71 &()
72 }
73
74 fn serialize_metadata(&self) -> Option<String> {
75 None
76 }
77
78 fn deserialize_metadata(
79 metadata: Option<&str>,
80 ) -> std::result::Result<Self::Metadata, ArrowError> {
81 match metadata {
82 None | Some("") => Ok(()),
83 Some(other) => Err(ArrowError::InvalidArgumentError(format!(
84 "arrow.parquet.variant extension type takes no metadata, got {other:?}"
85 ))),
86 }
87 }
88
89 fn supports_data_type(&self, data_type: &DataType) -> std::result::Result<(), ArrowError> {
90 match data_type {
91 DataType::Struct(_) => Ok(()),
92 other => Err(ArrowError::InvalidArgumentError(format!(
93 "arrow.parquet.variant extension type requires a Struct storage type, got {other}"
94 ))),
95 }
96 }
97
98 fn try_new(
99 data_type: &DataType,
100 _metadata: Self::Metadata,
101 ) -> std::result::Result<Self, ArrowError> {
102 Self.supports_data_type(data_type)?;
103 Ok(Self)
104 }
105}
106
107fn to_arrow_wkb_edges(algorithm: EdgeInterpolationAlgorithm) -> WkbEdges {
108 match algorithm {
109 EdgeInterpolationAlgorithm::Spherical => WkbEdges::Spherical,
110 EdgeInterpolationAlgorithm::Vincenty => WkbEdges::Vincenty,
111 EdgeInterpolationAlgorithm::Thomas => WkbEdges::Thomas,
112 EdgeInterpolationAlgorithm::Andoyer => WkbEdges::Andoyer,
113 EdgeInterpolationAlgorithm::Karney => WkbEdges::Karney,
114 }
115}
116
117impl From<WkbEdges> for EdgeInterpolationAlgorithm {
118 fn from(edges: WkbEdges) -> Self {
119 match edges {
120 WkbEdges::Spherical => Self::Spherical,
121 WkbEdges::Vincenty => Self::Vincenty,
122 WkbEdges::Thomas => Self::Thomas,
123 WkbEdges::Andoyer => Self::Andoyer,
124 WkbEdges::Karney => Self::Karney,
125 }
126 }
127}
128
129fn iceberg_crs_from_wkb_metadata(metadata: &WkbMetadata) -> Result<String> {
130 match metadata.crs.as_ref() {
131 None => Ok(UNSET_GEOSPATIAL_CRS.to_string()),
132 Some(serde_json::Value::String(crs)) => Ok(crs.clone()),
133 Some(serde_json::Value::Object(crs)) => {
134 let id = crs.get("id");
135 let authority = id
136 .and_then(|id| id.get("authority"))
137 .and_then(serde_json::Value::as_str)
138 .filter(|authority| !authority.is_empty());
139 let code = match id.and_then(|id| id.get("code")) {
140 Some(serde_json::Value::String(code)) => Some(code.clone()),
141 Some(serde_json::Value::Number(code)) => Some(code.to_string()),
142 _ => None,
143 };
144
145 match (authority, code) {
146 (Some(authority), Some(code)) => Ok(format!("{authority}:{code}")),
147 _ => Err(Error::new(
148 ErrorKind::DataInvalid,
149 "Cannot determine Iceberg geospatial type from PROJJSON CRS without an embedded authority/code",
150 )),
151 }
152 }
153 Some(_) => Err(Error::new(
154 ErrorKind::DataInvalid,
155 "Geospatial CRS metadata must be a string or PROJJSON object",
156 )),
157 }
158}
159
160pub trait ArrowSchemaVisitor {
164 type T;
166
167 type U;
169
170 fn before_field(&mut self, _field: &FieldRef) -> Result<()> {
174 Ok(())
175 }
176
177 fn after_field(&mut self, _field: &FieldRef) -> Result<()> {
181 Ok(())
182 }
183
184 fn before_list_element(&mut self, _field: &FieldRef) -> Result<()> {
186 Ok(())
187 }
188
189 fn after_list_element(&mut self, _field: &FieldRef) -> Result<()> {
191 Ok(())
192 }
193
194 fn before_map_key(&mut self, _field: &FieldRef) -> Result<()> {
196 Ok(())
197 }
198
199 fn after_map_key(&mut self, _field: &FieldRef) -> Result<()> {
201 Ok(())
202 }
203
204 fn before_map_value(&mut self, _field: &FieldRef) -> Result<()> {
206 Ok(())
207 }
208
209 fn after_map_value(&mut self, _field: &FieldRef) -> Result<()> {
211 Ok(())
212 }
213
214 fn schema(&mut self, schema: &ArrowSchema, values: Vec<Self::T>) -> Result<Self::U>;
216
217 fn r#struct(&mut self, fields: &Fields, results: Vec<Self::T>) -> Result<Self::T>;
219
220 fn list(&mut self, list: &DataType, value: Self::T) -> Result<Self::T>;
222
223 fn map(&mut self, map: &DataType, key_value: Self::T, value: Self::T) -> Result<Self::T>;
225
226 fn primitive(&mut self, p: &DataType) -> Result<Self::T>;
228
229 fn variant(&mut self, field: &FieldRef) -> Result<Self::T>
240 where Self: Sized {
241 visit_type(field.data_type(), self)
242 }
243
244 fn uuid(&mut self, field: &FieldRef) -> Result<Self::T>
254 where Self: Sized {
255 visit_type(field.data_type(), self)
256 }
257}
258
259fn visit_type<V: ArrowSchemaVisitor>(r#type: &DataType, visitor: &mut V) -> Result<V::T> {
261 match r#type {
262 p if p.is_primitive()
263 || matches!(
264 p,
265 DataType::Boolean
266 | DataType::Utf8
267 | DataType::LargeUtf8
268 | DataType::Utf8View
269 | DataType::Null
270 | DataType::Binary
271 | DataType::LargeBinary
272 | DataType::BinaryView
273 | DataType::FixedSizeBinary(_)
274 ) =>
275 {
276 visitor.primitive(p)
277 }
278 DataType::List(element_field) => visit_list(r#type, element_field, visitor),
279 DataType::LargeList(element_field) => visit_list(r#type, element_field, visitor),
280 DataType::FixedSizeList(element_field, _) => visit_list(r#type, element_field, visitor),
281 DataType::Map(field, _) => match field.data_type() {
282 DataType::Struct(fields) => {
283 if fields.len() != 2 {
284 return Err(invalid_data!("Map field must have exactly 2 fields"));
285 }
286
287 let key_field = &fields[0];
288 let value_field = &fields[1];
289
290 let key_result = {
291 visitor.before_map_key(key_field)?;
292 let ret = visit_field(key_field, visitor)?;
293 visitor.after_map_key(key_field)?;
294 ret
295 };
296
297 let value_result = {
298 visitor.before_map_value(value_field)?;
299 let ret = visit_field(value_field, visitor)?;
300 visitor.after_map_value(value_field)?;
301 ret
302 };
303
304 visitor.map(r#type, key_result, value_result)
305 }
306 _ => Err(invalid_data!("Map field must have struct type")),
307 },
308 DataType::Struct(fields) => visit_struct(fields, visitor),
309 DataType::Dictionary(_key_type, value_type) => visit_type(value_type, visitor),
310 other => Err(invalid_data!("Cannot visit Arrow data type: {other}")),
311 }
312}
313
314fn visit_field<V: ArrowSchemaVisitor>(field: &FieldRef, visitor: &mut V) -> Result<V::T> {
319 if field.extension_type_name() == Some(VariantExtensionType::NAME) {
320 visitor.variant(field)
321 } else if field.extension_type_name() == Some(UuidExtensionType::NAME) {
322 visitor.uuid(field)
323 } else {
324 visit_type(field.data_type(), visitor)
325 }
326}
327
328fn visit_list<V: ArrowSchemaVisitor>(
330 data_type: &DataType,
331 element_field: &FieldRef,
332 visitor: &mut V,
333) -> Result<V::T> {
334 visitor.before_list_element(element_field)?;
335 let value = visit_field(element_field, visitor)?;
336 visitor.after_list_element(element_field)?;
337 visitor.list(data_type, value)
338}
339
340fn visit_struct<V: ArrowSchemaVisitor>(fields: &Fields, visitor: &mut V) -> Result<V::T> {
342 let mut results = Vec::with_capacity(fields.len());
343 for field in fields {
344 visitor.before_field(field)?;
345 let result = visit_field(field, visitor)?;
346 visitor.after_field(field)?;
347 results.push(result);
348 }
349
350 visitor.r#struct(fields, results)
351}
352
353pub(crate) fn visit_schema<V: ArrowSchemaVisitor>(
355 schema: &ArrowSchema,
356 visitor: &mut V,
357) -> Result<V::U> {
358 let mut results = Vec::with_capacity(schema.fields().len());
359 for field in schema.fields() {
360 visitor.before_field(field)?;
361 let result = visit_field(field, visitor)?;
362 visitor.after_field(field)?;
363 results.push(result);
364 }
365 visitor.schema(schema, results)
366}
367
368pub fn arrow_schema_to_schema(schema: &ArrowSchema) -> Result<Schema> {
374 let mut visitor = ArrowSchemaConverter::new();
375 visit_schema(schema, &mut visitor)
376}
377
378pub fn arrow_schema_to_schema_auto_assign_ids(schema: &ArrowSchema) -> Result<Schema> {
387 let mut visitor = ArrowSchemaConverter::new_with_field_ids_from(FIRST_FIELD_ID);
388 visit_schema(schema, &mut visitor)
389}
390
391pub fn arrow_type_to_type(ty: &DataType) -> Result<Type> {
393 let mut visitor = ArrowSchemaConverter::new();
394 visit_type(ty, &mut visitor)
395}
396
397const ARROW_FIELD_DOC_KEY: &str = "doc";
398
399pub(super) fn try_get_field_id_from_metadata(field: &Field) -> Result<Option<i32>> {
400 field
401 .metadata()
402 .get(PARQUET_FIELD_ID_META_KEY)
403 .map(|value| {
404 value.parse::<i32>().map_err(|e| {
405 invalid_data!("Failed to parse field id")
406 .with_context("value", value)
407 .with_source(e)
408 })
409 })
410 .transpose()
411}
412
413pub(super) fn get_field_id_from_metadata(field: &FieldRef) -> Result<i32> {
414 try_get_field_id_from_metadata(field)?
415 .ok_or_else(|| invalid_data!("Field id not found in metadata"))
416}
417
418fn get_field_doc(field: &FieldRef) -> Option<String> {
419 if let Some(value) = field.metadata().get(ARROW_FIELD_DOC_KEY) {
420 return Some(value.clone());
421 }
422 None
423}
424
425struct ArrowSchemaConverter {
426 reassign_field_ids_from: Option<i32>,
429 next_field_id: i32,
433}
434
435impl ArrowSchemaConverter {
436 fn new() -> Self {
437 Self {
438 reassign_field_ids_from: None,
439 next_field_id: 0,
440 }
441 }
442
443 fn new_with_field_ids_from(start_from: i32) -> Self {
444 Self {
445 reassign_field_ids_from: Some(start_from),
446 next_field_id: 0,
447 }
448 }
449
450 fn get_field_id(&mut self, field: &FieldRef) -> Result<i32> {
451 if self.reassign_field_ids_from.is_some() {
452 let temp_id = self.next_field_id;
456 self.next_field_id += 1;
457 Ok(temp_id)
458 } else {
459 get_field_id_from_metadata(field)
461 }
462 }
463
464 fn convert_fields(
465 &mut self,
466 fields: &Fields,
467 field_results: &[Type],
468 ) -> Result<Vec<NestedFieldRef>> {
469 let mut results = Vec::with_capacity(fields.len());
470 for i in 0..fields.len() {
471 let field = &fields[i];
472 let field_type = self.apply_arrow_extension(field, field_results[i].clone())?;
473 let id = self.get_field_id(field)?;
474 let doc = get_field_doc(field);
475 let nested_field = NestedField {
476 id,
477 doc,
478 name: field.name().clone(),
479 required: !field.is_nullable(),
480 field_type: Box::new(field_type),
481 initial_default: None,
482 write_default: None,
483 };
484 results.push(Arc::new(nested_field));
485 }
486 Ok(results)
487 }
488
489 fn apply_arrow_extension(&self, arrow_field: &FieldRef, iceberg_type: Type) -> Result<Type> {
490 if arrow_field.extension_type_name() != Some(WkbType::NAME) {
491 return Ok(iceberg_type);
492 }
493
494 if !matches!(&iceberg_type, Type::Primitive(PrimitiveType::Binary)) {
495 return Err(Error::new(
496 ErrorKind::DataInvalid,
497 format!(
498 "WKB extension type on field {} requires binary storage, got {iceberg_type}",
499 arrow_field.name()
500 ),
501 ));
502 }
503
504 let wkb_type = arrow_field.try_extension_type::<WkbType>().map_err(|err| {
505 Error::new(
506 ErrorKind::DataInvalid,
507 format!(
508 "Invalid geospatial Arrow extension metadata for field {}",
509 arrow_field.name()
510 ),
511 )
512 .with_source(err)
513 })?;
514
515 let crs = iceberg_crs_from_wkb_metadata(wkb_type.metadata())?;
516
517 match wkb_type.metadata().type_hint() {
518 WkbTypeHint::Geometry => Ok(Type::Primitive(PrimitiveType::Geometry(
519 GeometryType::new(Some(crs))?,
520 ))),
521 WkbTypeHint::Geography => Ok(Type::Primitive(PrimitiveType::Geography(
522 GeographyType::new(
523 Some(crs),
524 wkb_type
525 .metadata()
526 .algorithm
527 .unwrap_or(WkbEdges::Spherical)
528 .into(),
529 )?,
530 ))),
531 }
532 }
533}
534
535impl ArrowSchemaVisitor for ArrowSchemaConverter {
536 type T = Type;
537 type U = Schema;
538
539 fn schema(&mut self, schema: &ArrowSchema, values: Vec<Self::T>) -> Result<Self::U> {
540 let fields = self.convert_fields(schema.fields(), &values)?;
541 let mut builder = Schema::builder().with_fields(fields);
542 if let Some(start_from) = self.reassign_field_ids_from {
543 builder = builder.with_reassigned_field_ids(start_from)
544 }
545 builder.build()
546 }
547
548 fn r#struct(&mut self, fields: &Fields, results: Vec<Self::T>) -> Result<Self::T> {
549 let fields = self.convert_fields(fields, &results)?;
550 Ok(Type::Struct(StructType::new(fields)))
551 }
552
553 fn list(&mut self, list: &DataType, value: Self::T) -> Result<Self::T> {
554 let element_field = match list {
555 DataType::List(element_field) => element_field,
556 DataType::LargeList(element_field) => element_field,
557 DataType::FixedSizeList(element_field, _) => element_field,
558 _ => {
559 return Err(invalid_data!("List type must have list data type"));
560 }
561 };
562
563 let value = self.apply_arrow_extension(element_field, value)?;
564 let id = self.get_field_id(element_field)?;
565 let doc = get_field_doc(element_field);
566 let mut element_field =
567 NestedField::list_element(id, value.clone(), !element_field.is_nullable());
568 if let Some(doc) = doc {
569 element_field = element_field.with_doc(doc);
570 }
571 let element_field = Arc::new(element_field);
572 Ok(Type::List(ListType { element_field }))
573 }
574
575 fn map(&mut self, map: &DataType, key_value: Self::T, value: Self::T) -> Result<Self::T> {
576 match map {
577 DataType::Map(field, _) => match field.data_type() {
578 DataType::Struct(fields) => {
579 if fields.len() != 2 {
580 return Err(invalid_data!("Map field must have exactly 2 fields"));
581 }
582
583 let key_field = &fields[0];
584 let value_field = &fields[1];
585 let key_value = self.apply_arrow_extension(key_field, key_value)?;
586 let value = self.apply_arrow_extension(value_field, value)?;
587
588 let key_id = self.get_field_id(key_field)?;
589 let key_doc = get_field_doc(key_field);
590 let mut key_field = NestedField::map_key_element(key_id, key_value.clone());
591 if let Some(doc) = key_doc {
592 key_field = key_field.with_doc(doc);
593 }
594 let key_field = Arc::new(key_field);
595
596 let value_id = self.get_field_id(value_field)?;
597 let value_doc = get_field_doc(value_field);
598 let mut value_field = NestedField::map_value_element(
599 value_id,
600 value.clone(),
601 !value_field.is_nullable(),
602 );
603 if let Some(doc) = value_doc {
604 value_field = value_field.with_doc(doc);
605 }
606 let value_field = Arc::new(value_field);
607
608 Ok(Type::Map(MapType {
609 key_field,
610 value_field,
611 }))
612 }
613 _ => Err(invalid_data!("Map field must have struct type")),
614 },
615 _ => Err(invalid_data!("Map type must have map data type")),
616 }
617 }
618
619 fn primitive(&mut self, p: &DataType) -> Result<Self::T> {
620 match p {
621 DataType::Null => Ok(Type::Primitive(PrimitiveType::Unknown)),
622 DataType::Boolean => Ok(Type::Primitive(PrimitiveType::Boolean)),
623 DataType::Int8 | DataType::Int16 | DataType::Int32 => {
624 Ok(Type::Primitive(PrimitiveType::Int))
625 }
626 DataType::UInt8 | DataType::UInt16 => Ok(Type::Primitive(PrimitiveType::Int)),
627 DataType::UInt32 => Ok(Type::Primitive(PrimitiveType::Long)),
628 DataType::Int64 => Ok(Type::Primitive(PrimitiveType::Long)),
629 DataType::UInt64 => {
630 Err(invalid_data!(
632 "UInt64 is not supported. Use Int64 for values ≤ 9,223,372,036,854,775,807 or Decimal(20,0) for full uint64 range."
633 ))
634 }
635 DataType::Float32 => Ok(Type::Primitive(PrimitiveType::Float)),
636 DataType::Float64 => Ok(Type::Primitive(PrimitiveType::Double)),
637 DataType::Decimal128(p, s) => Type::decimal(*p as u32, *s as u32)
638 .map_err(|e| invalid_data!("Failed to create decimal type").with_source(e)),
639 DataType::Date32 => Ok(Type::Primitive(PrimitiveType::Date)),
640 DataType::Time64(unit) if unit == &TimeUnit::Microsecond => {
641 Ok(Type::Primitive(PrimitiveType::Time))
642 }
643 DataType::Timestamp(unit, None) if unit == &TimeUnit::Microsecond => {
644 Ok(Type::Primitive(PrimitiveType::Timestamp))
645 }
646 DataType::Timestamp(unit, None) if unit == &TimeUnit::Nanosecond => {
647 Ok(Type::Primitive(PrimitiveType::TimestampNs))
648 }
649 DataType::Timestamp(unit, Some(zone))
650 if unit == &TimeUnit::Microsecond
651 && (zone.as_ref() == "UTC" || zone.as_ref() == "+00:00") =>
652 {
653 Ok(Type::Primitive(PrimitiveType::Timestamptz))
654 }
655 DataType::Timestamp(unit, Some(zone))
656 if unit == &TimeUnit::Nanosecond
657 && (zone.as_ref() == "UTC" || zone.as_ref() == "+00:00") =>
658 {
659 Ok(Type::Primitive(PrimitiveType::TimestamptzNs))
660 }
661 DataType::Binary | DataType::LargeBinary | DataType::BinaryView => {
662 Ok(Type::Primitive(PrimitiveType::Binary))
663 }
664 DataType::FixedSizeBinary(width) => {
665 Ok(Type::Primitive(PrimitiveType::Fixed(*width as u64)))
666 }
667 DataType::Utf8View | DataType::Utf8 | DataType::LargeUtf8 => {
668 Ok(Type::Primitive(PrimitiveType::String))
669 }
670 _ => Err(invalid_data!("Unsupported Arrow data type: {p}")),
671 }
672 }
673
674 fn variant(&mut self, field: &FieldRef) -> Result<Self::T> {
675 if !matches!(field.data_type(), DataType::Struct(_)) {
678 return Err(invalid_data!(
679 "arrow.parquet.variant extension requires Struct storage"
680 ));
681 }
682 Ok(Type::Variant(VariantType))
686 }
687
688 fn uuid(&mut self, field: &FieldRef) -> Result<Self::T> {
689 let field_data_type = field.data_type();
692 let expected_data_type = DataType::FixedSizeBinary(16);
693 if *field_data_type != expected_data_type {
694 let error_message = format!(
695 "{} extension requires {expected_data_type} storage, found {field_data_type}",
696 UuidExtensionType::NAME,
697 );
698
699 return Err(Error::new(ErrorKind::DataInvalid, error_message));
700 }
701
702 Ok(Type::Primitive(PrimitiveType::Uuid))
703 }
704}
705
706struct ToArrowSchemaConverter;
707
708enum ArrowSchemaOrFieldOrType {
709 Schema(ArrowSchema),
710 Field(Field),
711 Type(DataType),
712}
713
714impl SchemaVisitor for ToArrowSchemaConverter {
715 type T = ArrowSchemaOrFieldOrType;
716
717 fn schema(
718 &mut self,
719 _schema: &Schema,
720 value: ArrowSchemaOrFieldOrType,
721 ) -> Result<ArrowSchemaOrFieldOrType> {
722 let struct_type = match value {
723 ArrowSchemaOrFieldOrType::Type(DataType::Struct(fields)) => fields,
724 _ => unreachable!(),
725 };
726 Ok(ArrowSchemaOrFieldOrType::Schema(ArrowSchema::new(
727 struct_type,
728 )))
729 }
730
731 fn field(
732 &mut self,
733 field: &NestedFieldRef,
734 value: ArrowSchemaOrFieldOrType,
735 ) -> Result<ArrowSchemaOrFieldOrType> {
736 let ty = match value {
737 ArrowSchemaOrFieldOrType::Type(ty) => ty,
738 _ => unreachable!(),
739 };
740 let metadata = if let Some(doc) = &field.doc {
741 HashMap::from([
742 (PARQUET_FIELD_ID_META_KEY.to_string(), field.id.to_string()),
743 (ARROW_FIELD_DOC_KEY.to_string(), doc.clone()),
744 ])
745 } else {
746 HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), field.id.to_string())])
747 };
748 let mut arrow_field =
749 Field::new(field.name.clone(), ty, !field.required).with_metadata(metadata);
750
751 match field.field_type.as_ref() {
752 Type::Variant(_) => {
753 arrow_field.try_with_extension_type(VariantExtensionType)?;
756 }
757 Type::Primitive(PrimitiveType::Uuid) => {
758 arrow_field.try_with_extension_type(UuidExtensionType)?;
760 }
761 Type::Primitive(PrimitiveType::Geometry(geometry)) => {
762 let metadata = WkbMetadata::new(Some(geometry.crs()), None);
763 arrow_field.try_with_extension_type(WkbType::new(Some(metadata)))?;
764 }
765 Type::Primitive(PrimitiveType::Geography(geography)) => {
766 let metadata = WkbMetadata::new(
767 Some(geography.crs()),
768 Some(to_arrow_wkb_edges(geography.algorithm())),
769 );
770 arrow_field.try_with_extension_type(WkbType::new(Some(metadata)))?;
771 }
772 _ => {}
773 }
774
775 Ok(ArrowSchemaOrFieldOrType::Field(arrow_field))
776 }
777
778 fn r#struct(
779 &mut self,
780 _: &StructType,
781 results: Vec<ArrowSchemaOrFieldOrType>,
782 ) -> Result<ArrowSchemaOrFieldOrType> {
783 let fields = results
784 .into_iter()
785 .map(|result| match result {
786 ArrowSchemaOrFieldOrType::Field(field) => field,
787 _ => unreachable!(),
788 })
789 .collect();
790 Ok(ArrowSchemaOrFieldOrType::Type(DataType::Struct(fields)))
791 }
792
793 fn list(&mut self, list: &ListType, value: ArrowSchemaOrFieldOrType) -> Result<Self::T> {
794 let field = match self.field(&list.element_field, value)? {
798 ArrowSchemaOrFieldOrType::Field(field) => field,
799 _ => unreachable!(),
800 };
801 Ok(ArrowSchemaOrFieldOrType::Type(DataType::List(Arc::new(
802 field,
803 ))))
804 }
805
806 fn map(
807 &mut self,
808 map: &MapType,
809 key_value: ArrowSchemaOrFieldOrType,
810 value: ArrowSchemaOrFieldOrType,
811 ) -> Result<ArrowSchemaOrFieldOrType> {
812 let key_field = match self.field(&map.key_field, key_value)? {
813 ArrowSchemaOrFieldOrType::Field(field) => field,
814 _ => unreachable!(),
815 };
816 let value_field = match self.field(&map.value_field, value)? {
817 ArrowSchemaOrFieldOrType::Field(field) => field,
818 _ => unreachable!(),
819 };
820 let field = Field::new(
821 DEFAULT_MAP_FIELD_NAME,
822 DataType::Struct(vec![key_field, value_field].into()),
823 false,
825 );
826
827 Ok(ArrowSchemaOrFieldOrType::Type(DataType::Map(
828 field.into(),
829 false,
830 )))
831 }
832
833 fn primitive(&mut self, p: &PrimitiveType) -> Result<ArrowSchemaOrFieldOrType> {
834 match p {
835 PrimitiveType::Unknown => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Null)),
836 PrimitiveType::Boolean => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Boolean)),
837 PrimitiveType::Int => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Int32)),
838 PrimitiveType::Long => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Int64)),
839 PrimitiveType::Float => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Float32)),
840 PrimitiveType::Double => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Float64)),
841 PrimitiveType::Decimal { precision, scale } => {
842 let (precision, scale) = {
843 let precision: u8 = precision.to_owned().try_into().map_err(|err| {
844 invalid_data!("incompatible precision for decimal type convert")
845 .with_source(err)
846 })?;
847 let scale = scale.to_owned().try_into().map_err(|err| {
848 invalid_data!("incompatible scale for decimal type convert")
849 .with_source(err)
850 })?;
851 (precision, scale)
852 };
853 validate_decimal_precision_and_scale::<Decimal128Type>(precision, scale).map_err(
854 |err| {
855 invalid_data!("incompatible precision and scale for decimal type convert")
856 .with_source(err)
857 },
858 )?;
859 Ok(ArrowSchemaOrFieldOrType::Type(DataType::Decimal128(
860 precision, scale,
861 )))
862 }
863 PrimitiveType::Date => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Date32)),
864 PrimitiveType::Time => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Time64(
865 TimeUnit::Microsecond,
866 ))),
867 PrimitiveType::Timestamp => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Timestamp(
868 TimeUnit::Microsecond,
869 None,
870 ))),
871 PrimitiveType::Timestamptz => Ok(ArrowSchemaOrFieldOrType::Type(
872 DataType::Timestamp(TimeUnit::Microsecond, Some(UTC_TIME_ZONE.into())),
874 )),
875 PrimitiveType::TimestampNs => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Timestamp(
876 TimeUnit::Nanosecond,
877 None,
878 ))),
879 PrimitiveType::TimestamptzNs => Ok(ArrowSchemaOrFieldOrType::Type(
880 DataType::Timestamp(TimeUnit::Nanosecond, Some(UTC_TIME_ZONE.into())),
882 )),
883 PrimitiveType::String => Ok(ArrowSchemaOrFieldOrType::Type(DataType::Utf8)),
884 PrimitiveType::Uuid => Ok(ArrowSchemaOrFieldOrType::Type(DataType::FixedSizeBinary(
885 16,
886 ))),
887 PrimitiveType::Fixed(len) => Ok(ArrowSchemaOrFieldOrType::Type(
888 i32::try_from(*len)
889 .ok()
890 .map(DataType::FixedSizeBinary)
891 .unwrap_or(DataType::LargeBinary),
892 )),
893 PrimitiveType::Binary | PrimitiveType::Geometry(_) | PrimitiveType::Geography(_) => {
894 Ok(ArrowSchemaOrFieldOrType::Type(DataType::LargeBinary))
895 }
896 }
897 }
898
899 fn variant(&mut self, _v: &VariantType) -> Result<ArrowSchemaOrFieldOrType> {
900 let metadata_field = Field::new("metadata", DataType::Binary, false);
906 let value_field = Field::new("value", DataType::Binary, true);
907 Ok(ArrowSchemaOrFieldOrType::Type(DataType::Struct(
908 vec![metadata_field, value_field].into(),
909 )))
910 }
911}
912
913pub fn schema_to_arrow_schema(schema: &Schema) -> Result<ArrowSchema> {
915 let mut converter = ToArrowSchemaConverter;
916 match crate::spec::visit_schema(schema, &mut converter)? {
917 ArrowSchemaOrFieldOrType::Schema(schema) => Ok(schema),
918 _ => unreachable!(),
919 }
920}
921
922pub fn type_to_arrow_type(ty: &Type) -> Result<DataType> {
924 let mut converter = ToArrowSchemaConverter;
925 match crate::spec::visit_type(ty, &mut converter)? {
926 ArrowSchemaOrFieldOrType::Type(ty) => Ok(ty),
927 _ => unreachable!(),
928 }
929}
930
931pub(crate) fn get_arrow_datum(datum: &Datum) -> Result<Arc<dyn ArrowDatum + Send + Sync>> {
933 match (datum.data_type(), datum.literal()) {
934 (PrimitiveType::Boolean, PrimitiveLiteral::Boolean(value)) => {
935 Ok(Arc::new(BooleanArray::new_scalar(*value)))
936 }
937 (PrimitiveType::Int, PrimitiveLiteral::Int(value)) => {
938 Ok(Arc::new(Int32Array::new_scalar(*value)))
939 }
940 (PrimitiveType::Long, PrimitiveLiteral::Long(value)) => {
941 Ok(Arc::new(Int64Array::new_scalar(*value)))
942 }
943 (PrimitiveType::Float, PrimitiveLiteral::Float(value)) => {
944 Ok(Arc::new(Float32Array::new_scalar(value.into_inner())))
945 }
946 (PrimitiveType::Double, PrimitiveLiteral::Double(value)) => {
947 Ok(Arc::new(Float64Array::new_scalar(value.into_inner())))
948 }
949 (PrimitiveType::String, PrimitiveLiteral::String(value)) => {
950 Ok(Arc::new(StringArray::new_scalar(value.as_str())))
951 }
952 (PrimitiveType::Binary, PrimitiveLiteral::Binary(value)) => {
953 Ok(Arc::new(BinaryArray::new_scalar(value.as_slice())))
954 }
955 (PrimitiveType::Date, PrimitiveLiteral::Int(value)) => {
956 Ok(Arc::new(Date32Array::new_scalar(*value)))
957 }
958 (PrimitiveType::Timestamp, PrimitiveLiteral::Long(value)) => {
959 Ok(Arc::new(TimestampMicrosecondArray::new_scalar(*value)))
960 }
961 (PrimitiveType::Timestamptz, PrimitiveLiteral::Long(value)) => Ok(Arc::new(Scalar::new(
962 TimestampMicrosecondArray::new(vec![*value; 1].into(), None).with_timezone_utc(),
963 ))),
964 (PrimitiveType::TimestampNs, PrimitiveLiteral::Long(value)) => {
965 Ok(Arc::new(TimestampNanosecondArray::new_scalar(*value)))
966 }
967 (PrimitiveType::TimestamptzNs, PrimitiveLiteral::Long(value)) => Ok(Arc::new(Scalar::new(
968 TimestampNanosecondArray::new(vec![*value; 1].into(), None).with_timezone_utc(),
969 ))),
970 (PrimitiveType::Decimal { precision, scale }, PrimitiveLiteral::Int128(value)) => {
971 let array = Decimal128Array::from_value(*value, 1)
972 .with_precision_and_scale(*precision as _, *scale as _)
973 .unwrap();
974 Ok(Arc::new(Scalar::new(array)))
975 }
976 (PrimitiveType::Uuid, PrimitiveLiteral::UInt128(value)) => {
977 let bytes = Uuid::from_u128(*value).into_bytes();
978 let array = FixedSizeBinaryArray::try_from_iter(vec![bytes].into_iter()).unwrap();
979 Ok(Arc::new(Scalar::new(array)))
980 }
981 (PrimitiveType::Fixed(_), PrimitiveLiteral::Binary(value)) => {
982 let array = FixedSizeBinaryArray::try_from_iter(std::iter::once(value.as_slice()))
983 .map_err(|e| invalid_data!("FixedSizeBinary conversion failed").with_source(e))?;
984 Ok(Arc::new(Scalar::new(array)))
985 }
986
987 (primitive_type, _) => Err(Error::new(
988 ErrorKind::FeatureUnsupported,
989 format!("Converting datum from type {primitive_type:?} to arrow not supported yet."),
990 )),
991 }
992}
993
994pub(crate) fn get_parquet_stat_min_as_datum(
995 primitive_type: &PrimitiveType,
996 stats: &Statistics,
997) -> Result<Option<Datum>> {
998 Ok(match (primitive_type, stats) {
999 (PrimitiveType::Boolean, Statistics::Boolean(stats)) => {
1000 stats.min_opt().map(|val| Datum::bool(*val))
1001 }
1002 (PrimitiveType::Int, Statistics::Int32(stats)) => {
1003 stats.min_opt().map(|val| Datum::int(*val))
1004 }
1005 (PrimitiveType::Date, Statistics::Int32(stats)) => {
1006 stats.min_opt().map(|val| Datum::date(*val))
1007 }
1008 (PrimitiveType::Long, Statistics::Int64(stats)) => {
1009 stats.min_opt().map(|val| Datum::long(*val))
1010 }
1011 (PrimitiveType::Time, Statistics::Int64(stats)) => {
1012 let Some(val) = stats.min_opt() else {
1013 return Ok(None);
1014 };
1015
1016 Some(Datum::time_micros(*val)?)
1017 }
1018 (PrimitiveType::Timestamp, Statistics::Int64(stats)) => {
1019 stats.min_opt().map(|val| Datum::timestamp_micros(*val))
1020 }
1021 (PrimitiveType::Timestamptz, Statistics::Int64(stats)) => {
1022 stats.min_opt().map(|val| Datum::timestamptz_micros(*val))
1023 }
1024 (PrimitiveType::TimestampNs, Statistics::Int64(stats)) => {
1025 stats.min_opt().map(|val| Datum::timestamp_nanos(*val))
1026 }
1027 (PrimitiveType::TimestamptzNs, Statistics::Int64(stats)) => {
1028 stats.min_opt().map(|val| Datum::timestamptz_nanos(*val))
1029 }
1030 (PrimitiveType::Float, Statistics::Float(stats)) => {
1031 stats.min_opt().map(|val| Datum::float(*val))
1032 }
1033 (PrimitiveType::Double, Statistics::Double(stats)) => {
1034 stats.min_opt().map(|val| Datum::double(*val))
1035 }
1036 (PrimitiveType::String, Statistics::ByteArray(stats)) => {
1037 let Some(val) = stats.min_opt() else {
1038 return Ok(None);
1039 };
1040
1041 Some(Datum::string(val.as_utf8()?))
1042 }
1043 (
1044 PrimitiveType::Decimal {
1045 precision: _,
1046 scale: _,
1047 },
1048 Statistics::ByteArray(stats),
1049 ) => {
1050 let Some(bytes) = stats.min_bytes_opt() else {
1051 return Ok(None);
1052 };
1053 Some(Datum::new(
1054 primitive_type.clone(),
1055 PrimitiveLiteral::Int128(
1056 i128_from_be_bytes(bytes)
1059 .filter(|_| !bytes.is_empty())
1060 .ok_or_else(|| invalid_data!("Can't convert bytes to i128: {bytes:?}"))?,
1061 ),
1062 ))
1063 }
1064 (
1065 PrimitiveType::Decimal {
1066 precision: _,
1067 scale: _,
1068 },
1069 Statistics::FixedLenByteArray(stats),
1070 ) => {
1071 let Some(bytes) = stats.min_bytes_opt() else {
1072 return Ok(None);
1073 };
1074 Some(Datum::new(
1075 primitive_type.clone(),
1076 PrimitiveLiteral::Int128(
1077 i128_from_be_bytes(bytes)
1078 .ok_or_else(|| invalid_data!("Can't convert bytes to i128: {bytes:?}"))?,
1079 ),
1080 ))
1081 }
1082 (
1083 PrimitiveType::Decimal {
1084 precision: _,
1085 scale: _,
1086 },
1087 Statistics::Int32(stats),
1088 ) => stats.min_opt().map(|val| {
1089 Datum::new(
1090 primitive_type.clone(),
1091 PrimitiveLiteral::Int128(i128::from(*val)),
1092 )
1093 }),
1094
1095 (
1096 PrimitiveType::Decimal {
1097 precision: _,
1098 scale: _,
1099 },
1100 Statistics::Int64(stats),
1101 ) => stats.min_opt().map(|val| {
1102 Datum::new(
1103 primitive_type.clone(),
1104 PrimitiveLiteral::Int128(i128::from(*val)),
1105 )
1106 }),
1107 (PrimitiveType::Uuid, Statistics::FixedLenByteArray(stats)) => {
1108 let Some(bytes) = stats.min_bytes_opt() else {
1109 return Ok(None);
1110 };
1111 if bytes.len() != 16 {
1112 return Err(Error::new(
1113 ErrorKind::Unexpected,
1114 "Invalid length of uuid bytes.",
1115 ));
1116 }
1117 Some(Datum::uuid(Uuid::from_bytes(
1118 bytes[..16].try_into().unwrap(),
1119 )))
1120 }
1121 (PrimitiveType::Fixed(len), Statistics::FixedLenByteArray(stat)) => {
1122 let Some(bytes) = stat.min_bytes_opt() else {
1123 return Ok(None);
1124 };
1125 if bytes.len() != *len as usize {
1126 return Err(Error::new(
1127 ErrorKind::Unexpected,
1128 "Invalid length of fixed bytes.",
1129 ));
1130 }
1131 Some(Datum::fixed(bytes.to_vec()))
1132 }
1133 (PrimitiveType::Binary, Statistics::ByteArray(stat)) => {
1134 return Ok(stat
1135 .min_bytes_opt()
1136 .map(|bytes| Datum::binary(bytes.to_vec())));
1137 }
1138 _ => {
1139 return Ok(None);
1140 }
1141 })
1142}
1143
1144pub(crate) fn get_parquet_stat_max_as_datum(
1145 primitive_type: &PrimitiveType,
1146 stats: &Statistics,
1147) -> Result<Option<Datum>> {
1148 Ok(match (primitive_type, stats) {
1149 (PrimitiveType::Boolean, Statistics::Boolean(stats)) => {
1150 stats.max_opt().map(|val| Datum::bool(*val))
1151 }
1152 (PrimitiveType::Int, Statistics::Int32(stats)) => {
1153 stats.max_opt().map(|val| Datum::int(*val))
1154 }
1155 (PrimitiveType::Date, Statistics::Int32(stats)) => {
1156 stats.max_opt().map(|val| Datum::date(*val))
1157 }
1158 (PrimitiveType::Long, Statistics::Int64(stats)) => {
1159 stats.max_opt().map(|val| Datum::long(*val))
1160 }
1161 (PrimitiveType::Time, Statistics::Int64(stats)) => {
1162 let Some(val) = stats.max_opt() else {
1163 return Ok(None);
1164 };
1165
1166 Some(Datum::time_micros(*val)?)
1167 }
1168 (PrimitiveType::Timestamp, Statistics::Int64(stats)) => {
1169 stats.max_opt().map(|val| Datum::timestamp_micros(*val))
1170 }
1171 (PrimitiveType::Timestamptz, Statistics::Int64(stats)) => {
1172 stats.max_opt().map(|val| Datum::timestamptz_micros(*val))
1173 }
1174 (PrimitiveType::TimestampNs, Statistics::Int64(stats)) => {
1175 stats.max_opt().map(|val| Datum::timestamp_nanos(*val))
1176 }
1177 (PrimitiveType::TimestamptzNs, Statistics::Int64(stats)) => {
1178 stats.max_opt().map(|val| Datum::timestamptz_nanos(*val))
1179 }
1180 (PrimitiveType::Float, Statistics::Float(stats)) => {
1181 stats.max_opt().map(|val| Datum::float(*val))
1182 }
1183 (PrimitiveType::Double, Statistics::Double(stats)) => {
1184 stats.max_opt().map(|val| Datum::double(*val))
1185 }
1186 (PrimitiveType::String, Statistics::ByteArray(stats)) => {
1187 let Some(val) = stats.max_opt() else {
1188 return Ok(None);
1189 };
1190
1191 Some(Datum::string(val.as_utf8()?))
1192 }
1193 (
1194 PrimitiveType::Decimal {
1195 precision: _,
1196 scale: _,
1197 },
1198 Statistics::ByteArray(stats),
1199 ) => {
1200 let Some(bytes) = stats.max_bytes_opt() else {
1201 return Ok(None);
1202 };
1203 Some(Datum::new(
1204 primitive_type.clone(),
1205 PrimitiveLiteral::Int128(
1206 i128_from_be_bytes(bytes)
1209 .filter(|_| !bytes.is_empty())
1210 .ok_or_else(|| invalid_data!("Can't convert bytes to i128: {bytes:?}"))?,
1211 ),
1212 ))
1213 }
1214 (
1215 PrimitiveType::Decimal {
1216 precision: _,
1217 scale: _,
1218 },
1219 Statistics::FixedLenByteArray(stats),
1220 ) => {
1221 let Some(bytes) = stats.max_bytes_opt() else {
1222 return Ok(None);
1223 };
1224 Some(Datum::new(
1225 primitive_type.clone(),
1226 PrimitiveLiteral::Int128(
1227 i128_from_be_bytes(bytes)
1228 .ok_or_else(|| invalid_data!("Can't convert bytes to i128: {bytes:?}"))?,
1229 ),
1230 ))
1231 }
1232 (
1233 PrimitiveType::Decimal {
1234 precision: _,
1235 scale: _,
1236 },
1237 Statistics::Int32(stats),
1238 ) => stats.max_opt().map(|val| {
1239 Datum::new(
1240 primitive_type.clone(),
1241 PrimitiveLiteral::Int128(i128::from(*val)),
1242 )
1243 }),
1244
1245 (
1246 PrimitiveType::Decimal {
1247 precision: _,
1248 scale: _,
1249 },
1250 Statistics::Int64(stats),
1251 ) => stats.max_opt().map(|val| {
1252 Datum::new(
1253 primitive_type.clone(),
1254 PrimitiveLiteral::Int128(i128::from(*val)),
1255 )
1256 }),
1257 (PrimitiveType::Uuid, Statistics::FixedLenByteArray(stats)) => {
1258 let Some(bytes) = stats.max_bytes_opt() else {
1259 return Ok(None);
1260 };
1261 if bytes.len() != 16 {
1262 return Err(Error::new(
1263 ErrorKind::Unexpected,
1264 "Invalid length of uuid bytes.",
1265 ));
1266 }
1267 Some(Datum::uuid(Uuid::from_bytes(
1268 bytes[..16].try_into().unwrap(),
1269 )))
1270 }
1271 (PrimitiveType::Fixed(len), Statistics::FixedLenByteArray(stat)) => {
1272 let Some(bytes) = stat.max_bytes_opt() else {
1273 return Ok(None);
1274 };
1275 if bytes.len() != *len as usize {
1276 return Err(Error::new(
1277 ErrorKind::Unexpected,
1278 "Invalid length of fixed bytes.",
1279 ));
1280 }
1281 Some(Datum::fixed(bytes.to_vec()))
1282 }
1283 (PrimitiveType::Binary, Statistics::ByteArray(stat)) => {
1284 return Ok(stat
1285 .max_bytes_opt()
1286 .map(|bytes| Datum::binary(bytes.to_vec())));
1287 }
1288 _ => {
1289 return Ok(None);
1290 }
1291 })
1292}
1293
1294impl TryFrom<&ArrowSchema> for Schema {
1295 type Error = Error;
1296
1297 fn try_from(schema: &ArrowSchema) -> Result<Self> {
1298 arrow_schema_to_schema(schema)
1299 }
1300}
1301
1302impl TryFrom<&Schema> for ArrowSchema {
1303 type Error = Error;
1304
1305 fn try_from(schema: &Schema) -> Result<Self> {
1306 schema_to_arrow_schema(schema)
1307 }
1308}
1309
1310pub fn datum_to_arrow_type_with_ree(datum: &Datum) -> DataType {
1332 primitive_type_to_arrow_type_with_ree(datum.data_type())
1333}
1334
1335pub(crate) fn primitive_type_to_arrow_type_with_ree(primitive_type: &PrimitiveType) -> DataType {
1339 let make_ree = |values_type: DataType| -> DataType {
1343 let run_ends_field = Arc::new(Field::new("run_ends", DataType::Int32, false));
1344 let values_field = Arc::new(Field::new("values", values_type, true));
1345 DataType::RunEndEncoded(run_ends_field, values_field)
1346 };
1347
1348 match primitive_type {
1349 PrimitiveType::Unknown => make_ree(DataType::Null),
1350 PrimitiveType::Boolean => make_ree(DataType::Boolean),
1351 PrimitiveType::Int => make_ree(DataType::Int32),
1352 PrimitiveType::Long => make_ree(DataType::Int64),
1353 PrimitiveType::Float => make_ree(DataType::Float32),
1354 PrimitiveType::Double => make_ree(DataType::Float64),
1355 PrimitiveType::Date => make_ree(DataType::Date32),
1356 PrimitiveType::Time => make_ree(DataType::Int64),
1357 PrimitiveType::Timestamp => make_ree(DataType::Int64),
1358 PrimitiveType::Timestamptz => make_ree(DataType::Int64),
1359 PrimitiveType::TimestampNs => make_ree(DataType::Int64),
1360 PrimitiveType::TimestamptzNs => make_ree(DataType::Int64),
1361 PrimitiveType::String => make_ree(DataType::Utf8),
1362 PrimitiveType::Uuid => make_ree(DataType::Binary),
1363 PrimitiveType::Fixed(_) => make_ree(DataType::Binary),
1364 PrimitiveType::Binary => make_ree(DataType::Binary),
1365 PrimitiveType::Geometry(_) | PrimitiveType::Geography(_) => make_ree(DataType::LargeBinary),
1368 PrimitiveType::Decimal { precision, scale } => {
1369 make_ree(DataType::Decimal128(*precision as u8, *scale as i8))
1370 }
1371 }
1372}
1373
1374struct MetadataStripVisitor {
1380 pending_fields: Vec<PendingField>,
1382}
1383
1384struct PendingField {
1386 name: String,
1387 nullable: bool,
1388}
1389
1390impl MetadataStripVisitor {
1391 fn new() -> Self {
1392 Self {
1393 pending_fields: Vec::new(),
1394 }
1395 }
1396
1397 fn start_field(&mut self, name: &str, nullable: bool) -> Result<()> {
1399 self.pending_fields.push(PendingField {
1400 name: name.to_string(),
1401 nullable,
1402 });
1403 Ok(())
1404 }
1405
1406 fn finish_field(&mut self, data_type: DataType, kind: &str) -> Result<Field> {
1409 let PendingField { name, nullable } = self.pending_fields.pop().ok_or_else(|| {
1410 Error::new(
1411 ErrorKind::Unexpected,
1412 format!("Field stack underflow in {kind}"),
1413 )
1414 })?;
1415 Ok(Field::new(name, data_type, nullable))
1416 }
1417}
1418
1419impl ArrowSchemaVisitor for MetadataStripVisitor {
1420 type T = Field;
1421 type U = ArrowSchema;
1422
1423 fn before_field(&mut self, field: &FieldRef) -> Result<()> {
1424 self.start_field(field.name(), field.is_nullable())
1425 }
1426
1427 fn before_list_element(&mut self, field: &FieldRef) -> Result<()> {
1428 self.start_field(LIST_FIELD_NAME, field.is_nullable())
1429 }
1430
1431 fn before_map_key(&mut self, _field: &FieldRef) -> Result<()> {
1432 self.start_field(MAP_KEY_FIELD_NAME, false)
1434 }
1435
1436 fn before_map_value(&mut self, field: &FieldRef) -> Result<()> {
1437 self.start_field(MAP_VALUE_FIELD_NAME, field.is_nullable())
1438 }
1439
1440 fn schema(&mut self, _schema: &ArrowSchema, values: Vec<Self::T>) -> Result<Self::U> {
1441 if !self.pending_fields.is_empty() {
1442 return Err(Error::new(
1443 ErrorKind::Unexpected,
1444 format!(
1445 "{} fields left on the field stack after traversal",
1446 self.pending_fields.len()
1447 ),
1448 ));
1449 }
1450 Ok(ArrowSchema::new(values))
1451 }
1452
1453 fn r#struct(&mut self, _fields: &Fields, results: Vec<Self::T>) -> Result<Self::T> {
1454 self.finish_field(DataType::Struct(Fields::from(results)), "struct")
1455 }
1456
1457 fn list(&mut self, list: &DataType, value: Self::T) -> Result<Self::T> {
1458 let list_type = match list {
1459 DataType::List(_) => DataType::List(Arc::new(value)),
1460 DataType::LargeList(_) => DataType::LargeList(Arc::new(value)),
1461 DataType::FixedSizeList(_, size) => DataType::FixedSizeList(Arc::new(value), *size),
1462 _ => {
1463 return Err(Error::new(
1464 ErrorKind::Unexpected,
1465 format!("Expected list type, got {list}"),
1466 ));
1467 }
1468 };
1469
1470 self.finish_field(list_type, "list")
1471 }
1472
1473 fn map(&mut self, map: &DataType, key_value: Self::T, value: Self::T) -> Result<Self::T> {
1474 let struct_field = Field::new(
1476 DEFAULT_MAP_FIELD_NAME,
1477 DataType::Struct(Fields::from(vec![key_value, value])),
1478 false,
1479 );
1480
1481 let sorted = match map {
1483 DataType::Map(_, sorted) => *sorted,
1484 _ => {
1485 return Err(Error::new(
1486 ErrorKind::Unexpected,
1487 format!("Expected map type, got {map}"),
1488 ));
1489 }
1490 };
1491
1492 self.finish_field(DataType::Map(Arc::new(struct_field), sorted), "map")
1493 }
1494
1495 fn primitive(&mut self, p: &DataType) -> Result<Self::T> {
1496 self.finish_field(p.clone(), "primitive")
1497 }
1498}
1499
1500pub fn strip_metadata_from_schema(schema: &ArrowSchema) -> Result<ArrowSchema> {
1545 let mut visitor = MetadataStripVisitor::new();
1546 visit_schema(schema, &mut visitor)
1547}
1548
1549#[cfg(test)]
1550mod tests {
1551 use std::collections::HashMap;
1552 use std::sync::Arc;
1553
1554 use arrow_schema::extension::ExtensionType;
1555 use arrow_schema::{DataType, Field, Schema as ArrowSchema, TimeUnit};
1556 use parquet_geospatial::WkbEdges;
1557
1558 use super::*;
1559 use crate::spec::decimal_utils::decimal_new;
1560 use crate::spec::geospatial::{
1561 DEFAULT_GEOSPATIAL_CRS, EdgeInterpolationAlgorithm as IcebergEdgeInterpolationAlgorithm,
1562 };
1563 use crate::spec::{Literal, Schema};
1564
1565 fn simple_field(name: &str, ty: DataType, nullable: bool, value: &str) -> Field {
1567 Field::new(name, ty, nullable).with_metadata(HashMap::from([(
1568 PARQUET_FIELD_ID_META_KEY.to_string(),
1569 value.to_string(),
1570 )]))
1571 }
1572
1573 fn arrow_schema_for_arrow_schema_to_schema_test() -> ArrowSchema {
1574 let fields = Fields::from(vec![
1575 simple_field("key", DataType::Int32, false, "28"),
1576 simple_field("value", DataType::Utf8, true, "29"),
1577 ]);
1578
1579 let r#struct = DataType::Struct(fields);
1580 let map = DataType::Map(
1581 Arc::new(simple_field(DEFAULT_MAP_FIELD_NAME, r#struct, false, "17")),
1582 false,
1583 );
1584 let dictionary = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8));
1585
1586 let fields = Fields::from(vec![
1587 simple_field("aa", DataType::Int32, false, "18"),
1588 simple_field("bb", DataType::Utf8, true, "19"),
1589 simple_field(
1590 "cc",
1591 DataType::Timestamp(TimeUnit::Microsecond, None),
1592 false,
1593 "20",
1594 ),
1595 ]);
1596
1597 let r#struct = DataType::Struct(fields);
1598
1599 ArrowSchema::new(vec![
1600 simple_field("a", DataType::Int32, false, "2"),
1601 simple_field("b", DataType::Int64, false, "1"),
1602 simple_field("c", DataType::Utf8, false, "3"),
1603 simple_field("n", DataType::Utf8, false, "21"),
1604 simple_field(
1605 "d",
1606 DataType::Timestamp(TimeUnit::Microsecond, None),
1607 true,
1608 "4",
1609 ),
1610 simple_field("e", DataType::Boolean, true, "6"),
1611 simple_field("f", DataType::Float32, false, "5"),
1612 simple_field("g", DataType::Float64, false, "7"),
1613 simple_field("p", DataType::Decimal128(10, 2), false, "27"),
1614 simple_field("h", DataType::Date32, false, "8"),
1615 simple_field("i", DataType::Time64(TimeUnit::Microsecond), false, "9"),
1616 simple_field(
1617 "j",
1618 DataType::Timestamp(TimeUnit::Microsecond, Some("UTC".into())),
1619 false,
1620 "10",
1621 ),
1622 simple_field(
1623 "k",
1624 DataType::Timestamp(TimeUnit::Microsecond, Some("+00:00".into())),
1625 false,
1626 "12",
1627 ),
1628 simple_field("l", DataType::Binary, false, "13"),
1629 simple_field("o", DataType::LargeBinary, false, "22"),
1630 simple_field("m", DataType::FixedSizeBinary(10), false, "11"),
1631 simple_field(
1632 "list",
1633 DataType::List(Arc::new(simple_field(
1634 "element",
1635 DataType::Int32,
1636 false,
1637 "15",
1638 ))),
1639 true,
1640 "14",
1641 ),
1642 simple_field(
1643 "large_list",
1644 DataType::LargeList(Arc::new(simple_field(
1645 "element",
1646 DataType::Utf8,
1647 false,
1648 "23",
1649 ))),
1650 true,
1651 "24",
1652 ),
1653 simple_field(
1654 "fixed_list",
1655 DataType::FixedSizeList(
1656 Arc::new(simple_field("element", DataType::Binary, false, "26")),
1657 10,
1658 ),
1659 true,
1660 "25",
1661 ),
1662 simple_field("map", map, false, "16"),
1663 simple_field("struct", r#struct, false, "17"),
1664 simple_field("dictionary", dictionary, false, "30"),
1665 ])
1666 }
1667
1668 fn iceberg_schema_for_arrow_schema_to_schema_test() -> Schema {
1669 let schema_json = r#"{
1670 "type":"struct",
1671 "schema-id":0,
1672 "fields":[
1673 {
1674 "id":2,
1675 "name":"a",
1676 "required":true,
1677 "type":"int"
1678 },
1679 {
1680 "id":1,
1681 "name":"b",
1682 "required":true,
1683 "type":"long"
1684 },
1685 {
1686 "id":3,
1687 "name":"c",
1688 "required":true,
1689 "type":"string"
1690 },
1691 {
1692 "id":21,
1693 "name":"n",
1694 "required":true,
1695 "type":"string"
1696 },
1697 {
1698 "id":4,
1699 "name":"d",
1700 "required":false,
1701 "type":"timestamp"
1702 },
1703 {
1704 "id":6,
1705 "name":"e",
1706 "required":false,
1707 "type":"boolean"
1708 },
1709 {
1710 "id":5,
1711 "name":"f",
1712 "required":true,
1713 "type":"float"
1714 },
1715 {
1716 "id":7,
1717 "name":"g",
1718 "required":true,
1719 "type":"double"
1720 },
1721 {
1722 "id":27,
1723 "name":"p",
1724 "required":true,
1725 "type":"decimal(10,2)"
1726 },
1727 {
1728 "id":8,
1729 "name":"h",
1730 "required":true,
1731 "type":"date"
1732 },
1733 {
1734 "id":9,
1735 "name":"i",
1736 "required":true,
1737 "type":"time"
1738 },
1739 {
1740 "id":10,
1741 "name":"j",
1742 "required":true,
1743 "type":"timestamptz"
1744 },
1745 {
1746 "id":12,
1747 "name":"k",
1748 "required":true,
1749 "type":"timestamptz"
1750 },
1751 {
1752 "id":13,
1753 "name":"l",
1754 "required":true,
1755 "type":"binary"
1756 },
1757 {
1758 "id":22,
1759 "name":"o",
1760 "required":true,
1761 "type":"binary"
1762 },
1763 {
1764 "id":11,
1765 "name":"m",
1766 "required":true,
1767 "type":"fixed[10]"
1768 },
1769 {
1770 "id":14,
1771 "name":"list",
1772 "required": false,
1773 "type": {
1774 "type": "list",
1775 "element-id": 15,
1776 "element-required": true,
1777 "element": "int"
1778 }
1779 },
1780 {
1781 "id":24,
1782 "name":"large_list",
1783 "required": false,
1784 "type": {
1785 "type": "list",
1786 "element-id": 23,
1787 "element-required": true,
1788 "element": "string"
1789 }
1790 },
1791 {
1792 "id":25,
1793 "name":"fixed_list",
1794 "required": false,
1795 "type": {
1796 "type": "list",
1797 "element-id": 26,
1798 "element-required": true,
1799 "element": "binary"
1800 }
1801 },
1802 {
1803 "id":16,
1804 "name":"map",
1805 "required": true,
1806 "type": {
1807 "type": "map",
1808 "key-id": 28,
1809 "key": "int",
1810 "value-id": 29,
1811 "value-required": false,
1812 "value": "string"
1813 }
1814 },
1815 {
1816 "id":17,
1817 "name":"struct",
1818 "required": true,
1819 "type": {
1820 "type": "struct",
1821 "fields": [
1822 {
1823 "id":18,
1824 "name":"aa",
1825 "required":true,
1826 "type":"int"
1827 },
1828 {
1829 "id":19,
1830 "name":"bb",
1831 "required":false,
1832 "type":"string"
1833 },
1834 {
1835 "id":20,
1836 "name":"cc",
1837 "required":true,
1838 "type":"timestamp"
1839 }
1840 ]
1841 }
1842 },
1843 {
1844 "id":30,
1845 "name":"dictionary",
1846 "required":true,
1847 "type":"string"
1848 }
1849 ],
1850 "identifier-field-ids":[]
1851 }"#;
1852
1853 let schema: Schema = serde_json::from_str(schema_json).unwrap();
1854 schema
1855 }
1856
1857 #[test]
1858 fn test_arrow_schema_to_schema() {
1859 let arrow_schema = arrow_schema_for_arrow_schema_to_schema_test();
1860 let schema = iceberg_schema_for_arrow_schema_to_schema_test();
1861 let converted_schema = arrow_schema_to_schema(&arrow_schema).unwrap();
1862 pretty_assertions::assert_eq!(converted_schema, schema);
1863 }
1864
1865 #[test]
1866 fn test_converting_uuid_from_arrow_to_iceberg_to_arrow_should_give_uuid() {
1867 let arrow_schema = ArrowSchema::new(vec![
1868 simple_field("uuid_field", DataType::FixedSizeBinary(16), false, "1")
1869 .with_extension_type(UuidExtensionType),
1870 ]);
1871 let output =
1872 schema_to_arrow_schema(&arrow_schema_to_schema(&arrow_schema).unwrap()).unwrap();
1873
1874 assert_eq!(arrow_schema, output);
1875 }
1876
1877 #[test]
1878 fn test_converting_uuid_from_iceberg_to_arrow_to_iceberg_should_give_uuid() {
1879 let iceberg_schema = Schema::builder()
1880 .with_fields(vec![
1881 NestedField::optional(1, "uuid_field", Type::Primitive(PrimitiveType::Uuid)).into(),
1882 ])
1883 .build()
1884 .unwrap();
1885 let output =
1886 arrow_schema_to_schema(&schema_to_arrow_schema(&iceberg_schema).unwrap()).unwrap();
1887
1888 assert_eq!(iceberg_schema, output);
1889 }
1890
1891 #[test]
1892 fn test_arrow_schema_to_schema_should_convert_uuid_when_fixed_size_binary() {
1893 let converted_schema = arrow_schema_to_schema(&ArrowSchema::new(vec![
1894 simple_field("uuid_field", DataType::FixedSizeBinary(16), false, "1")
1895 .with_extension_type(UuidExtensionType),
1896 ]))
1897 .unwrap();
1898
1899 let expected = Schema::builder()
1900 .with_fields([NestedField::required(
1901 1,
1902 "uuid_field",
1903 Type::Primitive(PrimitiveType::Uuid),
1904 )
1905 .into()])
1906 .build()
1907 .unwrap();
1908
1909 pretty_assertions::assert_eq!(expected, converted_schema);
1910 }
1911
1912 #[test]
1913 fn test_arrow_schema_to_schema_should_reject_uuid_when_not_a_fixed_size_binary() {
1914 let field = simple_field_with_uuid_ext(DataType::Utf8);
1917
1918 let error = arrow_schema_to_schema(&ArrowSchema::new(vec![field])).unwrap_err();
1919
1920 pretty_assertions::assert_eq!(
1921 "arrow.uuid extension requires FixedSizeBinary(16) storage, found Utf8",
1922 error.message()
1923 );
1924 }
1925
1926 #[test]
1927 fn test_arrow_schema_to_schema_should_reject_uuid_when_fixed_size_binary_that_is_less_than_16()
1928 {
1929 let field = simple_field_with_uuid_ext(DataType::FixedSizeBinary(8));
1930 let error = arrow_schema_to_schema(&ArrowSchema::new(vec![field])).unwrap_err();
1931
1932 pretty_assertions::assert_eq!(
1933 "arrow.uuid extension requires FixedSizeBinary(16) storage, found FixedSizeBinary(8)",
1934 error.message()
1935 );
1936 }
1937
1938 #[test]
1939 fn test_arrow_schema_to_schema_should_reject_uuid_when_fixed_size_binary_that_is_more_than_16()
1940 {
1941 let field = simple_field_with_uuid_ext(DataType::FixedSizeBinary(17));
1942 let error = arrow_schema_to_schema(&ArrowSchema::new(vec![field])).unwrap_err();
1943
1944 pretty_assertions::assert_eq!(
1945 "arrow.uuid extension requires FixedSizeBinary(16) storage, found FixedSizeBinary(17)",
1946 error.message()
1947 );
1948 }
1949
1950 fn simple_field_with_uuid_ext(field_data_type: DataType) -> Field {
1951 let mut field = simple_field("uuid_field", DataType::FixedSizeBinary(16), false, "1")
1952 .with_extension_type(UuidExtensionType);
1953
1954 field = field.with_data_type(field_data_type);
1960
1961 field
1962 }
1963
1964 fn arrow_schema_for_schema_to_arrow_schema_test() -> ArrowSchema {
1965 let fields = Fields::from(vec![
1966 simple_field("key", DataType::Int32, false, "28"),
1967 simple_field("value", DataType::Utf8, true, "29"),
1968 ]);
1969
1970 let r#struct = DataType::Struct(fields);
1971 let map = DataType::Map(
1972 Arc::new(Field::new(DEFAULT_MAP_FIELD_NAME, r#struct, false)),
1973 false,
1974 );
1975
1976 let fields = Fields::from(vec![
1977 simple_field("aa", DataType::Int32, false, "18"),
1978 simple_field("bb", DataType::Utf8, true, "19"),
1979 simple_field(
1980 "cc",
1981 DataType::Timestamp(TimeUnit::Microsecond, None),
1982 false,
1983 "20",
1984 ),
1985 ]);
1986
1987 let r#struct = DataType::Struct(fields);
1988
1989 ArrowSchema::new(vec![
1990 simple_field("a", DataType::Int32, false, "2"),
1991 simple_field("b", DataType::Int64, false, "1"),
1992 simple_field("c", DataType::Utf8, false, "3"),
1993 simple_field("n", DataType::Utf8, false, "21"),
1994 simple_field(
1995 "d",
1996 DataType::Timestamp(TimeUnit::Microsecond, None),
1997 true,
1998 "4",
1999 ),
2000 simple_field("e", DataType::Boolean, true, "6"),
2001 simple_field("f", DataType::Float32, false, "5"),
2002 simple_field("g", DataType::Float64, false, "7"),
2003 simple_field("p", DataType::Decimal128(10, 2), false, "27"),
2004 simple_field("h", DataType::Date32, false, "8"),
2005 simple_field("i", DataType::Time64(TimeUnit::Microsecond), false, "9"),
2006 simple_field(
2007 "j",
2008 DataType::Timestamp(TimeUnit::Microsecond, Some("+00:00".into())),
2009 false,
2010 "10",
2011 ),
2012 simple_field(
2013 "k",
2014 DataType::Timestamp(TimeUnit::Microsecond, Some("+00:00".into())),
2015 false,
2016 "12",
2017 ),
2018 simple_field("l", DataType::LargeBinary, false, "13"),
2019 simple_field("o", DataType::LargeBinary, false, "22"),
2020 simple_field("m", DataType::FixedSizeBinary(10), false, "11"),
2021 simple_field(
2022 "list",
2023 DataType::List(Arc::new(simple_field(
2024 "element",
2025 DataType::Int32,
2026 false,
2027 "15",
2028 ))),
2029 true,
2030 "14",
2031 ),
2032 simple_field(
2033 "large_list",
2034 DataType::List(Arc::new(simple_field(
2035 "element",
2036 DataType::Utf8,
2037 false,
2038 "23",
2039 ))),
2040 true,
2041 "24",
2042 ),
2043 simple_field(
2044 "fixed_list",
2045 DataType::List(Arc::new(simple_field(
2046 "element",
2047 DataType::LargeBinary,
2048 false,
2049 "26",
2050 ))),
2051 true,
2052 "25",
2053 ),
2054 simple_field("map", map, false, "16"),
2055 simple_field("struct", r#struct, false, "17"),
2056 simple_field("uuid", DataType::FixedSizeBinary(16), false, "30")
2057 .with_extension_type(UuidExtensionType),
2058 Field::new(
2059 "v",
2060 DataType::Struct(Fields::from(vec![
2061 Field::new("metadata", DataType::Binary, false),
2062 Field::new("value", DataType::Binary, true),
2063 ])),
2064 true,
2065 )
2066 .with_metadata(HashMap::from([
2067 (PARQUET_FIELD_ID_META_KEY.to_string(), "31".to_string()),
2068 (
2069 arrow_schema::extension::EXTENSION_TYPE_NAME_KEY.to_string(),
2070 "arrow.parquet.variant".to_string(),
2071 ),
2072 ])),
2073 ])
2074 }
2075
2076 fn iceberg_schema_for_schema_to_arrow_schema() -> Schema {
2077 let schema_json = r#"{
2078 "type":"struct",
2079 "schema-id":0,
2080 "fields":[
2081 {
2082 "id":2,
2083 "name":"a",
2084 "required":true,
2085 "type":"int"
2086 },
2087 {
2088 "id":1,
2089 "name":"b",
2090 "required":true,
2091 "type":"long"
2092 },
2093 {
2094 "id":3,
2095 "name":"c",
2096 "required":true,
2097 "type":"string"
2098 },
2099 {
2100 "id":21,
2101 "name":"n",
2102 "required":true,
2103 "type":"string"
2104 },
2105 {
2106 "id":4,
2107 "name":"d",
2108 "required":false,
2109 "type":"timestamp"
2110 },
2111 {
2112 "id":6,
2113 "name":"e",
2114 "required":false,
2115 "type":"boolean"
2116 },
2117 {
2118 "id":5,
2119 "name":"f",
2120 "required":true,
2121 "type":"float"
2122 },
2123 {
2124 "id":7,
2125 "name":"g",
2126 "required":true,
2127 "type":"double"
2128 },
2129 {
2130 "id":27,
2131 "name":"p",
2132 "required":true,
2133 "type":"decimal(10,2)"
2134 },
2135 {
2136 "id":8,
2137 "name":"h",
2138 "required":true,
2139 "type":"date"
2140 },
2141 {
2142 "id":9,
2143 "name":"i",
2144 "required":true,
2145 "type":"time"
2146 },
2147 {
2148 "id":10,
2149 "name":"j",
2150 "required":true,
2151 "type":"timestamptz"
2152 },
2153 {
2154 "id":12,
2155 "name":"k",
2156 "required":true,
2157 "type":"timestamptz"
2158 },
2159 {
2160 "id":13,
2161 "name":"l",
2162 "required":true,
2163 "type":"binary"
2164 },
2165 {
2166 "id":22,
2167 "name":"o",
2168 "required":true,
2169 "type":"binary"
2170 },
2171 {
2172 "id":11,
2173 "name":"m",
2174 "required":true,
2175 "type":"fixed[10]"
2176 },
2177 {
2178 "id":14,
2179 "name":"list",
2180 "required": false,
2181 "type": {
2182 "type": "list",
2183 "element-id": 15,
2184 "element-required": true,
2185 "element": "int"
2186 }
2187 },
2188 {
2189 "id":24,
2190 "name":"large_list",
2191 "required": false,
2192 "type": {
2193 "type": "list",
2194 "element-id": 23,
2195 "element-required": true,
2196 "element": "string"
2197 }
2198 },
2199 {
2200 "id":25,
2201 "name":"fixed_list",
2202 "required": false,
2203 "type": {
2204 "type": "list",
2205 "element-id": 26,
2206 "element-required": true,
2207 "element": "binary"
2208 }
2209 },
2210 {
2211 "id":16,
2212 "name":"map",
2213 "required": true,
2214 "type": {
2215 "type": "map",
2216 "key-id": 28,
2217 "key": "int",
2218 "value-id": 29,
2219 "value-required": false,
2220 "value": "string"
2221 }
2222 },
2223 {
2224 "id":17,
2225 "name":"struct",
2226 "required": true,
2227 "type": {
2228 "type": "struct",
2229 "fields": [
2230 {
2231 "id":18,
2232 "name":"aa",
2233 "required":true,
2234 "type":"int"
2235 },
2236 {
2237 "id":19,
2238 "name":"bb",
2239 "required":false,
2240 "type":"string"
2241 },
2242 {
2243 "id":20,
2244 "name":"cc",
2245 "required":true,
2246 "type":"timestamp"
2247 }
2248 ]
2249 }
2250 },
2251 {
2252 "id":30,
2253 "name":"uuid",
2254 "required":true,
2255 "type":"uuid"
2256 },
2257 {
2258 "id":31,
2259 "name":"v",
2260 "required":false,
2261 "type":"variant"
2262 }
2263 ],
2264 "identifier-field-ids":[]
2265 }"#;
2266
2267 let schema: Schema = serde_json::from_str(schema_json).unwrap();
2268 schema
2269 }
2270
2271 #[test]
2272 fn test_schema_to_arrow_schema() {
2273 let expected_arrow_schema = arrow_schema_for_schema_to_arrow_schema_test();
2274 let schema = iceberg_schema_for_schema_to_arrow_schema();
2275 let converted_arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2276 assert_eq!(converted_arrow_schema, expected_arrow_schema);
2277 }
2278
2279 #[test]
2280 fn test_variant_type_to_arrow_type() {
2281 let arrow_type = type_to_arrow_type(&Type::Variant(VariantType)).unwrap();
2284 assert_eq!(
2285 arrow_type,
2286 DataType::Struct(Fields::from(vec![
2287 Field::new("metadata", DataType::Binary, false),
2288 Field::new("value", DataType::Binary, true),
2289 ]))
2290 );
2291 }
2292
2293 #[test]
2294 fn test_variant_field_carries_arrow_extension_type() {
2295 let schema = Schema::builder()
2298 .with_fields(vec![
2299 NestedField::optional(1, "v", Type::Variant(VariantType)).into(),
2300 ])
2301 .build()
2302 .unwrap();
2303
2304 let arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2305 let field = arrow_schema.field_with_name("v").unwrap();
2306
2307 assert_eq!(field.extension_type_name(), Some("arrow.parquet.variant"));
2308 assert_eq!(
2310 field.metadata().get(PARQUET_FIELD_ID_META_KEY),
2311 Some(&"1".to_string())
2312 );
2313 assert_eq!(
2314 field.data_type(),
2315 &DataType::Struct(Fields::from(vec![
2316 Field::new("metadata", DataType::Binary, false),
2317 Field::new("value", DataType::Binary, true),
2318 ]))
2319 );
2320 }
2321
2322 #[test]
2323 fn test_schema_to_arrow_schema_should_convert_uuid_to_arrow_uuid() {
2324 let schema = Schema::builder()
2325 .with_fields(vec![
2326 NestedField::optional(1, "uuid_field", Type::Primitive(PrimitiveType::Uuid)).into(),
2327 ])
2328 .build()
2329 .unwrap();
2330
2331 let arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2332 let field = arrow_schema.field_with_name("uuid_field").unwrap();
2333
2334 assert_eq!(field.extension_type_name(), Some(UuidExtensionType::NAME));
2335 assert_eq!(
2337 field.metadata().get(PARQUET_FIELD_ID_META_KEY),
2338 Some(&"1".to_string())
2339 );
2340 assert_eq!(*field.data_type(), DataType::FixedSizeBinary(16));
2341 }
2342
2343 #[test]
2344 fn test_variant_nested_in_list_and_map_carries_arrow_extension_type() {
2345 let schema = Schema::builder()
2349 .with_fields(vec![
2350 NestedField::optional(
2351 1,
2352 "l",
2353 Type::List(ListType::new(
2354 NestedField::optional(2, "element", Type::Variant(VariantType)).into(),
2355 )),
2356 )
2357 .into(),
2358 NestedField::optional(
2359 3,
2360 "m",
2361 Type::Map(MapType::new(
2362 NestedField::map_key_element(4, Type::Primitive(PrimitiveType::String))
2363 .into(),
2364 NestedField::map_value_element(5, Type::Variant(VariantType), false).into(),
2365 )),
2366 )
2367 .into(),
2368 ])
2369 .build()
2370 .unwrap();
2371
2372 let arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2373
2374 let DataType::List(element) = arrow_schema.field_with_name("l").unwrap().data_type() else {
2375 panic!("expected a list");
2376 };
2377 assert_eq!(element.extension_type_name(), Some("arrow.parquet.variant"));
2378
2379 let DataType::Map(entries, _) = arrow_schema.field_with_name("m").unwrap().data_type()
2380 else {
2381 panic!("expected a map");
2382 };
2383 let DataType::Struct(kv) = entries.data_type() else {
2384 panic!("expected a key_value struct");
2385 };
2386 let value = kv.iter().find(|f| f.name() == "value").unwrap();
2387 assert_eq!(value.extension_type_name(), Some("arrow.parquet.variant"));
2388 }
2389
2390 fn variant_storage() -> DataType {
2393 DataType::Struct(Fields::from(vec![
2394 Field::new("metadata", DataType::Binary, false),
2395 Field::new("value", DataType::Binary, true),
2396 ]))
2397 }
2398
2399 #[test]
2400 fn test_variant_arrow_field_folds_to_iceberg_variant() {
2401 let field = simple_field("v", variant_storage(), true, "1")
2405 .with_extension_type(VariantExtensionType);
2406 let arrow_schema = ArrowSchema::new(vec![field]);
2407
2408 let converted = arrow_schema_to_schema(&arrow_schema).unwrap();
2409 let expected = Schema::builder()
2410 .with_fields(vec![
2411 NestedField::optional(1, "v", Type::Variant(VariantType)).into(),
2412 ])
2413 .build()
2414 .unwrap();
2415 pretty_assertions::assert_eq!(converted, expected);
2416 }
2417
2418 #[test]
2419 fn test_variant_schema_round_trips() {
2420 let schema = Schema::builder()
2423 .with_fields(vec![
2424 NestedField::optional(1, "v", Type::Variant(VariantType)).into(),
2425 NestedField::optional(
2426 2,
2427 "s",
2428 Type::Struct(StructType::new(vec![
2429 NestedField::optional(3, "sv", Type::Variant(VariantType)).into(),
2430 ])),
2431 )
2432 .into(),
2433 NestedField::optional(
2434 4,
2435 "l",
2436 Type::List(ListType::new(
2437 NestedField::optional(5, "element", Type::Variant(VariantType)).into(),
2438 )),
2439 )
2440 .into(),
2441 NestedField::optional(
2442 6,
2443 "m",
2444 Type::Map(MapType::new(
2445 NestedField::map_key_element(7, Type::Primitive(PrimitiveType::String))
2446 .into(),
2447 NestedField::map_value_element(8, Type::Variant(VariantType), false).into(),
2448 )),
2449 )
2450 .into(),
2451 ])
2452 .build()
2453 .unwrap();
2454
2455 let arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2456 let round_tripped = arrow_schema_to_schema(&arrow_schema).unwrap();
2457 pretty_assertions::assert_eq!(round_tripped, schema);
2458 }
2459
2460 #[test]
2461 fn test_variant_recognized_with_auto_assigned_ids() {
2462 let field =
2465 Field::new("v", variant_storage(), true).with_extension_type(VariantExtensionType);
2466 let arrow_schema = ArrowSchema::new(vec![field]);
2467
2468 let converted = arrow_schema_to_schema_auto_assign_ids(&arrow_schema).unwrap();
2469 let expected = Schema::builder()
2470 .with_fields(vec![
2471 NestedField::optional(1, "v", Type::Variant(VariantType)).into(),
2472 ])
2473 .build()
2474 .unwrap();
2475 pretty_assertions::assert_eq!(converted, expected);
2476 }
2477
2478 #[test]
2479 fn test_variant_extension_on_non_struct_storage_is_rejected() {
2480 let field = Field::new("v", DataType::Int32, true).with_metadata(HashMap::from([
2483 (PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string()),
2484 (
2485 arrow_schema::extension::EXTENSION_TYPE_NAME_KEY.to_string(),
2486 VariantExtensionType::NAME.to_string(),
2487 ),
2488 ]));
2489 let arrow_schema = ArrowSchema::new(vec![field]);
2490
2491 let err = arrow_schema_to_schema(&arrow_schema).unwrap_err();
2492 assert!(
2493 err.to_string().contains("requires Struct storage"),
2494 "unexpected error: {err}"
2495 );
2496 }
2497
2498 #[test]
2499 fn test_geospatial_arrow_schema_roundtrip() {
2500 let schema = Schema::builder()
2501 .with_schema_id(1)
2502 .with_fields(vec![
2503 NestedField::required(
2504 1,
2505 "geom",
2506 Type::Primitive(PrimitiveType::Geometry(
2507 GeometryType::new(Some("EPSG:3857".to_string())).unwrap(),
2508 )),
2509 )
2510 .into(),
2511 NestedField::optional(
2512 2,
2513 "geog",
2514 Type::Primitive(PrimitiveType::Geography(
2515 GeographyType::new(
2516 Some("OGC:CRS27".to_string()),
2517 IcebergEdgeInterpolationAlgorithm::Karney,
2518 )
2519 .unwrap(),
2520 )),
2521 )
2522 .into(),
2523 NestedField::optional(
2524 3,
2525 "geom_list",
2526 Type::List(ListType::new(
2527 NestedField::list_element(
2528 4,
2529 Type::Primitive(PrimitiveType::Geometry(GeometryType::default())),
2530 true,
2531 )
2532 .into(),
2533 )),
2534 )
2535 .into(),
2536 ])
2537 .build()
2538 .unwrap();
2539
2540 let arrow_schema = schema_to_arrow_schema(&schema).unwrap();
2541 let geom = arrow_schema.field(0);
2542 assert_eq!(geom.data_type(), &DataType::LargeBinary);
2543
2544 let geog = arrow_schema.field(1);
2545 let geog_wkb = geog.try_extension_type::<WkbType>().unwrap();
2546 assert_eq!(geog_wkb.metadata().algorithm, Some(WkbEdges::Karney));
2547 let geog_metadata: serde_json::Value =
2548 serde_json::from_str(&geog_wkb.serialize_metadata().unwrap()).unwrap();
2549 assert_eq!(geog_metadata.get("edges").unwrap(), "karney");
2550 assert!(geog_metadata.get("algorithm").is_none());
2551
2552 let list = arrow_schema.field(2);
2553 let DataType::List(element) = list.data_type() else {
2554 panic!("Expected list field");
2555 };
2556 assert!(
2557 matches!(
2558 element
2559 .try_extension_type::<WkbType>()
2560 .unwrap()
2561 .metadata()
2562 .type_hint(),
2563 WkbTypeHint::Geometry
2564 ),
2565 "Expected list element to retain WKB extension metadata"
2566 );
2567 assert_eq!(
2568 element
2569 .try_extension_type::<WkbType>()
2570 .unwrap()
2571 .metadata()
2572 .crs
2573 .as_ref()
2574 .and_then(serde_json::Value::as_str),
2575 Some(DEFAULT_GEOSPATIAL_CRS)
2576 );
2577
2578 let converted = arrow_schema_to_schema(&arrow_schema).unwrap();
2579 assert_eq!(converted.as_struct().fields(), schema.as_struct().fields());
2580 }
2581
2582 #[test]
2583 fn test_geospatial_arrow_crs_import() {
2584 let mut geom = simple_field("geom", DataType::LargeBinary, true, "1");
2585 geom.try_with_extension_type(WkbType::new(Some(WkbMetadata::new(
2586 Some(r#"{"id":{"authority":"EPSG","code":3857}}"#),
2587 None,
2588 ))))
2589 .unwrap();
2590 let mut unset_geom = simple_field("unset_geom", DataType::LargeBinary, true, "2");
2591 unset_geom
2592 .try_with_extension_type(WkbType::new(Some(WkbMetadata::new(None, None))))
2593 .unwrap();
2594
2595 let arrow_schema = ArrowSchema::new(vec![geom, unset_geom]);
2596 let schema = arrow_schema_to_schema(&arrow_schema).unwrap();
2597
2598 assert_eq!(
2599 schema.field_by_id(1).unwrap().field_type.as_ref(),
2600 &Type::Primitive(PrimitiveType::Geometry(
2601 GeometryType::new(Some("EPSG:3857".to_string())).unwrap()
2602 ))
2603 );
2604 assert_eq!(
2605 schema.field_by_id(2).unwrap().field_type.as_ref(),
2606 &Type::Primitive(PrimitiveType::Geometry(
2607 GeometryType::new(Some(UNSET_GEOSPATIAL_CRS.to_string())).unwrap()
2608 ))
2609 );
2610 }
2611
2612 #[test]
2613 fn test_geospatial_arrow_invalid_crs_is_rejected() {
2614 for crs in [r#"{}"#, "42"] {
2615 let mut field = simple_field("geom", DataType::LargeBinary, true, "1");
2616 field
2617 .try_with_extension_type(WkbType::new(Some(WkbMetadata::new(Some(crs), None))))
2618 .unwrap();
2619
2620 let err = arrow_schema_to_schema(&ArrowSchema::new(vec![field])).unwrap_err();
2621 assert_eq!(err.kind(), ErrorKind::DataInvalid, "{err}");
2622 }
2623 }
2624
2625 #[test]
2626 fn test_geospatial_arrow_extension_requires_binary_storage() {
2627 use arrow_schema::extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY};
2628
2629 let metadata = WkbType::new(Some(WkbMetadata::new(None, None)))
2630 .serialize_metadata()
2631 .unwrap();
2632 let field =
2633 simple_field("geom", DataType::Int32, true, "1").with_metadata(HashMap::from([
2634 (PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string()),
2635 (
2636 EXTENSION_TYPE_NAME_KEY.to_string(),
2637 WkbType::NAME.to_string(),
2638 ),
2639 (EXTENSION_TYPE_METADATA_KEY.to_string(), metadata),
2640 ]));
2641
2642 let err = arrow_schema_to_schema(&ArrowSchema::new(vec![field])).unwrap_err();
2643 assert_eq!(err.kind(), ErrorKind::DataInvalid, "{err}");
2644 assert!(
2645 err.message().contains("requires binary storage"),
2646 "unexpected error: {err}"
2647 );
2648 }
2649
2650 #[test]
2651 fn test_geospatial_parquet_statistics_are_ignored() {
2652 use parquet::data_type::ByteArray;
2653 use parquet::file::statistics::ValueStatistics;
2654
2655 let stats = Statistics::ByteArray(ValueStatistics::new(
2656 Some(ByteArray::from(vec![1, 2, 3])),
2657 Some(ByteArray::from(vec![4, 5, 6])),
2658 None,
2659 None,
2660 false,
2661 ));
2662
2663 for primitive_type in [
2664 PrimitiveType::Geometry(GeometryType::default()),
2665 PrimitiveType::Geography(GeographyType::default()),
2666 ] {
2667 assert_eq!(
2668 get_parquet_stat_min_as_datum(&primitive_type, &stats).unwrap(),
2669 None
2670 );
2671 assert_eq!(
2672 get_parquet_stat_max_as_datum(&primitive_type, &stats).unwrap(),
2673 None
2674 );
2675 }
2676 }
2677
2678 #[test]
2679 fn test_type_conversion() {
2680 {
2682 let arrow_type = DataType::Int32;
2683 let iceberg_type = Type::Primitive(PrimitiveType::Int);
2684 assert_eq!(arrow_type, type_to_arrow_type(&iceberg_type).unwrap());
2685 assert_eq!(iceberg_type, arrow_type_to_type(&arrow_type).unwrap());
2686 }
2687
2688 {
2689 let arrow_type = DataType::Null;
2690 let iceberg_type = Type::Primitive(PrimitiveType::Unknown);
2691 assert_eq!(arrow_type, type_to_arrow_type(&iceberg_type).unwrap());
2692 assert_eq!(iceberg_type, arrow_type_to_type(&arrow_type).unwrap());
2693 }
2694
2695 {
2697 let arrow_type = DataType::Struct(Fields::from(vec![
2699 Field::new("a", DataType::Int64, false),
2700 Field::new("b", DataType::Utf8, true),
2701 ]));
2702 assert_eq!(
2703 &arrow_type_to_type(&arrow_type).unwrap_err().to_string(),
2704 "DataInvalid => Field id not found in metadata"
2705 );
2706
2707 let arrow_type = DataType::Struct(Fields::from(vec![
2708 Field::new("a", DataType::Int64, false).with_metadata(HashMap::from_iter([(
2709 PARQUET_FIELD_ID_META_KEY.to_string(),
2710 1.to_string(),
2711 )])),
2712 Field::new("b", DataType::Utf8, true).with_metadata(HashMap::from_iter([(
2713 PARQUET_FIELD_ID_META_KEY.to_string(),
2714 2.to_string(),
2715 )])),
2716 ]));
2717 let iceberg_type = Type::Struct(StructType::new(vec![
2718 NestedField {
2719 id: 1,
2720 doc: None,
2721 name: "a".to_string(),
2722 required: true,
2723 field_type: Box::new(Type::Primitive(PrimitiveType::Long)),
2724 initial_default: None,
2725 write_default: None,
2726 }
2727 .into(),
2728 NestedField {
2729 id: 2,
2730 doc: None,
2731 name: "b".to_string(),
2732 required: false,
2733 field_type: Box::new(Type::Primitive(PrimitiveType::String)),
2734 initial_default: None,
2735 write_default: None,
2736 }
2737 .into(),
2738 ]));
2739 assert_eq!(iceberg_type, arrow_type_to_type(&arrow_type).unwrap());
2740 assert_eq!(arrow_type, type_to_arrow_type(&iceberg_type).unwrap());
2741
2742 let iceberg_type = Type::Struct(StructType::new(vec![
2744 NestedField {
2745 id: 1,
2746 doc: None,
2747 name: "a".to_string(),
2748 required: true,
2749 field_type: Box::new(Type::Primitive(PrimitiveType::Long)),
2750 initial_default: Some(Literal::Primitive(PrimitiveLiteral::Int(114514))),
2751 write_default: None,
2752 }
2753 .into(),
2754 NestedField {
2755 id: 2,
2756 doc: None,
2757 name: "b".to_string(),
2758 required: false,
2759 field_type: Box::new(Type::Primitive(PrimitiveType::String)),
2760 initial_default: None,
2761 write_default: Some(Literal::Primitive(PrimitiveLiteral::String(
2762 "514".to_string(),
2763 ))),
2764 }
2765 .into(),
2766 ]));
2767 assert_eq!(arrow_type, type_to_arrow_type(&iceberg_type).unwrap());
2768 }
2769
2770 {
2772 let arrow_type =
2773 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Int8));
2774 let iceberg_type = Type::Primitive(PrimitiveType::Int);
2775 assert_eq!(
2776 iceberg_type,
2777 arrow_type_to_type(&arrow_type).unwrap(),
2778 "Expected dictionary conversion to use the contained value"
2779 );
2780
2781 let arrow_type =
2782 DataType::Dictionary(Box::new(DataType::Utf8), Box::new(DataType::Boolean));
2783 let iceberg_type = Type::Primitive(PrimitiveType::Boolean);
2784 assert_eq!(iceberg_type, arrow_type_to_type(&arrow_type).unwrap());
2785 }
2786 }
2787
2788 #[test]
2789 fn test_unsigned_integer_type_conversion() {
2790 let test_cases = vec![
2791 (DataType::UInt8, PrimitiveType::Int),
2792 (DataType::UInt16, PrimitiveType::Int),
2793 (DataType::UInt32, PrimitiveType::Long),
2794 ];
2795
2796 for (arrow_type, expected_iceberg_type) in test_cases {
2797 let arrow_field = Field::new("test", arrow_type.clone(), false).with_metadata(
2798 HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string())]),
2799 );
2800 let arrow_schema = ArrowSchema::new(vec![arrow_field]);
2801
2802 let iceberg_schema = arrow_schema_to_schema(&arrow_schema).unwrap();
2803 let iceberg_field = iceberg_schema.as_struct().fields().first().unwrap();
2804
2805 assert!(
2806 matches!(iceberg_field.field_type.as_ref(), Type::Primitive(t) if *t == expected_iceberg_type),
2807 "Expected {arrow_type:?} to map to {expected_iceberg_type:?}"
2808 );
2809 }
2810
2811 {
2813 let arrow_field = Field::new("test", DataType::UInt64, false).with_metadata(
2814 HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string())]),
2815 );
2816 let arrow_schema = ArrowSchema::new(vec![arrow_field]);
2817
2818 let result = arrow_schema_to_schema(&arrow_schema);
2819 assert!(result.is_err());
2820 assert!(
2821 result
2822 .unwrap_err()
2823 .to_string()
2824 .contains("UInt64 is not supported")
2825 );
2826 }
2827 }
2828
2829 #[test]
2830 fn test_datum_conversion() {
2831 {
2832 let datum = Datum::bool(true);
2833 let arrow_datum = get_arrow_datum(&datum).unwrap();
2834 let (array, is_scalar) = arrow_datum.get();
2835 let array = array.as_any().downcast_ref::<BooleanArray>().unwrap();
2836 assert!(is_scalar);
2837 assert!(array.value(0));
2838 }
2839 {
2840 let datum = Datum::int(42);
2841 let arrow_datum = get_arrow_datum(&datum).unwrap();
2842 let (array, is_scalar) = arrow_datum.get();
2843 let array = array.as_any().downcast_ref::<Int32Array>().unwrap();
2844 assert!(is_scalar);
2845 assert_eq!(array.value(0), 42);
2846 }
2847 {
2848 let datum = Datum::long(42);
2849 let arrow_datum = get_arrow_datum(&datum).unwrap();
2850 let (array, is_scalar) = arrow_datum.get();
2851 let array = array.as_any().downcast_ref::<Int64Array>().unwrap();
2852 assert!(is_scalar);
2853 assert_eq!(array.value(0), 42);
2854 }
2855 {
2856 let datum = Datum::float(42.42_f32);
2857 let arrow_datum = get_arrow_datum(&datum).unwrap();
2858 let (array, is_scalar) = arrow_datum.get();
2859 let array = array.as_any().downcast_ref::<Float32Array>().unwrap();
2860 assert!(is_scalar);
2861 assert_eq!(array.value(0), 42.42);
2862 }
2863 {
2864 let datum = Datum::double(42.42);
2865 let arrow_datum = get_arrow_datum(&datum).unwrap();
2866 let (array, is_scalar) = arrow_datum.get();
2867 let array = array.as_any().downcast_ref::<Float64Array>().unwrap();
2868 assert!(is_scalar);
2869 assert_eq!(array.value(0), 42.42);
2870 }
2871 {
2872 let datum = Datum::string("abc");
2873 let arrow_datum = get_arrow_datum(&datum).unwrap();
2874 let (array, is_scalar) = arrow_datum.get();
2875 let array = array.as_any().downcast_ref::<StringArray>().unwrap();
2876 assert!(is_scalar);
2877 assert_eq!(array.value(0), "abc");
2878 }
2879 {
2880 let datum = Datum::binary(vec![1, 2, 3, 4]);
2881 let arrow_datum = get_arrow_datum(&datum).unwrap();
2882 let (array, is_scalar) = arrow_datum.get();
2883 let array = array.as_any().downcast_ref::<BinaryArray>().unwrap();
2884 assert!(is_scalar);
2885 assert_eq!(array.value(0), &[1, 2, 3, 4]);
2886 }
2887 {
2888 let datum = Datum::date(42);
2889 let arrow_datum = get_arrow_datum(&datum).unwrap();
2890 let (array, is_scalar) = arrow_datum.get();
2891 let array = array.as_any().downcast_ref::<Date32Array>().unwrap();
2892 assert!(is_scalar);
2893 assert_eq!(array.value(0), 42);
2894 }
2895 {
2896 let datum = Datum::timestamp_micros(42);
2897 let arrow_datum = get_arrow_datum(&datum).unwrap();
2898 let (array, is_scalar) = arrow_datum.get();
2899 let array = array
2900 .as_any()
2901 .downcast_ref::<TimestampMicrosecondArray>()
2902 .unwrap();
2903 assert!(is_scalar);
2904 assert_eq!(array.value(0), 42);
2905 }
2906 {
2907 let datum = Datum::timestamptz_micros(42);
2908 let arrow_datum = get_arrow_datum(&datum).unwrap();
2909 let (array, is_scalar) = arrow_datum.get();
2910 let array = array
2911 .as_any()
2912 .downcast_ref::<TimestampMicrosecondArray>()
2913 .unwrap();
2914 assert!(is_scalar);
2915 assert_eq!(array.timezone(), Some("+00:00"));
2916 assert_eq!(array.value(0), 42);
2917 }
2918 {
2919 let datum = Datum::decimal_with_precision(decimal_new(123, 2), 30).unwrap();
2920 let arrow_datum = get_arrow_datum(&datum).unwrap();
2921 let (array, is_scalar) = arrow_datum.get();
2922 let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
2923 assert!(is_scalar);
2924 assert_eq!(array.precision(), 30);
2925 assert_eq!(array.scale(), 2);
2926 assert_eq!(array.value(0), 123);
2927 }
2928 {
2929 let datum = Datum::uuid_from_str("42424242-4242-4242-4242-424242424242").unwrap();
2930 let arrow_datum = get_arrow_datum(&datum).unwrap();
2931 let (array, is_scalar) = arrow_datum.get();
2932 let array = array
2933 .as_any()
2934 .downcast_ref::<FixedSizeBinaryArray>()
2935 .unwrap();
2936 assert!(is_scalar);
2937 assert_eq!(array.value(0), [66u8; 16]);
2938 }
2939 {
2940 let datum = Datum::fixed(vec![1u8, 2, 3, 4, 5, 6, 7, 8]);
2941 let arrow_datum = get_arrow_datum(&datum).unwrap();
2942 let (array, is_scalar) = arrow_datum.get();
2943 let array = array
2944 .as_any()
2945 .downcast_ref::<FixedSizeBinaryArray>()
2946 .unwrap();
2947 assert!(is_scalar);
2948 assert_eq!(array.value_length(), 8);
2949 assert_eq!(array.value(0), &[1u8, 2, 3, 4, 5, 6, 7, 8]);
2950 }
2951 }
2952
2953 #[test]
2954 fn test_arrow_schema_to_schema_with_field_id() {
2955 let arrow_schema = ArrowSchema::new(vec![
2958 Field::new("id", DataType::Int64, false),
2959 Field::new("name", DataType::Utf8, true),
2960 Field::new("price", DataType::Decimal128(10, 2), false),
2961 Field::new("uuid", DataType::FixedSizeBinary(16), false)
2962 .with_extension_type(UuidExtensionType),
2963 Field::new(
2964 "created_at",
2965 DataType::Timestamp(TimeUnit::Microsecond, Some("+00:00".into())),
2966 true,
2967 ),
2968 Field::new(
2969 "tags",
2970 DataType::List(Arc::new(Field::new("item", DataType::Utf8, true))),
2971 true,
2972 ),
2973 Field::new(
2974 "address",
2975 DataType::Struct(Fields::from(vec![
2976 Field::new("street", DataType::Utf8, true),
2977 Field::new("city", DataType::Utf8, false),
2978 Field::new("zip", DataType::Int32, true),
2979 ])),
2980 true,
2981 ),
2982 Field::new(
2983 "attributes",
2984 DataType::Map(
2985 Arc::new(Field::new(
2986 DEFAULT_MAP_FIELD_NAME,
2987 DataType::Struct(Fields::from(vec![
2988 Field::new("key", DataType::Utf8, false),
2989 Field::new("value", DataType::Utf8, true),
2990 ])),
2991 false,
2992 )),
2993 false,
2994 ),
2995 true,
2996 ),
2997 Field::new(
2998 "orders",
2999 DataType::List(Arc::new(Field::new(
3000 "element",
3001 DataType::Struct(Fields::from(vec![
3002 Field::new("order_id", DataType::Int64, false),
3003 Field::new("amount", DataType::Float64, false),
3004 ])),
3005 true,
3006 ))),
3007 true,
3008 ),
3009 ]);
3010
3011 let schema = arrow_schema_to_schema_auto_assign_ids(&arrow_schema).unwrap();
3012
3013 let expected = Schema::builder()
3018 .with_fields(vec![
3019 NestedField::required(1, "id", Type::Primitive(PrimitiveType::Long)).into(),
3020 NestedField::optional(2, "name", Type::Primitive(PrimitiveType::String)).into(),
3021 NestedField::required(
3022 3,
3023 "price",
3024 Type::Primitive(PrimitiveType::Decimal {
3025 precision: 10,
3026 scale: 2,
3027 }),
3028 )
3029 .into(),
3030 NestedField::required(4, "uuid", Type::Primitive(PrimitiveType::Uuid)).into(),
3031 NestedField::optional(5, "created_at", Type::Primitive(PrimitiveType::Timestamptz))
3032 .into(),
3033 NestedField::optional(
3034 6,
3035 "tags",
3036 Type::List(ListType {
3037 element_field: NestedField::list_element(
3038 10,
3039 Type::Primitive(PrimitiveType::String),
3040 false,
3041 )
3042 .into(),
3043 }),
3044 )
3045 .into(),
3046 NestedField::optional(
3047 7,
3048 "address",
3049 Type::Struct(StructType::new(vec![
3050 NestedField::optional(11, "street", Type::Primitive(PrimitiveType::String))
3051 .into(),
3052 NestedField::required(12, "city", Type::Primitive(PrimitiveType::String))
3053 .into(),
3054 NestedField::optional(13, "zip", Type::Primitive(PrimitiveType::Int))
3055 .into(),
3056 ])),
3057 )
3058 .into(),
3059 NestedField::optional(
3060 8,
3061 "attributes",
3062 Type::Map(MapType {
3063 key_field: NestedField::map_key_element(
3064 14,
3065 Type::Primitive(PrimitiveType::String),
3066 )
3067 .into(),
3068 value_field: NestedField::map_value_element(
3069 15,
3070 Type::Primitive(PrimitiveType::String),
3071 false,
3072 )
3073 .into(),
3074 }),
3075 )
3076 .into(),
3077 NestedField::optional(
3078 9,
3079 "orders",
3080 Type::List(ListType {
3081 element_field: NestedField::list_element(
3082 16,
3083 Type::Struct(StructType::new(vec![
3084 NestedField::required(
3085 17,
3086 "order_id",
3087 Type::Primitive(PrimitiveType::Long),
3088 )
3089 .into(),
3090 NestedField::required(
3091 18,
3092 "amount",
3093 Type::Primitive(PrimitiveType::Double),
3094 )
3095 .into(),
3096 ])),
3097 false,
3098 )
3099 .into(),
3100 }),
3101 )
3102 .into(),
3103 ])
3104 .build()
3105 .unwrap();
3106
3107 pretty_assertions::assert_eq!(schema, expected);
3108 assert_eq!(schema.highest_field_id(), 18);
3109 }
3110
3111 fn decimal_byte_array_stats(min: Vec<u8>, max: Vec<u8>) -> Statistics {
3112 Statistics::byte_array(
3113 Some(parquet::data_type::ByteArray::from(min)),
3114 Some(parquet::data_type::ByteArray::from(max)),
3115 None,
3116 Some(0),
3117 false,
3118 )
3119 }
3120
3121 fn decimal_bounds(stats: &Statistics) -> Result<(Option<Datum>, Option<Datum>)> {
3122 let ty = PrimitiveType::Decimal {
3123 precision: 38,
3124 scale: 2,
3125 };
3126 Ok((
3127 get_parquet_stat_min_as_datum(&ty, stats)?,
3128 get_parquet_stat_max_as_datum(&ty, stats)?,
3129 ))
3130 }
3131
3132 fn decimal_datum(value: i128) -> Datum {
3133 Datum::new(
3134 PrimitiveType::Decimal {
3135 precision: 38,
3136 scale: 2,
3137 },
3138 PrimitiveLiteral::Int128(value),
3139 )
3140 }
3141
3142 #[test]
3143 fn test_byte_array_decimal_stats_short_bounds() {
3144 let stats = decimal_byte_array_stats(vec![0xFB, 0x2E], vec![0x04, 0xD2]);
3146 let (min, max) = decimal_bounds(&stats).unwrap();
3147 assert_eq!(min, Some(decimal_datum(-1234)));
3148 assert_eq!(max, Some(decimal_datum(1234)));
3149
3150 let stats = decimal_byte_array_stats(vec![0x80], vec![0x7F]);
3152 let (min, max) = decimal_bounds(&stats).unwrap();
3153 assert_eq!(min, Some(decimal_datum(-128)));
3154 assert_eq!(max, Some(decimal_datum(127)));
3155 }
3156
3157 #[test]
3158 fn test_byte_array_decimal_stats_full_width_bounds() {
3159 let stats = decimal_byte_array_stats(
3160 i128::MIN.to_be_bytes().to_vec(),
3161 i128::MAX.to_be_bytes().to_vec(),
3162 );
3163 let (min, max) = decimal_bounds(&stats).unwrap();
3164 assert_eq!(min, Some(decimal_datum(i128::MIN)));
3165 assert_eq!(max, Some(decimal_datum(i128::MAX)));
3166 }
3167
3168 #[test]
3169 fn test_byte_array_decimal_stats_empty_bound_errors() {
3170 let stats = decimal_byte_array_stats(vec![], vec![0x01]);
3171 let err = decimal_bounds(&stats).unwrap_err();
3172 assert_eq!(err.kind(), ErrorKind::DataInvalid);
3173
3174 let stats = decimal_byte_array_stats(vec![0x01], vec![]);
3175 let err = decimal_bounds(&stats).unwrap_err();
3176 assert_eq!(err.kind(), ErrorKind::DataInvalid);
3177 }
3178
3179 #[test]
3180 fn test_byte_array_decimal_stats_oversized_bound_errors() {
3181 let stats = decimal_byte_array_stats(vec![0; 17], vec![0x01]);
3182 let err = decimal_bounds(&stats).unwrap_err();
3183 assert_eq!(err.kind(), ErrorKind::DataInvalid);
3184
3185 let stats = decimal_byte_array_stats(vec![0x01], vec![0; 17]);
3186 let err = decimal_bounds(&stats).unwrap_err();
3187 assert_eq!(err.kind(), ErrorKind::DataInvalid);
3188 }
3189
3190 #[test]
3195 fn test_strip_metadata_from_nested_schema() {
3196 let schema = |with_metadata: bool| {
3198 let metadata = |value: &str| {
3199 if with_metadata {
3200 HashMap::from([("k".to_string(), value.to_string())])
3201 } else {
3202 HashMap::new()
3203 }
3204 };
3205 let field = |name: &str, ty: DataType, nullable: bool| {
3206 Field::new(name, ty, nullable).with_metadata(metadata(name))
3207 };
3208 let element = |ty: DataType| Arc::new(field(LIST_FIELD_NAME, ty, false));
3209 let ints = || DataType::List(element(DataType::Int32));
3210 let map = |value: DataType, sorted: bool| {
3211 let entries = Fields::from(vec![
3212 field(MAP_KEY_FIELD_NAME, DataType::Utf8, false),
3213 field(MAP_VALUE_FIELD_NAME, value, true),
3214 ]);
3215 let entries = field(DEFAULT_MAP_FIELD_NAME, DataType::Struct(entries), false);
3216 DataType::Map(Arc::new(entries), sorted)
3217 };
3218 let struct_of = |fields: Vec<Field>| DataType::Struct(Fields::from(fields));
3219
3220 let a_b_a_b = struct_of(vec![field(
3221 "b",
3222 struct_of(vec![field(
3223 "a",
3224 struct_of(vec![field("b", DataType::Int32, true)]),
3225 false,
3226 )]),
3227 false,
3228 )]);
3229 ArrowSchema::new(vec![
3230 field("list", ints(), true),
3231 field(
3232 "large_list",
3233 DataType::LargeList(element(DataType::Int32)),
3234 false,
3235 ),
3236 field(
3237 "fixed_list",
3238 DataType::FixedSizeList(element(DataType::Int32), 2),
3239 true,
3240 ),
3241 field("list_of_list", DataType::List(element(ints())), false),
3242 field("sorted_map", map(ints(), true), false),
3243 field(
3244 "map_of_struct",
3245 map(
3246 struct_of(vec![
3247 field("a", DataType::Int32, true),
3248 field("b", ints(), false),
3249 ]),
3250 false,
3251 ),
3252 true,
3253 ),
3254 field(
3255 "list_of_struct",
3256 DataType::List(element(struct_of(vec![field(
3257 "m",
3258 map(ints(), false),
3259 true,
3260 )]))),
3261 true,
3262 ),
3263 field("a", a_b_a_b, true),
3264 ])
3265 .with_metadata(metadata("schema"))
3266 };
3267
3268 let (input, expected) = (schema(true), schema(false));
3269 assert_ne!(input, expected);
3271 pretty_assertions::assert_eq!(strip_metadata_from_schema(&input).unwrap(), expected);
3272 }
3273
3274 #[test]
3279 fn test_strip_metadata_normalizes_container_field_names() {
3280 let schema = |[element, entries, key, value]: [&str; 4], key_nullable: bool| {
3283 let ints =
3284 |nullable| DataType::List(Arc::new(Field::new(element, DataType::Int32, nullable)));
3285 let key_value = Fields::from(vec![
3286 Field::new(key, DataType::Utf8, key_nullable),
3287 Field::new(value, ints(false), true),
3288 ]);
3289 let entries = Field::new(entries, DataType::Struct(key_value), false);
3290 ArrowSchema::new(vec![
3291 Field::new("l", ints(true), true),
3292 Field::new("m", DataType::Map(Arc::new(entries), false), true),
3293 ])
3294 };
3295 let arrow_rs = schema(["item", "entries", "keys", "values"], true);
3296 let expected = schema(
3297 [
3298 LIST_FIELD_NAME,
3299 DEFAULT_MAP_FIELD_NAME,
3300 MAP_KEY_FIELD_NAME,
3301 MAP_VALUE_FIELD_NAME,
3302 ],
3303 false,
3304 );
3305
3306 let int_list = |id, required| {
3308 let int = Type::Primitive(PrimitiveType::Int);
3309 Type::List(ListType::new(
3310 NestedField::list_element(id, int, required).into(),
3311 ))
3312 };
3313 let string_to_int_list = MapType::new(
3314 NestedField::map_key_element(4, Type::Primitive(PrimitiveType::String)).into(),
3315 NestedField::map_value_element(5, int_list(6, true), false).into(),
3316 );
3317 let iceberg = Schema::builder()
3318 .with_fields(vec![
3319 NestedField::optional(1, "l", int_list(2, false)).into(),
3320 NestedField::optional(3, "m", Type::Map(string_to_int_list)).into(),
3321 ])
3322 .build()
3323 .unwrap();
3324
3325 pretty_assertions::assert_eq!(strip_metadata_from_schema(&arrow_rs).unwrap(), expected);
3326 pretty_assertions::assert_eq!(
3327 strip_metadata_from_schema(&schema_to_arrow_schema(&iceberg).unwrap()).unwrap(),
3328 expected
3329 );
3330 }
3331
3332 #[test]
3335 fn test_strip_metadata_keeps_other_differences() {
3336 let schema = |child: &str, element_nullable: bool| {
3337 let child = Field::new(child, DataType::Int32, true);
3338 let element = Field::new("item", DataType::Int32, element_nullable);
3339 ArrowSchema::new(vec![
3340 Field::new("s", DataType::Struct(Fields::from(vec![child])), true),
3341 Field::new("l", DataType::List(Arc::new(element)), true),
3342 ])
3343 };
3344 let strip = |schema: ArrowSchema| strip_metadata_from_schema(&schema).unwrap();
3345
3346 assert_ne!(strip(schema("a", true)), strip(schema("b", true)));
3347 assert_ne!(strip(schema("a", true)), strip(schema("a", false)));
3348 }
3349
3350 #[test]
3353 fn test_strip_metadata_turns_variants_into_structs() {
3354 let variant = || Type::Variant(VariantType);
3355 let schema = Schema::builder()
3356 .with_fields(vec![
3357 NestedField::optional(1, "v", variant()).into(),
3358 NestedField::optional(
3359 2,
3360 "l",
3361 Type::List(ListType::new(
3362 NestedField::list_element(3, variant(), false).into(),
3363 )),
3364 )
3365 .into(),
3366 NestedField::optional(
3367 4,
3368 "m",
3369 Type::Map(MapType::new(
3370 NestedField::map_key_element(5, Type::Primitive(PrimitiveType::String))
3371 .into(),
3372 NestedField::map_value_element(6, variant(), false).into(),
3373 )),
3374 )
3375 .into(),
3376 ])
3377 .build()
3378 .unwrap();
3379
3380 let storage = || {
3381 DataType::Struct(Fields::from(vec![
3382 Field::new("metadata", DataType::Binary, false),
3383 Field::new("value", DataType::Binary, true),
3384 ]))
3385 };
3386 let entries = Fields::from(vec![
3387 Field::new(MAP_KEY_FIELD_NAME, DataType::Utf8, false),
3388 Field::new(MAP_VALUE_FIELD_NAME, storage(), true),
3389 ]);
3390 let entries = Field::new(DEFAULT_MAP_FIELD_NAME, DataType::Struct(entries), false);
3391 let expected = ArrowSchema::new(vec![
3392 Field::new("v", storage(), true),
3393 Field::new(
3394 "l",
3395 DataType::List(Arc::new(Field::new(LIST_FIELD_NAME, storage(), true))),
3396 true,
3397 ),
3398 Field::new("m", DataType::Map(Arc::new(entries), false), true),
3399 ]);
3400
3401 pretty_assertions::assert_eq!(
3402 strip_metadata_from_schema(&schema_to_arrow_schema(&schema).unwrap()).unwrap(),
3403 expected
3404 );
3405 }
3406
3407 #[test]
3410 fn test_strip_metadata_unwraps_dictionaries() {
3411 let schema = |dictionary_encoded: bool| {
3412 let maybe_dictionary = |key: DataType, value: DataType| {
3413 if dictionary_encoded {
3414 DataType::Dictionary(Box::new(key), Box::new(value))
3415 } else {
3416 value
3417 }
3418 };
3419 let element = Field::new(
3420 LIST_FIELD_NAME,
3421 maybe_dictionary(DataType::Int8, DataType::LargeUtf8),
3422 false,
3423 );
3424 ArrowSchema::new(vec![
3425 Field::new("d", maybe_dictionary(DataType::Int32, DataType::Utf8), true),
3426 Field::new("l", DataType::List(Arc::new(element)), true),
3427 ])
3428 };
3429
3430 pretty_assertions::assert_eq!(
3431 strip_metadata_from_schema(&schema(true)).unwrap(),
3432 schema(false)
3433 );
3434 }
3435}