1use std::cell::RefCell;
16use std::ops::Div;
17use std::sync::{Arc, LazyLock};
18
19use arrow_array::ArrayRef;
20use arrow_array::cast::AsArray;
21use arrow_schema::extension::ExtensionType;
22use num_traits::abs;
23use parquet_variant_compute::{VariantArray as ParquetVariantArray, VariantType};
24use risingwave_common_log::LogSuppressor;
25use thiserror_ext::AsReport;
26
27pub use super::arrow_58::{
28 FromArrow, ToArrow, arrow_array, arrow_buffer, arrow_cast, arrow_schema,
29 is_parquet_field_match_source_schema, is_parquet_schema_match_source_schema,
30};
31use crate::array::{
32 Array, ArrayBuilder, ArrayError, ArrayImpl, DataChunk, DataType, DecimalArray, IntervalArray,
33 VariantArray as RwVariantArray, VariantArrayBuilder,
34};
35use crate::types::{Scalar, StructType, VariantVal};
36
37pub struct IcebergArrowConvert;
38
39struct DefaultIcebergFromArrow;
40
41impl FromArrow for DefaultIcebergFromArrow {}
42
43pub const ICEBERG_DECIMAL_PRECISION: u8 = 38;
52pub const ICEBERG_DECIMAL_SCALE: i8 = 10;
53
54impl IcebergArrowConvert {
55 pub fn to_record_batch(
56 &self,
57 schema: arrow_schema::SchemaRef,
58 chunk: &DataChunk,
59 ) -> Result<arrow_array::RecordBatch, ArrayError> {
60 ToArrow::to_record_batch(self, schema, chunk)
61 }
62
63 pub fn chunk_from_record_batch(
64 &self,
65 batch: &arrow_array::RecordBatch,
66 ) -> Result<DataChunk, ArrayError> {
67 FromArrow::from_record_batch(self, batch)
68 }
69
70 pub fn type_from_field(&self, field: &arrow_schema::Field) -> Result<DataType, ArrayError> {
71 FromArrow::from_field(self, field)
72 }
73
74 pub fn to_arrow_field(
75 &self,
76 name: &str,
77 data_type: &DataType,
78 ) -> Result<arrow_schema::Field, ArrayError> {
79 ToArrow::to_arrow_field(self, name, data_type)
80 }
81
82 pub fn struct_from_fields(
83 &self,
84 fields: &arrow_schema::Fields,
85 ) -> Result<StructType, ArrayError> {
86 FromArrow::from_fields(self, fields)
87 }
88
89 pub fn to_arrow_array(
90 &self,
91 data_type: &arrow_schema::DataType,
92 array: &ArrayImpl,
93 ) -> Result<arrow_array::ArrayRef, ArrayError> {
94 ToArrow::to_array(self, data_type, array)
95 }
96
97 pub fn array_from_arrow_array(
98 &self,
99 field: &arrow_schema::Field,
100 array: &arrow_array::ArrayRef,
101 ) -> Result<ArrayImpl, ArrayError> {
102 FromArrow::from_array(self, field, array)
103 }
104
105 pub fn array_from_arrow_array_raw(
110 &self,
111 array: &arrow_array::ArrayRef,
112 ) -> Result<ArrayImpl, ArrayError> {
113 static FIELD_DUMMY: LazyLock<arrow_schema::Field> =
114 LazyLock::new(|| arrow_schema::Field::new("dummy", arrow_schema::DataType::Null, true));
115 FromArrow::from_array(self, &FIELD_DUMMY, array)
116 }
117}
118
119impl ToArrow for IcebergArrowConvert {
120 fn to_arrow_field(
121 &self,
122 name: &str,
123 data_type: &DataType,
124 ) -> Result<arrow_schema::Field, ArrayError> {
125 let data_type = match data_type {
126 DataType::Boolean => self.bool_type_to_arrow(),
127 DataType::Int16 => self.int32_type_to_arrow(),
128 DataType::Int32 => self.int32_type_to_arrow(),
129 DataType::Int64 => self.int64_type_to_arrow(),
130 DataType::Int256 => self.int256_type_to_arrow(),
131 DataType::Float32 => self.float32_type_to_arrow(),
132 DataType::Float64 => self.float64_type_to_arrow(),
133 DataType::Date => self.date_type_to_arrow(),
134 DataType::Time => self.time_type_to_arrow(),
135 DataType::Timestamp => self.timestamp_type_to_arrow(),
136 DataType::Timestamptz => self.timestamptz_type_to_arrow(),
137 DataType::Interval => self.interval_type_to_arrow(),
138 DataType::Varchar => self.varchar_type_to_arrow(),
139 DataType::Bytea => self.bytea_type_to_arrow(),
140 DataType::Serial => self.serial_type_to_arrow(),
141 DataType::Decimal => return Ok(self.decimal_type_to_arrow(name)),
142 DataType::Jsonb => self.varchar_type_to_arrow(),
143 DataType::Variant => return Ok(variant_arrow_field(name)),
144 DataType::Struct(fields) => self.struct_type_to_arrow(fields)?,
145 DataType::List(list) => self.list_type_to_arrow(list)?,
146 DataType::Map(map) => self.map_type_to_arrow(map)?,
147 DataType::Vector(_) => self.vector_type_to_arrow()?,
148 };
149 Ok(arrow_schema::Field::new(name, data_type, true))
150 }
151
152 #[inline]
153 fn interval_type_to_arrow(&self) -> arrow_schema::DataType {
154 arrow_schema::DataType::Utf8
155 }
156
157 #[inline]
158 fn decimal_type_to_arrow(&self, name: &str) -> arrow_schema::Field {
159 let data_type =
161 arrow_schema::DataType::Decimal128(ICEBERG_DECIMAL_PRECISION, ICEBERG_DECIMAL_SCALE);
162 arrow_schema::Field::new(name, data_type, true)
163 }
164
165 fn decimal_to_arrow(
166 &self,
167 data_type: &arrow_schema::DataType,
168 array: &DecimalArray,
169 ) -> Result<arrow_array::ArrayRef, ArrayError> {
170 let (precision, max_scale) = match data_type {
171 arrow_schema::DataType::Decimal128(precision, scale) => (*precision, *scale),
172 _ => return Err(ArrayError::to_arrow("Invalid decimal type")),
173 };
174
175 let max_value = 10_i128.pow(precision as u32) - 1;
177 let values: Vec<Option<i128>> = array
178 .iter()
179 .map(|e| {
180 e.and_then(|e| match e {
181 crate::array::Decimal::Normalized(e) => {
182 let value = e.mantissa();
183 let scale = e.scale() as i8;
184 let diff_scale = abs(max_scale - scale);
185 let value = match scale {
186 _ if scale < max_scale => value
187 .checked_mul(10_i128.pow(diff_scale as u32))
188 .filter(|&v| abs(v) <= max_value)
189 .unwrap_or_else(|| {
190 tracing::warn!(
191 "Decimal overflow when converting to arrow decimal with precision {} and scale {}. It will be replaced with inf/-inf.",
192 precision, max_scale
193 );
194 if value >= 0 { max_value } else { -max_value }
195 }),
196 _ if scale > max_scale => value.div(10_i128.pow(diff_scale as u32)),
197 _ => value,
198 };
199 Some(value)
200 }
201 crate::array::Decimal::PositiveInf => {
203 Some(max_value)
204 }
205 crate::array::Decimal::NegativeInf => {
206 Some(-max_value)
207 }
208 crate::array::Decimal::NaN => None,
209 })
210 })
211 .collect();
212
213 let array = arrow_array::Decimal128Array::from(values)
214 .with_precision_and_scale(precision, max_scale)
215 .map_err(ArrayError::from_arrow)?;
216 Ok(Arc::new(array) as ArrayRef)
217 }
218
219 fn interval_to_arrow(
220 &self,
221 array: &IntervalArray,
222 ) -> Result<arrow_array::ArrayRef, ArrayError> {
223 Ok(Arc::new(arrow_array::StringArray::from(array)))
224 }
225
226 fn variant_to_arrow(
227 &self,
228 array: &RwVariantArray,
229 ) -> Result<arrow_array::ArrayRef, ArrayError> {
230 let metadata = Arc::new(arrow_array::BinaryArray::from_iter_values(
234 array.raw_iter().map(|variant| variant.metadata()),
235 )) as ArrayRef;
236 let value = Arc::new(arrow_array::BinaryArray::from_iter_values(
237 array.raw_iter().map(|variant| variant.value()),
238 )) as ArrayRef;
239 let nulls = (!array.null_bitmap().all()).then(|| array.null_bitmap().into());
240
241 Ok(Arc::new(arrow_array::StructArray::new(
242 variant_arrow_fields(),
243 vec![metadata, value],
244 nulls,
245 )))
246 }
247}
248
249impl FromArrow for IcebergArrowConvert {
250 fn from_extension_type(
251 &self,
252 type_name: &str,
253 physical_type: &arrow_schema::DataType,
254 ) -> Result<DataType, ArrayError> {
255 match (type_name, physical_type) {
256 (VariantType::NAME, arrow_schema::DataType::Struct(_)) => Ok(DataType::Variant),
257 (VariantType::NAME, _) => Err(ArrayError::from_arrow(format!(
258 "variant extension type requires a struct physical type, got: {physical_type}"
259 ))),
260 _ => DefaultIcebergFromArrow.from_extension_type(type_name, physical_type),
261 }
262 }
263
264 fn from_extension_array(
265 &self,
266 type_name: &str,
267 array: &arrow_array::ArrayRef,
268 ) -> Result<ArrayImpl, ArrayError> {
269 match type_name {
270 VariantType::NAME => variant_array_to_variant(array),
271 _ => DefaultIcebergFromArrow.from_extension_array(type_name, array),
272 }
273 }
274}
275
276fn variant_arrow_field(name: &str) -> arrow_schema::Field {
279 arrow_schema::Field::new(
280 name,
281 arrow_schema::DataType::Struct(variant_arrow_fields()),
282 true,
283 )
284 .with_extension_type(VariantType)
285}
286
287fn variant_arrow_fields() -> arrow_schema::Fields {
288 [
289 Arc::new(arrow_schema::Field::new(
290 "metadata",
291 arrow_schema::DataType::Binary,
292 false,
293 )),
294 Arc::new(arrow_schema::Field::new(
295 "value",
296 arrow_schema::DataType::Binary,
297 false,
298 )),
299 ]
300 .into()
301}
302
303fn variant_array_to_variant(array: &arrow_array::ArrayRef) -> Result<ArrayImpl, ArrayError> {
304 let variant_array =
305 ParquetVariantArray::try_new(array.as_ref()).map_err(ArrayError::from_arrow)?;
306 if variant_array.typed_value_field().is_some() {
309 static LOG_SUPPRESSOR: LazyLock<LogSuppressor> = LazyLock::new(LogSuppressor::default);
310 if let Ok(suppressed_count) = LOG_SUPPRESSOR.check() {
311 tracing::warn!(
312 suppressed_count,
313 "shredded variant column (with a `typed_value` field) is not supported yet; \
314 reading it as NULL",
315 );
316 }
317 let mut builder = VariantArrayBuilder::new(variant_array.len());
318 for _ in 0..variant_array.len() {
319 builder.append_null();
320 }
321 return Ok(ArrayImpl::Variant(builder.finish()));
322 }
323 let metadata_accessor = BinaryArrayAccessor::resolve(variant_array.metadata_field())?;
326 let value_accessor = variant_array
327 .value_field()
328 .map(BinaryArrayAccessor::resolve)
329 .transpose()?;
330 let mut builder = VariantArrayBuilder::new(variant_array.len());
331
332 for idx in 0..variant_array.len() {
333 if variant_array.is_null(idx) {
334 builder.append_null();
335 continue;
336 }
337
338 let variant_result = match &value_accessor {
343 Some(value) if value.is_valid(idx) => {
344 VariantVal::from_parts(metadata_accessor.value(idx), value.value(idx))
345 }
346 _ => Ok(VariantVal::null()),
351 };
352
353 match variant_result {
354 Ok(variant) => builder.append(Some(variant.as_scalar_ref())),
355 Err(err) => {
356 static LOG_SUPPRESSOR: LazyLock<LogSuppressor> =
358 LazyLock::new(LogSuppressor::default);
359 if let Ok(suppressed_count) = LOG_SUPPRESSOR.check() {
360 tracing::warn!(
361 error = %err.as_report(),
362 suppressed_count,
363 "failed to decode iceberg variant value at index {}. It will be replaced with null.",
364 idx,
365 );
366 }
367 builder.append_null();
368 }
369 }
370 }
371
372 Ok(ArrayImpl::Variant(builder.finish()))
373}
374
375enum BinaryArrayAccessor<'a> {
383 Binary(&'a arrow_array::BinaryArray),
384 LargeBinary(&'a arrow_array::LargeBinaryArray),
385 BinaryView(&'a arrow_array::BinaryViewArray),
386}
387
388impl<'a> BinaryArrayAccessor<'a> {
389 fn resolve(array: &'a ArrayRef) -> Result<Self, ArrayError> {
390 use arrow_schema::DataType;
391 match array.data_type() {
392 DataType::Binary => Ok(Self::Binary(array.as_binary::<i32>())),
393 DataType::LargeBinary => Ok(Self::LargeBinary(array.as_binary::<i64>())),
394 DataType::BinaryView => Ok(Self::BinaryView(array.as_binary_view())),
395 other => Err(ArrayError::from_arrow(format!(
396 "variant metadata/value has unsupported binary layout: {other}"
397 ))),
398 }
399 }
400
401 #[inline]
402 fn is_valid(&self, index: usize) -> bool {
403 use arrow_array::Array as _;
404 match self {
405 Self::Binary(array) => array.is_valid(index),
406 Self::LargeBinary(array) => array.is_valid(index),
407 Self::BinaryView(array) => array.is_valid(index),
408 }
409 }
410
411 #[inline]
412 fn value(&self, index: usize) -> &[u8] {
413 match self {
414 Self::Binary(array) => array.value(index),
415 Self::LargeBinary(array) => array.value(index),
416 Self::BinaryView(array) => array.value(index),
417 }
418 }
419}
420
421#[derive(Default)]
430pub struct IcebergCreateTableArrowConvert {
431 next_field_id: RefCell<u32>,
432}
433
434impl IcebergCreateTableArrowConvert {
435 pub fn to_arrow_field(
436 &self,
437 name: &str,
438 data_type: &DataType,
439 ) -> Result<arrow_schema::Field, ArrayError> {
440 ToArrow::to_arrow_field(self, name, data_type)
441 }
442
443 fn add_field_id(&self, arrow_field: &mut arrow_schema::Field) {
444 *self.next_field_id.borrow_mut() += 1;
445 let field_id = *self.next_field_id.borrow();
446
447 let mut metadata = arrow_field.metadata().clone();
450 metadata.insert("PARQUET:field_id".to_owned(), field_id.to_string());
452 arrow_field.set_metadata(metadata);
453 }
454}
455
456impl ToArrow for IcebergCreateTableArrowConvert {
457 #[inline]
458 fn decimal_type_to_arrow(&self, name: &str) -> arrow_schema::Field {
459 let data_type =
465 arrow_schema::DataType::Decimal128(ICEBERG_DECIMAL_PRECISION, ICEBERG_DECIMAL_SCALE);
466
467 let mut arrow_field = arrow_schema::Field::new(name, data_type, true);
468 self.add_field_id(&mut arrow_field);
469 arrow_field
470 }
471
472 #[inline]
473 fn interval_type_to_arrow(&self) -> arrow_schema::DataType {
474 arrow_schema::DataType::Utf8
475 }
476
477 fn jsonb_type_to_arrow(&self, name: &str) -> arrow_schema::Field {
478 let data_type = arrow_schema::DataType::Utf8;
479
480 let mut arrow_field = arrow_schema::Field::new(name, data_type, true);
481 self.add_field_id(&mut arrow_field);
482 arrow_field
483 }
484
485 fn to_arrow_field(
490 &self,
491 name: &str,
492 value: &DataType,
493 ) -> Result<arrow_schema::Field, ArrayError> {
494 let data_type = match value {
495 DataType::Boolean => self.bool_type_to_arrow(),
497 DataType::Int16 => self.int32_type_to_arrow(),
498 DataType::Int32 => self.int32_type_to_arrow(),
499 DataType::Int64 => self.int64_type_to_arrow(),
500 DataType::Int256 => self.varchar_type_to_arrow(),
501 DataType::Float32 => self.float32_type_to_arrow(),
502 DataType::Float64 => self.float64_type_to_arrow(),
503 DataType::Date => self.date_type_to_arrow(),
504 DataType::Time => self.time_type_to_arrow(),
505 DataType::Timestamp => self.timestamp_type_to_arrow(),
506 DataType::Timestamptz => self.timestamptz_type_to_arrow(),
507 DataType::Interval => self.interval_type_to_arrow(),
508 DataType::Varchar => self.varchar_type_to_arrow(),
509 DataType::Bytea => self.bytea_type_to_arrow(),
510 DataType::Serial => self.serial_type_to_arrow(),
511 DataType::Decimal => return Ok(self.decimal_type_to_arrow(name)),
512 DataType::Jsonb => self.varchar_type_to_arrow(),
513 DataType::Variant => {
514 let mut arrow_field = variant_arrow_field(name);
515 self.add_field_id(&mut arrow_field);
516 return Ok(arrow_field);
517 }
518 DataType::Struct(fields) => self.struct_type_to_arrow(fields)?,
519 DataType::List(list) => self.list_type_to_arrow(list)?,
520 DataType::Map(map) => self.map_type_to_arrow(map)?,
521 DataType::Vector(_) => self.vector_type_to_arrow()?,
522 };
523
524 let mut arrow_field = arrow_schema::Field::new(name, data_type, true);
525 self.add_field_id(&mut arrow_field);
526 Ok(arrow_field)
527 }
528}
529
530#[cfg(test)]
531mod test {
532 use std::sync::Arc;
533
534 use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
535 use parquet_variant::{
536 ShortString, Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16,
537 };
538 use parquet_variant_compute::{VariantArrayBuilder, json_to_variant};
539 use uuid::Uuid;
540
541 use super::arrow_array::{ArrayRef, Decimal128Array};
542 use super::arrow_schema::DataType as ArrowDataType;
543 use super::*;
544 use crate::array::{Decimal, DecimalArray};
545 use crate::types::{MapType, ToText};
546
547 #[test]
548 fn decimal() {
549 let array = DecimalArray::from_iter([
550 None,
551 Some(Decimal::NaN),
552 Some(Decimal::PositiveInf),
553 Some(Decimal::NegativeInf),
554 Some(Decimal::Normalized("123.4".parse().unwrap())),
555 Some(Decimal::Normalized("123.456".parse().unwrap())),
556 ]);
557 let ty = ArrowDataType::Decimal128(6, 3);
558 let arrow_array = IcebergArrowConvert.decimal_to_arrow(&ty, &array).unwrap();
559 let expect_array = Arc::new(
560 Decimal128Array::from(vec![
561 None,
562 None,
563 Some(999999),
564 Some(-999999),
565 Some(123400),
566 Some(123456),
567 ])
568 .with_data_type(ty),
569 ) as ArrayRef;
570 assert_eq!(&arrow_array, &expect_array);
571 }
572
573 #[test]
574 fn decimal_with_large_scale() {
575 let array = DecimalArray::from_iter([
576 None,
577 Some(Decimal::NaN),
578 Some(Decimal::PositiveInf),
579 Some(Decimal::NegativeInf),
580 Some(Decimal::Normalized("123.4".parse().unwrap())),
581 Some(Decimal::Normalized("123.456".parse().unwrap())),
582 ]);
583 let ty = ArrowDataType::Decimal128(ICEBERG_DECIMAL_PRECISION, ICEBERG_DECIMAL_SCALE);
584 let arrow_array = IcebergArrowConvert.decimal_to_arrow(&ty, &array).unwrap();
585 let expect_array = Arc::new(
586 Decimal128Array::from(vec![
587 None,
588 None,
589 Some(99999999999999999999999999999999999999),
591 Some(-99999999999999999999999999999999999999),
592 Some(1234000000000),
593 Some(1234560000000),
594 ])
595 .with_data_type(ty),
596 ) as ArrayRef;
597 assert_eq!(&arrow_array, &expect_array);
598 }
599
600 #[test]
601 fn decimal_edge_cases_risingwave_precision() {
602 let array = DecimalArray::from_iter([
604 Some(Decimal::Normalized(
606 "999999999999999999999999999".parse().unwrap(),
607 )),
608 Some(Decimal::Normalized(
610 "9999999999999999999999999999".parse().unwrap(),
611 )),
612 Some(Decimal::Normalized(
614 "999999999999999999.9999999999".parse().unwrap(),
615 )),
616 Some(Decimal::Normalized(
618 "0.9999999999999999999999999999".parse().unwrap(),
619 )),
620 Some(Decimal::Normalized(
622 "-999999999999999999999999999".parse().unwrap(),
623 )),
624 Some(Decimal::Normalized("1000000000000000000".parse().unwrap())),
626 Some(Decimal::Normalized("0.0000000001".parse().unwrap())),
628 Some(Decimal::Normalized("0.0000000000".parse().unwrap())),
630 ]);
631
632 let ty = ArrowDataType::Decimal128(ICEBERG_DECIMAL_PRECISION, ICEBERG_DECIMAL_SCALE);
633 let arrow_array = IcebergArrowConvert.decimal_to_arrow(&ty, &array).unwrap();
634
635 let expect_array = Arc::new(
636 Decimal128Array::from(vec![
637 Some(9999999999999999999999999990000000000),
639 Some(99999999999999999999999999990000000000),
641 Some(9999999999999999999999999999),
643 Some(9999999999),
645 Some(-9999999999999999999999999990000000000),
647 Some(10000000000000000000000000000),
649 Some(1),
651 Some(0),
653 ])
654 .with_data_type(ty),
655 ) as ArrayRef;
656
657 assert_eq!(&arrow_array, &expect_array);
658 }
659
660 #[test]
661 fn decimal_special_values_roundtrip() {
662 use crate::array::Array;
664
665 let original_array = DecimalArray::from_iter([
666 Some(Decimal::PositiveInf),
667 Some(Decimal::NegativeInf),
668 Some(Decimal::NaN),
669 Some(Decimal::Normalized("123.45".parse().unwrap())),
670 None,
671 ]);
672
673 let ty = ArrowDataType::Decimal128(ICEBERG_DECIMAL_PRECISION, ICEBERG_DECIMAL_SCALE);
675 let arrow_array = IcebergArrowConvert
676 .decimal_to_arrow(&ty, &original_array)
677 .unwrap();
678
679 let arrow_decimal: &arrow_array::Decimal128Array = arrow_array
681 .as_any()
682 .downcast_ref()
683 .expect("should be Decimal128Array");
684
685 let roundtrip_array: DecimalArray = arrow_decimal.try_into().unwrap();
686
687 assert_eq!(original_array.len(), roundtrip_array.len());
689
690 assert_eq!(roundtrip_array.value_at(0), Some(Decimal::PositiveInf));
692
693 assert_eq!(roundtrip_array.value_at(1), Some(Decimal::NegativeInf));
695
696 assert_eq!(roundtrip_array.value_at(2), None);
698
699 assert!(matches!(
701 roundtrip_array.value_at(3),
702 Some(Decimal::Normalized(_))
703 ));
704
705 assert_eq!(roundtrip_array.value_at(4), None);
707 }
708
709 #[test]
710 fn all_variant_internal_types_convert_to_variant() {
711 let long_string = "x".repeat(64);
712 let binary_bytes = [0x0a_u8, 0x0b, 0x0c, 0x0d];
713 let object_and_list_json = Arc::new(arrow_array::StringArray::from(vec![
714 Some(r#"{"a":1,"b":[true,null]}"#),
715 Some(r#"[1,{"x":2},"tail"]"#),
716 ])) as ArrayRef;
717 let object_and_list = json_to_variant(&object_and_list_json).unwrap();
718
719 let timestamp_micros = DateTime::parse_from_rfc3339("2024-11-07T12:33:54.123456+00:00")
720 .unwrap()
721 .with_timezone(&Utc);
722 let timestamp_ntz_micros =
723 NaiveDateTime::parse_from_str("2024-11-07 12:33:54.123456", "%Y-%m-%d %H:%M:%S%.f")
724 .unwrap();
725 let timestamp_nanos = DateTime::parse_from_rfc3339("2024-11-07T12:33:54.123456789+00:00")
726 .unwrap()
727 .with_timezone(&Utc);
728 let timestamp_ntz_nanos =
729 NaiveDateTime::parse_from_str("2024-11-07 12:33:54.123456789", "%Y-%m-%d %H:%M:%S%.f")
730 .unwrap();
731 let time = NaiveTime::from_hms_micro_opt(12, 33, 54, 123_456).unwrap();
732 let uuid = Uuid::parse_str("123e4567-e89b-12d3-a456-426614174000").unwrap();
733
734 let mut builder = VariantArrayBuilder::new(25);
735 builder.append_null(); builder.append_variant(Variant::Null);
737 builder.append_variant(Variant::BooleanTrue);
738 builder.append_variant(Variant::BooleanFalse);
739 builder.append_variant(Variant::Int8(34));
740 builder.append_variant(Variant::Int16(1234));
741 builder.append_variant(Variant::Int32(100_000));
742 builder.append_variant(Variant::Int64(5_000_000_000));
743 builder.append_variant(Variant::Float(3.5));
744 builder.append_variant(Variant::Double(14.25));
745 builder.append_variant(Variant::Double(f64::NAN));
747 builder.append_variant(Variant::Decimal4(
748 VariantDecimal4::try_new(12_345, 0).unwrap(),
749 ));
750 builder.append_variant(Variant::Decimal8(
751 VariantDecimal8::try_new(1_234_567_890_123, 0).unwrap(),
752 ));
753 builder.append_variant(Variant::Decimal16(
754 VariantDecimal16::try_new(1_234_567_890_123_456_789, 0).unwrap(),
755 ));
756 builder.append_variant(Variant::Date(NaiveDate::from_ymd_opt(2024, 11, 7).unwrap()));
757 builder.append_variant(Variant::TimestampMicros(timestamp_micros));
758 builder.append_variant(Variant::TimestampNtzMicros(timestamp_ntz_micros));
759 builder.append_variant(Variant::TimestampNanos(timestamp_nanos));
760 builder.append_variant(Variant::TimestampNtzNanos(timestamp_ntz_nanos));
761 builder.append_variant(Variant::Time(time));
762 builder.append_variant(Variant::Binary(&binary_bytes));
763 builder.append_variant(Variant::ShortString(
764 ShortString::try_new("iceberg").unwrap(),
765 ));
766 builder.append_variant(Variant::from(long_string.as_str()));
767 builder.append_variant(Variant::Uuid(uuid));
768 builder.append_variant(object_and_list.value(0));
769 builder.append_variant(object_and_list.value(1));
770
771 let variant_array = builder.build();
772 let field = variant_array.field("variant_col");
773
774 assert_eq!(
775 IcebergArrowConvert.type_from_field(&field).unwrap(),
776 DataType::Variant
777 );
778
779 let array = Arc::new(variant_array.into_inner()) as ArrayRef;
780
781 let converted = IcebergArrowConvert
782 .array_from_arrow_array(&field, &array)
783 .unwrap();
784 let values = converted
785 .into_variant()
786 .iter()
787 .map(|value| value.map(|value| value.to_text()))
788 .collect::<Vec<_>>();
789
790 let timestamp_micros_json =
791 serde_json::to_string("2024-11-07T12:33:54.123456+00:00").unwrap();
792 let timestamp_ntz_micros_json =
793 serde_json::to_string("2024-11-07T12:33:54.123456").unwrap();
794 let timestamp_nanos_json =
795 serde_json::to_string("2024-11-07T12:33:54.123456789+00:00").unwrap();
796 let timestamp_ntz_nanos_json =
797 serde_json::to_string("2024-11-07T12:33:54.123456789").unwrap();
798 let time_json = serde_json::to_string("12:33:54.123456").unwrap();
799 let binary_json = serde_json::to_string("CgsMDQ==").unwrap();
800 let short_string_json = serde_json::to_string("iceberg").unwrap();
801 let long_string_json = serde_json::to_string(&long_string).unwrap();
802 let uuid_json = serde_json::to_string("123e4567-e89b-12d3-a456-426614174000").unwrap();
803
804 assert_eq!(
805 values,
806 vec![
807 None, Some("null".to_owned()),
809 Some("true".to_owned()),
810 Some("false".to_owned()),
811 Some("34".to_owned()),
812 Some("1234".to_owned()),
813 Some("100000".to_owned()),
814 Some("5000000000".to_owned()),
815 Some("3.5".to_owned()),
816 Some("14.25".to_owned()),
817 Some(r#""NaN""#.to_owned()),
818 Some("12345".to_owned()),
819 Some("1234567890123".to_owned()),
820 Some("1234567890123456789".to_owned()),
821 Some(r#""2024-11-07""#.to_owned()),
822 Some(timestamp_micros_json),
823 Some(timestamp_ntz_micros_json),
824 Some(timestamp_nanos_json),
825 Some(timestamp_ntz_nanos_json),
826 Some(time_json),
827 Some(binary_json),
828 Some(short_string_json),
829 Some(long_string_json),
830 Some(uuid_json),
831 Some(r#"{"a":1,"b":[true,null]}"#.to_owned()),
832 Some(r#"[1,{"x":2},"tail"]"#.to_owned()),
833 ],
834 );
835 }
836
837 #[test]
838 fn variant_type_recurses_in_nested_types() {
839 let payload_field = arrow_schema::Field::new(
840 "payload",
841 ArrowDataType::Struct(
842 vec![
843 Arc::new(variant_arrow_field("top_variant")),
844 Arc::new(arrow_schema::Field::new(
845 "variant_list",
846 ArrowDataType::List(Arc::new(variant_arrow_field("element"))),
847 true,
848 )),
849 Arc::new(arrow_schema::Field::new(
850 "variant_map",
851 ArrowDataType::Map(
852 Arc::new(arrow_schema::Field::new(
853 "entries",
854 ArrowDataType::Struct(
855 vec![
856 Arc::new(arrow_schema::Field::new(
857 "key",
858 ArrowDataType::Utf8,
859 false,
860 )),
861 Arc::new(variant_arrow_field("value")),
862 ]
863 .into(),
864 ),
865 false,
866 )),
867 false,
868 ),
869 true,
870 )),
871 ]
872 .into(),
873 ),
874 true,
875 );
876
877 assert_eq!(
878 IcebergArrowConvert.type_from_field(&payload_field).unwrap(),
879 DataType::Struct(StructType::new(vec![
880 ("top_variant", DataType::Variant),
881 ("variant_list", DataType::list(DataType::Variant)),
882 (
883 "variant_map",
884 DataType::Map(MapType::from_kv(DataType::Varchar, DataType::Variant)),
885 ),
886 ])),
887 );
888 }
889
890 #[test]
891 fn variant_map_key_is_rejected() {
892 let field = arrow_schema::Field::new(
893 "variant_map_key",
894 ArrowDataType::Map(
895 Arc::new(arrow_schema::Field::new(
896 "entries",
897 ArrowDataType::Struct(
898 vec![
899 Arc::new(variant_arrow_field("key")),
900 Arc::new(arrow_schema::Field::new("value", ArrowDataType::Utf8, true)),
901 ]
902 .into(),
903 ),
904 false,
905 )),
906 false,
907 ),
908 true,
909 );
910
911 let err = IcebergArrowConvert.type_from_field(&field).unwrap_err();
912 assert!(err.to_string().contains("invalid map key type: variant"));
913 }
914
915 #[test]
916 fn invalid_variant_value_decodes_to_null() {
917 let field = variant_arrow_field("variant_col");
918 let array = Arc::new(arrow_array::StructArray::from(vec![
920 (
921 Arc::new(arrow_schema::Field::new(
922 "metadata",
923 ArrowDataType::Binary,
924 false,
925 )),
926 Arc::new(arrow_array::BinaryArray::from_iter_values([
927 &[1_u8, 0, 0][..],
928 &[1_u8, 0, 0][..],
929 ])) as ArrayRef,
930 ),
931 (
932 Arc::new(arrow_schema::Field::new(
933 "value",
934 ArrowDataType::Binary,
935 true,
936 )),
937 Arc::new(arrow_array::BinaryArray::from_iter_values([
938 &[0x09_u8, b'H', b'I'][..],
939 &[255_u8][..],
940 ])) as ArrayRef,
941 ),
942 ])) as ArrayRef;
943
944 let converted = IcebergArrowConvert
945 .array_from_arrow_array(&field, &array)
946 .unwrap();
947 let values = converted
948 .into_variant()
949 .iter()
950 .map(|value| value.map(|value| value.to_text()))
951 .collect::<Vec<_>>();
952
953 assert_eq!(values, vec![Some(r#""HI""#.to_owned()), None]);
954 }
955
956 #[test]
957 fn shredded_variant_decodes_as_null() {
958 let field = variant_arrow_field("variant_col");
959 let array = Arc::new(arrow_array::StructArray::from(vec![
960 (
961 Arc::new(arrow_schema::Field::new(
962 "metadata",
963 ArrowDataType::Binary,
964 false,
965 )),
966 Arc::new(arrow_array::BinaryArray::from_iter_values([
967 &[1_u8, 0, 0][..]
968 ])) as ArrayRef,
969 ),
970 (
971 Arc::new(arrow_schema::Field::new(
972 "typed_value",
973 ArrowDataType::Int64,
974 true,
975 )),
976 Arc::new(arrow_array::Int64Array::from(vec![7_i64])) as ArrayRef,
977 ),
978 ])) as ArrayRef;
979
980 let decoded = IcebergArrowConvert
981 .array_from_arrow_array(&field, &array)
982 .unwrap();
983 assert_eq!(decoded.len(), 1);
984 assert!(decoded.as_variant().is_null(0));
985 }
986
987 #[test]
988 fn variant_in_projected_struct_decodes_by_declared_type() {
989 let mut builder = VariantArrayBuilder::new(1);
990 builder.append_variant(Variant::from(7_i64));
991 let variant_array = builder.build();
992 let variant_field = variant_array.field("v");
993 let variant_child = Arc::new(variant_array.into_inner()) as ArrayRef;
994 let extra_child = Arc::new(arrow_array::Int64Array::from(vec![1_i64])) as ArrayRef;
995 let actual = Arc::new(arrow_array::StructArray::from(vec![
996 (
997 Arc::new(arrow_schema::Field::new(
998 "extra",
999 ArrowDataType::Int64,
1000 true,
1001 )),
1002 extra_child,
1003 ),
1004 (Arc::new(variant_field), variant_child),
1005 ])) as ArrayRef;
1006
1007 let declared_field = arrow_schema::Field::new(
1010 "s",
1011 ArrowDataType::Struct(vec![Arc::new(variant_arrow_field("v"))].into()),
1012 true,
1013 );
1014 let converted = IcebergArrowConvert
1015 .array_from_arrow_array(&declared_field, &actual)
1016 .unwrap();
1017 assert_eq!(
1018 converted.data_type(),
1019 DataType::Struct(StructType::new(vec![("v", DataType::Variant)])),
1020 );
1021 let ArrayImpl::Struct(struct_array) = &converted else {
1022 panic!("expected struct array");
1023 };
1024 let values = struct_array.field_at(0).as_variant();
1025 assert_eq!(
1026 values.iter().next().unwrap().map(|v| v.to_text()),
1027 Some("7".to_owned())
1028 );
1029 }
1030}