1use std::sync::Arc;
7
8use arrow_array::Scalar as ArrowScalar;
9use arrow_array::*;
10use arrow_buffer::NullBuffer;
11use arrow_buffer::OffsetBuffer;
12use arrow_schema::Field;
13use arrow_schema::Fields;
14use vortex_array::dtype::DType;
15use vortex_array::dtype::PType;
16use vortex_array::dtype::i256;
17use vortex_array::extension::datetime::AnyTemporal;
18use vortex_array::extension::datetime::TemporalMetadata;
19use vortex_array::extension::datetime::TimeUnit;
20use vortex_array::scalar::BinaryScalar;
21use vortex_array::scalar::BoolScalar;
22use vortex_array::scalar::DecimalScalar;
23use vortex_array::scalar::ExtScalar;
24use vortex_array::scalar::MapScalar;
25use vortex_array::scalar::PrimitiveScalar;
26use vortex_array::scalar::Scalar;
27use vortex_array::scalar::Utf8Scalar;
28use vortex_error::VortexError;
29use vortex_error::vortex_bail;
30use vortex_error::vortex_err;
31
32use crate::dtype::to_data_type_naive;
33
34const SCALAR_ARRAY_LEN: usize = 1;
36
37macro_rules! value_to_arrow_scalar {
39 ($V:expr, $AR:ty) => {
40 Ok(std::sync::Arc::new(
41 $V.map(<$AR>::new_scalar)
42 .unwrap_or_else(|| arrow_array::Scalar::new(<$AR>::new_null(SCALAR_ARRAY_LEN))),
43 ))
44 };
45}
46
47macro_rules! timestamp_to_arrow_scalar {
49 ($V:expr, $TZ:expr, $AR:ty) => {{
50 let array = match $V {
51 Some(v) => <$AR>::new_scalar(v).into_inner(),
52 None => <$AR>::new_null(SCALAR_ARRAY_LEN),
53 }
54 .with_timezone_opt($TZ);
55 Ok(Arc::new(ArrowScalar::new(array)))
56 }};
57}
58
59pub trait ToArrowDatum {
64 fn to_arrow_datum(&self) -> Result<Arc<dyn Datum>, VortexError>;
66}
67
68impl ToArrowDatum for Scalar {
69 fn to_arrow_datum(&self) -> Result<Arc<dyn Datum>, VortexError> {
70 let value = self;
71 match value.dtype() {
72 DType::Null => Ok(Arc::new(NullArray::new(SCALAR_ARRAY_LEN))),
73 DType::Bool(_) => bool_to_arrow(value.as_bool()),
74 DType::Primitive(..) => primitive_to_arrow(value.as_primitive()),
75 DType::Decimal(..) => decimal_to_arrow(value.as_decimal()),
76 DType::Utf8(_) => utf8_to_arrow(value.as_utf8()),
77 DType::Binary(_) => binary_to_arrow(value.as_binary()),
78 DType::List(..) => vortex_bail!("list scalar conversion is not supported"),
79 DType::FixedSizeList(..) => {
80 vortex_bail!("fixed-size list scalar conversion is not supported")
81 }
82 DType::Map(..) => map_to_arrow(value.as_map()),
83 DType::Struct(..) => vortex_bail!("struct scalar conversion is not supported"),
84 DType::Union(..) => vortex_bail!("union scalar conversion is not supported"),
85 DType::Variant(_) => vortex_bail!("Variant scalar conversion is not supported"),
86 DType::Extension(..) => extension_to_arrow(value.as_extension()),
87 }
88 }
89}
90
91fn bool_to_arrow(scalar: BoolScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
93 value_to_arrow_scalar!(scalar.value(), BooleanArray)
94}
95
96fn primitive_to_arrow(scalar: PrimitiveScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
98 match scalar.ptype() {
99 PType::U8 => value_to_arrow_scalar!(scalar.typed_value(), UInt8Array),
100 PType::U16 => value_to_arrow_scalar!(scalar.typed_value(), UInt16Array),
101 PType::U32 => value_to_arrow_scalar!(scalar.typed_value(), UInt32Array),
102 PType::U64 => value_to_arrow_scalar!(scalar.typed_value(), UInt64Array),
103 PType::I8 => value_to_arrow_scalar!(scalar.typed_value(), Int8Array),
104 PType::I16 => value_to_arrow_scalar!(scalar.typed_value(), Int16Array),
105 PType::I32 => value_to_arrow_scalar!(scalar.typed_value(), Int32Array),
106 PType::I64 => value_to_arrow_scalar!(scalar.typed_value(), Int64Array),
107 PType::F16 => value_to_arrow_scalar!(scalar.typed_value(), Float16Array),
108 PType::F32 => value_to_arrow_scalar!(scalar.typed_value(), Float32Array),
109 PType::F64 => value_to_arrow_scalar!(scalar.typed_value(), Float64Array),
110 }
111}
112
113fn decimal_to_arrow(scalar: DecimalScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
115 let DType::Decimal(decimal_dtype, _) = scalar.dtype() else {
116 vortex_bail!("Expected decimal scalar, got {}", scalar.dtype());
117 };
118 let precision = decimal_dtype.precision();
119 let scale = decimal_dtype.scale();
120 match scalar.decimal_value() {
122 Some(value) => {
123 let value = value.as_i256();
124 if precision <= 38 {
125 let value = value.maybe_i128().ok_or_else(|| {
126 vortex_err!(
127 "Decimal value {value} cannot fit in Arrow Decimal128 for precision {precision}"
128 )
129 })?;
130 decimal128_scalar(value, precision, scale)
131 } else {
132 decimal256_scalar(value, precision, scale)
133 }
134 }
135 None => {
136 let data_type = to_data_type_naive(scalar.dtype())?;
137 Ok(Arc::new(ArrowScalar::new(new_null_array(
138 &data_type,
139 SCALAR_ARRAY_LEN,
140 ))))
141 }
142 }
143}
144
145fn decimal128_scalar(value: i128, precision: u8, scale: i8) -> Result<Arc<dyn Datum>, VortexError> {
146 let array = Decimal128Array::new_scalar(value)
147 .into_inner()
148 .with_precision_and_scale(precision, scale)?;
149 Ok(Arc::new(ArrowScalar::new(array)))
150}
151
152fn decimal256_scalar(value: i256, precision: u8, scale: i8) -> Result<Arc<dyn Datum>, VortexError> {
153 let array = Decimal256Array::new_scalar(value.into())
154 .into_inner()
155 .with_precision_and_scale(precision, scale)?;
156 Ok(Arc::new(ArrowScalar::new(array)))
157}
158
159fn utf8_to_arrow(scalar: Utf8Scalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
161 value_to_arrow_scalar!(scalar.value(), StringViewArray)
162}
163
164fn binary_to_arrow(scalar: BinaryScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
166 value_to_arrow_scalar!(scalar.value(), BinaryViewArray)
167}
168
169fn map_to_arrow(scalar: MapScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
171 let map_dtype = scalar.map_dtype();
172 let key_dtype = map_dtype.key_dtype();
173 let value_dtype = map_dtype.value_dtype();
174 let key_field = Field::new("key", to_data_type_naive(&key_dtype)?, false);
175 let value_field = Field::new(
176 "value",
177 to_data_type_naive(&value_dtype)?,
178 value_dtype.is_nullable(),
179 );
180 let fields = Fields::from(vec![key_field, value_field]);
181
182 let entries = scalar.entries().collect::<Vec<_>>();
183 let keys = entries
184 .iter()
185 .map(|(key, _)| key.to_arrow_datum())
186 .collect::<Result<Vec<_>, _>>()?;
187 let values = entries
188 .iter()
189 .map(|(_, value)| value.to_arrow_datum())
190 .collect::<Result<Vec<_>, _>>()?;
191
192 let key_array = concat_scalar_arrays(&keys, &key_dtype)?;
193 let value_array = concat_scalar_arrays(&values, &value_dtype)?;
194 let entries = StructArray::try_new_with_length(
195 fields.clone(),
196 vec![key_array, value_array],
197 None,
198 entries.len(),
199 )?;
200
201 let entries_len = entries.len();
202 let entries_len = i32::try_from(entries_len).map_err(|_| {
203 vortex_err!(
204 "Cannot convert map scalar with {entries_len} entries to Arrow: MapArray offsets are i32"
205 )
206 })?;
207 let offsets = OffsetBuffer::new(vec![0_i32, entries_len].into());
208 let entries_field = Arc::new(Field::new_struct("entries", fields, false));
209 let nulls = scalar
210 .is_null()
211 .then(|| NullBuffer::new_null(SCALAR_ARRAY_LEN));
212 let map = MapArray::try_new(
213 entries_field,
214 offsets,
215 entries,
216 nulls,
217 map_dtype.keys_sorted(),
218 )?;
219 Ok(Arc::new(ArrowScalar::new(map)))
220}
221
222fn concat_scalar_arrays(
223 scalars: &[Arc<dyn Datum>],
224 dtype: &DType,
225) -> Result<ArrayRef, VortexError> {
226 if scalars.is_empty() {
227 return Ok(new_empty_array(&to_data_type_naive(dtype)?));
228 }
229
230 let arrays = scalars
231 .iter()
232 .map(|scalar| scalar.get().0)
233 .collect::<Vec<_>>();
234 Ok(arrow_select::concat::concat(&arrays)?)
235}
236
237fn extension_to_arrow(scalar: ExtScalar<'_>) -> Result<Arc<dyn Datum>, VortexError> {
241 let ext_dtype = scalar.ext_dtype();
242 let Some(temporal) = ext_dtype.metadata_opt::<AnyTemporal>() else {
243 vortex_bail!(
244 "Cannot convert extension scalar {} to Arrow",
245 ext_dtype.id()
246 )
247 };
248
249 let storage_scalar = scalar.to_storage_scalar();
250 let primitive = storage_scalar
251 .as_primitive_opt()
252 .ok_or_else(|| vortex_err!("Expected primitive scalar"))?;
253
254 match temporal {
255 TemporalMetadata::Timestamp(unit, tz) => {
256 let value = primitive.as_::<i64>();
257 match unit {
258 TimeUnit::Nanoseconds => {
259 timestamp_to_arrow_scalar!(value, tz.clone(), TimestampNanosecondArray)
260 }
261 TimeUnit::Microseconds => {
262 timestamp_to_arrow_scalar!(value, tz.clone(), TimestampMicrosecondArray)
263 }
264 TimeUnit::Milliseconds => {
265 timestamp_to_arrow_scalar!(value, tz.clone(), TimestampMillisecondArray)
266 }
267 TimeUnit::Seconds => {
268 timestamp_to_arrow_scalar!(value, tz.clone(), TimestampSecondArray)
269 }
270 TimeUnit::Days => {
271 vortex_bail!("Unsupported TimeUnit {unit} for {}", ext_dtype.id())
272 }
273 }
274 }
275 TemporalMetadata::Date(unit) => match unit {
276 TimeUnit::Milliseconds => {
277 value_to_arrow_scalar!(primitive.as_::<i64>(), Date64Array)
278 }
279 TimeUnit::Days => {
280 value_to_arrow_scalar!(primitive.as_::<i32>(), Date32Array)
281 }
282 TimeUnit::Nanoseconds | TimeUnit::Microseconds | TimeUnit::Seconds => {
283 vortex_bail!("Unsupported TimeUnit {unit} for {}", ext_dtype.id())
284 }
285 },
286 TemporalMetadata::Time(unit) => match unit {
287 TimeUnit::Nanoseconds => {
288 value_to_arrow_scalar!(primitive.as_::<i64>(), Time64NanosecondArray)
289 }
290 TimeUnit::Microseconds => {
291 value_to_arrow_scalar!(primitive.as_::<i64>(), Time64MicrosecondArray)
292 }
293 TimeUnit::Milliseconds => {
294 value_to_arrow_scalar!(primitive.as_::<i32>(), Time32MillisecondArray)
295 }
296 TimeUnit::Seconds => {
297 value_to_arrow_scalar!(primitive.as_::<i32>(), Time32SecondArray)
298 }
299 TimeUnit::Days => {
300 vortex_bail!("Unsupported TimeUnit {unit} for {}", ext_dtype.id())
301 }
302 },
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use std::sync::Arc;
309
310 use arrow_array::Array;
311 use arrow_array::BinaryViewArray;
312 use arrow_array::BooleanArray;
313 use arrow_array::Date32Array;
314 use arrow_array::Date64Array;
315 use arrow_array::Decimal128Array;
316 use arrow_array::Decimal256Array;
317 use arrow_array::Float16Array;
318 use arrow_array::Float32Array;
319 use arrow_array::Float64Array;
320 use arrow_array::Int8Array;
321 use arrow_array::Int16Array;
322 use arrow_array::Int32Array;
323 use arrow_array::Int64Array;
324 use arrow_array::MapArray;
325 use arrow_array::NullArray;
326 use arrow_array::StringViewArray;
327 use arrow_array::Time32MillisecondArray;
328 use arrow_array::Time32SecondArray;
329 use arrow_array::Time64MicrosecondArray;
330 use arrow_array::Time64NanosecondArray;
331 use arrow_array::TimestampMicrosecondArray;
332 use arrow_array::TimestampMillisecondArray;
333 use arrow_array::TimestampNanosecondArray;
334 use arrow_array::TimestampSecondArray;
335 use arrow_array::UInt8Array;
336 use arrow_array::UInt16Array;
337 use arrow_array::UInt32Array;
338 use arrow_array::UInt64Array;
339 use arrow_schema::DataType;
340 use rstest::rstest;
341 use vortex_array::dtype::DType;
342 use vortex_array::dtype::DecimalDType;
343 use vortex_array::dtype::FieldDType;
344 use vortex_array::dtype::NativeDType;
345 use vortex_array::dtype::Nullability;
346 use vortex_array::dtype::PType;
347 use vortex_array::dtype::StructFields;
348 use vortex_array::dtype::extension::ExtDType;
349 use vortex_array::dtype::extension::ExtId;
350 use vortex_array::dtype::extension::ExtVTable;
351 use vortex_array::dtype::i256;
352 use vortex_array::extension::datetime::Date;
353 use vortex_array::extension::datetime::Time;
354 use vortex_array::extension::datetime::TimeUnit;
355 use vortex_array::extension::datetime::Timestamp;
356 use vortex_array::extension::datetime::TimestampOptions;
357 use vortex_array::scalar::DecimalValue;
358 use vortex_array::scalar::Scalar;
359 use vortex_array::scalar::ScalarValue;
360 use vortex_error::VortexResult;
361 use vortex_error::vortex_bail;
362
363 use super::ToArrowDatum;
364
365 #[test]
366 fn test_null_scalar_to_arrow() {
367 let scalar = Scalar::null(DType::Null);
368 let datum = scalar.to_arrow_datum().unwrap();
369 assert_eq!(datum.get().0.to_data(), NullArray::new(1).to_data());
370 }
371
372 #[test]
373 fn test_bool_scalar_to_arrow() {
374 let scalar = Scalar::bool(true, Nullability::NonNullable);
375 assert_arrow_scalar(&scalar, &BooleanArray::from(vec![true])).unwrap();
376 }
377
378 #[test]
379 fn test_null_bool_scalar_to_arrow() {
380 let scalar = Scalar::null(bool::dtype().as_nullable());
381 assert_arrow_scalar(&scalar, &BooleanArray::from(vec![None::<bool>])).unwrap();
382 }
383
384 #[test]
385 fn test_primitive_u8_to_arrow() {
386 let scalar = Scalar::primitive(42u8, Nullability::NonNullable);
387 assert_arrow_scalar(&scalar, &UInt8Array::from(vec![42u8])).unwrap();
388 }
389
390 #[test]
391 fn test_primitive_u16_to_arrow() {
392 let scalar = Scalar::primitive(1000u16, Nullability::NonNullable);
393 assert_arrow_scalar(&scalar, &UInt16Array::from(vec![1000u16])).unwrap();
394 }
395
396 #[test]
397 fn test_primitive_u32_to_arrow() {
398 let scalar = Scalar::primitive(100000u32, Nullability::NonNullable);
399 assert_arrow_scalar(&scalar, &UInt32Array::from(vec![100000u32])).unwrap();
400 }
401
402 #[test]
403 fn test_primitive_u64_to_arrow() {
404 let scalar = Scalar::primitive(10000000000u64, Nullability::NonNullable);
405 assert_arrow_scalar(&scalar, &UInt64Array::from(vec![10000000000u64])).unwrap();
406 }
407
408 #[test]
409 fn test_primitive_i8_to_arrow() {
410 let scalar = Scalar::primitive(-42i8, Nullability::NonNullable);
411 assert_arrow_scalar(&scalar, &Int8Array::from(vec![-42i8])).unwrap();
412 }
413
414 #[test]
415 fn test_primitive_i16_to_arrow() {
416 let scalar = Scalar::primitive(-1000i16, Nullability::NonNullable);
417 assert_arrow_scalar(&scalar, &Int16Array::from(vec![-1000i16])).unwrap();
418 }
419
420 #[test]
421 fn test_primitive_i32_to_arrow() {
422 let scalar = Scalar::primitive(-100000i32, Nullability::NonNullable);
423 assert_arrow_scalar(&scalar, &Int32Array::from(vec![-100000i32])).unwrap();
424 }
425
426 #[test]
427 fn test_primitive_i64_to_arrow() {
428 let scalar = Scalar::primitive(-10000000000i64, Nullability::NonNullable);
429 assert_arrow_scalar(&scalar, &Int64Array::from(vec![-10000000000i64])).unwrap();
430 }
431
432 #[test]
433 fn test_primitive_f16_to_arrow() {
434 use vortex_array::dtype::half::f16;
435
436 let scalar = Scalar::primitive(f16::from_f32(1.234), Nullability::NonNullable);
437 assert_arrow_scalar(&scalar, &Float16Array::from(vec![f16::from_f32(1.234)])).unwrap();
438 }
439
440 #[test]
441 fn test_primitive_f32_to_arrow() {
442 let scalar = Scalar::primitive(1.234f32, Nullability::NonNullable);
443 assert_arrow_scalar(&scalar, &Float32Array::from(vec![1.234f32])).unwrap();
444 }
445
446 #[test]
447 fn test_primitive_f64_to_arrow() {
448 let scalar = Scalar::primitive(1.234567890123f64, Nullability::NonNullable);
449 assert_arrow_scalar(&scalar, &Float64Array::from(vec![1.234567890123f64])).unwrap();
450 }
451
452 #[test]
453 fn test_null_primitive_to_arrow() {
454 let scalar = Scalar::null(i32::dtype().as_nullable());
455 assert_arrow_scalar(&scalar, &Int32Array::from(vec![None::<i32>])).unwrap();
456 }
457
458 #[test]
459 fn test_utf8_scalar_to_arrow() {
460 let scalar = Scalar::utf8("hello world".to_string(), Nullability::NonNullable);
461 assert_arrow_scalar(&scalar, &StringViewArray::from(vec!["hello world"])).unwrap();
462 }
463
464 #[test]
465 fn test_null_utf8_scalar_to_arrow() {
466 let scalar = Scalar::null(String::dtype().as_nullable());
467 assert_arrow_scalar(&scalar, &StringViewArray::from(vec![None::<&str>])).unwrap();
468 }
469
470 #[test]
471 fn test_binary_scalar_to_arrow() {
472 let data = vec![1u8, 2, 3, 4, 5];
473 let scalar = Scalar::binary(data, Nullability::NonNullable);
474 assert_arrow_scalar(
475 &scalar,
476 &BinaryViewArray::from(vec![&[1u8, 2, 3, 4, 5][..]]),
477 )
478 .unwrap();
479 }
480
481 #[test]
482 fn test_null_binary_scalar_to_arrow() {
483 let scalar = Scalar::null(DType::Binary(Nullability::Nullable));
484 assert_arrow_scalar(&scalar, &BinaryViewArray::from(vec![None::<&[u8]>])).unwrap();
485 }
486
487 fn assert_arrow_scalar(scalar: &Scalar, expected: &dyn Array) -> VortexResult<()> {
488 let datum = scalar.to_arrow_datum()?;
489 let (array, is_scalar) = datum.get();
490 assert!(is_scalar);
491 assert_eq!(array.to_data(), expected.to_data());
492 Ok(())
493 }
494
495 fn assert_arrow_scalar_data_type(
496 scalar: &Scalar,
497 expected: DataType,
498 value: Option<i128>,
499 ) -> VortexResult<()> {
500 let datum = scalar.to_arrow_datum()?;
501 let (array, is_scalar) = datum.get();
502 assert!(is_scalar);
503 assert_eq!(array.data_type(), &expected);
504 let expected: Arc<dyn Array> = match expected {
505 DataType::Decimal128(precision, scale) => Arc::new(
506 Decimal128Array::from(vec![value]).with_precision_and_scale(precision, scale)?,
507 ),
508 DataType::Decimal256(precision, scale) => Arc::new(
509 Decimal256Array::from(vec![value.map(arrow_buffer::i256::from_i128)])
510 .with_precision_and_scale(precision, scale)?,
511 ),
512 _ => unreachable!(),
513 };
514 assert_eq!(array.to_data(), expected.to_data());
515 Ok(())
516 }
517
518 #[test]
519 fn test_decimal_scalars_to_arrow() -> VortexResult<()> {
520 let decimal_dtype = DecimalDType::new(5, 2);
522
523 let scalar_i8 = Scalar::decimal(
524 DecimalValue::I8(100),
525 decimal_dtype,
526 Nullability::NonNullable,
527 );
528 assert_arrow_scalar_data_type(&scalar_i8, DataType::Decimal128(5, 2), Some(100))?;
529
530 let scalar_i16 = Scalar::decimal(
531 DecimalValue::I16(10000),
532 decimal_dtype,
533 Nullability::NonNullable,
534 );
535 assert_arrow_scalar_data_type(&scalar_i16, DataType::Decimal128(5, 2), Some(10000))?;
536
537 let scalar_i32 = Scalar::decimal(
538 DecimalValue::I32(99999),
539 decimal_dtype,
540 Nullability::NonNullable,
541 );
542 assert_arrow_scalar_data_type(&scalar_i32, DataType::Decimal128(5, 2), Some(99999))?;
543
544 let scalar_i64 = Scalar::decimal(
545 DecimalValue::I64(99999),
546 decimal_dtype,
547 Nullability::NonNullable,
548 );
549 assert_arrow_scalar_data_type(&scalar_i64, DataType::Decimal128(5, 2), Some(99999))?;
550
551 let scalar_i128 = Scalar::decimal(
552 DecimalValue::I128(99999),
553 decimal_dtype,
554 Nullability::NonNullable,
555 );
556 assert_arrow_scalar_data_type(&scalar_i128, DataType::Decimal128(5, 2), Some(99999))?;
557
558 let value_i256 = i256::from_i128(99999);
559 let scalar_i256 = Scalar::decimal(
560 DecimalValue::I256(value_i256),
561 decimal_dtype,
562 Nullability::NonNullable,
563 );
564 assert_arrow_scalar_data_type(&scalar_i256, DataType::Decimal128(5, 2), Some(99999))?;
565
566 Ok(())
567 }
568
569 #[test]
570 fn decimal_i64_with_wide_precision_exports_decimal256() -> VortexResult<()> {
571 let scalar = Scalar::decimal(
572 DecimalValue::I64(1),
573 DecimalDType::new(39, 0),
574 Nullability::NonNullable,
575 );
576
577 assert_arrow_scalar_data_type(&scalar, DataType::Decimal256(39, 0), Some(1))
578 }
579
580 #[test]
581 fn decimal_i256_with_narrow_precision_exports_decimal128() -> VortexResult<()> {
582 let scalar = Scalar::decimal(
583 DecimalValue::I256(i256::from_i128(1234)),
584 DecimalDType::new(4, 2),
585 Nullability::NonNullable,
586 );
587
588 assert_arrow_scalar_data_type(&scalar, DataType::Decimal128(4, 2), Some(1234))
589 }
590
591 #[test]
592 fn test_null_decimal_to_arrow() -> VortexResult<()> {
593 let decimal_dtype = DecimalDType::new(10, 2);
594 let scalar = Scalar::null(DType::Decimal(decimal_dtype, Nullability::Nullable));
595 assert_arrow_scalar_data_type(&scalar, DataType::Decimal128(10, 2), None)?;
596
597 let decimal_dtype = DecimalDType::new(39, 2);
598 let scalar = Scalar::null(DType::Decimal(decimal_dtype, Nullability::Nullable));
599 assert_arrow_scalar_data_type(&scalar, DataType::Decimal256(39, 2), None)
600 }
601
602 #[test]
603 fn decimal_scalar_to_arrow_preserves_precision_and_scale() -> VortexResult<()> {
604 let decimal_dtype = DecimalDType::new(12, 3);
605 let scalar = Scalar::decimal(
606 DecimalValue::I128(12345),
607 decimal_dtype,
608 Nullability::NonNullable,
609 );
610
611 let datum = scalar.to_arrow_datum()?;
612 let (array, is_scalar) = datum.get();
613 assert!(is_scalar);
614 let decimal = array
615 .as_any()
616 .downcast_ref::<Decimal128Array>()
617 .expect("decimal scalar should convert to Decimal128");
618 assert_eq!(decimal.precision(), 12);
619 assert_eq!(decimal.scale(), 3);
620
621 assert_eq!(decimal.iter().collect::<Vec<_>>(), vec![Some(12345)]);
622 Ok(())
623 }
624
625 #[test]
626 fn test_map_scalar_to_arrow() -> VortexResult<()> {
627 let dtype = DType::map(
628 DType::Primitive(PType::I32, Nullability::NonNullable),
629 DType::Utf8(Nullability::Nullable),
630 true,
631 Nullability::Nullable,
632 )?;
633 let scalar = Scalar::try_map(
634 dtype,
635 [
636 (
637 Scalar::primitive(1i32, Nullability::NonNullable),
638 Scalar::utf8("one", Nullability::Nullable),
639 ),
640 (
641 Scalar::primitive(2i32, Nullability::NonNullable),
642 Scalar::null(DType::Utf8(Nullability::Nullable)),
643 ),
644 ],
645 )?;
646
647 let datum = scalar.to_arrow_datum()?;
648 let (array, is_scalar) = datum.get();
649 assert!(is_scalar);
650 let map = array
651 .as_any()
652 .downcast_ref::<MapArray>()
653 .expect("map scalar should convert to MapArray");
654 assert_eq!(map.len(), 1);
655 assert_eq!(map.value_offsets(), &[0, 2]);
656 assert!(map.is_valid(0));
657 assert_eq!(
658 map.keys()
659 .as_any()
660 .downcast_ref::<Int32Array>()
661 .expect("map key array should be Int32")
662 .values(),
663 &[1, 2]
664 );
665 let values = map
666 .values()
667 .as_any()
668 .downcast_ref::<StringViewArray>()
669 .expect("map value array should be StringView");
670 assert_eq!(values.value(0), "one");
671 assert!(values.is_null(1));
672
673 let DataType::Map(entries, true) = map.data_type() else {
674 panic!("map scalar must retain sorted keys");
675 };
676 assert!(!entries.is_nullable());
677 let DataType::Struct(fields) = entries.data_type() else {
678 unreachable!()
679 };
680 assert!(!fields[0].is_nullable());
681 assert!(fields[1].is_nullable());
682 Ok(())
683 }
684
685 #[test]
686 fn map_decimal_scalar_to_arrow_preserves_decimal_type() -> VortexResult<()> {
687 let decimal_dtype = DecimalDType::new(9, 2);
688 let dtype = DType::map(
689 DType::Primitive(PType::I32, Nullability::NonNullable),
690 DType::Decimal(decimal_dtype, Nullability::Nullable),
691 false,
692 Nullability::NonNullable,
693 )?;
694 let scalar = Scalar::try_map(
695 dtype,
696 [(
697 Scalar::primitive(1i32, Nullability::NonNullable),
698 Scalar::decimal(
699 DecimalValue::I256(i256::from_i128(12345)),
700 decimal_dtype,
701 Nullability::Nullable,
702 ),
703 )],
704 )?;
705
706 let datum = scalar.to_arrow_datum()?;
707 let (array, is_scalar) = datum.get();
708 assert!(is_scalar);
709 let map = array
710 .as_any()
711 .downcast_ref::<MapArray>()
712 .expect("map scalar should convert to MapArray");
713 assert_eq!(map.values().data_type(), &DataType::Decimal128(9, 2));
714 assert_eq!(map.len(), 1);
715 assert!(map.is_valid(0));
716 assert_eq!(map.value_offsets(), &[0, 1]);
717 assert_eq!(map.keys().to_data(), Int32Array::from(vec![1]).to_data());
718 assert_eq!(
719 map.values().to_data(),
720 Decimal128Array::from(vec![Some(12345)])
721 .with_precision_and_scale(9, 2)?
722 .to_data()
723 );
724
725 let wide_decimal_dtype = DecimalDType::new(39, 2);
726 let dtype = DType::map(
727 DType::Primitive(PType::I32, Nullability::NonNullable),
728 DType::Decimal(wide_decimal_dtype, Nullability::Nullable),
729 false,
730 Nullability::NonNullable,
731 )?;
732 let scalar = Scalar::try_map(
733 dtype,
734 [(
735 Scalar::primitive(1i32, Nullability::NonNullable),
736 Scalar::decimal(
737 DecimalValue::I64(12345),
738 wide_decimal_dtype,
739 Nullability::Nullable,
740 ),
741 )],
742 )?;
743
744 let datum = scalar.to_arrow_datum()?;
745 let (array, is_scalar) = datum.get();
746 assert!(is_scalar);
747 let map = array
748 .as_any()
749 .downcast_ref::<MapArray>()
750 .expect("map scalar should convert to MapArray");
751 assert_eq!(map.values().data_type(), &DataType::Decimal256(39, 2));
752 assert_eq!(map.len(), 1);
753 assert!(map.is_valid(0));
754 assert_eq!(map.value_offsets(), &[0, 1]);
755 assert_eq!(map.keys().to_data(), Int32Array::from(vec![1]).to_data());
756 assert_eq!(
757 map.values().to_data(),
758 Decimal256Array::from(vec![Some(arrow_buffer::i256::from_i128(12345))])
759 .with_precision_and_scale(39, 2)?
760 .to_data()
761 );
762
763 Ok(())
764 }
765
766 #[test]
767 fn map_scalar_with_unsupported_nested_value_errors_without_panic() -> VortexResult<()> {
768 let struct_dtype = DType::Struct(
769 StructFields::from_iter([(
770 "field1",
771 FieldDType::from(DType::Primitive(PType::I32, Nullability::NonNullable)),
772 )]),
773 Nullability::NonNullable,
774 );
775 let dtype = DType::map(
776 DType::Primitive(PType::I32, Nullability::NonNullable),
777 struct_dtype.clone(),
778 false,
779 Nullability::NonNullable,
780 )?;
781 let scalar = Scalar::try_map(
782 dtype,
783 [(
784 Scalar::primitive(1i32, Nullability::NonNullable),
785 Scalar::struct_(
786 struct_dtype,
787 vec![Scalar::primitive(42i32, Nullability::NonNullable)],
788 ),
789 )],
790 )?;
791
792 let error = scalar
793 .to_arrow_datum()
794 .err()
795 .expect("unsupported nested map value should error");
796 assert!(error.to_string().contains("struct scalar conversion"));
797
798 Ok(())
799 }
800
801 #[test]
802 fn test_struct_scalar_to_arrow_todo() {
803 let struct_dtype = DType::Struct(
804 StructFields::from_iter([(
805 "field1",
806 FieldDType::from(DType::Primitive(PType::I32, Nullability::NonNullable)),
807 )]),
808 Nullability::NonNullable,
809 );
810
811 let struct_scalar = Scalar::struct_(
812 struct_dtype,
813 vec![Scalar::primitive(42i32, Nullability::NonNullable)],
814 );
815 let error = struct_scalar
816 .to_arrow_datum()
817 .err()
818 .expect("struct scalar should error");
819 assert!(error.to_string().contains("struct scalar conversion"));
820 }
821
822 #[test]
823 fn test_list_scalar_to_arrow_todo() {
824 let element_dtype = Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable));
825 let list_scalar = Scalar::list(
826 element_dtype,
827 vec![
828 Scalar::primitive(1i32, Nullability::NonNullable),
829 Scalar::primitive(2i32, Nullability::NonNullable),
830 ],
831 Nullability::NonNullable,
832 );
833
834 let error = list_scalar
835 .to_arrow_datum()
836 .err()
837 .expect("list scalar should error");
838 assert!(error.to_string().contains("list scalar conversion"));
839 }
840
841 #[test]
842 fn test_non_temporal_extension_to_arrow_todo() {
843 #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
844 struct SomeExt;
845 impl ExtVTable for SomeExt {
846 type Metadata = String;
847 type NativeValue<'a> = &'a str;
848
849 #[expect(clippy::disallowed_methods, reason = "test-only id")]
850 fn id(&self) -> ExtId {
851 ExtId::new("some_ext")
852 }
853
854 fn serialize_metadata(&self, _options: &Self::Metadata) -> VortexResult<Vec<u8>> {
855 vortex_bail!("not implemented")
856 }
857
858 fn deserialize_metadata(&self, _data: &[u8]) -> VortexResult<Self::Metadata> {
859 vortex_bail!("not implemented")
860 }
861
862 fn validate_dtype(_ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
863 Ok(())
864 }
865
866 fn unpack_native<'a>(
867 _ext_dtype: &'a ExtDType<Self>,
868 _storage_value: &'a ScalarValue,
869 ) -> VortexResult<Self::NativeValue<'a>> {
870 Ok("")
871 }
872 }
873
874 let scalar = Scalar::extension::<SomeExt>(
875 "".into(),
876 Scalar::primitive(42i32, Nullability::NonNullable),
877 );
878
879 let error = scalar
880 .to_arrow_datum()
881 .err()
882 .expect("non-temporal extension scalar should error");
883 assert!(
884 error
885 .to_string()
886 .contains("Cannot convert extension scalar")
887 );
888 }
889
890 #[rstest]
891 #[case(TimeUnit::Nanoseconds, PType::I64, 123456789i64)]
892 #[case(TimeUnit::Microseconds, PType::I64, 123456789i64)]
893 #[case(TimeUnit::Milliseconds, PType::I32, 123456i64)]
894 #[case(TimeUnit::Seconds, PType::I32, 1234i64)]
895 fn test_temporal_time_to_arrow(
896 #[case] time_unit: TimeUnit,
897 #[case] ptype: PType,
898 #[case] value: i64,
899 ) {
900 let scalar = Scalar::extension::<Time>(
901 time_unit,
902 match ptype {
903 PType::I32 => {
904 let i32_value = i32::try_from(value).expect("test value should fit in i32");
905 Scalar::primitive(i32_value, Nullability::NonNullable)
906 }
907 PType::I64 => Scalar::primitive(value, Nullability::NonNullable),
908 _ => unreachable!(),
909 },
910 );
911
912 match time_unit {
913 TimeUnit::Nanoseconds => {
914 assert_arrow_scalar(&scalar, &Time64NanosecondArray::from(vec![value]))
915 }
916 TimeUnit::Microseconds => {
917 assert_arrow_scalar(&scalar, &Time64MicrosecondArray::from(vec![value]))
918 }
919 TimeUnit::Milliseconds => assert_arrow_scalar(
920 &scalar,
921 &Time32MillisecondArray::from(vec![i32::try_from(value).unwrap()]),
922 ),
923 TimeUnit::Seconds => assert_arrow_scalar(
924 &scalar,
925 &Time32SecondArray::from(vec![i32::try_from(value).unwrap()]),
926 ),
927 TimeUnit::Days => unreachable!(),
928 }
929 .unwrap();
930 }
931
932 #[rstest]
933 #[case(TimeUnit::Milliseconds, PType::I64, 1234567890000i64)]
934 #[case(TimeUnit::Days, PType::I32, 19000i64)]
935 fn test_temporal_date_to_arrow(
936 #[case] time_unit: TimeUnit,
937 #[case] ptype: PType,
938 #[case] value: i64,
939 ) {
940 let scalar = Scalar::extension::<Date>(
941 time_unit,
942 match ptype {
943 PType::I32 => {
944 let i32_value = i32::try_from(value).expect("test value should fit in i32");
945 Scalar::primitive(i32_value, Nullability::NonNullable)
946 }
947 PType::I64 => Scalar::primitive(value, Nullability::NonNullable),
948 _ => unreachable!(),
949 },
950 );
951
952 match time_unit {
953 TimeUnit::Days => assert_arrow_scalar(
954 &scalar,
955 &Date32Array::from(vec![i32::try_from(value).unwrap()]),
956 ),
957 TimeUnit::Milliseconds => assert_arrow_scalar(&scalar, &Date64Array::from(vec![value])),
958 _ => unreachable!(),
959 }
960 .unwrap();
961 }
962
963 #[rstest]
964 #[case(TimeUnit::Nanoseconds, 1234567890000000000i64)]
965 #[case(TimeUnit::Microseconds, 1234567890000000i64)]
966 #[case(TimeUnit::Milliseconds, 1234567890000i64)]
967 #[case(TimeUnit::Seconds, 1234567890i64)]
968 fn test_temporal_timestamp_to_arrow(#[case] time_unit: TimeUnit, #[case] value: i64) {
969 let scalar = Scalar::extension::<Timestamp>(
970 TimestampOptions {
971 unit: time_unit,
972 tz: None,
973 },
974 Scalar::primitive(value, Nullability::NonNullable),
975 );
976
977 match time_unit {
978 TimeUnit::Seconds => {
979 assert_arrow_scalar(&scalar, &TimestampSecondArray::from(vec![value]))
980 }
981 TimeUnit::Milliseconds => {
982 assert_arrow_scalar(&scalar, &TimestampMillisecondArray::from(vec![value]))
983 }
984 TimeUnit::Microseconds => {
985 assert_arrow_scalar(&scalar, &TimestampMicrosecondArray::from(vec![value]))
986 }
987 TimeUnit::Nanoseconds => {
988 assert_arrow_scalar(&scalar, &TimestampNanosecondArray::from(vec![value]))
989 }
990 TimeUnit::Days => unreachable!(),
991 }
992 .unwrap();
993 }
994
995 #[rstest]
996 #[case(TimeUnit::Nanoseconds, "UTC", 1234567890000000000i64)]
997 #[case(TimeUnit::Microseconds, "America/New_York", 1234567890000000i64)]
998 #[case(TimeUnit::Microseconds, "Asia/Qatar", 1234567890000000i64)]
999 #[case(TimeUnit::Microseconds, "Australia/Sydney", 1234567890000000i64)]
1000 #[case(TimeUnit::Milliseconds, "Pacific/Honolulu", 1234567890000i64)]
1001 #[case(TimeUnit::Seconds, "GMT", 1234567890i64)]
1002 fn test_temporal_timestamp_tz_to_arrow(
1003 #[case] time_unit: TimeUnit,
1004 #[case] tz: &str,
1005 #[case] value: i64,
1006 ) {
1007 let scalar = Scalar::extension::<Timestamp>(
1008 TimestampOptions {
1009 unit: time_unit,
1010 tz: Some(tz.into()),
1011 },
1012 Scalar::primitive(value, Nullability::NonNullable),
1013 );
1014
1015 match time_unit {
1016 TimeUnit::Seconds => assert_arrow_scalar(
1017 &scalar,
1018 &TimestampSecondArray::from(vec![value]).with_timezone(tz),
1019 ),
1020 TimeUnit::Milliseconds => assert_arrow_scalar(
1021 &scalar,
1022 &TimestampMillisecondArray::from(vec![value]).with_timezone(tz),
1023 ),
1024 TimeUnit::Microseconds => assert_arrow_scalar(
1025 &scalar,
1026 &TimestampMicrosecondArray::from(vec![value]).with_timezone(tz),
1027 ),
1028 TimeUnit::Nanoseconds => assert_arrow_scalar(
1029 &scalar,
1030 &TimestampNanosecondArray::from(vec![value]).with_timezone(tz),
1031 ),
1032 TimeUnit::Days => unreachable!(),
1033 }
1034 .unwrap();
1035 }
1036
1037 #[test]
1038 fn test_temporal_with_null_value() {
1039 let scalar = Scalar::extension::<Time>(
1040 TimeUnit::Milliseconds,
1041 Scalar::null(DType::Primitive(PType::I32, Nullability::Nullable)),
1042 );
1043
1044 assert_arrow_scalar(&scalar, &Time32MillisecondArray::from(vec![None::<i32>])).unwrap();
1045 }
1046
1047 #[test]
1048 #[should_panic(expected = "DType utf8 is not a primitive type")]
1049 fn test_temporal_non_primitive_storage_error() {
1050 let _scalar = Scalar::extension::<Time>(
1051 TimeUnit::Nanoseconds,
1052 Scalar::utf8("not a timestamp", Nullability::NonNullable),
1053 );
1054 }
1055}