Skip to main content

iceberg/arrow/
value.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::sync::Arc;
19
20use arrow_array::{
21    Array, ArrayRef, BinaryArray, BinaryViewArray, BooleanArray, Date32Array, Decimal128Array,
22    FixedSizeBinaryArray, FixedSizeListArray, Float32Array, Float64Array, Int32Array, Int64Array,
23    LargeBinaryArray, LargeListArray, LargeStringArray, ListArray, MapArray, StringArray,
24    StructArray, Time64MicrosecondArray, TimestampMicrosecondArray, TimestampNanosecondArray,
25    new_null_array,
26};
27use arrow_buffer::BooleanBuffer;
28use arrow_schema::{DataType, FieldRef, TimeUnit};
29use uuid::Uuid;
30
31use super::get_field_id_from_metadata;
32use crate::error::invalid_data;
33use crate::spec::{
34    ListType, Literal, Map, MapType, NestedField, PartnerAccessor, PrimitiveLiteral, PrimitiveType,
35    SchemaWithPartnerVisitor, Struct, StructType, Type, VariantType, visit_struct_with_partner,
36    visit_type_with_partner,
37};
38use crate::{Error, ErrorKind, Result};
39
40struct ArrowArrayToIcebergStructConverter;
41
42impl SchemaWithPartnerVisitor<ArrayRef> for ArrowArrayToIcebergStructConverter {
43    type T = Vec<Option<Literal>>;
44
45    fn schema(
46        &mut self,
47        _schema: &crate::spec::Schema,
48        _partner: &ArrayRef,
49        value: Vec<Option<Literal>>,
50    ) -> Result<Vec<Option<Literal>>> {
51        Ok(value)
52    }
53
54    fn field(
55        &mut self,
56        field: &crate::spec::NestedFieldRef,
57        _partner: &ArrayRef,
58        value: Vec<Option<Literal>>,
59    ) -> Result<Vec<Option<Literal>>> {
60        // Make there is no null value if the field is required
61        if field.required && value.iter().any(Option::is_none) {
62            return Err(invalid_data!("The field is required but has null value")
63                .with_context("field_id", field.id.to_string())
64                .with_context("field_name", &field.name));
65        }
66        Ok(value)
67    }
68
69    fn r#struct(
70        &mut self,
71        _struct: &StructType,
72        array: &ArrayRef,
73        results: Vec<Vec<Option<Literal>>>,
74    ) -> Result<Vec<Option<Literal>>> {
75        let row_len = results.first().map(|column| column.len()).unwrap_or(0);
76        if let Some(col) = results.iter().find(|col| col.len() != row_len) {
77            return Err(
78                invalid_data!("The struct columns have different row length")
79                    .with_context("first col length", row_len.to_string())
80                    .with_context("actual col length", col.len().to_string()),
81            );
82        }
83
84        let mut struct_literals = Vec::with_capacity(row_len);
85        let mut columns_iters = results
86            .into_iter()
87            .map(|column| column.into_iter())
88            .collect::<Vec<_>>();
89
90        for i in 0..row_len {
91            let mut literals = Vec::with_capacity(columns_iters.len());
92            for column_iter in columns_iters.iter_mut() {
93                literals.push(column_iter.next().unwrap());
94            }
95            if array.is_null(i) {
96                struct_literals.push(None);
97            } else {
98                struct_literals.push(Some(Literal::Struct(Struct::from_iter(literals))));
99            }
100        }
101
102        Ok(struct_literals)
103    }
104
105    fn list(
106        &mut self,
107        list: &ListType,
108        array: &ArrayRef,
109        elements: Vec<Option<Literal>>,
110    ) -> Result<Vec<Option<Literal>>> {
111        if list.element_field.required && elements.iter().any(Option::is_none) {
112            return Err(invalid_data!("The list should not have null value"));
113        }
114        match array.data_type() {
115            DataType::List(_) => {
116                let offset = array
117                    .as_any()
118                    .downcast_ref::<ListArray>()
119                    .ok_or_else(|| invalid_data!("The partner is not a list array"))?
120                    .offsets();
121                // combine the result according to the offset
122                let mut result = Vec::with_capacity(offset.len() - 1);
123                for i in 0..offset.len() - 1 {
124                    let start = offset[i] as usize;
125                    let end = offset[i + 1] as usize;
126                    result.push(Some(Literal::List(elements[start..end].to_vec())));
127                }
128                Ok(result)
129            }
130            DataType::LargeList(_) => {
131                let offset = array
132                    .as_any()
133                    .downcast_ref::<LargeListArray>()
134                    .ok_or_else(|| invalid_data!("The partner is not a large list array"))?
135                    .offsets();
136                // combine the result according to the offset
137                let mut result = Vec::with_capacity(offset.len() - 1);
138                for i in 0..offset.len() - 1 {
139                    let start = offset[i] as usize;
140                    let end = offset[i + 1] as usize;
141                    result.push(Some(Literal::List(elements[start..end].to_vec())));
142                }
143                Ok(result)
144            }
145            DataType::FixedSizeList(_, len) => {
146                let mut result = Vec::with_capacity(elements.len() / *len as usize);
147                for i in 0..elements.len() / *len as usize {
148                    let start = i * *len as usize;
149                    let end = (i + 1) * *len as usize;
150                    result.push(Some(Literal::List(elements[start..end].to_vec())));
151                }
152                Ok(result)
153            }
154            _ => Err(invalid_data!("The partner is not a list type")),
155        }
156    }
157
158    fn map(
159        &mut self,
160        _map: &MapType,
161        partner: &ArrayRef,
162        key_values: Vec<Option<Literal>>,
163        values: Vec<Option<Literal>>,
164    ) -> Result<Vec<Option<Literal>>> {
165        // Make sure key_value and value have the same row length
166        if key_values.len() != values.len() {
167            return Err(invalid_data!(
168                "The key value and value of map should have the same row length"
169            ));
170        }
171
172        let offsets = partner
173            .as_any()
174            .downcast_ref::<MapArray>()
175            .ok_or_else(|| invalid_data!("The partner is not a map array"))?
176            .offsets();
177        // combine the result according to the offset
178        let mut result = Vec::with_capacity(offsets.len() - 1);
179        for i in 0..offsets.len() - 1 {
180            let start = offsets[i] as usize;
181            let end = offsets[i + 1] as usize;
182            let mut map = Map::new();
183            for (key, value) in key_values[start..end].iter().zip(values[start..end].iter()) {
184                map.insert(key.clone().unwrap(), value.clone());
185            }
186            result.push(Some(Literal::Map(map)));
187        }
188        Ok(result)
189    }
190
191    fn primitive(&mut self, p: &PrimitiveType, partner: &ArrayRef) -> Result<Vec<Option<Literal>>> {
192        match p {
193            PrimitiveType::Unknown => Ok(vec![None; partner.len()]),
194            PrimitiveType::Boolean => {
195                let array = partner
196                    .as_any()
197                    .downcast_ref::<BooleanArray>()
198                    .ok_or_else(|| invalid_data!("The partner is not a boolean array"))?;
199                Ok(array.iter().map(|v| v.map(Literal::bool)).collect())
200            }
201            PrimitiveType::Int => {
202                let array = partner
203                    .as_any()
204                    .downcast_ref::<Int32Array>()
205                    .ok_or_else(|| invalid_data!("The partner is not a int32 array"))?;
206                Ok(array.iter().map(|v| v.map(Literal::int)).collect())
207            }
208            PrimitiveType::Long => {
209                let array = partner
210                    .as_any()
211                    .downcast_ref::<Int64Array>()
212                    .ok_or_else(|| invalid_data!("The partner is not a int64 array"))?;
213                Ok(array.iter().map(|v| v.map(Literal::long)).collect())
214            }
215            PrimitiveType::Float => {
216                let array = partner
217                    .as_any()
218                    .downcast_ref::<Float32Array>()
219                    .ok_or_else(|| invalid_data!("The partner is not a float32 array"))?;
220                Ok(array.iter().map(|v| v.map(Literal::float)).collect())
221            }
222            PrimitiveType::Double => {
223                let array = partner
224                    .as_any()
225                    .downcast_ref::<Float64Array>()
226                    .ok_or_else(|| invalid_data!("The partner is not a float64 array"))?;
227                Ok(array.iter().map(|v| v.map(Literal::double)).collect())
228            }
229            PrimitiveType::Decimal { precision, scale } => {
230                let array = partner
231                    .as_any()
232                    .downcast_ref::<Decimal128Array>()
233                    .ok_or_else(|| invalid_data!("The partner is not a decimal128 array"))?;
234                if let DataType::Decimal128(arrow_precision, arrow_scale) = array.data_type()
235                    && (*arrow_precision as u32 != *precision || *arrow_scale as u32 != *scale)
236                {
237                    return Err(invalid_data!(
238                        "The precision or scale ({arrow_precision},{arrow_scale}) of arrow decimal128 array is not compatible with iceberg decimal type ({precision},{scale})"
239                    ));
240                }
241                Ok(array.iter().map(|v| v.map(Literal::decimal)).collect())
242            }
243            PrimitiveType::Date => {
244                let array = partner
245                    .as_any()
246                    .downcast_ref::<Date32Array>()
247                    .ok_or_else(|| invalid_data!("The partner is not a date32 array"))?;
248                Ok(array.iter().map(|v| v.map(Literal::date)).collect())
249            }
250            PrimitiveType::Time => {
251                let array = partner
252                    .as_any()
253                    .downcast_ref::<Time64MicrosecondArray>()
254                    .ok_or_else(|| invalid_data!("The partner is not a time64 array"))?;
255                Ok(array.iter().map(|v| v.map(Literal::time)).collect())
256            }
257            PrimitiveType::Timestamp => {
258                let array = partner
259                    .as_any()
260                    .downcast_ref::<TimestampMicrosecondArray>()
261                    .ok_or_else(|| invalid_data!("The partner is not a timestamp array"))?;
262                Ok(array.iter().map(|v| v.map(Literal::timestamp)).collect())
263            }
264            PrimitiveType::Timestamptz => {
265                let array = partner
266                    .as_any()
267                    .downcast_ref::<TimestampMicrosecondArray>()
268                    .ok_or_else(|| invalid_data!("The partner is not a timestamptz array"))?;
269                Ok(array.iter().map(|v| v.map(Literal::timestamptz)).collect())
270            }
271            PrimitiveType::TimestampNs => {
272                let array = partner
273                    .as_any()
274                    .downcast_ref::<TimestampNanosecondArray>()
275                    .ok_or_else(|| invalid_data!("The partner is not a timestamp_ns array"))?;
276                Ok(array
277                    .iter()
278                    .map(|v| v.map(Literal::timestamp_nano))
279                    .collect())
280            }
281            PrimitiveType::TimestamptzNs => {
282                let array = partner
283                    .as_any()
284                    .downcast_ref::<TimestampNanosecondArray>()
285                    .ok_or_else(|| invalid_data!("The partner is not a timestamptz_ns array"))?;
286                Ok(array
287                    .iter()
288                    .map(|v| v.map(Literal::timestamptz_nano))
289                    .collect())
290            }
291            PrimitiveType::String => {
292                if let Some(array) = partner.as_any().downcast_ref::<LargeStringArray>() {
293                    Ok(array.iter().map(|v| v.map(Literal::string)).collect())
294                } else if let Some(array) = partner.as_any().downcast_ref::<StringArray>() {
295                    Ok(array.iter().map(|v| v.map(Literal::string)).collect())
296                } else {
297                    Err(invalid_data!("The partner is not a string array"))
298                }
299            }
300            PrimitiveType::Uuid => {
301                if let Some(array) = partner.as_any().downcast_ref::<FixedSizeBinaryArray>() {
302                    if array.value_length() != 16 {
303                        return Err(invalid_data!("The partner is not a uuid array"));
304                    }
305                    Ok(array
306                        .iter()
307                        .map(|v| {
308                            v.map(|v| {
309                                Ok(Literal::uuid(Uuid::from_bytes(v.try_into().map_err(
310                                    |_| invalid_data!("Failed to convert binary to uuid"),
311                                )?)))
312                            })
313                            .transpose()
314                        })
315                        .collect::<Result<Vec<_>>>()?)
316                } else {
317                    Err(invalid_data!("The partner is not a uuid array"))
318                }
319            }
320            PrimitiveType::Fixed(len) => {
321                let array = partner
322                    .as_any()
323                    .downcast_ref::<FixedSizeBinaryArray>()
324                    .ok_or_else(|| invalid_data!("The partner is not a fixed array"))?;
325                if array.value_length() != *len as i32 {
326                    return Err(invalid_data!(
327                        "The length of fixed size binary array is not compatible with iceberg fixed type"
328                    ));
329                }
330                Ok(array
331                    .iter()
332                    .map(|v| v.map(|v| Literal::fixed(v.iter().cloned())))
333                    .collect())
334            }
335            PrimitiveType::Binary | PrimitiveType::Geometry(_) | PrimitiveType::Geography(_) => {
336                if let Some(array) = partner.as_any().downcast_ref::<LargeBinaryArray>() {
337                    Ok(array
338                        .iter()
339                        .map(|v| v.map(|v| Literal::binary(v.to_vec())))
340                        .collect())
341                } else if let Some(array) = partner.as_any().downcast_ref::<BinaryArray>() {
342                    Ok(array
343                        .iter()
344                        .map(|v| v.map(|v| Literal::binary(v.to_vec())))
345                        .collect())
346                } else if let Some(array) = partner.as_any().downcast_ref::<BinaryViewArray>() {
347                    Ok(array
348                        .iter()
349                        .map(|v| v.map(|v| Literal::binary(v.to_vec())))
350                        .collect())
351                } else {
352                    Err(invalid_data!("The partner is not a binary array"))
353                }
354            }
355        }
356    }
357
358    fn variant(&mut self, _v: &VariantType, _partner: &ArrayRef) -> Result<Vec<Option<Literal>>> {
359        Err(Error::new(
360            ErrorKind::FeatureUnsupported,
361            "Converting variant Arrow array to Iceberg literal is not supported yet",
362        ))
363    }
364}
365
366/// Defines how Arrow fields are matched with Iceberg fields when converting data.
367///
368/// This enum provides two strategies for matching fields:
369/// - `Id`: Match fields by their ID, which is stored in Arrow field metadata.
370/// - `Name`: Match fields by their name, ignoring the field ID.
371///
372/// The ID matching mode is the default and preferred approach as it's more robust
373/// against schema evolution where field names might change but IDs remain stable.
374/// The name matching mode can be useful in scenarios where field IDs are not available
375/// or when working with systems that don't preserve field IDs.
376#[derive(Clone, Copy, Debug)]
377pub enum FieldMatchMode {
378    /// Match fields by their ID stored in Arrow field metadata
379    Id,
380    /// Match fields by their name, ignoring field IDs
381    Name,
382}
383
384impl FieldMatchMode {
385    /// Determines if an Arrow field matches an Iceberg field based on the matching mode.
386    pub fn match_field(&self, arrow_field: &FieldRef, iceberg_field: &NestedField) -> bool {
387        match self {
388            FieldMatchMode::Id => get_field_id_from_metadata(arrow_field)
389                .map(|id| id == iceberg_field.id)
390                .unwrap_or(false),
391            FieldMatchMode::Name => arrow_field.name() == &iceberg_field.name,
392        }
393    }
394}
395
396/// Partner type representing accessing and walking arrow arrays alongside iceberg schema
397pub struct ArrowArrayAccessor {
398    match_mode: FieldMatchMode,
399}
400
401impl ArrowArrayAccessor {
402    /// Creates a new instance of ArrowArrayAccessor with the default ID matching mode
403    pub fn new() -> Self {
404        Self {
405            match_mode: FieldMatchMode::Id,
406        }
407    }
408
409    /// Creates a new instance of ArrowArrayAccessor with the specified matching mode
410    pub fn new_with_match_mode(match_mode: FieldMatchMode) -> Self {
411        Self { match_mode }
412    }
413}
414
415impl Default for ArrowArrayAccessor {
416    fn default() -> Self {
417        Self::new()
418    }
419}
420
421impl PartnerAccessor<ArrayRef> for ArrowArrayAccessor {
422    fn struct_partner<'a>(&self, schema_partner: &'a ArrayRef) -> Result<&'a ArrayRef> {
423        if !matches!(schema_partner.data_type(), DataType::Struct(_)) {
424            return Err(invalid_data!("The schema partner is not a struct type"));
425        }
426
427        Ok(schema_partner)
428    }
429
430    fn field_partner<'a>(
431        &self,
432        struct_partner: &'a ArrayRef,
433        field: &NestedField,
434    ) -> Result<&'a ArrayRef> {
435        let struct_array = struct_partner
436            .as_any()
437            .downcast_ref::<StructArray>()
438            .ok_or_else(|| {
439                invalid_data!(
440                    "The struct partner is not a struct array, partner: {struct_partner:?}"
441                )
442            })?;
443
444        let field_pos = struct_array
445            .fields()
446            .iter()
447            .position(|arrow_field| self.match_mode.match_field(arrow_field, field))
448            .ok_or_else(|| invalid_data!("Field id {} not found in struct array", field.id))?;
449
450        Ok(struct_array.column(field_pos))
451    }
452
453    fn list_element_partner<'a>(&self, list_partner: &'a ArrayRef) -> Result<&'a ArrayRef> {
454        match list_partner.data_type() {
455            DataType::List(_) => {
456                let list_array = list_partner
457                    .as_any()
458                    .downcast_ref::<ListArray>()
459                    .ok_or_else(|| invalid_data!("The list partner is not a list array"))?;
460                Ok(list_array.values())
461            }
462            DataType::LargeList(_) => {
463                let list_array = list_partner
464                    .as_any()
465                    .downcast_ref::<LargeListArray>()
466                    .ok_or_else(|| invalid_data!("The list partner is not a large list array"))?;
467                Ok(list_array.values())
468            }
469            DataType::FixedSizeList(_, _) => {
470                let list_array = list_partner
471                    .as_any()
472                    .downcast_ref::<FixedSizeListArray>()
473                    .ok_or_else(|| {
474                        invalid_data!("The list partner is not a fixed size list array")
475                    })?;
476                Ok(list_array.values())
477            }
478            _ => Err(invalid_data!("The list partner is not a list type")),
479        }
480    }
481
482    fn map_key_partner<'a>(&self, map_partner: &'a ArrayRef) -> Result<&'a ArrayRef> {
483        let map_array = map_partner
484            .as_any()
485            .downcast_ref::<MapArray>()
486            .ok_or_else(|| invalid_data!("The map partner is not a map array"))?;
487        Ok(map_array.keys())
488    }
489
490    fn map_value_partner<'a>(&self, map_partner: &'a ArrayRef) -> Result<&'a ArrayRef> {
491        let map_array = map_partner
492            .as_any()
493            .downcast_ref::<MapArray>()
494            .ok_or_else(|| invalid_data!("The map partner is not a map array"))?;
495        Ok(map_array.values())
496    }
497}
498
499/// Convert arrow struct array to iceberg struct value array.
500/// This function will assume the schema of arrow struct array is the same as iceberg struct type.
501pub fn arrow_struct_to_literal(
502    struct_array: &ArrayRef,
503    ty: &StructType,
504) -> Result<Vec<Option<Literal>>> {
505    visit_struct_with_partner(
506        ty,
507        struct_array,
508        &mut ArrowArrayToIcebergStructConverter,
509        &ArrowArrayAccessor::new(),
510    )
511}
512
513/// Convert arrow primitive array to iceberg primitive value array.
514/// This function will assume the schema of arrow struct array is the same as iceberg struct type.
515pub fn arrow_primitive_to_literal(
516    primitive_array: &ArrayRef,
517    ty: &Type,
518) -> Result<Vec<Option<Literal>>> {
519    visit_type_with_partner(
520        ty,
521        primitive_array,
522        &mut ArrowArrayToIcebergStructConverter,
523        &ArrowArrayAccessor::new(),
524    )
525}
526
527/// Create a single-element array from a primitive literal.
528///
529/// This is used for creating constant arrays (Run-End Encoded arrays) where we need
530/// a single value that represents all rows.
531pub(crate) fn create_primitive_array_single_element(
532    data_type: &DataType,
533    prim_lit: Option<&PrimitiveLiteral>,
534) -> Result<ArrayRef> {
535    // No value: a single NULL of any (possibly nested) type.
536    if prim_lit.is_none() {
537        return Ok(new_null_array(data_type, 1));
538    }
539    match (data_type, prim_lit) {
540        (DataType::Boolean, Some(PrimitiveLiteral::Boolean(v))) => {
541            Ok(Arc::new(BooleanArray::from(vec![*v])))
542        }
543        (DataType::Int32, Some(PrimitiveLiteral::Int(v))) => {
544            Ok(Arc::new(Int32Array::from(vec![*v])))
545        }
546        (DataType::Date32, Some(PrimitiveLiteral::Int(v))) => {
547            Ok(Arc::new(Date32Array::from(vec![*v])))
548        }
549        (DataType::Int64, Some(PrimitiveLiteral::Long(v))) => {
550            Ok(Arc::new(Int64Array::from(vec![*v])))
551        }
552        (DataType::Timestamp(TimeUnit::Microsecond, timezone), Some(PrimitiveLiteral::Long(v))) => {
553            let array = TimestampMicrosecondArray::from(vec![*v]);
554            if let Some(timezone) = timezone {
555                Ok(Arc::new(array.with_timezone(timezone.clone())))
556            } else {
557                Ok(Arc::new(array))
558            }
559        }
560        (DataType::Timestamp(TimeUnit::Nanosecond, timezone), Some(PrimitiveLiteral::Long(v))) => {
561            let array = TimestampNanosecondArray::from(vec![*v]);
562            if let Some(timezone) = timezone {
563                Ok(Arc::new(array.with_timezone(timezone.clone())))
564            } else {
565                Ok(Arc::new(array))
566            }
567        }
568        (DataType::Float32, Some(PrimitiveLiteral::Float(v))) => {
569            Ok(Arc::new(Float32Array::from(vec![v.0])))
570        }
571        (DataType::Float64, Some(PrimitiveLiteral::Double(v))) => {
572            Ok(Arc::new(Float64Array::from(vec![v.0])))
573        }
574        (DataType::Utf8, Some(PrimitiveLiteral::String(v))) => {
575            Ok(Arc::new(StringArray::from(vec![v.as_str()])))
576        }
577        (DataType::Binary, Some(PrimitiveLiteral::Binary(v))) => {
578            Ok(Arc::new(BinaryArray::from_vec(vec![v.as_slice()])))
579        }
580        (DataType::LargeBinary, Some(PrimitiveLiteral::Binary(v))) => {
581            Ok(Arc::new(LargeBinaryArray::from_vec(vec![v.as_slice()])))
582        }
583        (DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::Int128(v))) => {
584            let array = Decimal128Array::from(vec![{ *v }])
585                .with_precision_and_scale(*precision, *scale)
586                .map_err(|e| {
587                    invalid_data!(
588                            "Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
589                        )
590                })?;
591            Ok(Arc::new(array))
592        }
593        (DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::UInt128(v))) => {
594            let array = Decimal128Array::from(vec![*v as i128])
595                .with_precision_and_scale(*precision, *scale)
596                .map_err(|e| {
597                    invalid_data!(
598                            "Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
599                        )
600                })?;
601            Ok(Arc::new(array))
602        }
603        _ => Err(Error::new(
604            ErrorKind::Unexpected,
605            format!("Unsupported constant type combination: {data_type:?} with {prim_lit:?}"),
606        )),
607    }
608}
609
610/// Create a repeated array from a primitive literal for a given number of rows.
611///
612/// This is used for creating non-constant arrays where we need the same value
613/// repeated for each row.
614pub(crate) fn create_primitive_array_repeated(
615    data_type: &DataType,
616    prim_lit: Option<&PrimitiveLiteral>,
617    num_rows: usize,
618) -> Result<ArrayRef> {
619    // No value to repeat: an all-NULL column of any (possibly nested) type.
620    if prim_lit.is_none() {
621        return Ok(new_null_array(data_type, num_rows));
622    }
623    Ok(match (data_type, prim_lit) {
624        // --- Primitive Some arms ---
625        (DataType::Boolean, Some(PrimitiveLiteral::Boolean(value))) => {
626            let buffer = if *value {
627                BooleanBuffer::new_set(num_rows)
628            } else {
629                BooleanBuffer::new_unset(num_rows)
630            };
631            Arc::new(BooleanArray::new(buffer, None))
632        }
633        (DataType::Int32, Some(PrimitiveLiteral::Int(value))) => {
634            Arc::new(Int32Array::from(vec![*value; num_rows]))
635        }
636        (DataType::Date32, Some(PrimitiveLiteral::Int(value))) => {
637            Arc::new(Date32Array::from(vec![*value; num_rows]))
638        }
639        (DataType::Int64, Some(PrimitiveLiteral::Int(value))) => {
640            Arc::new(Int64Array::from(vec![i64::from(*value); num_rows]))
641        }
642        (DataType::Int64, Some(PrimitiveLiteral::Long(value))) => {
643            Arc::new(Int64Array::from(vec![*value; num_rows]))
644        }
645        (
646            DataType::Timestamp(TimeUnit::Microsecond, timezone),
647            Some(PrimitiveLiteral::Long(value)),
648        ) => {
649            let array = TimestampMicrosecondArray::from(vec![*value; num_rows]);
650            if let Some(timezone) = timezone {
651                Arc::new(array.with_timezone(timezone.clone()))
652            } else {
653                Arc::new(array)
654            }
655        }
656        (
657            DataType::Timestamp(TimeUnit::Nanosecond, timezone),
658            Some(PrimitiveLiteral::Long(value)),
659        ) => {
660            let array = TimestampNanosecondArray::from(vec![*value; num_rows]);
661            if let Some(timezone) = timezone {
662                Arc::new(array.with_timezone(timezone.clone()))
663            } else {
664                Arc::new(array)
665            }
666        }
667        (DataType::Float32, Some(PrimitiveLiteral::Float(value))) => {
668            Arc::new(Float32Array::from(vec![value.0; num_rows]))
669        }
670        (DataType::Float64, Some(PrimitiveLiteral::Double(value))) => {
671            Arc::new(Float64Array::from(vec![value.0; num_rows]))
672        }
673        (DataType::Utf8, Some(PrimitiveLiteral::String(value))) => Arc::new(
674            StringArray::from_iter_values(std::iter::repeat_n(value.as_str(), num_rows)),
675        ),
676        (DataType::Binary, Some(PrimitiveLiteral::Binary(value))) => Arc::new(
677            BinaryArray::from_iter_values(std::iter::repeat_n(value.as_slice(), num_rows)),
678        ),
679        (DataType::LargeBinary, Some(PrimitiveLiteral::Binary(value))) => Arc::new(
680            LargeBinaryArray::from_iter_values(std::iter::repeat_n(value.as_slice(), num_rows)),
681        ),
682        (DataType::FixedSizeBinary(len), Some(PrimitiveLiteral::Binary(value))) => {
683            // try_from_iter infers the width from the data, never from `len`, so a
684            // wrong-width literal would otherwise produce a FixedSizeBinary of the
685            // wrong width instead of erroring.
686            if value.len() != *len as usize {
687                return Err(invalid_data!(
688                    "FixedSizeBinary literal length {} does not match declared width {len}",
689                    value.len()
690                ));
691            }
692
693            Arc::new(
694                FixedSizeBinaryArray::try_from_iter(std::iter::repeat_n(value.as_slice(), num_rows))
695                    .map_err(|e| {
696                        invalid_data!("Failed to create FixedSizeBinary({len}) array: {e}")
697                    })?,
698            )
699        }
700        (DataType::Time64(TimeUnit::Microsecond), Some(PrimitiveLiteral::Long(value))) => {
701            Arc::new(Time64MicrosecondArray::from(vec![*value; num_rows]))
702        }
703        (DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::Int128(value))) => {
704            Arc::new(
705                Decimal128Array::from(vec![*value; num_rows])
706                    .with_precision_and_scale(*precision, *scale)
707                    .map_err(|e| {
708                        invalid_data!(
709                                "Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
710                            )
711                    })?,
712            )
713        }
714        (DataType::Decimal128(precision, scale), Some(PrimitiveLiteral::UInt128(value))) => {
715            Arc::new(
716                Decimal128Array::from(vec![*value as i128; num_rows])
717                    .with_precision_and_scale(*precision, *scale)
718                    .map_err(|e| {
719                        invalid_data!(
720                                "Failed to create Decimal128Array with precision {precision} and scale {scale}: {e}"
721                            )
722                    })?,
723            )
724        }
725
726        (dt, _) => {
727            return Err(Error::new(
728                ErrorKind::Unexpected,
729                format!("unexpected target column type {dt}, prim_lit {prim_lit:?}"),
730            ));
731        }
732    })
733}
734
735#[cfg(test)]
736mod test {
737    use std::collections::HashMap;
738    use std::sync::Arc;
739
740    use arrow_array::builder::{Int32Builder, ListBuilder, MapBuilder, StructBuilder};
741    use arrow_array::{
742        ArrayRef, BinaryArray, BooleanArray, Date32Array, Decimal128Array, Float32Array,
743        Float64Array, Int32Array, Int64Array, StringArray, StructArray, Time64MicrosecondArray,
744        TimestampMicrosecondArray, TimestampNanosecondArray,
745    };
746    use arrow_schema::{DataType, Field, Fields, TimeUnit};
747    use parquet::arrow::PARQUET_FIELD_ID_META_KEY;
748
749    use super::*;
750    use crate::spec::{ListType, Literal, MapType, NestedField, PrimitiveType, StructType, Type};
751
752    #[test]
753    fn test_arrow_struct_to_iceberg_struct() {
754        let bool_array = BooleanArray::from(vec![Some(true), Some(false), None]);
755        let int32_array = Int32Array::from(vec![Some(3), Some(4), None]);
756        let int64_array = Int64Array::from(vec![Some(5), Some(6), None]);
757        let float32_array = Float32Array::from(vec![Some(1.1), Some(2.2), None]);
758        let float64_array = Float64Array::from(vec![Some(3.3), Some(4.4), None]);
759        let decimal_array = Decimal128Array::from(vec![Some(1000), Some(2000), None])
760            .with_precision_and_scale(10, 2)
761            .unwrap();
762        let date_array = Date32Array::from(vec![Some(18628), Some(18629), None]);
763        let time_array = Time64MicrosecondArray::from(vec![Some(123456789), Some(987654321), None]);
764        let timestamp_micro_array = TimestampMicrosecondArray::from(vec![
765            Some(1622548800000000),
766            Some(1622635200000000),
767            None,
768        ]);
769        let timestamp_nano_array = TimestampNanosecondArray::from(vec![
770            Some(1622548800000000000),
771            Some(1622635200000000000),
772            None,
773        ]);
774        let string_array = StringArray::from(vec![Some("a"), Some("b"), None]);
775        let binary_array =
776            BinaryArray::from(vec![Some(b"abc".as_ref()), Some(b"def".as_ref()), None]);
777
778        let struct_array = Arc::new(StructArray::from(vec![
779            (
780                Arc::new(
781                    Field::new("bool_field", DataType::Boolean, true).with_metadata(HashMap::from(
782                        [(PARQUET_FIELD_ID_META_KEY.to_string(), "0".to_string())],
783                    )),
784                ),
785                Arc::new(bool_array) as ArrayRef,
786            ),
787            (
788                Arc::new(
789                    Field::new("int32_field", DataType::Int32, true).with_metadata(HashMap::from(
790                        [(PARQUET_FIELD_ID_META_KEY.to_string(), "2".to_string())],
791                    )),
792                ),
793                Arc::new(int32_array) as ArrayRef,
794            ),
795            (
796                Arc::new(
797                    Field::new("int64_field", DataType::Int64, true).with_metadata(HashMap::from(
798                        [(PARQUET_FIELD_ID_META_KEY.to_string(), "3".to_string())],
799                    )),
800                ),
801                Arc::new(int64_array) as ArrayRef,
802            ),
803            (
804                Arc::new(
805                    Field::new("float32_field", DataType::Float32, true).with_metadata(
806                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "4".to_string())]),
807                    ),
808                ),
809                Arc::new(float32_array) as ArrayRef,
810            ),
811            (
812                Arc::new(
813                    Field::new("float64_field", DataType::Float64, true).with_metadata(
814                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "5".to_string())]),
815                    ),
816                ),
817                Arc::new(float64_array) as ArrayRef,
818            ),
819            (
820                Arc::new(
821                    Field::new("decimal_field", DataType::Decimal128(10, 2), true).with_metadata(
822                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "6".to_string())]),
823                    ),
824                ),
825                Arc::new(decimal_array) as ArrayRef,
826            ),
827            (
828                Arc::new(
829                    Field::new("date_field", DataType::Date32, true).with_metadata(HashMap::from(
830                        [(PARQUET_FIELD_ID_META_KEY.to_string(), "7".to_string())],
831                    )),
832                ),
833                Arc::new(date_array) as ArrayRef,
834            ),
835            (
836                Arc::new(
837                    Field::new("time_field", DataType::Time64(TimeUnit::Microsecond), true)
838                        .with_metadata(HashMap::from([(
839                            PARQUET_FIELD_ID_META_KEY.to_string(),
840                            "8".to_string(),
841                        )])),
842                ),
843                Arc::new(time_array) as ArrayRef,
844            ),
845            (
846                Arc::new(
847                    Field::new(
848                        "timestamp_micro_field",
849                        DataType::Timestamp(TimeUnit::Microsecond, None),
850                        true,
851                    )
852                    .with_metadata(HashMap::from([(
853                        PARQUET_FIELD_ID_META_KEY.to_string(),
854                        "9".to_string(),
855                    )])),
856                ),
857                Arc::new(timestamp_micro_array) as ArrayRef,
858            ),
859            (
860                Arc::new(
861                    Field::new(
862                        "timestamp_nano_field",
863                        DataType::Timestamp(TimeUnit::Nanosecond, None),
864                        true,
865                    )
866                    .with_metadata(HashMap::from([(
867                        PARQUET_FIELD_ID_META_KEY.to_string(),
868                        "10".to_string(),
869                    )])),
870                ),
871                Arc::new(timestamp_nano_array) as ArrayRef,
872            ),
873            (
874                Arc::new(
875                    Field::new("string_field", DataType::Utf8, true).with_metadata(HashMap::from(
876                        [(PARQUET_FIELD_ID_META_KEY.to_string(), "11".to_string())],
877                    )),
878                ),
879                Arc::new(string_array) as ArrayRef,
880            ),
881            (
882                Arc::new(
883                    Field::new("binary_field", DataType::Binary, true).with_metadata(
884                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "12".to_string())]),
885                    ),
886                ),
887                Arc::new(binary_array) as ArrayRef,
888            ),
889        ])) as ArrayRef;
890
891        let iceberg_struct_type = StructType::new(vec![
892            Arc::new(NestedField::optional(
893                0,
894                "bool_field",
895                Type::Primitive(PrimitiveType::Boolean),
896            )),
897            Arc::new(NestedField::optional(
898                2,
899                "int32_field",
900                Type::Primitive(PrimitiveType::Int),
901            )),
902            Arc::new(NestedField::optional(
903                3,
904                "int64_field",
905                Type::Primitive(PrimitiveType::Long),
906            )),
907            Arc::new(NestedField::optional(
908                4,
909                "float32_field",
910                Type::Primitive(PrimitiveType::Float),
911            )),
912            Arc::new(NestedField::optional(
913                5,
914                "float64_field",
915                Type::Primitive(PrimitiveType::Double),
916            )),
917            Arc::new(NestedField::optional(
918                6,
919                "decimal_field",
920                Type::Primitive(PrimitiveType::Decimal {
921                    precision: 10,
922                    scale: 2,
923                }),
924            )),
925            Arc::new(NestedField::optional(
926                7,
927                "date_field",
928                Type::Primitive(PrimitiveType::Date),
929            )),
930            Arc::new(NestedField::optional(
931                8,
932                "time_field",
933                Type::Primitive(PrimitiveType::Time),
934            )),
935            Arc::new(NestedField::optional(
936                9,
937                "timestamp_micro_field",
938                Type::Primitive(PrimitiveType::Timestamp),
939            )),
940            Arc::new(NestedField::optional(
941                10,
942                "timestamp_nao_field",
943                Type::Primitive(PrimitiveType::TimestampNs),
944            )),
945            Arc::new(NestedField::optional(
946                11,
947                "string_field",
948                Type::Primitive(PrimitiveType::String),
949            )),
950            Arc::new(NestedField::optional(
951                12,
952                "binary_field",
953                Type::Primitive(PrimitiveType::Binary),
954            )),
955        ]);
956
957        let result = arrow_struct_to_literal(&struct_array, &iceberg_struct_type).unwrap();
958
959        assert_eq!(result, vec![
960            Some(Literal::Struct(Struct::from_iter(vec![
961                Some(Literal::bool(true)),
962                Some(Literal::int(3)),
963                Some(Literal::long(5)),
964                Some(Literal::float(1.1_f32)),
965                Some(Literal::double(3.3)),
966                Some(Literal::decimal(1000)),
967                Some(Literal::date(18628)),
968                Some(Literal::time(123456789)),
969                Some(Literal::timestamp(1622548800000000)),
970                Some(Literal::timestamp_nano(1622548800000000000)),
971                Some(Literal::string("a".to_string())),
972                Some(Literal::binary(b"abc".to_vec())),
973            ]))),
974            Some(Literal::Struct(Struct::from_iter(vec![
975                Some(Literal::bool(false)),
976                Some(Literal::int(4)),
977                Some(Literal::long(6)),
978                Some(Literal::float(2.2_f32)),
979                Some(Literal::double(4.4)),
980                Some(Literal::decimal(2000)),
981                Some(Literal::date(18629)),
982                Some(Literal::time(987654321)),
983                Some(Literal::timestamp(1622635200000000)),
984                Some(Literal::timestamp_nano(1622635200000000000)),
985                Some(Literal::string("b".to_string())),
986                Some(Literal::binary(b"def".to_vec())),
987            ]))),
988            Some(Literal::Struct(Struct::from_iter(vec![
989                None, None, None, None, None, None, None, None, None, None, None, None,
990            ]))),
991        ]);
992    }
993
994    #[test]
995    fn test_nullable_struct() {
996        // test case that partial columns are null
997        // [
998        //   {a: null, b: null} // child column is null
999        //   {a: 1, b: null},   // partial child column is null
1000        //   null               // parent column is null
1001        // ]
1002        let struct_array = {
1003            let mut builder = StructBuilder::from_fields(
1004                Fields::from(vec![
1005                    Field::new("a", DataType::Int32, true).with_metadata(HashMap::from([(
1006                        PARQUET_FIELD_ID_META_KEY.to_string(),
1007                        "0".to_string(),
1008                    )])),
1009                    Field::new("b", DataType::Int32, true).with_metadata(HashMap::from([(
1010                        PARQUET_FIELD_ID_META_KEY.to_string(),
1011                        "1".to_string(),
1012                    )])),
1013                ]),
1014                3,
1015            );
1016            builder
1017                .field_builder::<Int32Builder>(0)
1018                .unwrap()
1019                .append_null();
1020            builder
1021                .field_builder::<Int32Builder>(1)
1022                .unwrap()
1023                .append_null();
1024            builder.append(true);
1025
1026            builder
1027                .field_builder::<Int32Builder>(0)
1028                .unwrap()
1029                .append_value(1);
1030            builder
1031                .field_builder::<Int32Builder>(1)
1032                .unwrap()
1033                .append_null();
1034            builder.append(true);
1035
1036            builder
1037                .field_builder::<Int32Builder>(0)
1038                .unwrap()
1039                .append_value(1);
1040            builder
1041                .field_builder::<Int32Builder>(1)
1042                .unwrap()
1043                .append_value(1);
1044            builder.append_null();
1045
1046            Arc::new(builder.finish()) as ArrayRef
1047        };
1048
1049        let iceberg_struct_type = StructType::new(vec![
1050            Arc::new(NestedField::optional(
1051                0,
1052                "a",
1053                Type::Primitive(PrimitiveType::Int),
1054            )),
1055            Arc::new(NestedField::optional(
1056                1,
1057                "b",
1058                Type::Primitive(PrimitiveType::Int),
1059            )),
1060        ]);
1061
1062        let result = arrow_struct_to_literal(&struct_array, &iceberg_struct_type).unwrap();
1063        assert_eq!(result, vec![
1064            Some(Literal::Struct(Struct::from_iter(vec![None, None,]))),
1065            Some(Literal::Struct(Struct::from_iter(vec![
1066                Some(Literal::int(1)),
1067                None,
1068            ]))),
1069            None,
1070        ]);
1071    }
1072
1073    #[test]
1074    fn test_empty_struct() {
1075        let struct_array = Arc::new(StructArray::new_null(Fields::empty(), 3)) as ArrayRef;
1076        let iceberg_struct_type = StructType::new(vec![]);
1077        let result = arrow_struct_to_literal(&struct_array, &iceberg_struct_type).unwrap();
1078        assert_eq!(result, vec![None; 0]);
1079    }
1080
1081    #[test]
1082    fn test_arrow_variant_to_literal_is_unsupported() {
1083        // Converting a variant Arrow array back to an Iceberg literal is not implemented;
1084        // the visitor must reject it rather than silently decode it incorrectly.
1085        let variant_child = Arc::new(StructArray::from(vec![
1086            (
1087                Arc::new(Field::new("metadata", DataType::Binary, false)),
1088                Arc::new(BinaryArray::from(vec![Some(b"m".as_ref())])) as ArrayRef,
1089            ),
1090            (
1091                Arc::new(Field::new("value", DataType::Binary, false)),
1092                Arc::new(BinaryArray::from(vec![Some(b"v".as_ref())])) as ArrayRef,
1093            ),
1094        ])) as ArrayRef;
1095
1096        let struct_array = Arc::new(StructArray::from(vec![(
1097            Arc::new(
1098                Field::new("v", variant_child.data_type().clone(), false).with_metadata(
1099                    HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string())]),
1100                ),
1101            ),
1102            variant_child,
1103        )])) as ArrayRef;
1104
1105        let ty = StructType::new(vec![
1106            NestedField::required(1, "v", Type::Variant(VariantType)).into(),
1107        ]);
1108
1109        let err = arrow_struct_to_literal(&struct_array, &ty).unwrap_err();
1110        assert_eq!(err.kind(), ErrorKind::FeatureUnsupported);
1111        assert!(
1112            err.to_string()
1113                .contains("Converting variant Arrow array to Iceberg literal is not supported yet"),
1114            "{err}"
1115        );
1116    }
1117
1118    #[test]
1119    fn test_arrow_geospatial_to_literal() {
1120        let values = Arc::new(LargeBinaryArray::from(vec![
1121            Some(b"wkb-1".as_slice()),
1122            None,
1123            Some(b"wkb-2".as_slice()),
1124        ])) as ArrayRef;
1125        let mut converter = ArrowArrayToIcebergStructConverter;
1126
1127        let result = converter
1128            .primitive(&PrimitiveType::Geometry(Default::default()), &values)
1129            .unwrap();
1130
1131        assert_eq!(result, vec![
1132            Some(Literal::binary(b"wkb-1".to_vec())),
1133            None,
1134            Some(Literal::binary(b"wkb-2".to_vec())),
1135        ]);
1136    }
1137
1138    #[test]
1139    fn test_arrow_geospatial_binary_view_to_literal() {
1140        let wkb = parquet_geospatial::testing::wkb_point_xy(1.0, 2.0);
1141        let values = Arc::new(BinaryViewArray::from(vec![Some(wkb.as_slice()), None])) as ArrayRef;
1142        let ty = Type::Primitive(PrimitiveType::Geography(Default::default()));
1143
1144        let result = arrow_primitive_to_literal(&values, &ty).unwrap();
1145
1146        assert_eq!(result, vec![Some(Literal::binary(wkb)), None]);
1147    }
1148
1149    #[test]
1150    fn test_find_field_by_id() {
1151        // Create Arrow arrays for the nested structure
1152        let field_a_array = Int32Array::from(vec![Some(42), Some(43), None]);
1153        let field_b_array = StringArray::from(vec![Some("value1"), Some("value2"), None]);
1154
1155        // Create the nested struct array with field IDs in metadata
1156        let nested_struct_array =
1157            Arc::new(StructArray::from(vec![
1158                (
1159                    Arc::new(Field::new("field_a", DataType::Int32, true).with_metadata(
1160                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "1".to_string())]),
1161                    )),
1162                    Arc::new(field_a_array) as ArrayRef,
1163                ),
1164                (
1165                    Arc::new(Field::new("field_b", DataType::Utf8, true).with_metadata(
1166                        HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "2".to_string())]),
1167                    )),
1168                    Arc::new(field_b_array) as ArrayRef,
1169                ),
1170            ])) as ArrayRef;
1171
1172        let field_c_array = Int32Array::from(vec![Some(100), Some(200), None]);
1173
1174        // Create the top-level struct array with field IDs in metadata
1175        let struct_array = Arc::new(StructArray::from(vec![
1176            (
1177                Arc::new(
1178                    Field::new(
1179                        "nested_struct",
1180                        DataType::Struct(Fields::from(vec![
1181                            Field::new("field_a", DataType::Int32, true).with_metadata(
1182                                HashMap::from([(
1183                                    PARQUET_FIELD_ID_META_KEY.to_string(),
1184                                    "1".to_string(),
1185                                )]),
1186                            ),
1187                            Field::new("field_b", DataType::Utf8, true).with_metadata(
1188                                HashMap::from([(
1189                                    PARQUET_FIELD_ID_META_KEY.to_string(),
1190                                    "2".to_string(),
1191                                )]),
1192                            ),
1193                        ])),
1194                        true,
1195                    )
1196                    .with_metadata(HashMap::from([(
1197                        PARQUET_FIELD_ID_META_KEY.to_string(),
1198                        "3".to_string(),
1199                    )])),
1200                ),
1201                nested_struct_array,
1202            ),
1203            (
1204                Arc::new(Field::new("field_c", DataType::Int32, true).with_metadata(
1205                    HashMap::from([(PARQUET_FIELD_ID_META_KEY.to_string(), "4".to_string())]),
1206                )),
1207                Arc::new(field_c_array) as ArrayRef,
1208            ),
1209        ])) as ArrayRef;
1210
1211        // Create an ArrowArrayAccessor with ID matching mode
1212        let accessor = ArrowArrayAccessor::new_with_match_mode(FieldMatchMode::Id);
1213
1214        // Test finding fields by ID
1215        let nested_field = NestedField::optional(
1216            3,
1217            "nested_struct",
1218            Type::Struct(StructType::new(vec![
1219                Arc::new(NestedField::optional(
1220                    1,
1221                    "field_a",
1222                    Type::Primitive(PrimitiveType::Int),
1223                )),
1224                Arc::new(NestedField::optional(
1225                    2,
1226                    "field_b",
1227                    Type::Primitive(PrimitiveType::String),
1228                )),
1229            ])),
1230        );
1231        let nested_partner = accessor
1232            .field_partner(&struct_array, &nested_field)
1233            .unwrap();
1234
1235        // Verify we can access the nested field
1236        let field_a = NestedField::optional(1, "field_a", Type::Primitive(PrimitiveType::Int));
1237        let field_a_partner = accessor.field_partner(nested_partner, &field_a).unwrap();
1238
1239        // Verify the field has the expected value
1240        let int_array = field_a_partner
1241            .as_any()
1242            .downcast_ref::<Int32Array>()
1243            .unwrap();
1244        assert_eq!(int_array.value(0), 42);
1245        assert_eq!(int_array.value(1), 43);
1246        assert!(int_array.is_null(2));
1247    }
1248
1249    #[test]
1250    fn test_find_field_by_name() {
1251        // Create Arrow arrays for the nested structure
1252        let field_a_array = Int32Array::from(vec![Some(42), Some(43), None]);
1253        let field_b_array = StringArray::from(vec![Some("value1"), Some("value2"), None]);
1254
1255        // Create the nested struct array WITHOUT field IDs in metadata
1256        let nested_struct_array = Arc::new(StructArray::from(vec![
1257            (
1258                Arc::new(Field::new("field_a", DataType::Int32, true)),
1259                Arc::new(field_a_array) as ArrayRef,
1260            ),
1261            (
1262                Arc::new(Field::new("field_b", DataType::Utf8, true)),
1263                Arc::new(field_b_array) as ArrayRef,
1264            ),
1265        ])) as ArrayRef;
1266
1267        let field_c_array = Int32Array::from(vec![Some(100), Some(200), None]);
1268
1269        // Create the top-level struct array WITHOUT field IDs in metadata
1270        let struct_array = Arc::new(StructArray::from(vec![
1271            (
1272                Arc::new(Field::new(
1273                    "nested_struct",
1274                    DataType::Struct(Fields::from(vec![
1275                        Field::new("field_a", DataType::Int32, true),
1276                        Field::new("field_b", DataType::Utf8, true),
1277                    ])),
1278                    true,
1279                )),
1280                nested_struct_array,
1281            ),
1282            (
1283                Arc::new(Field::new("field_c", DataType::Int32, true)),
1284                Arc::new(field_c_array) as ArrayRef,
1285            ),
1286        ])) as ArrayRef;
1287
1288        // Create an ArrowArrayAccessor with Name matching mode
1289        let accessor = ArrowArrayAccessor::new_with_match_mode(FieldMatchMode::Name);
1290
1291        // Test finding fields by name
1292        let nested_field = NestedField::optional(
1293            3,
1294            "nested_struct",
1295            Type::Struct(StructType::new(vec![
1296                Arc::new(NestedField::optional(
1297                    1,
1298                    "field_a",
1299                    Type::Primitive(PrimitiveType::Int),
1300                )),
1301                Arc::new(NestedField::optional(
1302                    2,
1303                    "field_b",
1304                    Type::Primitive(PrimitiveType::String),
1305                )),
1306            ])),
1307        );
1308        let nested_partner = accessor
1309            .field_partner(&struct_array, &nested_field)
1310            .unwrap();
1311
1312        // Verify we can access the nested field by name
1313        let field_a = NestedField::optional(1, "field_a", Type::Primitive(PrimitiveType::Int));
1314        let field_a_partner = accessor.field_partner(nested_partner, &field_a).unwrap();
1315
1316        // Verify the field has the expected value
1317        let int_array = field_a_partner
1318            .as_any()
1319            .downcast_ref::<Int32Array>()
1320            .unwrap();
1321        assert_eq!(int_array.value(0), 42);
1322        assert_eq!(int_array.value(1), 43);
1323        assert!(int_array.is_null(2));
1324    }
1325
1326    #[test]
1327    fn test_complex_nested() {
1328        // complex nested type for test
1329        // <
1330        //   A: list< struct(a1: int, a2: int) >,
1331        //   B: list< map<int, int> >,
1332        //   C: list< list<int> >,
1333        // >
1334        let struct_type = StructType::new(vec![
1335            Arc::new(NestedField::required(
1336                0,
1337                "A",
1338                Type::List(ListType::new(Arc::new(NestedField::required(
1339                    1,
1340                    "item",
1341                    Type::Struct(StructType::new(vec![
1342                        Arc::new(NestedField::required(
1343                            2,
1344                            "a1",
1345                            Type::Primitive(PrimitiveType::Int),
1346                        )),
1347                        Arc::new(NestedField::required(
1348                            3,
1349                            "a2",
1350                            Type::Primitive(PrimitiveType::Int),
1351                        )),
1352                    ])),
1353                )))),
1354            )),
1355            Arc::new(NestedField::required(
1356                4,
1357                "B",
1358                Type::List(ListType::new(Arc::new(NestedField::required(
1359                    5,
1360                    "item",
1361                    Type::Map(MapType::new(
1362                        NestedField::optional(6, "keys", Type::Primitive(PrimitiveType::Int))
1363                            .into(),
1364                        NestedField::optional(7, "values", Type::Primitive(PrimitiveType::Int))
1365                            .into(),
1366                    )),
1367                )))),
1368            )),
1369            Arc::new(NestedField::required(
1370                8,
1371                "C",
1372                Type::List(ListType::new(Arc::new(NestedField::required(
1373                    9,
1374                    "item",
1375                    Type::List(ListType::new(Arc::new(NestedField::optional(
1376                        10,
1377                        "item",
1378                        Type::Primitive(PrimitiveType::Int),
1379                    )))),
1380                )))),
1381            )),
1382        ]);
1383
1384        // Generate a complex nested struct array
1385        // [
1386        //   {A: [{a1: 10, a2: 20}, {a1: 11, a2: 21}], B: [{(1,100),(3,300)},{(2,200)}], C: [[100,101,102], [200,201]]},
1387        //   {A: [{a1: 12, a2: 22}, {a1: 13, a2: 23}], B: [{(3,300)},{(4,400)}], C: [[300,301,302], [400,401]]},
1388        // ]
1389        let struct_array =
1390            {
1391                let a_struct_a1_builder = Int32Builder::new();
1392                let a_struct_a2_builder = Int32Builder::new();
1393                let a_struct_builder =
1394                    StructBuilder::new(
1395                        vec![
1396                            Field::new("a1", DataType::Int32, false).with_metadata(HashMap::from(
1397                                [(PARQUET_FIELD_ID_META_KEY.to_string(), "2".to_string())],
1398                            )),
1399                            Field::new("a2", DataType::Int32, false).with_metadata(HashMap::from(
1400                                [(PARQUET_FIELD_ID_META_KEY.to_string(), "3".to_string())],
1401                            )),
1402                        ],
1403                        vec![Box::new(a_struct_a1_builder), Box::new(a_struct_a2_builder)],
1404                    );
1405                let a_builder = ListBuilder::new(a_struct_builder);
1406
1407                let map_key_builder = Int32Builder::new();
1408                let map_value_builder = Int32Builder::new();
1409                let map_builder = MapBuilder::new(None, map_key_builder, map_value_builder);
1410                let b_builder = ListBuilder::new(map_builder);
1411
1412                let inner_list_item_builder = Int32Builder::new();
1413                let inner_list_builder = ListBuilder::new(inner_list_item_builder);
1414                let c_builder = ListBuilder::new(inner_list_builder);
1415
1416                let mut top_struct_builder = {
1417                    let a_struct_type =
1418                        DataType::Struct(Fields::from(vec![
1419                            Field::new("a1", DataType::Int32, false).with_metadata(HashMap::from(
1420                                [(PARQUET_FIELD_ID_META_KEY.to_string(), "2".to_string())],
1421                            )),
1422                            Field::new("a2", DataType::Int32, false).with_metadata(HashMap::from(
1423                                [(PARQUET_FIELD_ID_META_KEY.to_string(), "3".to_string())],
1424                            )),
1425                        ]));
1426                    let a_type =
1427                        DataType::List(Arc::new(Field::new("item", a_struct_type.clone(), true)));
1428
1429                    let b_map_entry_struct = Field::new(
1430                        "entries",
1431                        DataType::Struct(Fields::from(vec![
1432                            Field::new("keys", DataType::Int32, false),
1433                            Field::new("values", DataType::Int32, true),
1434                        ])),
1435                        false,
1436                    );
1437                    let b_map_type =
1438                        DataType::Map(Arc::new(b_map_entry_struct), /* sorted_keys = */ false);
1439                    let b_type =
1440                        DataType::List(Arc::new(Field::new("item", b_map_type.clone(), true)));
1441
1442                    let c_inner_list_type =
1443                        DataType::List(Arc::new(Field::new("item", DataType::Int32, true)));
1444                    let c_type = DataType::List(Arc::new(Field::new(
1445                        "item",
1446                        c_inner_list_type.clone(),
1447                        true,
1448                    )));
1449                    StructBuilder::new(
1450                        Fields::from(vec![
1451                            Field::new("A", a_type.clone(), false).with_metadata(HashMap::from([
1452                                (PARQUET_FIELD_ID_META_KEY.to_string(), "0".to_string()),
1453                            ])),
1454                            Field::new("B", b_type.clone(), false).with_metadata(HashMap::from([
1455                                (PARQUET_FIELD_ID_META_KEY.to_string(), "4".to_string()),
1456                            ])),
1457                            Field::new("C", c_type.clone(), false).with_metadata(HashMap::from([
1458                                (PARQUET_FIELD_ID_META_KEY.to_string(), "8".to_string()),
1459                            ])),
1460                        ]),
1461                        vec![
1462                            Box::new(a_builder),
1463                            Box::new(b_builder),
1464                            Box::new(c_builder),
1465                        ],
1466                    )
1467                };
1468
1469                // first row
1470                // {A: [{a1: 10, a2: 20}, {a1: 11, a2: 21}], B: [{(1,100),(3,300)},{(2,200)}], C: [[100,101,102], [200,201]]},
1471                {
1472                    let a_builder = top_struct_builder
1473                        .field_builder::<ListBuilder<StructBuilder>>(0)
1474                        .unwrap();
1475                    let struct_builder = a_builder.values();
1476                    struct_builder
1477                        .field_builder::<Int32Builder>(0)
1478                        .unwrap()
1479                        .append_value(10);
1480                    struct_builder
1481                        .field_builder::<Int32Builder>(1)
1482                        .unwrap()
1483                        .append_value(20);
1484                    struct_builder.append(true);
1485                    let struct_builder = a_builder.values();
1486                    struct_builder
1487                        .field_builder::<Int32Builder>(0)
1488                        .unwrap()
1489                        .append_value(11);
1490                    struct_builder
1491                        .field_builder::<Int32Builder>(1)
1492                        .unwrap()
1493                        .append_value(21);
1494                    struct_builder.append(true);
1495                    a_builder.append(true);
1496                }
1497                {
1498                    let b_builder = top_struct_builder
1499                        .field_builder::<ListBuilder<MapBuilder<Int32Builder, Int32Builder>>>(1)
1500                        .unwrap();
1501                    let map_builder = b_builder.values();
1502                    map_builder.keys().append_value(1);
1503                    map_builder.values().append_value(100);
1504                    map_builder.keys().append_value(3);
1505                    map_builder.values().append_value(300);
1506                    map_builder.append(true).unwrap();
1507
1508                    map_builder.keys().append_value(2);
1509                    map_builder.values().append_value(200);
1510                    map_builder.append(true).unwrap();
1511
1512                    b_builder.append(true);
1513                }
1514                {
1515                    let c_builder = top_struct_builder
1516                        .field_builder::<ListBuilder<ListBuilder<Int32Builder>>>(2)
1517                        .unwrap();
1518                    let inner_list_builder = c_builder.values();
1519                    inner_list_builder.values().append_value(100);
1520                    inner_list_builder.values().append_value(101);
1521                    inner_list_builder.values().append_value(102);
1522                    inner_list_builder.append(true);
1523                    let inner_list_builder = c_builder.values();
1524                    inner_list_builder.values().append_value(200);
1525                    inner_list_builder.values().append_value(201);
1526                    inner_list_builder.append(true);
1527                    c_builder.append(true);
1528                }
1529                top_struct_builder.append(true);
1530
1531                // second row
1532                // {A: [{a1: 12, a2: 22}, {a1: 13, a2: 23}], B: [{(3,300)}], C: [[300,301,302], [400,401]]},
1533                {
1534                    let a_builder = top_struct_builder
1535                        .field_builder::<ListBuilder<StructBuilder>>(0)
1536                        .unwrap();
1537                    let struct_builder = a_builder.values();
1538                    struct_builder
1539                        .field_builder::<Int32Builder>(0)
1540                        .unwrap()
1541                        .append_value(12);
1542                    struct_builder
1543                        .field_builder::<Int32Builder>(1)
1544                        .unwrap()
1545                        .append_value(22);
1546                    struct_builder.append(true);
1547                    let struct_builder = a_builder.values();
1548                    struct_builder
1549                        .field_builder::<Int32Builder>(0)
1550                        .unwrap()
1551                        .append_value(13);
1552                    struct_builder
1553                        .field_builder::<Int32Builder>(1)
1554                        .unwrap()
1555                        .append_value(23);
1556                    struct_builder.append(true);
1557                    a_builder.append(true);
1558                }
1559                {
1560                    let b_builder = top_struct_builder
1561                        .field_builder::<ListBuilder<MapBuilder<Int32Builder, Int32Builder>>>(1)
1562                        .unwrap();
1563                    let map_builder = b_builder.values();
1564                    map_builder.keys().append_value(3);
1565                    map_builder.values().append_value(300);
1566                    map_builder.append(true).unwrap();
1567
1568                    b_builder.append(true);
1569                }
1570                {
1571                    let c_builder = top_struct_builder
1572                        .field_builder::<ListBuilder<ListBuilder<Int32Builder>>>(2)
1573                        .unwrap();
1574                    let inner_list_builder = c_builder.values();
1575                    inner_list_builder.values().append_value(300);
1576                    inner_list_builder.values().append_value(301);
1577                    inner_list_builder.values().append_value(302);
1578                    inner_list_builder.append(true);
1579                    let inner_list_builder = c_builder.values();
1580                    inner_list_builder.values().append_value(400);
1581                    inner_list_builder.values().append_value(401);
1582                    inner_list_builder.append(true);
1583                    c_builder.append(true);
1584                }
1585                top_struct_builder.append(true);
1586
1587                Arc::new(top_struct_builder.finish()) as ArrayRef
1588            };
1589
1590        let result = arrow_struct_to_literal(&struct_array, &struct_type).unwrap();
1591        assert_eq!(result, vec![
1592            Some(Literal::Struct(Struct::from_iter(vec![
1593                Some(Literal::List(vec![
1594                    Some(Literal::Struct(Struct::from_iter(vec![
1595                        Some(Literal::int(10)),
1596                        Some(Literal::int(20)),
1597                    ]))),
1598                    Some(Literal::Struct(Struct::from_iter(vec![
1599                        Some(Literal::int(11)),
1600                        Some(Literal::int(21)),
1601                    ]))),
1602                ])),
1603                Some(Literal::List(vec![
1604                    Some(Literal::Map(Map::from_iter(vec![
1605                        (Literal::int(1), Some(Literal::int(100))),
1606                        (Literal::int(3), Some(Literal::int(300))),
1607                    ]))),
1608                    Some(Literal::Map(Map::from_iter(vec![(
1609                        Literal::int(2),
1610                        Some(Literal::int(200))
1611                    ),]))),
1612                ])),
1613                Some(Literal::List(vec![
1614                    Some(Literal::List(vec![
1615                        Some(Literal::int(100)),
1616                        Some(Literal::int(101)),
1617                        Some(Literal::int(102)),
1618                    ])),
1619                    Some(Literal::List(vec![
1620                        Some(Literal::int(200)),
1621                        Some(Literal::int(201)),
1622                    ])),
1623                ])),
1624            ]))),
1625            Some(Literal::Struct(Struct::from_iter(vec![
1626                Some(Literal::List(vec![
1627                    Some(Literal::Struct(Struct::from_iter(vec![
1628                        Some(Literal::int(12)),
1629                        Some(Literal::int(22)),
1630                    ]))),
1631                    Some(Literal::Struct(Struct::from_iter(vec![
1632                        Some(Literal::int(13)),
1633                        Some(Literal::int(23)),
1634                    ]))),
1635                ])),
1636                Some(Literal::List(vec![Some(Literal::Map(Map::from_iter(
1637                    vec![(Literal::int(3), Some(Literal::int(300))),]
1638                ))),])),
1639                Some(Literal::List(vec![
1640                    Some(Literal::List(vec![
1641                        Some(Literal::int(300)),
1642                        Some(Literal::int(301)),
1643                        Some(Literal::int(302)),
1644                    ])),
1645                    Some(Literal::List(vec![
1646                        Some(Literal::int(400)),
1647                        Some(Literal::int(401)),
1648                    ])),
1649                ])),
1650            ]))),
1651        ]);
1652    }
1653
1654    #[test]
1655    fn test_create_decimal_array_respects_precision() {
1656        // Decimal128Array::from() uses Arrow's default precision (38) instead of the
1657        // target precision, causing RecordBatch construction to fail when schemas don't match.
1658        let target_precision = 18u8;
1659        let target_scale = 10i8;
1660        let target_type = DataType::Decimal128(target_precision, target_scale);
1661        let value = PrimitiveLiteral::Int128(10000000000);
1662
1663        let array = create_primitive_array_single_element(&target_type, Some(&value))
1664            .expect("Failed to create decimal array");
1665
1666        match array.data_type() {
1667            DataType::Decimal128(precision, scale) => {
1668                assert_eq!(*precision, target_precision);
1669                assert_eq!(*scale, target_scale);
1670            }
1671            other => panic!("Expected Decimal128, got {other:?}"),
1672        }
1673    }
1674
1675    #[test]
1676    fn test_create_null_arrays_preserve_type_and_length() {
1677        let data_types = [
1678            DataType::Decimal128(10, 2),
1679            DataType::Timestamp(TimeUnit::Microsecond, Some("+00:00".into())),
1680            DataType::Struct(
1681                vec![
1682                    Field::new("a", DataType::Utf8, true),
1683                    Field::new(
1684                        "ys",
1685                        DataType::List(Arc::new(Field::new("element", DataType::Int64, true))),
1686                        true,
1687                    ),
1688                ]
1689                .into(),
1690            ),
1691            DataType::Null,
1692        ];
1693
1694        // NullArray has no validity bitmap; logical_null_count includes its implicit NULLs.
1695        for data_type in data_types {
1696            let single = create_primitive_array_single_element(&data_type, None)
1697                .unwrap_or_else(|err| panic!("single, type={data_type:?}: {err}"));
1698            assert_eq!(single.data_type(), &data_type, "single, type={data_type:?}");
1699            assert_eq!(single.len(), 1, "single, type={data_type:?}");
1700            if data_type != DataType::Null {
1701                assert_eq!(single.null_count(), 1, "single, type={data_type:?}");
1702            }
1703            assert_eq!(single.logical_null_count(), 1, "single, type={data_type:?}");
1704
1705            for num_rows in [0, 1, 3] {
1706                let repeated = create_primitive_array_repeated(&data_type, None, num_rows)
1707                    .unwrap_or_else(|err| {
1708                        panic!("repeated, type={data_type:?}, rows={num_rows}: {err}")
1709                    });
1710                assert_eq!(
1711                    repeated.data_type(),
1712                    &data_type,
1713                    "repeated, type={data_type:?}, rows={num_rows}"
1714                );
1715                assert_eq!(
1716                    repeated.len(),
1717                    num_rows,
1718                    "repeated, type={data_type:?}, rows={num_rows}"
1719                );
1720                if data_type != DataType::Null {
1721                    assert_eq!(
1722                        repeated.null_count(),
1723                        num_rows,
1724                        "repeated, type={data_type:?}, rows={num_rows}"
1725                    );
1726                }
1727                assert_eq!(
1728                    repeated.logical_null_count(),
1729                    num_rows,
1730                    "repeated, type={data_type:?}, rows={num_rows}"
1731                );
1732            }
1733        }
1734    }
1735
1736    #[test]
1737    fn test_create_null_array_rejects_non_null_literal() {
1738        let literal = Some(PrimitiveLiteral::Int(1));
1739
1740        assert!(create_primitive_array_single_element(&DataType::Null, literal.as_ref()).is_err());
1741        assert!(create_primitive_array_repeated(&DataType::Null, literal.as_ref(), 2).is_err());
1742        assert_eq!(
1743            create_primitive_array_single_element(&DataType::Null, None)
1744                .unwrap()
1745                .len(),
1746            1
1747        );
1748        assert_eq!(
1749            create_primitive_array_repeated(&DataType::Null, None, 2)
1750                .unwrap()
1751                .len(),
1752            2
1753        );
1754    }
1755
1756    #[test]
1757    fn test_create_decimal_array_repeated_respects_precision() {
1758        // Ensure repeated arrays also respect target precision, not Arrow's default.
1759        let target_precision = 18u8;
1760        let target_scale = 10i8;
1761        let target_type = DataType::Decimal128(target_precision, target_scale);
1762        let value = PrimitiveLiteral::Int128(10000000000);
1763        let num_rows = 5;
1764
1765        let array = create_primitive_array_repeated(&target_type, Some(&value), num_rows)
1766            .expect("Failed to create repeated decimal array");
1767
1768        match array.data_type() {
1769            DataType::Decimal128(precision, scale) => {
1770                assert_eq!(*precision, target_precision);
1771                assert_eq!(*scale, target_scale);
1772            }
1773            other => panic!("Expected Decimal128, got {other:?}"),
1774        }
1775
1776        assert_eq!(array.len(), num_rows);
1777    }
1778
1779    #[test]
1780    fn test_create_timestamp_microsecond_array_repeated() {
1781        let target_type = DataType::Timestamp(TimeUnit::Microsecond, None);
1782        let value = PrimitiveLiteral::Long(1_740_600_000_000_000);
1783        let num_rows = 3;
1784
1785        let array = create_primitive_array_repeated(&target_type, Some(&value), num_rows)
1786            .expect("Failed to create repeated timestamp microsecond array");
1787
1788        assert_eq!(array.data_type(), &target_type);
1789        assert_eq!(array.len(), num_rows);
1790    }
1791
1792    #[test]
1793    fn test_create_timestamp_microsecond_with_timezone_array_repeated() {
1794        let target_type = DataType::Timestamp(TimeUnit::Microsecond, Some("UTC".into()));
1795        let value = PrimitiveLiteral::Long(1_740_600_000_000_000);
1796        let num_rows = 2;
1797
1798        let array = create_primitive_array_repeated(&target_type, Some(&value), num_rows)
1799            .expect("Failed to create repeated timestamp microsecond array with timezone");
1800
1801        assert_eq!(array.data_type(), &target_type);
1802        assert_eq!(array.len(), num_rows);
1803    }
1804
1805    #[test]
1806    fn test_create_string_and_binary_arrays_repeated() {
1807        let text = "partition-value-2026";
1808        let bytes: Vec<u8> = vec![0xDE, 0xAD, 0xBE, 0xEF];
1809        let num_rows = 4;
1810
1811        let utf8 = create_primitive_array_repeated(
1812            &DataType::Utf8,
1813            Some(&PrimitiveLiteral::String(text.to_string())),
1814            num_rows,
1815        )
1816        .unwrap();
1817        let utf8 = utf8.as_any().downcast_ref::<StringArray>().unwrap();
1818        assert_eq!(utf8.len(), num_rows);
1819        assert!((0..num_rows).all(|i| utf8.value(i) == text));
1820
1821        let binary = create_primitive_array_repeated(
1822            &DataType::Binary,
1823            Some(&PrimitiveLiteral::Binary(bytes.clone())),
1824            num_rows,
1825        )
1826        .unwrap();
1827        let binary = binary.as_any().downcast_ref::<BinaryArray>().unwrap();
1828        assert_eq!(binary.len(), num_rows);
1829        assert!((0..num_rows).all(|i| binary.value(i) == bytes.as_slice()));
1830
1831        let large = create_primitive_array_repeated(
1832            &DataType::LargeBinary,
1833            Some(&PrimitiveLiteral::Binary(bytes.clone())),
1834            num_rows,
1835        )
1836        .unwrap();
1837        let large = large.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
1838        assert_eq!(large.len(), num_rows);
1839        assert!((0..num_rows).all(|i| large.value(i) == bytes.as_slice()));
1840
1841        let fixed = create_primitive_array_repeated(
1842            &DataType::FixedSizeBinary(bytes.len() as i32),
1843            Some(&PrimitiveLiteral::Binary(bytes.clone())),
1844            num_rows,
1845        )
1846        .unwrap();
1847        let fixed = fixed
1848            .as_any()
1849            .downcast_ref::<FixedSizeBinaryArray>()
1850            .unwrap();
1851        assert_eq!(fixed.len(), num_rows);
1852        assert!((0..num_rows).all(|i| fixed.value(i) == bytes.as_slice()));
1853    }
1854
1855    #[test]
1856    fn test_create_boolean_array_repeated() {
1857        let num_rows = 4;
1858
1859        for value in [true, false] {
1860            let array = create_primitive_array_repeated(
1861                &DataType::Boolean,
1862                Some(&PrimitiveLiteral::Boolean(value)),
1863                num_rows,
1864            )
1865            .unwrap();
1866            let array = array.as_any().downcast_ref::<BooleanArray>().unwrap();
1867            assert_eq!(array.len(), num_rows);
1868            assert_eq!(array.null_count(), 0);
1869            assert!((0..num_rows).all(|i| array.value(i) == value));
1870        }
1871
1872        // num_rows == 0 must produce an empty (not one-element) array.
1873        let empty = create_primitive_array_repeated(
1874            &DataType::Boolean,
1875            Some(&PrimitiveLiteral::Boolean(true)),
1876            0,
1877        )
1878        .unwrap();
1879        assert_eq!(empty.len(), 0);
1880        assert_eq!(empty.null_count(), 0);
1881    }
1882
1883    #[test]
1884    fn test_create_string_array_repeated_empty() {
1885        // num_rows == 0 must produce an empty (not one-element) array.
1886        let array = create_primitive_array_repeated(
1887            &DataType::Utf8,
1888            Some(&PrimitiveLiteral::String("x".to_string())),
1889            0,
1890        )
1891        .unwrap();
1892        assert_eq!(array.len(), 0);
1893    }
1894
1895    #[test]
1896    fn test_create_fixed_size_binary_repeated_wrong_width_errors() {
1897        // A literal whose length differs from the declared width must error rather
1898        // than silently producing a FixedSizeBinary array of the literal's width.
1899        let err = create_primitive_array_repeated(
1900            &DataType::FixedSizeBinary(4),
1901            Some(&PrimitiveLiteral::Binary(vec![0x01, 0x02, 0x03])),
1902            2,
1903        )
1904        .unwrap_err();
1905        assert!(err.to_string().contains("does not match declared width"));
1906    }
1907
1908    #[test]
1909    fn test_create_fixed_size_binary_repeated_empty_errors() {
1910        // arrow's try_from_iter cannot infer the fixed width from an empty iterator,
1911        // so num_rows == 0 over a fixed[n] column surfaces as an error rather than an
1912        // empty fixed[n] array.
1913        let result = create_primitive_array_repeated(
1914            &DataType::FixedSizeBinary(4),
1915            Some(&PrimitiveLiteral::Binary(vec![0xDE, 0xAD, 0xBE, 0xEF])),
1916            0,
1917        );
1918        assert!(result.is_err());
1919    }
1920}