1use num_traits::ToBytes;
7use num_traits::ToPrimitive;
8use prost::Message;
9use vortex_buffer::BufferString;
10use vortex_buffer::ByteBuffer;
11use vortex_error::VortexExpect;
12use vortex_error::VortexResult;
13use vortex_error::vortex_bail;
14use vortex_error::vortex_ensure;
15use vortex_error::vortex_err;
16use vortex_proto::scalar as pb;
17use vortex_proto::scalar::ListValue;
18use vortex_proto::scalar::scalar_value::Kind;
19use vortex_session::VortexSession;
20
21use crate::dtype::DType;
22use crate::dtype::PType;
23use crate::dtype::half::f16;
24use crate::dtype::i256;
25use crate::scalar::DecimalValue;
26use crate::scalar::PValue;
27use crate::scalar::Scalar;
28use crate::scalar::ScalarValue;
29
30impl From<&Scalar> for pb::Scalar {
35 fn from(value: &Scalar) -> Self {
36 pb::Scalar {
37 dtype: Some(
38 (value.dtype())
39 .try_into()
40 .vortex_expect("Failed to convert DType to proto"),
41 ),
42 value: Some(Box::new(ScalarValue::to_proto(value.value()))),
43 }
44 }
45}
46
47impl ScalarValue {
48 pub fn to_proto(this: Option<&Self>) -> pb::ScalarValue {
58 match this {
59 None => pb::ScalarValue {
60 kind: Some(Kind::NullValue(0)),
61 },
62 Some(this) => pb::ScalarValue::from(this),
63 }
64 }
65
66 pub fn to_proto_bytes<B: Default + bytes::BufMut>(value: Option<&ScalarValue>) -> B {
68 let proto = Self::to_proto(value);
69 let mut buf = B::default();
70 proto
71 .encode(&mut buf)
72 .vortex_expect("Failed to encode scalar value");
73 buf
74 }
75}
76
77impl From<&ScalarValue> for pb::ScalarValue {
78 fn from(value: &ScalarValue) -> Self {
79 match value {
80 ScalarValue::Bool(v) => pb::ScalarValue {
81 kind: Some(Kind::BoolValue(*v)),
82 },
83 ScalarValue::Primitive(v) => pb::ScalarValue::from(v),
84 ScalarValue::Decimal(v) => {
85 let inner_value = match v {
86 DecimalValue::I8(v) => v.to_le_bytes().to_vec(),
87 DecimalValue::I16(v) => v.to_le_bytes().to_vec(),
88 DecimalValue::I32(v) => v.to_le_bytes().to_vec(),
89 DecimalValue::I64(v) => v.to_le_bytes().to_vec(),
90 DecimalValue::I128(v128) => v128.to_le_bytes().to_vec(),
91 DecimalValue::I256(v256) => v256.to_le_bytes().to_vec(),
92 };
93
94 pb::ScalarValue {
95 kind: Some(Kind::BytesValue(inner_value)),
96 }
97 }
98 ScalarValue::Utf8(v) => pb::ScalarValue {
99 kind: Some(Kind::StringValue(v.to_string())),
100 },
101 ScalarValue::Binary(v) => pb::ScalarValue {
102 kind: Some(Kind::BytesValue(v.to_vec())),
103 },
104 ScalarValue::Tuple(v) => {
105 let mut values = Vec::with_capacity(v.len());
106 for elem in v.iter() {
107 values.push(ScalarValue::to_proto(elem.as_ref()));
108 }
109 pb::ScalarValue {
110 kind: Some(Kind::ListValue(ListValue { values })),
111 }
112 }
113 ScalarValue::Variant(v) => pb::ScalarValue {
114 kind: Some(Kind::VariantValue(Box::new(pb::Scalar::from(v.as_ref())))),
115 },
116 }
117 }
118}
119
120impl From<&PValue> for pb::ScalarValue {
121 fn from(value: &PValue) -> Self {
122 match value {
123 PValue::I8(v) => pb::ScalarValue {
124 kind: Some(Kind::Int64Value(*v as i64)),
125 },
126 PValue::I16(v) => pb::ScalarValue {
127 kind: Some(Kind::Int64Value(*v as i64)),
128 },
129 PValue::I32(v) => pb::ScalarValue {
130 kind: Some(Kind::Int64Value(*v as i64)),
131 },
132 PValue::I64(v) => pb::ScalarValue {
133 kind: Some(Kind::Int64Value(*v)),
134 },
135 PValue::U8(v) => pb::ScalarValue {
136 kind: Some(Kind::Uint64Value(*v as u64)),
137 },
138 PValue::U16(v) => pb::ScalarValue {
139 kind: Some(Kind::Uint64Value(*v as u64)),
140 },
141 PValue::U32(v) => pb::ScalarValue {
142 kind: Some(Kind::Uint64Value(*v as u64)),
143 },
144 PValue::U64(v) => pb::ScalarValue {
145 kind: Some(Kind::Uint64Value(*v)),
146 },
147 PValue::F16(v) => pb::ScalarValue {
148 kind: Some(Kind::F16Value(v.to_bits() as u64)),
149 },
150 PValue::F32(v) => pb::ScalarValue {
151 kind: Some(Kind::F32Value(*v)),
152 },
153 PValue::F64(v) => pb::ScalarValue {
154 kind: Some(Kind::F64Value(*v)),
155 },
156 }
157 }
158}
159
160impl Scalar {
165 pub fn from_proto_value(
174 value: &pb::ScalarValue,
175 dtype: &DType,
176 session: &VortexSession,
177 ) -> VortexResult<Self> {
178 let scalar_value = ScalarValue::from_proto(value, dtype, session)?;
179
180 Scalar::try_new(dtype.clone(), scalar_value)
181 }
182
183 pub fn from_proto(value: &pb::Scalar, session: &VortexSession) -> VortexResult<Self> {
189 let dtype = DType::from_proto(
190 value
191 .dtype
192 .as_ref()
193 .ok_or_else(|| vortex_err!(Serde: "Scalar missing dtype"))?,
194 session,
195 )?;
196
197 let pb_scalar_value: &pb::ScalarValue = value
198 .value
199 .as_ref()
200 .ok_or_else(|| vortex_err!(Serde: "Scalar missing value"))?;
201
202 let value: Option<ScalarValue> = ScalarValue::from_proto(pb_scalar_value, &dtype, session)?;
203
204 Scalar::try_new(dtype, value)
205 }
206}
207
208impl ScalarValue {
209 pub fn from_proto_bytes(
218 bytes: &[u8],
219 dtype: &DType,
220 session: &VortexSession,
221 ) -> VortexResult<Option<Self>> {
222 let proto = pb::ScalarValue::decode(bytes)?;
223 Self::from_proto(&proto, dtype, session)
224 }
225
226 pub fn from_proto(
235 value: &pb::ScalarValue,
236 dtype: &DType,
237 session: &VortexSession,
238 ) -> VortexResult<Option<Self>> {
239 let kind = value
240 .kind
241 .as_ref()
242 .ok_or_else(|| vortex_err!(Serde: "Scalar value missing kind"))?;
243
244 let dtype = match dtype {
246 DType::Extension(ext) => ext.storage_dtype(),
247 _ => dtype,
248 };
249
250 Ok(match kind {
251 Kind::NullValue(_) => None,
252 Kind::BoolValue(v) => Some(bool_from_proto(*v, dtype)?),
253 Kind::Int64Value(v) => Some(int64_from_proto(*v, dtype)?),
254 Kind::Uint64Value(v) => Some(uint64_from_proto(*v, dtype)?),
255 Kind::F16Value(v) => Some(f16_from_proto(*v, dtype)?),
256 Kind::F32Value(v) => Some(f32_from_proto(*v, dtype)?),
257 Kind::F64Value(v) => Some(f64_from_proto(*v, dtype)?),
258 Kind::StringValue(s) => Some(string_from_proto(s, dtype)?),
259 Kind::BytesValue(b) => Some(bytes_from_proto(b, dtype)?),
260 Kind::ListValue(v) => Some(list_from_proto(v, dtype, session)?),
261 Kind::VariantValue(v) => match dtype {
262 DType::Variant(_) => Some(ScalarValue::Variant(Box::new(Scalar::from_proto(
263 v, session,
264 )?))),
265 _ => vortex_bail!(Serde: "expected non-Variant scalar proto for dtype {dtype}"),
266 },
267 })
268 }
269}
270
271fn bool_from_proto(v: bool, dtype: &DType) -> VortexResult<ScalarValue> {
273 vortex_ensure!(
274 dtype.is_boolean(),
275 Serde: "expected Bool dtype for BoolValue, got {dtype}"
276 );
277
278 Ok(ScalarValue::Bool(v))
279}
280
281fn int64_from_proto(v: i64, dtype: &DType) -> VortexResult<ScalarValue> {
286 vortex_ensure!(
287 dtype.is_primitive(),
288 Serde: "expected Primitive dtype for Int64Value, got {dtype}"
289 );
290
291 let pvalue = match dtype.as_ptype() {
292 PType::I8 => v.to_i8().map(PValue::I8),
293 PType::I16 => v.to_i16().map(PValue::I16),
294 PType::I32 => v.to_i32().map(PValue::I32),
295 PType::I64 => Some(PValue::I64(v)),
296 PType::U8 => v.to_u8().map(PValue::U8),
299 PType::U16 => v.to_u16().map(PValue::U16),
300 PType::U32 => v.to_u32().map(PValue::U32),
301 PType::U64 => v.to_u64().map(PValue::U64),
302 ftype @ (PType::F16 | PType::F32 | PType::F64) => vortex_bail!(
303 Serde: "expected signed integer ptype for serialized Int64Value, got float {ftype}"
304 ),
305 }
306 .ok_or_else(|| vortex_err!(Serde: "Int64 value {v} out of range for dtype {dtype}"))?;
307
308 Ok(ScalarValue::Primitive(pvalue))
309}
310
311fn uint64_from_proto(v: u64, dtype: &DType) -> VortexResult<ScalarValue> {
317 vortex_ensure!(
318 dtype.is_primitive(),
319 Serde: "expected Primitive dtype for Uint64Value, got {dtype}"
320 );
321
322 let pvalue = match dtype.as_ptype() {
323 PType::U8 => v.to_u8().map(PValue::U8),
324 PType::U16 => v.to_u16().map(PValue::U16),
325 PType::U32 => v.to_u32().map(PValue::U32),
326 PType::U64 => Some(PValue::U64(v)),
327 PType::I8 => v.to_i8().map(PValue::I8),
330 PType::I16 => v.to_i16().map(PValue::I16),
331 PType::I32 => v.to_i32().map(PValue::I32),
332 PType::I64 => v.to_i64().map(PValue::I64),
333 PType::F16 => v.to_u16().map(f16::from_bits).map(PValue::F16),
335 ftype @ (PType::F32 | PType::F64) => vortex_bail!(
336 Serde: "expected unsigned integer ptype for serialized Uint64Value, got {ftype}"
337 ),
338 }
339 .ok_or_else(|| vortex_err!(Serde: "Uint64 value {v} out of range for dtype {dtype}"))?;
340
341 Ok(ScalarValue::Primitive(pvalue))
342}
343
344fn f16_from_proto(v: u64, dtype: &DType) -> VortexResult<ScalarValue> {
346 vortex_ensure!(
347 matches!(dtype, DType::Primitive(PType::F16, _)),
348 Serde: "expected F16 dtype for F16Value, got {dtype}"
349 );
350
351 let bits = u16::try_from(v)
352 .map_err(|_| vortex_err!(Serde: "f16 bitwise representation has more than 16 bits: {v}"))?;
353
354 Ok(ScalarValue::Primitive(PValue::F16(f16::from_bits(bits))))
355}
356
357fn f32_from_proto(v: f32, dtype: &DType) -> VortexResult<ScalarValue> {
359 vortex_ensure!(
360 matches!(dtype, DType::Primitive(PType::F32, _)),
361 Serde: "expected F32 dtype for F32Value, got {dtype}"
362 );
363
364 Ok(ScalarValue::Primitive(PValue::F32(v)))
365}
366
367fn f64_from_proto(v: f64, dtype: &DType) -> VortexResult<ScalarValue> {
369 vortex_ensure!(
370 matches!(dtype, DType::Primitive(PType::F64, _)),
371 Serde: "expected F64 dtype for F64Value, got {dtype}"
372 );
373
374 Ok(ScalarValue::Primitive(PValue::F64(v)))
375}
376
377fn string_from_proto(s: &str, dtype: &DType) -> VortexResult<ScalarValue> {
380 match dtype {
381 DType::Utf8(_) => Ok(ScalarValue::Utf8(BufferString::from(s))),
382 DType::Binary(_) => Ok(ScalarValue::Binary(ByteBuffer::copy_from(s.as_bytes()))),
383 _ => vortex_bail!(
384 Serde: "expected Utf8 or Binary dtype for StringValue, got {dtype}"
385 ),
386 }
387}
388
389fn bytes_from_proto(bytes: &[u8], dtype: &DType) -> VortexResult<ScalarValue> {
394 match dtype {
395 DType::Utf8(_) => Ok(ScalarValue::Utf8(BufferString::try_from(bytes)?)),
396 DType::Binary(_) => Ok(ScalarValue::Binary(ByteBuffer::copy_from(bytes))),
397 DType::Decimal(..) => Ok(ScalarValue::Decimal(match bytes.len() {
399 1 => DecimalValue::I8(bytes[0] as i8),
400 2 => DecimalValue::I16(i16::from_le_bytes(
401 bytes
402 .try_into()
403 .ok()
404 .vortex_expect("Buffer has invalid number of bytes"),
405 )),
406 4 => DecimalValue::I32(i32::from_le_bytes(
407 bytes
408 .try_into()
409 .ok()
410 .vortex_expect("Buffer has invalid number of bytes"),
411 )),
412 8 => DecimalValue::I64(i64::from_le_bytes(
413 bytes
414 .try_into()
415 .ok()
416 .vortex_expect("Buffer has invalid number of bytes"),
417 )),
418 16 => DecimalValue::I128(i128::from_le_bytes(
419 bytes
420 .try_into()
421 .ok()
422 .vortex_expect("Buffer has invalid number of bytes"),
423 )),
424 32 => DecimalValue::I256(i256::from_le_bytes(
425 bytes
426 .try_into()
427 .ok()
428 .vortex_expect("Buffer has invalid number of bytes"),
429 )),
430 l => vortex_bail!(Serde: "invalid decimal byte length: {l}"),
431 })),
432 _ => vortex_bail!(
433 Serde: "expected Utf8, Binary, or Decimal dtype for BytesValue, got {dtype}"
434 ),
435 }
436}
437
438fn list_from_proto(
440 v: &ListValue,
441 dtype: &DType,
442 session: &VortexSession,
443) -> VortexResult<ScalarValue> {
444 let element_dtype = match dtype {
445 DType::List(edt, _) => edt,
446 DType::FixedSizeList(edt, ..) => edt,
447 _ => {
448 vortex_bail!(Serde: "expected List or FixedSizeList dtype for ListValue, got {dtype}")
449 }
450 };
451
452 let mut values = Vec::with_capacity(v.values.len());
453 for elem in v.values.iter() {
454 values.push(ScalarValue::from_proto(
455 elem,
456 element_dtype.as_ref(),
457 session,
458 )?);
459 }
460
461 Ok(ScalarValue::Tuple(values))
462}
463
464#[cfg(test)]
465mod tests {
466 use std::f32;
467 use std::f64;
468 use std::sync::Arc;
469
470 use vortex_buffer::BufferString;
471 use vortex_error::vortex_panic;
472 use vortex_proto::scalar as pb;
473 use vortex_session::VortexSession;
474
475 use super::*;
476 use crate::dtype::DType;
477 use crate::dtype::DecimalDType;
478 use crate::dtype::Nullability;
479 use crate::dtype::PType;
480 use crate::dtype::half::f16;
481 use crate::scalar::DecimalValue;
482 use crate::scalar::Scalar;
483 use crate::scalar::ScalarValue;
484
485 fn session() -> VortexSession {
486 VortexSession::empty()
487 }
488
489 fn round_trip(scalar: Scalar) {
490 assert_eq!(
491 scalar,
492 Scalar::from_proto(&pb::Scalar::from(&scalar), &session()).unwrap(),
493 );
494 }
495
496 #[test]
497 fn test_null() {
498 round_trip(Scalar::null(DType::Null));
499 }
500
501 #[test]
502 fn test_bool() {
503 round_trip(Scalar::new(
504 DType::Bool(Nullability::Nullable),
505 Some(ScalarValue::Bool(true)),
506 ));
507 }
508
509 #[test]
510 fn test_primitive() {
511 round_trip(Scalar::new(
512 DType::Primitive(PType::I32, Nullability::Nullable),
513 Some(ScalarValue::Primitive(42i32.into())),
514 ));
515 }
516
517 #[test]
518 fn test_buffer() {
519 round_trip(Scalar::new(
520 DType::Binary(Nullability::Nullable),
521 Some(ScalarValue::Binary(vec![1, 2, 3].into())),
522 ));
523 }
524
525 #[test]
526 fn test_buffer_string() {
527 round_trip(Scalar::new(
528 DType::Utf8(Nullability::Nullable),
529 Some(ScalarValue::Utf8(BufferString::from("hello".to_string()))),
530 ));
531 }
532
533 #[test]
534 fn test_list() {
535 round_trip(Scalar::new(
536 DType::List(
537 Arc::new(DType::Primitive(PType::I32, Nullability::Nullable)),
538 Nullability::Nullable,
539 ),
540 Some(ScalarValue::Tuple(vec![
541 Some(ScalarValue::Primitive(42i32.into())),
542 Some(ScalarValue::Primitive(43i32.into())),
543 ])),
544 ));
545 }
546
547 #[test]
548 fn test_f16() {
549 round_trip(Scalar::primitive(
550 f16::from_f32(0.42),
551 Nullability::Nullable,
552 ));
553 }
554
555 #[test]
556 fn test_i8() {
557 round_trip(Scalar::new(
558 DType::Primitive(PType::I8, Nullability::Nullable),
559 Some(ScalarValue::Primitive(i8::MIN.into())),
560 ));
561
562 round_trip(Scalar::new(
563 DType::Primitive(PType::I8, Nullability::Nullable),
564 Some(ScalarValue::Primitive(0i8.into())),
565 ));
566
567 round_trip(Scalar::new(
568 DType::Primitive(PType::I8, Nullability::Nullable),
569 Some(ScalarValue::Primitive(i8::MAX.into())),
570 ));
571 }
572
573 #[test]
574 fn test_decimal_i32_roundtrip() {
575 round_trip(Scalar::decimal(
577 DecimalValue::I32(123_456),
578 DecimalDType::new(10, 2),
579 Nullability::NonNullable,
580 ));
581 }
582
583 #[test]
584 fn test_decimal_i128_roundtrip() {
585 round_trip(Scalar::decimal(
587 DecimalValue::I128(99_999_999_999_999_999_999),
588 DecimalDType::new(38, 6),
589 Nullability::Nullable,
590 ));
591 }
592
593 #[test]
594 fn test_decimal_null_roundtrip() {
595 round_trip(Scalar::null(DType::Decimal(
596 DecimalDType::new(10, 2),
597 Nullability::Nullable,
598 )));
599 }
600
601 #[test]
602 fn test_scalar_value_serde_roundtrip_binary() {
603 round_trip(Scalar::binary(
604 ByteBuffer::copy_from(b"hello"),
605 Nullability::NonNullable,
606 ));
607 }
608
609 #[test]
610 fn test_scalar_value_serde_roundtrip_utf8() {
611 round_trip(Scalar::utf8("hello", Nullability::NonNullable));
612 }
613
614 #[test]
615 fn test_variant_scalar_roundtrip() {
616 let nums = Scalar::list(
617 Arc::new(DType::Variant(Nullability::NonNullable)),
618 vec![
619 Scalar::variant(Scalar::primitive(-7_i16, Nullability::NonNullable)),
620 Scalar::variant(Scalar::primitive(42_u32, Nullability::NonNullable)),
621 Scalar::variant(Scalar::decimal(
622 DecimalValue::I128(123_456_789),
623 DecimalDType::new(18, 0),
624 Nullability::NonNullable,
625 )),
626 ],
627 Nullability::NonNullable,
628 );
629
630 let nested = Scalar::list(
631 Arc::new(DType::Variant(Nullability::NonNullable)),
632 vec![
633 Scalar::variant(Scalar::from(true)),
634 Scalar::variant(nums),
635 Scalar::variant(Scalar::binary(
636 ByteBuffer::copy_from(b"abc"),
637 Nullability::NonNullable,
638 )),
639 Scalar::variant(Scalar::null(DType::Null)),
640 ],
641 Nullability::NonNullable,
642 );
643
644 round_trip(Scalar::variant(nested));
645 }
646
647 #[test]
648 fn test_variant_scalar_proto_preserves_scalar_null_vs_variant_null() {
649 let scalar_null = Scalar::null(DType::Variant(Nullability::Nullable));
650 let variant_null = Scalar::variant(Scalar::null(DType::Null));
651
652 let scalar_null_pb = pb::Scalar::from(&scalar_null);
653 let variant_null_pb = pb::Scalar::from(&variant_null);
654
655 assert_ne!(scalar_null_pb, variant_null_pb);
656 assert_eq!(
657 Scalar::from_proto(&scalar_null_pb, &session()).unwrap(),
658 scalar_null,
659 );
660 assert_eq!(
661 Scalar::from_proto(&variant_null_pb, &session()).unwrap(),
662 variant_null,
663 );
664 }
665
666 #[test]
667 fn test_backcompat_f16_serialized_as_u64() {
668 let f16_value = f16::from_f32(0.42);
684 let f16_bits_as_u64 = f16_value.to_bits() as u64; let pb_scalar_value = pb::ScalarValue {
687 kind: Some(Kind::Uint64Value(f16_bits_as_u64)),
688 };
689
690 let scalar_value = ScalarValue::from_proto(
692 &pb_scalar_value,
693 &DType::Primitive(PType::U64, Nullability::NonNullable),
694 &session(),
695 )
696 .unwrap();
697 assert_eq!(
698 scalar_value.as_ref().map(|v| v.as_primitive()),
699 Some(&PValue::U64(14008u64)),
700 );
701
702 let scalar_value_f16 = ScalarValue::from_proto(
704 &pb_scalar_value,
705 &DType::Primitive(PType::F16, Nullability::Nullable),
706 &session(),
707 )
708 .unwrap();
709
710 let scalar = Scalar::new(
711 DType::Primitive(PType::F16, Nullability::Nullable),
712 scalar_value_f16,
713 );
714
715 assert_eq!(
716 scalar.as_primitive().pvalue().unwrap(),
717 PValue::F16(f16::from_f32(0.42)),
718 "Uint64Value should be correctly interpreted as f16 when dtype is F16"
719 );
720 }
721
722 #[test]
723 fn test_scalar_value_direct_roundtrip_f16() {
724 let f16_values = vec![
726 f16::from_f32(0.0),
727 f16::from_f32(1.0),
728 f16::from_f32(-1.0),
729 f16::from_f32(0.42),
730 f16::from_f32(5.722046e-6),
731 f16::from_f32(f32::consts::PI),
732 f16::INFINITY,
733 f16::NEG_INFINITY,
734 f16::NAN,
735 ];
736
737 for f16_val in f16_values {
738 let scalar_value = ScalarValue::Primitive(PValue::F16(f16_val));
739 let pb_value = ScalarValue::to_proto(Some(&scalar_value));
740 let read_back = ScalarValue::from_proto(
741 &pb_value,
742 &DType::Primitive(PType::F16, Nullability::NonNullable),
743 &session(),
744 )
745 .unwrap();
746
747 match (&scalar_value, read_back.as_ref()) {
748 (
749 ScalarValue::Primitive(PValue::F16(original)),
750 Some(ScalarValue::Primitive(PValue::F16(roundtripped))),
751 ) => {
752 if original.is_nan() && roundtripped.is_nan() {
753 continue;
755 }
756 assert_eq!(
757 original, roundtripped,
758 "F16 value {original:?} did not roundtrip correctly"
759 );
760 }
761 _ => {
762 vortex_panic!(
763 "Expected f16 primitive values, got {scalar_value:?} and {read_back:?}"
764 )
765 }
766 }
767 }
768 }
769
770 #[test]
771 fn test_scalar_value_direct_roundtrip_preserves_values() {
772 let exact_roundtrip_cases: Vec<(&str, Option<ScalarValue>, DType)> = vec![
777 ("null", None, DType::Null),
778 (
779 "bool_true",
780 Some(ScalarValue::Bool(true)),
781 DType::Bool(Nullability::Nullable),
782 ),
783 (
784 "bool_false",
785 Some(ScalarValue::Bool(false)),
786 DType::Bool(Nullability::Nullable),
787 ),
788 (
789 "u64",
790 Some(ScalarValue::Primitive(PValue::U64(18446744073709551615))),
791 DType::Primitive(PType::U64, Nullability::Nullable),
792 ),
793 (
794 "i64",
795 Some(ScalarValue::Primitive(PValue::I64(-9223372036854775808))),
796 DType::Primitive(PType::I64, Nullability::Nullable),
797 ),
798 (
799 "f32",
800 Some(ScalarValue::Primitive(PValue::F32(f32::consts::E))),
801 DType::Primitive(PType::F32, Nullability::Nullable),
802 ),
803 (
804 "f64",
805 Some(ScalarValue::Primitive(PValue::F64(f64::consts::PI))),
806 DType::Primitive(PType::F64, Nullability::Nullable),
807 ),
808 (
809 "string",
810 Some(ScalarValue::Utf8(BufferString::from("test"))),
811 DType::Utf8(Nullability::Nullable),
812 ),
813 (
814 "bytes",
815 Some(ScalarValue::Binary(vec![1, 2, 3, 4, 5].into())),
816 DType::Binary(Nullability::Nullable),
817 ),
818 ];
819
820 for (name, value, dtype) in exact_roundtrip_cases {
821 let pb_value = ScalarValue::to_proto(value.as_ref());
822 let read_back = ScalarValue::from_proto(&pb_value, &dtype, &session()).unwrap();
823
824 let original_debug = format!("{value:?}");
825 let roundtrip_debug = format!("{read_back:?}");
826 assert_eq!(
827 original_debug, roundtrip_debug,
828 "ScalarValue {name} did not roundtrip exactly"
829 );
830 }
831
832 let unsigned_cases = vec![
835 (
836 "u8",
837 ScalarValue::Primitive(PValue::U8(255)),
838 DType::Primitive(PType::U8, Nullability::Nullable),
839 255u64,
840 ),
841 (
842 "u16",
843 ScalarValue::Primitive(PValue::U16(65535)),
844 DType::Primitive(PType::U16, Nullability::Nullable),
845 65535u64,
846 ),
847 (
848 "u32",
849 ScalarValue::Primitive(PValue::U32(4294967295)),
850 DType::Primitive(PType::U32, Nullability::Nullable),
851 4294967295u64,
852 ),
853 ];
854
855 for (name, value, dtype, expected) in unsigned_cases {
856 let pb_value = ScalarValue::to_proto(Some(&value));
857 let read_back = ScalarValue::from_proto(&pb_value, &dtype, &session()).unwrap();
858
859 match read_back.as_ref() {
860 Some(ScalarValue::Primitive(pv)) => {
861 let v = match pv {
862 PValue::U8(v) => *v as u64,
863 PValue::U16(v) => *v as u64,
864 PValue::U32(v) => *v as u64,
865 PValue::U64(v) => *v,
866 _ => vortex_panic!("Unexpected primitive type for {name}: {pv:?}"),
867 };
868 assert_eq!(
869 v, expected,
870 "ScalarValue {name} value not preserved: expected {expected}, got {v}"
871 );
872 }
873 _ => vortex_panic!("Unexpected type after roundtrip for {name}: {read_back:?}"),
874 }
875 }
876
877 let signed_cases = vec![
879 (
880 "i8",
881 ScalarValue::Primitive(PValue::I8(-128)),
882 DType::Primitive(PType::I8, Nullability::Nullable),
883 -128i64,
884 ),
885 (
886 "i16",
887 ScalarValue::Primitive(PValue::I16(-32768)),
888 DType::Primitive(PType::I16, Nullability::Nullable),
889 -32768i64,
890 ),
891 (
892 "i32",
893 ScalarValue::Primitive(PValue::I32(-2147483648)),
894 DType::Primitive(PType::I32, Nullability::Nullable),
895 -2147483648i64,
896 ),
897 ];
898
899 for (name, value, dtype, expected) in signed_cases {
900 let pb_value = ScalarValue::to_proto(Some(&value));
901 let read_back = ScalarValue::from_proto(&pb_value, &dtype, &session()).unwrap();
902
903 match read_back.as_ref() {
904 Some(ScalarValue::Primitive(pv)) => {
905 let v = match pv {
906 PValue::I8(v) => *v as i64,
907 PValue::I16(v) => *v as i64,
908 PValue::I32(v) => *v as i64,
909 PValue::I64(v) => *v,
910 _ => vortex_panic!("Unexpected primitive type for {name}: {pv:?}"),
911 };
912 assert_eq!(
913 v, expected,
914 "ScalarValue {name} value not preserved: expected {expected}, got {v}"
915 );
916 }
917 _ => vortex_panic!("Unexpected type after roundtrip for {name}: {read_back:?}"),
918 }
919 }
920 }
921
922 #[test]
925 fn test_backcompat_signed_integer_deserialized_as_unsigned() {
926 let v = ScalarValue::Primitive(PValue::I64(0));
927 assert_eq!(
928 Scalar::from_proto_value(
929 &pb::ScalarValue::from(&v),
930 &DType::Primitive(PType::U64, Nullability::Nullable),
931 &session()
932 )
933 .unwrap(),
934 Scalar::primitive(0u64, Nullability::Nullable)
935 );
936 }
937
938 #[test]
941 fn test_backcompat_unsigned_integer_deserialized_as_signed() {
942 let v = ScalarValue::Primitive(PValue::U64(0));
943 assert_eq!(
944 Scalar::from_proto_value(
945 &pb::ScalarValue::from(&v),
946 &DType::Primitive(PType::I64, Nullability::Nullable),
947 &session()
948 )
949 .unwrap(),
950 Scalar::primitive(0i64, Nullability::Nullable)
951 );
952 }
953}