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)]
21pub enum ArrowArrayToColumnConversionError {
23 #[snafu(display("arrow array must not contain nulls"))]
25 ArrayContainsNulls,
26 #[snafu(display(
28 "unsupported type: attempted conversion from ArrayRef of type {datatype} to OwnedColumn"
29 ))]
30 UnsupportedType {
31 datatype: DataType,
33 },
34 #[snafu(transparent)]
36 DecimalError {
37 source: crate::base::math::decimal::DecimalError,
39 },
40 #[snafu(display("decimal conversion failed: {number}"))]
42 DecimalConversionFailed {
43 number: i256,
45 },
46 #[snafu(display("index out of bounds: the len is {len} but the index is {index}"))]
48 IndexOutOfBounds {
49 len: usize,
51 index: usize,
53 },
54 #[snafu(transparent)]
56 TimestampConversionError {
57 source: PoSQLTimestampError,
59 },
60}
61
62pub trait ArrayRefExt {
64 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 #[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 if self.null_count() != 0 {
113 return Err(ArrowArrayToColumnConversionError::ArrayContainsNulls);
114 }
115
116 if range.end > self.len() {
118 return Err(ArrowArrayToColumnConversionError::IndexOutOfBounds {
119 len: self.len(),
120 index: range.end,
121 });
122 }
123 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 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]; 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]; 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]; 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]; 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]; let array: ArrayRef = Arc::new(TimestampSecondArray::with_timezone_opt(
1126 data.clone().into(),
1127 Some("Utc"),
1128 ));
1129
1130 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]; 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}