proof_of_sql/base/arrow/
arrow_array_to_column_conversion.rs

1use super::scalar_and_i256_conversions::convert_i256_to_scalar;
2use crate::base::{
3    database::Column,
4    math::decimal::Precision,
5    posql_time::{PoSQLTimeUnit, PoSQLTimeZone, PoSQLTimestampError},
6    scalar::{Scalar, ScalarExt},
7};
8use arrow::{
9    array::{
10        Array, ArrayRef, BinaryArray, BooleanArray, Decimal128Array, Decimal256Array, Int16Array,
11        Int32Array, Int64Array, Int8Array, StringArray, TimestampMicrosecondArray,
12        TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray, UInt8Array,
13    },
14    datatypes::{i256, DataType, TimeUnit as ArrowTimeUnit},
15};
16use bumpalo::Bump;
17use core::ops::Range;
18use snafu::Snafu;
19
20#[derive(Snafu, Debug, PartialEq)]
21/// Errors caused by conversions between Arrow and owned types.
22pub enum ArrowArrayToColumnConversionError {
23    /// This error occurs when an array contains a non-zero number of null elements
24    #[snafu(display("arrow array must not contain nulls"))]
25    ArrayContainsNulls,
26    /// This error occurs when trying to convert from an unsupported arrow type.
27    #[snafu(display(
28        "unsupported type: attempted conversion from ArrayRef of type {datatype} to OwnedColumn"
29    ))]
30    UnsupportedType {
31        /// The unsupported datatype
32        datatype: DataType,
33    },
34    /// Variant for decimal errors
35    #[snafu(transparent)]
36    DecimalError {
37        /// The underlying source error
38        source: crate::base::math::decimal::DecimalError,
39    },
40    /// This error occurs when trying to convert from an i256 to a Scalar.
41    #[snafu(display("decimal conversion failed: {number}"))]
42    DecimalConversionFailed {
43        /// The `i256` value for which conversion is attempted
44        number: i256,
45    },
46    /// This error occurs when the specified range is out of the bounds of the array.
47    #[snafu(display("index out of bounds: the len is {len} but the index is {index}"))]
48    IndexOutOfBounds {
49        /// The actual length of the array
50        len: usize,
51        /// The out of bounds index requested
52        index: usize,
53    },
54    /// Using `TimeError` to handle all time-related errors
55    #[snafu(transparent)]
56    TimestampConversionError {
57        /// The underlying source error
58        source: PoSQLTimestampError,
59    },
60}
61
62/// This trait is used to provide utility functions to convert [`ArrayRef`]s into proof types (Column, Scalars, etc.)
63pub trait ArrayRefExt {
64    /// Convert an [`ArrayRef`] into a Proof of SQL Column type
65    ///
66    /// Parameters:
67    /// - `alloc`: used to allocate a slice of data when necessary
68    ///    (vide [`StringArray`] into `Column::HashedBytes((_,_))`.
69    ///
70    /// - `range`: used to get a subslice out of [`ArrayRef`].
71    ///
72    /// - `scals`: scalar representation of each element in the [`ArrayRef`].
73    ///    Some types don't require this slice (see [`Column::BigInt`]). But for types requiring it,
74    ///    `scals` must be provided and have a length equal to `range.len()`.
75    ///
76    /// Note: this function must not be called from unsupported or nullable arrays as it will panic.
77    fn to_column<'a, S: Scalar>(
78        &'a self,
79        alloc: &'a Bump,
80        range: &Range<usize>,
81        scals: Option<&'a [S]>,
82    ) -> Result<Column<'a, S>, ArrowArrayToColumnConversionError>;
83}
84
85impl ArrayRefExt for ArrayRef {
86    /// Converts the given `ArrowArray` into a [`Column`] data type based on its [`DataType`]. Returns an
87    /// empty [`Column`] for any empty range if it is in-bounds.
88    ///
89    /// # Parameters
90    /// - `alloc`: Reference to a `Bump` allocator used for memory allocation during the conversion.
91    /// - `range`: Reference to a `Range<usize>` specifying the slice of the array to convert.
92    /// - `precomputed_scals`: Optional reference to a slice of `TestScalars` values.
93    ///    `VarChar` columns store hashes to their values as scalars, which can be provided here.
94    ///
95    /// # Supported types
96    /// - For `DataType::Int64` and `DataType::Decimal128(38, 0)`, it slices the array
97    ///   based on the provided range and returns the corresponding `BigInt` or `Int128` column.
98    /// - Decimal256, converts arrow i256 columns into Decimal75(precision, scale) columns.
99    /// - For `DataType::Utf8`, it extracts string values and scalar values (if `precomputed_scals`
100    ///   is provided) for the specified range and returns a `VarChar` column.
101    ///
102    /// # Panics
103    /// - When any range is OOB, i.e. indexing 3..6 or 5..5 on array of size 2.
104    #[expect(clippy::too_many_lines)]
105    fn to_column<'a, S: Scalar>(
106        &'a self,
107        alloc: &'a Bump,
108        range: &Range<usize>,
109        precomputed_scals: Option<&'a [S]>,
110    ) -> Result<Column<'a, S>, ArrowArrayToColumnConversionError> {
111        // Start by checking for nulls
112        if self.null_count() != 0 {
113            return Err(ArrowArrayToColumnConversionError::ArrayContainsNulls);
114        }
115
116        // Before performing any operations, check if the range is out of bounds
117        if range.end > self.len() {
118            return Err(ArrowArrayToColumnConversionError::IndexOutOfBounds {
119                len: self.len(),
120                index: range.end,
121            });
122        }
123        // Match supported types and attempt conversion
124        match self.data_type() {
125            DataType::Boolean => {
126                if let Some(array) = self.as_any().downcast_ref::<BooleanArray>() {
127                    let boolean_slice = array
128                        .iter()
129                        .skip(range.start)
130                        .take(range.len())
131                        .collect::<Option<Vec<bool>>>()
132                        .ok_or(ArrowArrayToColumnConversionError::ArrayContainsNulls)?;
133                    let values = alloc.alloc_slice_fill_with(range.len(), |i| boolean_slice[i]);
134                    Ok(Column::Boolean(values))
135                } else {
136                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
137                        datatype: self.data_type().clone(),
138                    })
139                }
140            }
141            DataType::UInt8 => {
142                if let Some(array) = self.as_any().downcast_ref::<UInt8Array>() {
143                    Ok(Column::Uint8(&array.values()[range.start..range.end]))
144                } else {
145                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
146                        datatype: self.data_type().clone(),
147                    })
148                }
149            }
150            DataType::Int8 => {
151                if let Some(array) = self.as_any().downcast_ref::<Int8Array>() {
152                    Ok(Column::TinyInt(&array.values()[range.start..range.end]))
153                } else {
154                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
155                        datatype: self.data_type().clone(),
156                    })
157                }
158            }
159            DataType::Int16 => {
160                if let Some(array) = self.as_any().downcast_ref::<Int16Array>() {
161                    Ok(Column::SmallInt(&array.values()[range.start..range.end]))
162                } else {
163                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
164                        datatype: self.data_type().clone(),
165                    })
166                }
167            }
168            DataType::Int32 => {
169                if let Some(array) = self.as_any().downcast_ref::<Int32Array>() {
170                    Ok(Column::Int(&array.values()[range.start..range.end]))
171                } else {
172                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
173                        datatype: self.data_type().clone(),
174                    })
175                }
176            }
177            DataType::Int64 => {
178                if let Some(array) = self.as_any().downcast_ref::<Int64Array>() {
179                    Ok(Column::BigInt(&array.values()[range.start..range.end]))
180                } else {
181                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
182                        datatype: self.data_type().clone(),
183                    })
184                }
185            }
186            DataType::Decimal128(38, 0) => {
187                if let Some(array) = self.as_any().downcast_ref::<Decimal128Array>() {
188                    Ok(Column::Int128(&array.values()[range.start..range.end]))
189                } else {
190                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
191                        datatype: self.data_type().clone(),
192                    })
193                }
194            }
195            DataType::Decimal256(precision, scale) if *precision <= 75 => {
196                if let Some(array) = self.as_any().downcast_ref::<Decimal256Array>() {
197                    let i256_slice = &array.values()[range.start..range.end];
198                    let scalars = alloc.alloc_slice_fill_default(i256_slice.len());
199                    for (scalar, value) in scalars.iter_mut().zip(i256_slice) {
200                        *scalar = convert_i256_to_scalar(value).ok_or(
201                            ArrowArrayToColumnConversionError::DecimalConversionFailed {
202                                number: *value,
203                            },
204                        )?;
205                    }
206                    Ok(Column::Decimal75(
207                        Precision::new(*precision)?,
208                        *scale,
209                        scalars,
210                    ))
211                } else {
212                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
213                        datatype: self.data_type().clone(),
214                    })
215                }
216            }
217            // Handle all possible TimeStamp TimeUnit instances
218            DataType::Timestamp(time_unit, tz) => match time_unit {
219                ArrowTimeUnit::Second => {
220                    if let Some(array) = self.as_any().downcast_ref::<TimestampSecondArray>() {
221                        Ok(Column::TimestampTZ(
222                            PoSQLTimeUnit::Second,
223                            PoSQLTimeZone::try_from(tz)?,
224                            &array.values()[range.start..range.end],
225                        ))
226                    } else {
227                        Err(ArrowArrayToColumnConversionError::UnsupportedType {
228                            datatype: self.data_type().clone(),
229                        })
230                    }
231                }
232                ArrowTimeUnit::Millisecond => {
233                    if let Some(array) = self.as_any().downcast_ref::<TimestampMillisecondArray>() {
234                        Ok(Column::TimestampTZ(
235                            PoSQLTimeUnit::Millisecond,
236                            PoSQLTimeZone::try_from(tz)?,
237                            &array.values()[range.start..range.end],
238                        ))
239                    } else {
240                        Err(ArrowArrayToColumnConversionError::UnsupportedType {
241                            datatype: self.data_type().clone(),
242                        })
243                    }
244                }
245                ArrowTimeUnit::Microsecond => {
246                    if let Some(array) = self.as_any().downcast_ref::<TimestampMicrosecondArray>() {
247                        Ok(Column::TimestampTZ(
248                            PoSQLTimeUnit::Microsecond,
249                            PoSQLTimeZone::try_from(tz)?,
250                            &array.values()[range.start..range.end],
251                        ))
252                    } else {
253                        Err(ArrowArrayToColumnConversionError::UnsupportedType {
254                            datatype: self.data_type().clone(),
255                        })
256                    }
257                }
258                ArrowTimeUnit::Nanosecond => {
259                    if let Some(array) = self.as_any().downcast_ref::<TimestampNanosecondArray>() {
260                        Ok(Column::TimestampTZ(
261                            PoSQLTimeUnit::Nanosecond,
262                            PoSQLTimeZone::try_from(tz)?,
263                            &array.values()[range.start..range.end],
264                        ))
265                    } else {
266                        Err(ArrowArrayToColumnConversionError::UnsupportedType {
267                            datatype: self.data_type().clone(),
268                        })
269                    }
270                }
271            },
272            DataType::Utf8 => {
273                if let Some(array) = self.as_any().downcast_ref::<StringArray>() {
274                    let vals = alloc
275                        .alloc_slice_fill_with(range.end - range.start, |i| -> &'a str {
276                            array.value(range.start + i)
277                        });
278
279                    let scals = if let Some(scals) = precomputed_scals {
280                        &scals[range.start..range.end]
281                    } else {
282                        alloc.alloc_slice_fill_with(vals.len(), |i| -> S { vals[i].into() })
283                    };
284
285                    Ok(Column::VarChar((vals, scals)))
286                } else {
287                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
288                        datatype: self.data_type().clone(),
289                    })
290                }
291            }
292            DataType::Binary => {
293                if let Some(array) = self.as_any().downcast_ref::<BinaryArray>() {
294                    let vals = alloc
295                        .alloc_slice_fill_with(range.end - range.start, |i| -> &'a [u8] {
296                            array.value(range.start + i)
297                        });
298
299                    let scals = if let Some(scals) = precomputed_scals {
300                        &scals[range.start..range.end]
301                    } else {
302                        alloc.alloc_slice_fill_with(vals.len(), |i| {
303                            S::from_byte_slice_via_hash(vals[i])
304                        })
305                    };
306
307                    Ok(Column::VarBinary((vals, scals)))
308                } else {
309                    Err(ArrowArrayToColumnConversionError::UnsupportedType {
310                        datatype: self.data_type().clone(),
311                    })
312                }
313            }
314            data_type => Err(ArrowArrayToColumnConversionError::UnsupportedType {
315                datatype: data_type.clone(),
316            }),
317        }
318    }
319}
320
321#[cfg(test)]
322mod tests {
323
324    use super::*;
325    use crate::{
326        base::{database::OwnedColumn, scalar::test_scalar::TestScalar},
327        proof_primitive::dory::DoryScalar,
328    };
329    use alloc::sync::Arc;
330    use arrow::array::Decimal256Builder;
331    use core::str::FromStr;
332    use proptest::prelude::*;
333
334    #[test]
335    fn we_can_convert_timestamp_array_normal_range() {
336        let alloc = Bump::new();
337        let data = vec![1_625_072_400, 1_625_076_000, 1_625_083_200]; // Example Unix timestamps
338        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
339            data.clone().into(),
340            Some("Z"),
341        ));
342
343        let result = array.to_column::<TestScalar>(&alloc, &(1..3), None);
344        assert_eq!(
345            result.unwrap(),
346            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &data[1..3])
347        );
348    }
349
350    #[test]
351    fn we_can_build_an_empty_column_from_an_empty_range_timestamp() {
352        let alloc = Bump::new();
353        let data = vec![1_625_072_400, 1_625_076_000]; // Example Unix timestamps
354        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
355            data.into(),
356            Some("+00:00"),
357        ));
358
359        let result = array
360            .to_column::<DoryScalar>(&alloc, &(2..2), None)
361            .unwrap();
362        assert_eq!(
363            result,
364            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &[])
365        );
366    }
367
368    #[test]
369    fn we_can_convert_timestamp_array_empty_range() {
370        let alloc = Bump::new();
371        let data = vec![1_625_072_400, 1_625_076_000, 1_625_083_200]; // Example Unix timestamps
372        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
373            data.into(),
374            Some("+0:00"),
375        ));
376
377        let result = array.to_column::<DoryScalar>(&alloc, &(1..1), None);
378        assert_eq!(
379            result.unwrap(),
380            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &[])
381        );
382    }
383
384    #[test]
385    fn we_cannot_convert_timestamp_array_oob_range() {
386        let alloc = Bump::new();
387        let data = vec![1_625_072_400, 1_625_076_000, 1_625_083_200];
388        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
389            data.into(),
390            Some("Utc"),
391        ));
392
393        let result = array.to_column::<TestScalar>(&alloc, &(3..5), None);
394        assert_eq!(
395            result,
396            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 5 })
397        );
398    }
399
400    #[test]
401    fn we_can_convert_timestamp_array_with_nulls() {
402        let alloc = Bump::new();
403        let data = vec![Some(1_625_072_400), None, Some(1_625_083_200)];
404        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
405            data.into(),
406            Some("00:00"),
407        ));
408
409        let result = array.to_column::<DoryScalar>(&alloc, &(0..3), None);
410        assert!(matches!(
411            result,
412            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
413        ));
414    }
415
416    #[test]
417    fn we_cannot_convert_utf8_array_oob_range() {
418        let alloc = Bump::new();
419        let array: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "test"]));
420        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
421        assert_eq!(
422            result,
423            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
424        );
425    }
426
427    #[test]
428    fn we_can_convert_utf8_array_normal_range() {
429        let alloc = Bump::new();
430        let array: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "test"]));
431        let result = array.to_column::<TestScalar>(&alloc, &(1..3), None);
432        let expected_vals = vec!["world", "test"];
433        let expected_scals: Vec<TestScalar> = expected_vals.iter().map(|&v| v.into()).collect();
434
435        assert_eq!(
436            result.unwrap(),
437            Column::VarChar((expected_vals.as_slice(), expected_scals.as_slice()))
438        );
439    }
440
441    #[test]
442    fn we_can_convert_utf8_array_empty_range() {
443        let alloc = Bump::new();
444        let array: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "test"]));
445        let result = array.to_column::<DoryScalar>(&alloc, &(1..1), None);
446        assert_eq!(result.unwrap(), Column::VarChar((&[], &[])));
447    }
448
449    #[test]
450    fn we_can_convert_utf8_array_with_nulls() {
451        let alloc = Bump::new();
452        let array: ArrayRef = Arc::new(StringArray::from(vec![Some("hello"), None, Some("test")]));
453        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
454        assert!(matches!(
455            result,
456            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
457        ));
458    }
459
460    #[test]
461    fn we_can_convert_utf8_array_with_precomputed_scalars() {
462        let alloc = Bump::new();
463        let array: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "test"]));
464        let precomputed_scals: Vec<DoryScalar> = ["hello", "world", "test"]
465            .iter()
466            .map(|&v| v.into())
467            .collect();
468        let result = array.to_column::<DoryScalar>(&alloc, &(1..3), Some(&precomputed_scals));
469        let expected_vals = vec!["world", "test"];
470        let expected_scals = &precomputed_scals[1..3];
471
472        assert_eq!(
473            result.unwrap(),
474            Column::VarChar((expected_vals.as_slice(), expected_scals))
475        );
476    }
477
478    #[test]
479    fn we_cannot_convert_decimal256_array_with_high_precision() {
480        let alloc = Bump::new();
481        let mut builder = Decimal256Builder::with_capacity(3);
482        builder.append_value(i256::from_str("100000000000000000000000000000000000000").unwrap());
483        builder.append_value(i256::from_str("-300000000000000000000000000000000000000").unwrap());
484        builder.append_value(i256::from_str("4200000000000000000000000000000000000000").unwrap());
485
486        let array: ArrayRef = Arc::new(builder.finish().with_precision_and_scale(76, 0).unwrap());
487        let result = array.to_column::<TestScalar>(&alloc, &(1..3), None);
488        assert!(result.is_err());
489    }
490
491    #[test]
492    fn we_can_convert_decimal256_array_normal_range() {
493        let alloc = Bump::new();
494        let mut builder = Decimal256Builder::with_capacity(3);
495        builder.append_value(i256::from_str("100000000000000000000000000000000000000").unwrap());
496        builder.append_value(i256::from_str("-300000000000000000000000000000000000000").unwrap());
497        builder.append_value(i256::from_str("4200000000000000000000000000000000000000").unwrap());
498        let array: ArrayRef = Arc::new(builder.finish().with_precision_and_scale(75, 0).unwrap());
499
500        let result = array.to_column::<TestScalar>(&alloc, &(1..3), None);
501        let expected_scalars: Vec<TestScalar> = vec![
502            convert_i256_to_scalar(
503                &i256::from_str("-300000000000000000000000000000000000000").unwrap(),
504            )
505            .unwrap(),
506            convert_i256_to_scalar(
507                &i256::from_str("4200000000000000000000000000000000000000").unwrap(),
508            )
509            .unwrap(),
510        ];
511        assert_eq!(
512            result.unwrap(),
513            Column::Decimal75(Precision::new(75).unwrap(), 0, expected_scalars.as_slice())
514        );
515    }
516
517    #[test]
518    fn we_can_convert_decimal256_array_empty_range() {
519        let alloc = Bump::new();
520        let mut builder = Decimal256Builder::with_capacity(3);
521        builder.append_value(i256::from_str("100000000000000000000000000000000000000").unwrap());
522        builder.append_value(i256::from_str("-300000000000000000000000000000000000000").unwrap());
523        builder.append_value(i256::from_str("4200000000000000000000000000000000000000").unwrap());
524        let array: ArrayRef = Arc::new(builder.finish().with_precision_and_scale(75, 0).unwrap());
525
526        let result = array.to_column::<TestScalar>(&alloc, &(1..1), None);
527        assert_eq!(
528            result.unwrap(),
529            Column::Decimal75(Precision::new(75).unwrap(), 0, &[])
530        );
531    }
532
533    #[test]
534    fn we_cannot_convert_decimal256_array_oob_range() {
535        let alloc = Bump::new();
536        let mut builder = Decimal256Builder::with_capacity(3);
537        builder.append_value(i256::from_str("100000000000000000000000000000000000000").unwrap());
538        builder.append_value(i256::from_str("-300000000000000000000000000000000000000").unwrap());
539        builder.append_value(i256::from_str("4200000000000000000000000000000000000000").unwrap());
540        let array: ArrayRef = Arc::new(builder.finish().with_precision_and_scale(75, 0).unwrap());
541
542        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
543        assert_eq!(
544            result,
545            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
546        );
547    }
548
549    #[test]
550    fn we_can_convert_decimal256_array_with_nulls() {
551        let alloc = Bump::new();
552        let mut builder = Decimal256Builder::with_capacity(3);
553        builder.append_value(i256::from_str("100000000000000000000000000000000000000").unwrap());
554        builder.append_null();
555        builder.append_value(i256::from_str("4200000000000000000000000000000000000000").unwrap());
556        let array: ArrayRef = Arc::new(builder.finish().with_precision_and_scale(75, 0).unwrap());
557
558        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
559        assert!(matches!(
560            result,
561            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
562        ));
563    }
564
565    #[test]
566    fn we_can_convert_decimal128_array_empty_range() {
567        let alloc = Bump::new();
568        let data = vec![100_i128, -300_i128, 4200_i128];
569        let array: ArrayRef = Arc::new(
570            Decimal128Array::from_iter_values(data.clone())
571                .with_precision_and_scale(38, 0)
572                .unwrap(),
573        );
574
575        let result = array.to_column::<DoryScalar>(&alloc, &(1..1), None);
576        assert_eq!(result.unwrap(), Column::Int128(&[]));
577    }
578
579    #[test]
580    fn we_cannot_convert_decimal128_array_oob_range() {
581        let alloc = Bump::new();
582        let data = vec![100_i128, -300_i128, 4200_i128];
583        let array: ArrayRef = Arc::new(
584            Decimal128Array::from_iter_values(data)
585                .with_precision_and_scale(38, 0)
586                .unwrap(),
587        );
588
589        let result = array.to_column::<TestScalar>(&alloc, &(2..4), None);
590        assert_eq!(
591            result,
592            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
593        );
594    }
595
596    #[test]
597    fn we_can_convert_decimal128_array_with_nulls() {
598        let alloc = Bump::new();
599        let data = vec![Some(100_i128), None, Some(4200_i128)];
600        let array: ArrayRef = Arc::new(
601            Decimal128Array::from(data.clone())
602                .with_precision_and_scale(38, 0)
603                .unwrap(),
604        );
605
606        let result = array.to_column::<DoryScalar>(&alloc, &(0..3), None);
607        assert!(matches!(
608            result,
609            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
610        ));
611    }
612
613    #[test]
614    fn we_can_convert_decimal128_array_normal_range() {
615        let alloc = Bump::new();
616        let data = vec![100_i128, -300_i128, 4200_i128];
617        let array: ArrayRef = Arc::new(
618            Decimal128Array::from_iter_values(data.clone())
619                .with_precision_and_scale(38, 0)
620                .unwrap(),
621        );
622
623        let result = array.to_column::<TestScalar>(&alloc, &(1..3), None);
624        assert_eq!(result.unwrap(), Column::Int128(&data[1..3]));
625    }
626
627    #[test]
628    fn we_can_convert_boolean_array_normal_range() {
629        let alloc = Bump::new();
630        let array: ArrayRef = Arc::new(BooleanArray::from(vec![
631            Some(true),
632            Some(false),
633            Some(true),
634        ]));
635        let result = array.to_column::<DoryScalar>(&alloc, &(1..3), None);
636        assert_eq!(result.unwrap(), Column::Boolean(&[false, true]));
637    }
638
639    #[test]
640    fn we_can_convert_boolean_array_empty_range() {
641        let alloc = Bump::new();
642        let array: ArrayRef = Arc::new(BooleanArray::from(vec![
643            Some(true),
644            Some(false),
645            Some(true),
646        ]));
647        let result = array.to_column::<TestScalar>(&alloc, &(1..1), None);
648        assert_eq!(result.unwrap(), Column::Boolean(&[]));
649    }
650
651    #[test]
652    fn we_cannot_convert_boolean_array_oob_range() {
653        let alloc = Bump::new();
654        let array: ArrayRef = Arc::new(BooleanArray::from(vec![
655            Some(true),
656            Some(false),
657            Some(true),
658        ]));
659
660        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
661
662        assert_eq!(
663            result,
664            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
665        );
666    }
667
668    #[test]
669    fn we_can_convert_boolean_array_with_nulls() {
670        let alloc = Bump::new();
671        let array: ArrayRef = Arc::new(BooleanArray::from(vec![Some(true), None, Some(true)]));
672        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
673        assert!(matches!(
674            result,
675            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
676        ));
677    }
678
679    #[test]
680    fn we_can_convert_int8_array_normal_range() {
681        let alloc = Bump::new();
682        let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42]));
683        let result = array.to_column::<DoryScalar>(&alloc, &(1..3), None);
684        assert_eq!(result.unwrap(), Column::TinyInt(&[-3, 42]));
685    }
686
687    #[test]
688    fn we_can_convert_int16_array_normal_range() {
689        let alloc = Bump::new();
690        let array: ArrayRef = Arc::new(Int16Array::from(vec![1, -3, 42]));
691        let result = array.to_column::<DoryScalar>(&alloc, &(1..3), None);
692        assert_eq!(result.unwrap(), Column::SmallInt(&[-3, 42]));
693    }
694
695    #[test]
696    fn we_can_convert_int8_array_empty_range() {
697        let alloc = Bump::new();
698        let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42]));
699        let result = array.to_column::<TestScalar>(&alloc, &(1..1), None);
700        assert_eq!(result.unwrap(), Column::TinyInt(&[]));
701    }
702
703    #[test]
704    fn we_can_convert_int16_array_empty_range() {
705        let alloc = Bump::new();
706        let array: ArrayRef = Arc::new(Int16Array::from(vec![1, -3, 42]));
707        let result = array.to_column::<TestScalar>(&alloc, &(1..1), None);
708        assert_eq!(result.unwrap(), Column::SmallInt(&[]));
709    }
710
711    #[test]
712    fn we_cannot_convert_int8_array_oob_range() {
713        let alloc = Bump::new();
714        let array: ArrayRef = Arc::new(Int8Array::from(vec![1, -3, 42]));
715
716        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
717
718        assert_eq!(
719            result,
720            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
721        );
722    }
723
724    #[test]
725    fn we_cannot_convert_int16_array_oob_range() {
726        let alloc = Bump::new();
727        let array: ArrayRef = Arc::new(Int16Array::from(vec![1, -3, 42]));
728
729        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
730
731        assert_eq!(
732            result,
733            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
734        );
735    }
736
737    #[test]
738    fn we_can_convert_int8_array_with_nulls() {
739        let alloc = Bump::new();
740        let array: ArrayRef = Arc::new(Int8Array::from(vec![Some(1), None, Some(42)]));
741        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
742        assert!(matches!(
743            result,
744            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
745        ));
746    }
747
748    #[test]
749    fn we_can_convert_int16_array_with_nulls() {
750        let alloc = Bump::new();
751        let array: ArrayRef = Arc::new(Int16Array::from(vec![Some(1), None, Some(42)]));
752        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
753        assert!(matches!(
754            result,
755            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
756        ));
757    }
758
759    #[test]
760    fn we_can_convert_int32_array_normal_range() {
761        let alloc = Bump::new();
762        let array: ArrayRef = Arc::new(Int32Array::from(vec![1, -3, 42]));
763        let result = array.to_column::<DoryScalar>(&alloc, &(1..3), None);
764        assert_eq!(result.unwrap(), Column::Int(&[-3, 42]));
765    }
766
767    #[test]
768    fn we_can_convert_int32_array_empty_range() {
769        let alloc = Bump::new();
770        let array: ArrayRef = Arc::new(Int32Array::from(vec![1, -3, 42]));
771        let result = array.to_column::<TestScalar>(&alloc, &(1..1), None);
772        assert_eq!(result.unwrap(), Column::Int(&[]));
773    }
774
775    #[test]
776    fn we_cannot_convert_int32_array_oob_range() {
777        let alloc = Bump::new();
778        let array: ArrayRef = Arc::new(Int32Array::from(vec![1, -3, 42]));
779
780        let result = array.to_column::<DoryScalar>(&alloc, &(2..4), None);
781
782        assert_eq!(
783            result,
784            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 3, index: 4 })
785        );
786    }
787
788    #[test]
789    fn we_can_convert_int32_array_with_nulls() {
790        let alloc = Bump::new();
791        let array: ArrayRef = Arc::new(Int32Array::from(vec![Some(1), None, Some(42)]));
792        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
793        assert!(matches!(
794            result,
795            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
796        ));
797    }
798
799    #[test]
800    fn we_cannot_index_on_oob_range() {
801        let alloc = Bump::new();
802
803        let array0: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![1, -3]));
804        let result0 = array0.to_column::<DoryScalar>(&alloc, &(2..3), None);
805        assert_eq!(
806            result0,
807            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 3 })
808        );
809
810        let array1: ArrayRef = Arc::new(arrow::array::Int16Array::from(vec![1, -3]));
811        let result1 = array1.to_column::<DoryScalar>(&alloc, &(2..3), None);
812        assert_eq!(
813            result1,
814            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 3 })
815        );
816
817        let array2: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![1, -3]));
818        let result2 = array2.to_column::<DoryScalar>(&alloc, &(2..3), None);
819        assert_eq!(
820            result2,
821            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 3 })
822        );
823
824        let array3: ArrayRef = Arc::new(arrow::array::Int64Array::from(vec![1, -3]));
825        let result3 = array3.to_column::<DoryScalar>(&alloc, &(2..3), None);
826        assert_eq!(
827            result3,
828            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 3 })
829        );
830    }
831
832    #[test]
833    fn we_cannot_index_on_empty_oob_range() {
834        let alloc = Bump::new();
835
836        let array0: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![1, -3]));
837        let result0 = array0.to_column::<DoryScalar>(&alloc, &(5..5), None);
838        assert_eq!(
839            result0,
840            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 5 })
841        );
842
843        let array1: ArrayRef = Arc::new(arrow::array::Int16Array::from(vec![1, -3]));
844        let result1 = array1.to_column::<TestScalar>(&alloc, &(5..5), None);
845        assert_eq!(
846            result1,
847            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 5 })
848        );
849
850        let array2: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![1, -3]));
851        let result2 = array2.to_column::<DoryScalar>(&alloc, &(5..5), None);
852        assert_eq!(
853            result2,
854            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 5 })
855        );
856
857        let array3: ArrayRef = Arc::new(arrow::array::Int64Array::from(vec![1, -3]));
858        let result3 = array3.to_column::<TestScalar>(&alloc, &(5..5), None);
859        assert_eq!(
860            result3,
861            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 5 })
862        );
863    }
864
865    #[test]
866    fn we_can_build_an_empty_column_from_an_empty_range_boolean() {
867        let alloc = Bump::new();
868        let array: ArrayRef = Arc::new(arrow::array::BooleanArray::from(vec![true, false]));
869        let result = array
870            .to_column::<DoryScalar>(&alloc, &(2..2), None)
871            .unwrap();
872        assert_eq!(result, Column::Boolean(&[]));
873    }
874
875    #[test]
876    fn we_can_build_an_empty_column_from_an_empty_range_int8() {
877        let alloc = Bump::new();
878        let array: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![1, -3]));
879        let result = array
880            .to_column::<TestScalar>(&alloc, &(2..2), None)
881            .unwrap();
882        assert_eq!(result, Column::TinyInt(&[]));
883    }
884
885    #[test]
886    fn we_can_build_an_empty_column_from_an_empty_range_int16() {
887        let alloc = Bump::new();
888        let array: ArrayRef = Arc::new(arrow::array::Int16Array::from(vec![1, -3]));
889        let result = array
890            .to_column::<TestScalar>(&alloc, &(2..2), None)
891            .unwrap();
892        assert_eq!(result, Column::SmallInt(&[]));
893    }
894
895    #[test]
896    fn we_can_build_an_empty_column_from_an_empty_range_int32() {
897        let alloc = Bump::new();
898        let array: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![1, -3]));
899        let result = array
900            .to_column::<DoryScalar>(&alloc, &(2..2), None)
901            .unwrap();
902        assert_eq!(result, Column::Int(&[]));
903    }
904
905    #[test]
906    fn we_can_build_an_empty_column_from_an_empty_range_int64() {
907        let alloc = Bump::new();
908        let array: ArrayRef = Arc::new(arrow::array::Int64Array::from(vec![1, -3]));
909        let result = array
910            .to_column::<TestScalar>(&alloc, &(2..2), None)
911            .unwrap();
912        assert_eq!(result, Column::BigInt(&[]));
913    }
914
915    #[test]
916    fn we_can_build_an_empty_column_from_an_empty_range_decimal128() {
917        let alloc = Bump::new();
918        let decimal_values = vec![
919            12_345_678_901_234_567_890_i128,
920            -12_345_678_901_234_567_890_i128,
921        ];
922        let array: ArrayRef = Arc::new(
923            Decimal128Array::from(decimal_values)
924                .with_precision_and_scale(38, 0)
925                .unwrap(),
926        );
927        let result = array
928            .to_column::<DoryScalar>(&alloc, &(0..0), None)
929            .unwrap();
930        assert_eq!(result, Column::Int128(&[]));
931    }
932
933    #[test]
934    fn we_can_build_an_empty_column_from_an_empty_range_utf8() {
935        let alloc = Bump::new();
936        let data = vec!["ab", "-f34"];
937        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.clone()));
938        assert_eq!(
939            array
940                .to_column::<TestScalar>(&alloc, &(1..1), None)
941                .unwrap(),
942            Column::VarChar((&[], &[]))
943        );
944    }
945
946    #[test]
947    fn we_cannot_build_a_column_from_an_array_with_nulls_utf8() {
948        let alloc = Bump::new();
949        let data = vec![Some("ab"), Some("-f34"), None];
950        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.clone()));
951        let result = array.to_column::<DoryScalar>(&alloc, &(0..3), None);
952        assert!(matches!(
953            result,
954            Err(ArrowArrayToColumnConversionError::ArrayContainsNulls)
955        ));
956    }
957
958    #[test]
959    fn we_cannot_convert_valid_string_array_refs_into_valid_columns_using_out_of_ranges_sizes() {
960        let alloc = Bump::new();
961        let data = vec!["ab", "-f34"];
962        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data));
963        let result = array.to_column::<TestScalar>(&alloc, &(0..3), None);
964        assert_eq!(
965            result,
966            Err(ArrowArrayToColumnConversionError::IndexOutOfBounds { len: 2, index: 3 })
967        );
968    }
969
970    #[test]
971    fn we_can_convert_valid_integer_array_refs_into_valid_columns() {
972        let alloc = Bump::new();
973        let array: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![1, -3]));
974        assert_eq!(
975            array
976                .to_column::<DoryScalar>(&alloc, &(0..2), None)
977                .unwrap(),
978            Column::TinyInt(&[1, -3])
979        );
980
981        let array: ArrayRef = Arc::new(arrow::array::Int16Array::from(vec![1, -3]));
982        assert_eq!(
983            array
984                .to_column::<TestScalar>(&alloc, &(0..2), None)
985                .unwrap(),
986            Column::SmallInt(&[1, -3])
987        );
988
989        let array: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![1, -3]));
990        assert_eq!(
991            array
992                .to_column::<TestScalar>(&alloc, &(0..2), None)
993                .unwrap(),
994            Column::Int(&[1, -3])
995        );
996
997        let array: ArrayRef = Arc::new(arrow::array::Int64Array::from(vec![1, -3]));
998        assert_eq!(
999            array
1000                .to_column::<TestScalar>(&alloc, &(0..2), None)
1001                .unwrap(),
1002            Column::BigInt(&[1, -3])
1003        );
1004    }
1005
1006    #[test]
1007    fn we_can_convert_valid_string_array_refs_into_valid_columns() {
1008        let alloc = Bump::new();
1009        let data = vec!["ab", "-f34"];
1010        let scals: Vec<_> = data.iter().map(core::convert::Into::into).collect();
1011        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.clone()));
1012        assert_eq!(
1013            array
1014                .to_column::<DoryScalar>(&alloc, &(0..2), None)
1015                .unwrap(),
1016            Column::VarChar((&data[..], &scals[..]))
1017        );
1018    }
1019
1020    #[test]
1021    fn we_can_convert_valid_binary_array_refs_into_valid_columns() {
1022        let alloc = Bump::new();
1023        let data = vec![b"cd".as_slice(), b"-f50".as_slice()];
1024        let scals: Vec<_> = data
1025            .iter()
1026            .copied()
1027            .map(DoryScalar::from_byte_slice_via_hash)
1028            .collect();
1029        let array: ArrayRef = Arc::new(arrow::array::BinaryArray::from(data.clone()));
1030        assert_eq!(
1031            array
1032                .to_column::<DoryScalar>(&alloc, &(0..2), None)
1033                .unwrap(),
1034            Column::VarBinary((&data[..], &scals[..]))
1035        );
1036    }
1037
1038    #[test]
1039    fn we_can_convert_valid_boolean_array_refs_into_valid_columns() {
1040        let alloc = Bump::new();
1041        let data = vec![true, false];
1042        let array: ArrayRef = Arc::new(arrow::array::BooleanArray::from(data.clone()));
1043        assert_eq!(
1044            array
1045                .to_column::<TestScalar>(&alloc, &(0..2), None)
1046                .unwrap(),
1047            Column::Boolean(&data[..])
1048        );
1049    }
1050
1051    #[test]
1052    fn we_can_convert_valid_timestamp_array_refs_into_valid_columns() {
1053        let alloc = Bump::new();
1054        let data = vec![1_625_072_400, 1_625_076_000]; // Example Unix timestamps
1055        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
1056            data.clone().into(),
1057            Some("UTC"),
1058        ));
1059
1060        let result = array
1061            .to_column::<TestScalar>(&alloc, &(0..2), None)
1062            .unwrap();
1063        assert_eq!(
1064            result,
1065            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &data[..])
1066        );
1067    }
1068
1069    #[test]
1070    fn we_can_convert_valid_boolean_array_refs_into_valid_columns_using_ranges_smaller_than_arrays()
1071    {
1072        let alloc = Bump::new();
1073        let array: ArrayRef = Arc::new(arrow::array::BooleanArray::from(vec![true, false, true]));
1074        assert_eq!(
1075            array
1076                .to_column::<DoryScalar>(&alloc, &(1..3), None)
1077                .unwrap(),
1078            Column::Boolean(&[false, true])
1079        );
1080    }
1081
1082    #[test]
1083    fn we_can_convert_valid_integer_array_refs_into_valid_columns_using_ranges_smaller_than_arrays()
1084    {
1085        let alloc = Bump::new();
1086
1087        let array: ArrayRef = Arc::new(arrow::array::Int8Array::from(vec![0, 1, 127]));
1088        assert_eq!(
1089            array
1090                .to_column::<DoryScalar>(&alloc, &(1..3), None)
1091                .unwrap(),
1092            Column::TinyInt(&[1, 127])
1093        );
1094
1095        let array: ArrayRef = Arc::new(arrow::array::Int16Array::from(vec![0, 1, 545]));
1096        assert_eq!(
1097            array
1098                .to_column::<TestScalar>(&alloc, &(1..3), None)
1099                .unwrap(),
1100            Column::SmallInt(&[1, 545])
1101        );
1102
1103        let array: ArrayRef = Arc::new(arrow::array::Int32Array::from(vec![0, 1, 545]));
1104        assert_eq!(
1105            array
1106                .to_column::<TestScalar>(&alloc, &(1..3), None)
1107                .unwrap(),
1108            Column::Int(&[1, 545])
1109        );
1110
1111        let array: ArrayRef = Arc::new(arrow::array::Int64Array::from(vec![0, 1, 545]));
1112        assert_eq!(
1113            array
1114                .to_column::<TestScalar>(&alloc, &(1..3), None)
1115                .unwrap(),
1116            Column::BigInt(&[1, 545])
1117        );
1118    }
1119
1120    #[test]
1121    fn we_can_convert_valid_timestamp_array_refs_into_valid_columns_using_ranges_smaller_than_arrays(
1122    ) {
1123        let alloc = Bump::new();
1124        let data = vec![1_625_072_400, 1_625_076_000, 1_625_083_200]; // Example Unix timestamps
1125        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
1126            data.clone().into(),
1127            Some("Utc"),
1128        ));
1129
1130        // Test using a range smaller than the array size
1131        assert_eq!(
1132            array
1133                .to_column::<TestScalar>(&alloc, &(1..3), None)
1134                .unwrap(),
1135            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &data[1..3])
1136        );
1137    }
1138
1139    #[test]
1140    fn we_can_convert_valid_string_array_refs_into_valid_columns_using_ranges_smaller_than_arrays()
1141    {
1142        let alloc = Bump::new();
1143        let data = ["ab", "-f34", "ehfh43"];
1144        let scals: Vec<_> = data.iter().map(core::convert::Into::into).collect();
1145
1146        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.to_vec()));
1147        assert_eq!(
1148            array
1149                .to_column::<DoryScalar>(&alloc, &(1..3), None)
1150                .unwrap(),
1151            Column::VarChar((&data[1..3], &scals[1..3]))
1152        );
1153    }
1154
1155    #[test]
1156    fn we_can_convert_valid_binary_array_refs_into_valid_columns_using_ranges_smaller_than_arrays()
1157    {
1158        let alloc = Bump::new();
1159        let data = [b"ab".as_slice(), b"-f34".as_slice(), b"ehfh43".as_slice()];
1160        let scals: Vec<_> = data
1161            .iter()
1162            .copied()
1163            .map(DoryScalar::from_byte_slice_via_hash)
1164            .collect();
1165
1166        let array: ArrayRef = Arc::new(arrow::array::BinaryArray::from(data.to_vec()));
1167        assert_eq!(
1168            array
1169                .to_column::<DoryScalar>(&alloc, &(1..3), None)
1170                .unwrap(),
1171            Column::VarBinary((&data[1..3], &scals[1..3]))
1172        );
1173    }
1174
1175    #[test]
1176    fn we_can_convert_valid_string_array_refs_into_valid_columns_using_precomputed_scalars() {
1177        let alloc = Bump::new();
1178        let data = vec!["ab", "-f34"];
1179        let scals: Vec<_> = data.iter().map(core::convert::Into::into).collect();
1180        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.clone()));
1181        assert_eq!(
1182            array
1183                .to_column::<TestScalar>(&alloc, &(0..2), Some(&scals))
1184                .unwrap(),
1185            Column::VarChar((&data[..], &scals[..]))
1186        );
1187    }
1188
1189    #[test]
1190    fn we_can_convert_valid_binary_array_refs_into_valid_columns_using_precomputed_scalars() {
1191        let alloc = Bump::new();
1192        let data = vec![b"ab".as_slice(), b"-f34".as_slice()];
1193        let scals: Vec<_> = data
1194            .iter()
1195            .copied()
1196            .map(TestScalar::from_byte_slice_via_hash)
1197            .collect();
1198        let array: ArrayRef = Arc::new(arrow::array::BinaryArray::from(data.clone()));
1199        assert_eq!(
1200            array
1201                .to_column::<TestScalar>(&alloc, &(0..2), Some(&scals))
1202                .unwrap(),
1203            Column::VarBinary((&data[..], &scals[..]))
1204        );
1205    }
1206
1207    #[test]
1208    fn we_can_convert_valid_string_array_refs_into_valid_columns_using_ranges_with_zero_size() {
1209        let alloc = Bump::new();
1210        let data = vec!["ab", "-f34"];
1211        let array: ArrayRef = Arc::new(arrow::array::StringArray::from(data.clone()));
1212        let result = array
1213            .to_column::<DoryScalar>(&alloc, &(0..0), None)
1214            .unwrap();
1215        assert_eq!(result, Column::VarChar((&[], &[])));
1216    }
1217
1218    #[test]
1219    fn we_can_convert_valid_binary_array_refs_into_valid_columns_using_ranges_with_zero_size() {
1220        let alloc = Bump::new();
1221        let data = vec![b"ab".as_slice(), b"-f34".as_slice()];
1222        let array: ArrayRef = Arc::new(arrow::array::BinaryArray::from(data.clone()));
1223        let result = array
1224            .to_column::<DoryScalar>(&alloc, &(0..0), None)
1225            .unwrap();
1226        assert_eq!(result, Column::VarBinary((&[], &[])));
1227    }
1228
1229    #[test]
1230    fn we_can_convert_valid_timestamp_array_refs_into_valid_columns_using_ranges_with_zero_size() {
1231        let alloc = Bump::new();
1232        let data = vec![1_625_072_400, 1_625_076_000]; // Example Unix timestamps
1233        let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
1234            data.clone().into(),
1235            Some("Utc"),
1236        ));
1237        let result = array
1238            .to_column::<DoryScalar>(&alloc, &(0..0), None)
1239            .unwrap();
1240        assert_eq!(
1241            result,
1242            Column::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc(), &[])
1243        );
1244    }
1245
1246    proptest! {
1247        #[test]
1248        fn we_can_roundtrip_arbitrary_column(owned_column: OwnedColumn<TestScalar>) {
1249            let arrow = ArrayRef::from(owned_column.clone());
1250            let alloc = Bump::new();
1251            let column = arrow.to_column::<TestScalar>(&alloc, &(0..arrow.len()), None).unwrap();
1252            let actual = OwnedColumn::from(&column);
1253
1254            prop_assert_eq!(actual, owned_column);
1255        }
1256    }
1257}