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::Decimal128Array;
312 use arrow_array::Int32Array;
313 use arrow_array::MapArray;
314 use arrow_array::StringViewArray;
315 use arrow_schema::DataType;
316 use rstest::rstest;
317 use vortex_array::dtype::DType;
318 use vortex_array::dtype::DecimalDType;
319 use vortex_array::dtype::FieldDType;
320 use vortex_array::dtype::NativeDType;
321 use vortex_array::dtype::Nullability;
322 use vortex_array::dtype::PType;
323 use vortex_array::dtype::StructFields;
324 use vortex_array::dtype::extension::ExtDType;
325 use vortex_array::dtype::extension::ExtId;
326 use vortex_array::dtype::extension::ExtVTable;
327 use vortex_array::dtype::i256;
328 use vortex_array::extension::datetime::Date;
329 use vortex_array::extension::datetime::Time;
330 use vortex_array::extension::datetime::TimeUnit;
331 use vortex_array::extension::datetime::Timestamp;
332 use vortex_array::extension::datetime::TimestampOptions;
333 use vortex_array::scalar::DecimalValue;
334 use vortex_array::scalar::Scalar;
335 use vortex_array::scalar::ScalarValue;
336 use vortex_error::VortexResult;
337 use vortex_error::vortex_bail;
338
339 use super::ToArrowDatum;
340
341 #[test]
342 fn test_null_scalar_to_arrow() {
343 let scalar = Scalar::null(DType::Null);
344 let result = scalar.to_arrow_datum();
345 assert!(result.is_ok());
346 }
347
348 #[test]
349 fn test_bool_scalar_to_arrow() {
350 let scalar = Scalar::bool(true, Nullability::NonNullable);
351 let result = scalar.to_arrow_datum();
352 assert!(result.is_ok());
353 }
354
355 #[test]
356 fn test_null_bool_scalar_to_arrow() {
357 let scalar = Scalar::null(bool::dtype().as_nullable());
358 let result = scalar.to_arrow_datum();
359 assert!(result.is_ok());
360 }
361
362 #[test]
363 fn test_primitive_u8_to_arrow() {
364 let scalar = Scalar::primitive(42u8, Nullability::NonNullable);
365 let result = scalar.to_arrow_datum();
366 assert!(result.is_ok());
367 }
368
369 #[test]
370 fn test_primitive_u16_to_arrow() {
371 let scalar = Scalar::primitive(1000u16, Nullability::NonNullable);
372 let result = scalar.to_arrow_datum();
373 assert!(result.is_ok());
374 }
375
376 #[test]
377 fn test_primitive_u32_to_arrow() {
378 let scalar = Scalar::primitive(100000u32, Nullability::NonNullable);
379 let result = scalar.to_arrow_datum();
380 assert!(result.is_ok());
381 }
382
383 #[test]
384 fn test_primitive_u64_to_arrow() {
385 let scalar = Scalar::primitive(10000000000u64, Nullability::NonNullable);
386 let result = scalar.to_arrow_datum();
387 assert!(result.is_ok());
388 }
389
390 #[test]
391 fn test_primitive_i8_to_arrow() {
392 let scalar = Scalar::primitive(-42i8, Nullability::NonNullable);
393 let result = scalar.to_arrow_datum();
394 assert!(result.is_ok());
395 }
396
397 #[test]
398 fn test_primitive_i16_to_arrow() {
399 let scalar = Scalar::primitive(-1000i16, Nullability::NonNullable);
400 let result = scalar.to_arrow_datum();
401 assert!(result.is_ok());
402 }
403
404 #[test]
405 fn test_primitive_i32_to_arrow() {
406 let scalar = Scalar::primitive(-100000i32, Nullability::NonNullable);
407 let result = scalar.to_arrow_datum();
408 assert!(result.is_ok());
409 }
410
411 #[test]
412 fn test_primitive_i64_to_arrow() {
413 let scalar = Scalar::primitive(-10000000000i64, Nullability::NonNullable);
414 let result = scalar.to_arrow_datum();
415 assert!(result.is_ok());
416 }
417
418 #[test]
419 fn test_primitive_f16_to_arrow() {
420 use vortex_array::dtype::half::f16;
421
422 let scalar = Scalar::primitive(f16::from_f32(1.234), Nullability::NonNullable);
423 let result = scalar.to_arrow_datum();
424 assert!(result.is_ok());
425 }
426
427 #[test]
428 fn test_primitive_f32_to_arrow() {
429 let scalar = Scalar::primitive(1.234f32, Nullability::NonNullable);
430 let result = scalar.to_arrow_datum();
431 assert!(result.is_ok());
432 }
433
434 #[test]
435 fn test_primitive_f64_to_arrow() {
436 let scalar = Scalar::primitive(1.234567890123f64, Nullability::NonNullable);
437 let result = scalar.to_arrow_datum();
438 assert!(result.is_ok());
439 }
440
441 #[test]
442 fn test_null_primitive_to_arrow() {
443 let scalar = Scalar::null(i32::dtype().as_nullable());
444 let result = scalar.to_arrow_datum();
445 assert!(result.is_ok());
446 }
447
448 #[test]
449 fn test_utf8_scalar_to_arrow() {
450 let scalar = Scalar::utf8("hello world".to_string(), Nullability::NonNullable);
451 let result = scalar.to_arrow_datum();
452 assert!(result.is_ok());
453 }
454
455 #[test]
456 fn test_null_utf8_scalar_to_arrow() {
457 let scalar = Scalar::null(String::dtype().as_nullable());
458 let result = scalar.to_arrow_datum();
459 assert!(result.is_ok());
460 }
461
462 #[test]
463 fn test_binary_scalar_to_arrow() {
464 let data = vec![1u8, 2, 3, 4, 5];
465 let scalar = Scalar::binary(data, Nullability::NonNullable);
466 let result = scalar.to_arrow_datum();
467 assert!(result.is_ok());
468 }
469
470 #[test]
471 fn test_null_binary_scalar_to_arrow() {
472 let scalar = Scalar::null(DType::Binary(Nullability::Nullable));
473 let result = scalar.to_arrow_datum();
474 assert!(result.is_ok());
475 }
476
477 fn assert_arrow_scalar_data_type(scalar: &Scalar, expected: DataType) -> VortexResult<()> {
478 let datum = scalar.to_arrow_datum()?;
479 let (array, is_scalar) = datum.get();
480 assert!(is_scalar);
481 assert_eq!(array.data_type(), &expected);
482 Ok(())
483 }
484
485 #[test]
486 fn test_decimal_scalars_to_arrow() -> VortexResult<()> {
487 let decimal_dtype = DecimalDType::new(5, 2);
489
490 let scalar_i8 = Scalar::decimal(
491 DecimalValue::I8(100),
492 decimal_dtype,
493 Nullability::NonNullable,
494 );
495 assert_arrow_scalar_data_type(&scalar_i8, DataType::Decimal128(5, 2))?;
496
497 let scalar_i16 = Scalar::decimal(
498 DecimalValue::I16(10000),
499 decimal_dtype,
500 Nullability::NonNullable,
501 );
502 assert_arrow_scalar_data_type(&scalar_i16, DataType::Decimal128(5, 2))?;
503
504 let scalar_i32 = Scalar::decimal(
505 DecimalValue::I32(99999),
506 decimal_dtype,
507 Nullability::NonNullable,
508 );
509 assert_arrow_scalar_data_type(&scalar_i32, DataType::Decimal128(5, 2))?;
510
511 let scalar_i64 = Scalar::decimal(
512 DecimalValue::I64(99999),
513 decimal_dtype,
514 Nullability::NonNullable,
515 );
516 assert_arrow_scalar_data_type(&scalar_i64, DataType::Decimal128(5, 2))?;
517
518 let scalar_i128 = Scalar::decimal(
519 DecimalValue::I128(99999),
520 decimal_dtype,
521 Nullability::NonNullable,
522 );
523 assert_arrow_scalar_data_type(&scalar_i128, DataType::Decimal128(5, 2))?;
524
525 let value_i256 = i256::from_i128(99999);
526 let scalar_i256 = Scalar::decimal(
527 DecimalValue::I256(value_i256),
528 decimal_dtype,
529 Nullability::NonNullable,
530 );
531 assert_arrow_scalar_data_type(&scalar_i256, DataType::Decimal128(5, 2))?;
532
533 Ok(())
534 }
535
536 #[test]
537 fn decimal_i64_with_wide_precision_exports_decimal256() -> VortexResult<()> {
538 let scalar = Scalar::decimal(
539 DecimalValue::I64(1),
540 DecimalDType::new(39, 0),
541 Nullability::NonNullable,
542 );
543
544 assert_arrow_scalar_data_type(&scalar, DataType::Decimal256(39, 0))
545 }
546
547 #[test]
548 fn decimal_i256_with_narrow_precision_exports_decimal128() -> VortexResult<()> {
549 let scalar = Scalar::decimal(
550 DecimalValue::I256(i256::from_i128(1234)),
551 DecimalDType::new(4, 2),
552 Nullability::NonNullable,
553 );
554
555 assert_arrow_scalar_data_type(&scalar, DataType::Decimal128(4, 2))
556 }
557
558 #[test]
559 fn test_null_decimal_to_arrow() -> VortexResult<()> {
560 let decimal_dtype = DecimalDType::new(10, 2);
561 let scalar = Scalar::null(DType::Decimal(decimal_dtype, Nullability::Nullable));
562 assert_arrow_scalar_data_type(&scalar, DataType::Decimal128(10, 2))?;
563
564 let decimal_dtype = DecimalDType::new(39, 2);
565 let scalar = Scalar::null(DType::Decimal(decimal_dtype, Nullability::Nullable));
566 assert_arrow_scalar_data_type(&scalar, DataType::Decimal256(39, 2))
567 }
568
569 #[test]
570 fn decimal_scalar_to_arrow_preserves_precision_and_scale() -> VortexResult<()> {
571 let decimal_dtype = DecimalDType::new(12, 3);
572 let scalar = Scalar::decimal(
573 DecimalValue::I128(12345),
574 decimal_dtype,
575 Nullability::NonNullable,
576 );
577
578 let datum = scalar.to_arrow_datum()?;
579 let (array, is_scalar) = datum.get();
580 assert!(is_scalar);
581 let decimal = array
582 .as_any()
583 .downcast_ref::<Decimal128Array>()
584 .expect("decimal scalar should convert to Decimal128");
585 assert_eq!(decimal.precision(), 12);
586 assert_eq!(decimal.scale(), 3);
587
588 Ok(())
589 }
590
591 #[test]
592 fn test_map_scalar_to_arrow() -> VortexResult<()> {
593 let dtype = DType::map(
594 DType::Primitive(PType::I32, Nullability::NonNullable),
595 DType::Utf8(Nullability::Nullable),
596 true,
597 Nullability::Nullable,
598 )?;
599 let scalar = Scalar::try_map(
600 dtype,
601 [
602 (
603 Scalar::primitive(1i32, Nullability::NonNullable),
604 Scalar::utf8("one", Nullability::Nullable),
605 ),
606 (
607 Scalar::primitive(2i32, Nullability::NonNullable),
608 Scalar::null(DType::Utf8(Nullability::Nullable)),
609 ),
610 ],
611 )?;
612
613 let datum = scalar.to_arrow_datum()?;
614 let (array, is_scalar) = datum.get();
615 assert!(is_scalar);
616 let map = array
617 .as_any()
618 .downcast_ref::<MapArray>()
619 .expect("map scalar should convert to MapArray");
620 assert_eq!(map.len(), 1);
621 assert_eq!(map.value_offsets(), &[0, 2]);
622 assert!(map.is_valid(0));
623 assert_eq!(
624 map.keys()
625 .as_any()
626 .downcast_ref::<Int32Array>()
627 .expect("map key array should be Int32")
628 .values(),
629 &[1, 2]
630 );
631 let values = map
632 .values()
633 .as_any()
634 .downcast_ref::<StringViewArray>()
635 .expect("map value array should be StringView");
636 assert_eq!(values.value(0), "one");
637 assert!(values.is_null(1));
638
639 Ok(())
640 }
641
642 #[test]
643 fn map_decimal_scalar_to_arrow_preserves_decimal_type() -> VortexResult<()> {
644 let decimal_dtype = DecimalDType::new(9, 2);
645 let dtype = DType::map(
646 DType::Primitive(PType::I32, Nullability::NonNullable),
647 DType::Decimal(decimal_dtype, Nullability::Nullable),
648 false,
649 Nullability::NonNullable,
650 )?;
651 let scalar = Scalar::try_map(
652 dtype,
653 [(
654 Scalar::primitive(1i32, Nullability::NonNullable),
655 Scalar::decimal(
656 DecimalValue::I256(i256::from_i128(12345)),
657 decimal_dtype,
658 Nullability::Nullable,
659 ),
660 )],
661 )?;
662
663 let datum = scalar.to_arrow_datum()?;
664 let (array, is_scalar) = datum.get();
665 assert!(is_scalar);
666 let map = array
667 .as_any()
668 .downcast_ref::<MapArray>()
669 .expect("map scalar should convert to MapArray");
670 assert_eq!(map.values().data_type(), &DataType::Decimal128(9, 2));
671
672 let wide_decimal_dtype = DecimalDType::new(39, 2);
673 let dtype = DType::map(
674 DType::Primitive(PType::I32, Nullability::NonNullable),
675 DType::Decimal(wide_decimal_dtype, Nullability::Nullable),
676 false,
677 Nullability::NonNullable,
678 )?;
679 let scalar = Scalar::try_map(
680 dtype,
681 [(
682 Scalar::primitive(1i32, Nullability::NonNullable),
683 Scalar::decimal(
684 DecimalValue::I64(12345),
685 wide_decimal_dtype,
686 Nullability::Nullable,
687 ),
688 )],
689 )?;
690
691 let datum = scalar.to_arrow_datum()?;
692 let (array, is_scalar) = datum.get();
693 assert!(is_scalar);
694 let map = array
695 .as_any()
696 .downcast_ref::<MapArray>()
697 .expect("map scalar should convert to MapArray");
698 assert_eq!(map.values().data_type(), &DataType::Decimal256(39, 2));
699
700 Ok(())
701 }
702
703 #[test]
704 fn map_scalar_with_unsupported_nested_value_errors_without_panic() -> VortexResult<()> {
705 let struct_dtype = DType::Struct(
706 StructFields::from_iter([(
707 "field1",
708 FieldDType::from(DType::Primitive(PType::I32, Nullability::NonNullable)),
709 )]),
710 Nullability::NonNullable,
711 );
712 let dtype = DType::map(
713 DType::Primitive(PType::I32, Nullability::NonNullable),
714 struct_dtype.clone(),
715 false,
716 Nullability::NonNullable,
717 )?;
718 let scalar = Scalar::try_map(
719 dtype,
720 [(
721 Scalar::primitive(1i32, Nullability::NonNullable),
722 Scalar::struct_(
723 struct_dtype,
724 vec![Scalar::primitive(42i32, Nullability::NonNullable)],
725 ),
726 )],
727 )?;
728
729 let error = scalar
730 .to_arrow_datum()
731 .err()
732 .expect("unsupported nested map value should error");
733 assert!(error.to_string().contains("struct scalar conversion"));
734
735 Ok(())
736 }
737
738 #[test]
739 fn test_struct_scalar_to_arrow_todo() {
740 let struct_dtype = DType::Struct(
741 StructFields::from_iter([(
742 "field1",
743 FieldDType::from(DType::Primitive(PType::I32, Nullability::NonNullable)),
744 )]),
745 Nullability::NonNullable,
746 );
747
748 let struct_scalar = Scalar::struct_(
749 struct_dtype,
750 vec![Scalar::primitive(42i32, Nullability::NonNullable)],
751 );
752 let error = struct_scalar
753 .to_arrow_datum()
754 .err()
755 .expect("struct scalar should error");
756 assert!(error.to_string().contains("struct scalar conversion"));
757 }
758
759 #[test]
760 fn test_list_scalar_to_arrow_todo() {
761 let element_dtype = Arc::new(DType::Primitive(PType::I32, Nullability::NonNullable));
762 let list_scalar = Scalar::list(
763 element_dtype,
764 vec![
765 Scalar::primitive(1i32, Nullability::NonNullable),
766 Scalar::primitive(2i32, Nullability::NonNullable),
767 ],
768 Nullability::NonNullable,
769 );
770
771 let error = list_scalar
772 .to_arrow_datum()
773 .err()
774 .expect("list scalar should error");
775 assert!(error.to_string().contains("list scalar conversion"));
776 }
777
778 #[test]
779 fn test_non_temporal_extension_to_arrow_todo() {
780 #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
781 struct SomeExt;
782 impl ExtVTable for SomeExt {
783 type Metadata = String;
784 type NativeValue<'a> = &'a str;
785
786 #[expect(clippy::disallowed_methods, reason = "test-only id")]
787 fn id(&self) -> ExtId {
788 ExtId::new("some_ext")
789 }
790
791 fn serialize_metadata(&self, _options: &Self::Metadata) -> VortexResult<Vec<u8>> {
792 vortex_bail!("not implemented")
793 }
794
795 fn deserialize_metadata(&self, _data: &[u8]) -> VortexResult<Self::Metadata> {
796 vortex_bail!("not implemented")
797 }
798
799 fn validate_dtype(_ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
800 Ok(())
801 }
802
803 fn unpack_native<'a>(
804 _ext_dtype: &'a ExtDType<Self>,
805 _storage_value: &'a ScalarValue,
806 ) -> VortexResult<Self::NativeValue<'a>> {
807 Ok("")
808 }
809 }
810
811 let scalar = Scalar::extension::<SomeExt>(
812 "".into(),
813 Scalar::primitive(42i32, Nullability::NonNullable),
814 );
815
816 let error = scalar
817 .to_arrow_datum()
818 .err()
819 .expect("non-temporal extension scalar should error");
820 assert!(
821 error
822 .to_string()
823 .contains("Cannot convert extension scalar")
824 );
825 }
826
827 #[rstest]
828 #[case(TimeUnit::Nanoseconds, PType::I64, 123456789i64)]
829 #[case(TimeUnit::Microseconds, PType::I64, 123456789i64)]
830 #[case(TimeUnit::Milliseconds, PType::I32, 123456i64)]
831 #[case(TimeUnit::Seconds, PType::I32, 1234i64)]
832 fn test_temporal_time_to_arrow(
833 #[case] time_unit: TimeUnit,
834 #[case] ptype: PType,
835 #[case] value: i64,
836 ) {
837 let scalar = Scalar::extension::<Time>(
838 time_unit,
839 match ptype {
840 PType::I32 => {
841 let i32_value = i32::try_from(value).expect("test value should fit in i32");
842 Scalar::primitive(i32_value, Nullability::NonNullable)
843 }
844 PType::I64 => Scalar::primitive(value, Nullability::NonNullable),
845 _ => unreachable!(),
846 },
847 );
848
849 let result = scalar.to_arrow_datum();
850 assert!(result.is_ok());
851 }
852
853 #[rstest]
854 #[case(TimeUnit::Milliseconds, PType::I64, 1234567890000i64)]
855 #[case(TimeUnit::Days, PType::I32, 19000i64)]
856 fn test_temporal_date_to_arrow(
857 #[case] time_unit: TimeUnit,
858 #[case] ptype: PType,
859 #[case] value: i64,
860 ) {
861 let scalar = Scalar::extension::<Date>(
862 time_unit,
863 match ptype {
864 PType::I32 => {
865 let i32_value = i32::try_from(value).expect("test value should fit in i32");
866 Scalar::primitive(i32_value, Nullability::NonNullable)
867 }
868 PType::I64 => Scalar::primitive(value, Nullability::NonNullable),
869 _ => unreachable!(),
870 },
871 );
872
873 let result = scalar.to_arrow_datum();
874 assert!(result.is_ok());
875 }
876
877 #[rstest]
878 #[case(TimeUnit::Nanoseconds, 1234567890000000000i64)]
879 #[case(TimeUnit::Microseconds, 1234567890000000i64)]
880 #[case(TimeUnit::Milliseconds, 1234567890000i64)]
881 #[case(TimeUnit::Seconds, 1234567890i64)]
882 fn test_temporal_timestamp_to_arrow(#[case] time_unit: TimeUnit, #[case] value: i64) {
883 let scalar = Scalar::extension::<Timestamp>(
884 TimestampOptions {
885 unit: time_unit,
886 tz: None,
887 },
888 Scalar::primitive(value, Nullability::NonNullable),
889 );
890
891 let result = scalar.to_arrow_datum();
892 assert!(result.is_ok());
893 }
894
895 #[rstest]
896 #[case(TimeUnit::Nanoseconds, "UTC", 1234567890000000000i64)]
897 #[case(TimeUnit::Microseconds, "America/New_York", 1234567890000000i64)]
898 #[case(TimeUnit::Microseconds, "Asia/Qatar", 1234567890000000i64)]
899 #[case(TimeUnit::Microseconds, "Australia/Sydney", 1234567890000000i64)]
900 #[case(TimeUnit::Milliseconds, "Pacific/Honolulu", 1234567890000i64)]
901 #[case(TimeUnit::Seconds, "GMT", 1234567890i64)]
902 fn test_temporal_timestamp_tz_to_arrow(
903 #[case] time_unit: TimeUnit,
904 #[case] tz: &str,
905 #[case] value: i64,
906 ) {
907 let scalar = Scalar::extension::<Timestamp>(
908 TimestampOptions {
909 unit: time_unit,
910 tz: Some(tz.into()),
911 },
912 Scalar::primitive(value, Nullability::NonNullable),
913 );
914
915 let result = scalar.to_arrow_datum();
916 assert!(result.is_ok());
917 }
918
919 #[test]
920 fn test_temporal_with_null_value() {
921 let scalar = Scalar::extension::<Time>(
922 TimeUnit::Milliseconds,
923 Scalar::null(DType::Primitive(PType::I32, Nullability::Nullable)),
924 );
925
926 let _result = scalar.to_arrow_datum().unwrap();
927 }
928
929 #[test]
930 #[should_panic(expected = "DType utf8 is not a primitive type")]
931 fn test_temporal_non_primitive_storage_error() {
932 let _scalar = Scalar::extension::<Time>(
933 TimeUnit::Nanoseconds,
934 Scalar::utf8("not a timestamp", Nullability::NonNullable),
935 );
936 }
937}