1use std::collections::HashMap;
19use std::fmt;
20use std::hash::Hash;
21use std::sync::Arc;
22
23use crate::physical_expr::PhysicalExpr;
24
25use arrow::compute::{CastOptions, can_cast_types};
26use arrow::datatypes::{DataType, DataType::*, Field, FieldRef, Schema};
27use arrow::record_batch::RecordBatch;
28use arrow_schema::extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY};
29use datafusion_common::datatype::DataTypeExt;
30use datafusion_common::format::DEFAULT_FORMAT_OPTIONS;
31use datafusion_common::nested_struct::{
32 requires_nested_struct_cast, validate_data_type_compatibility,
33};
34use datafusion_common::{Result, not_impl_err};
35use datafusion_expr_common::columnar_value::ColumnarValue;
36use datafusion_expr_common::interval_arithmetic::Interval;
37use datafusion_expr_common::sort_properties::ExprProperties;
38
39const DEFAULT_CAST_OPTIONS: CastOptions<'static> = CastOptions {
40 safe: false,
41 format_options: DEFAULT_FORMAT_OPTIONS,
42};
43
44const DEFAULT_SAFE_CAST_OPTIONS: CastOptions<'static> = CastOptions {
45 safe: true,
46 format_options: DEFAULT_FORMAT_OPTIONS,
47};
48
49fn can_cast_named_struct_types(source: &DataType, target: &DataType) -> bool {
56 validate_data_type_compatibility("", source, target).is_ok()
57}
58
59#[derive(Debug, Clone, Eq)]
61pub struct CastExpr {
62 pub expr: Arc<dyn PhysicalExpr>,
64 target_field: FieldRef,
72 explicit_target: bool,
76 cast_options: CastOptions<'static>,
78}
79
80impl PartialEq for CastExpr {
82 fn eq(&self, other: &Self) -> bool {
83 self.expr.eq(&other.expr)
86 && self.cast_type().eq(other.cast_type())
87 && self.target_metadata().eq(&other.target_metadata())
88 && self.target_nullable().eq(&other.target_nullable())
89 && self.cast_options.eq(&other.cast_options)
90 }
91}
92
93impl Hash for CastExpr {
94 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
95 self.expr.hash(state);
96 self.cast_type().hash(state);
97 if let Some(metadata) = self.target_metadata() {
99 let mut entries: Vec<_> = metadata.iter().collect();
100 entries.sort_by_key(|(k, _)| *k);
101 for (k, v) in entries {
102 k.hash(state);
103 v.hash(state);
104 }
105 }
106 self.target_nullable().hash(state);
107 self.cast_options.hash(state);
108 }
109}
110
111impl CastExpr {
112 pub fn new(
122 expr: Arc<dyn PhysicalExpr>,
123 cast_type: DataType,
124 cast_options: Option<CastOptions<'static>>,
125 ) -> Self {
126 Self {
127 expr,
128 target_field: cast_type.into_nullable_field_ref(),
129 explicit_target: false,
130 cast_options: cast_options.unwrap_or(DEFAULT_CAST_OPTIONS),
131 }
132 }
133
134 pub fn new_with_target_field(
148 expr: Arc<dyn PhysicalExpr>,
149 target_field: FieldRef,
150 cast_options: Option<CastOptions<'static>>,
151 ) -> Self {
152 Self {
153 expr,
154 target_field,
155 explicit_target: true,
156 cast_options: cast_options.unwrap_or(DEFAULT_CAST_OPTIONS),
157 }
158 }
159
160 pub fn expr(&self) -> &Arc<dyn PhysicalExpr> {
162 &self.expr
163 }
164
165 pub fn cast_type(&self) -> &DataType {
167 self.target_field.data_type()
168 }
169
170 pub fn target_metadata(&self) -> Option<&HashMap<String, String>> {
172 self.explicit_target.then(|| self.target_field.metadata())
173 }
174
175 pub fn target_nullable(&self) -> Option<bool> {
177 self.explicit_target
178 .then(|| self.target_field.is_nullable())
179 }
180
181 pub fn target_field(&self) -> &FieldRef {
195 &self.target_field
196 }
197
198 pub fn cast_options(&self) -> &CastOptions<'static> {
200 &self.cast_options
201 }
202
203 pub fn has_explicit_metadata(&self) -> bool {
205 self.explicit_target
206 }
207
208 pub fn has_explicit_nullability(&self) -> bool {
210 self.explicit_target
211 }
212
213 fn resolved_target_field(&self, input_schema: &Schema) -> Result<FieldRef> {
214 let source_result = self.expr.return_field(input_schema);
218
219 if self.explicit_target {
220 let name = source_result
222 .as_ref()
223 .map(|f| f.name().to_string())
224 .unwrap_or_default();
225 return Ok(Arc::new(
226 Field::new(
227 name,
228 self.cast_type().clone(),
229 self.target_field.is_nullable(),
230 )
231 .with_metadata(self.target_field.metadata().clone()),
232 ));
233 }
234
235 source_result.map(|source_field| {
238 let mut metadata = source_field.metadata().clone();
239 metadata.remove(EXTENSION_TYPE_NAME_KEY);
240 metadata.remove(EXTENSION_TYPE_METADATA_KEY);
241
242 Arc::new(
243 source_field
244 .as_ref()
245 .clone()
246 .with_data_type(self.cast_type().clone())
247 .with_metadata(metadata),
248 )
249 })
250 }
251
252 pub fn check_bigger_cast(cast_type: &DataType, src: &DataType) -> bool {
255 if cast_type.eq(src) {
256 return true;
257 }
258 matches!(
259 (src, cast_type),
260 (Int8, Int16 | Int32 | Int64)
261 | (Int16, Int32 | Int64)
262 | (Int32, Int64)
263 | (UInt8, UInt16 | UInt32 | UInt64)
264 | (UInt16, UInt32 | UInt64)
265 | (UInt32, UInt64)
266 | (Int8 | Int16 | UInt8 | UInt16, Float32)
267 | (Int8 | Int16 | Int32 | UInt8 | UInt16 | UInt32, Float64)
268 | (Utf8, LargeUtf8)
269 )
270 }
271
272 pub fn is_bigger_cast(&self, src: &DataType) -> bool {
274 Self::check_bigger_cast(self.cast_type(), src)
275 }
276}
277
278pub(crate) fn is_order_preserving_cast_family(
279 source_type: &DataType,
280 target_type: &DataType,
281) -> bool {
282 (source_type.is_numeric() || *source_type == Boolean) && target_type.is_numeric()
283 || source_type.is_temporal() && target_type.is_temporal()
284 || source_type.eq(target_type)
285}
286
287pub(crate) fn cast_expr_properties(
288 child: &ExprProperties,
289 target_type: &DataType,
290) -> Result<ExprProperties> {
291 let unbounded = Interval::make_unbounded(target_type)?;
292 let source_type = child.range.data_type();
293 let bigger_cast = CastExpr::check_bigger_cast(target_type, &source_type);
297 if is_order_preserving_cast_family(&source_type, target_type) || bigger_cast {
298 Ok(child
299 .clone()
300 .with_range(unbounded)
301 .with_strictly_order_preserving(
302 child.strictly_order_preserving && bigger_cast,
303 ))
304 } else {
305 Ok(ExprProperties::new_unknown().with_range(unbounded))
306 }
307}
308
309impl fmt::Display for CastExpr {
310 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311 write!(f, "CAST({} AS {})", self.expr, self.cast_type())
312 }
313}
314
315impl PhysicalExpr for CastExpr {
316 fn data_type(&self, _input_schema: &Schema) -> Result<DataType> {
317 Ok(self.cast_type().clone())
318 }
319
320 fn nullable(&self, input_schema: &Schema) -> Result<bool> {
321 let child_nullable = self.expr.nullable(input_schema)?;
327 let target_nullable = self.resolved_target_field(input_schema)?.is_nullable();
328 Ok(child_nullable || target_nullable)
329 }
330
331 fn evaluate(&self, batch: &RecordBatch) -> Result<ColumnarValue> {
332 let value = self.expr.evaluate(batch)?;
333 value.cast_to(self.cast_type(), Some(&self.cast_options))
334 }
335
336 fn return_field(&self, input_schema: &Schema) -> Result<FieldRef> {
337 self.resolved_target_field(input_schema)
338 }
339
340 fn children(&self) -> Vec<&Arc<dyn PhysicalExpr>> {
341 vec![&self.expr]
342 }
343
344 fn with_new_children(
345 self: Arc<Self>,
346 children: Vec<Arc<dyn PhysicalExpr>>,
347 ) -> Result<Arc<dyn PhysicalExpr>> {
348 Ok(Arc::new(CastExpr {
349 expr: Arc::clone(&children[0]),
350 target_field: Arc::clone(&self.target_field),
351 explicit_target: self.explicit_target,
352 cast_options: self.cast_options.clone(),
353 }))
354 }
355
356 fn evaluate_bounds(&self, children: &[&Interval]) -> Result<Interval> {
357 children[0].cast_to(self.cast_type(), &self.cast_options)
359 }
360
361 fn propagate_constraints(
362 &self,
363 interval: &Interval,
364 children: &[&Interval],
365 ) -> Result<Option<Vec<Interval>>> {
366 let child_interval = children[0];
367 let cast_type = child_interval.data_type();
369 Ok(Some(vec![
370 interval.cast_to(&cast_type, &DEFAULT_SAFE_CAST_OPTIONS)?,
371 ]))
372 }
373
374 fn get_properties(&self, children: &[ExprProperties]) -> Result<ExprProperties> {
377 cast_expr_properties(&children[0], self.cast_type())
378 }
379
380 fn fmt_sql(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 write!(f, "CAST(")?;
382 self.expr.fmt_sql(f)?;
383 write!(f, " AS {:?}", self.cast_type())?;
384
385 write!(f, ")")
386 }
387
388 #[cfg(feature = "proto")]
389 fn try_to_proto(
390 &self,
391 ctx: &datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncodeCtx<'_>,
392 ) -> Result<Option<datafusion_proto_models::protobuf::PhysicalExprNode>> {
393 use datafusion_proto_models::protobuf;
394
395 Ok(Some(protobuf::PhysicalExprNode {
396 expr_id: None,
397 expr_type: Some(protobuf::physical_expr_node::ExprType::Cast(Box::new(
398 protobuf::PhysicalCastNode {
399 expr: Some(Box::new(ctx.encode_child(self.expr())?)),
400 arrow_type: Some(self.cast_type().try_into()?),
401 },
402 ))),
403 }))
404 }
405}
406
407#[cfg(feature = "proto")]
408impl CastExpr {
409 pub fn try_from_proto(
417 node: &datafusion_proto_models::protobuf::PhysicalExprNode,
418 ctx: &datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecodeCtx<'_>,
419 ) -> Result<Arc<dyn PhysicalExpr>> {
420 use datafusion_common::internal_datafusion_err;
421 use datafusion_common::internal_err;
422 use datafusion_proto_models::protobuf;
423
424 let cast_expr = match &node.expr_type {
425 Some(protobuf::physical_expr_node::ExprType::Cast(cast_expr)) => {
426 cast_expr.as_ref()
427 }
428 _ => return internal_err!("PhysicalExprNode is not a CastExpr"),
429 };
430
431 let expr = ctx.decode_required_expression(
432 cast_expr.expr.as_deref(),
433 "CastExpr",
434 "expr",
435 )?;
436 let arrow_type = cast_expr.arrow_type.as_ref().ok_or_else(|| {
437 internal_datafusion_err!("CastExpr is missing required field 'arrow_type'")
438 })?;
439
440 Ok(Arc::new(CastExpr::new(expr, arrow_type.try_into()?, None)))
441 }
442}
443
444pub fn cast_with_options(
449 expr: Arc<dyn PhysicalExpr>,
450 input_schema: &Schema,
451 cast_type: DataType,
452 cast_options: Option<CastOptions<'static>>,
453) -> Result<Arc<dyn PhysicalExpr>> {
454 let expr_type = expr.data_type(input_schema)?;
455
456 if expr_type == cast_type {
458 return Ok(Arc::clone(&expr));
459 }
460
461 let can_build_cast = if requires_nested_struct_cast(&expr_type, &cast_type) {
462 can_cast_named_struct_types(&expr_type, &cast_type)
463 } else {
464 can_cast_types(&expr_type, &cast_type)
465 };
466
467 if !can_build_cast {
468 return not_impl_err!("Unsupported CAST from {expr_type} to {cast_type}");
469 }
470
471 Ok(Arc::new(CastExpr::new(expr, cast_type, cast_options)))
472}
473
474pub fn cast_with_target_field(
482 expr: Arc<dyn PhysicalExpr>,
483 input_schema: &Schema,
484 target_field: &FieldRef,
485 cast_options: Option<CastOptions<'static>>,
486) -> Result<Arc<dyn PhysicalExpr>> {
487 let expr_type = expr.data_type(input_schema)?;
488 let cast_type = target_field.data_type();
489
490 let is_type_only = target_field.name().is_empty()
494 && target_field.is_nullable()
495 && target_field.metadata().is_empty();
496
497 if expr_type == *cast_type && is_type_only {
502 let source_field = expr.return_field(input_schema)?;
503 let has_extension_metadata = source_field
504 .metadata()
505 .contains_key(EXTENSION_TYPE_NAME_KEY);
506 if !has_extension_metadata {
507 return Ok(Arc::clone(&expr));
508 }
509 }
510
511 let can_build_cast = if requires_nested_struct_cast(&expr_type, cast_type) {
512 can_cast_named_struct_types(&expr_type, cast_type)
518 } else {
519 can_cast_types(&expr_type, cast_type)
520 };
521
522 if !can_build_cast {
523 return not_impl_err!("Unsupported CAST from {expr_type} to {cast_type}");
524 }
525
526 if is_type_only {
530 Ok(Arc::new(CastExpr::new(
531 expr,
532 cast_type.clone(),
533 cast_options,
534 )))
535 } else {
536 Ok(Arc::new(CastExpr::new_with_target_field(
537 expr,
538 Arc::clone(target_field),
539 cast_options,
540 )))
541 }
542}
543
544pub fn cast(
549 expr: Arc<dyn PhysicalExpr>,
550 input_schema: &Schema,
551 cast_type: DataType,
552) -> Result<Arc<dyn PhysicalExpr>> {
553 cast_with_options(expr, input_schema, cast_type, None)
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559
560 use crate::expressions::column::col;
561
562 use arrow::{
563 array::{
564 Array, ArrayRef, Decimal128Array, Float32Array, Float64Array, Int8Array,
565 Int16Array, Int32Array, Int64Array, StringArray, StructArray,
566 Time64NanosecondArray, TimestampNanosecondArray, UInt32Array,
567 },
568 datatypes::*,
569 };
570 use datafusion_common::ScalarValue;
571 use datafusion_common::cast::{
572 as_boolean_array, as_int64_array, as_string_array, as_struct_array,
573 as_uint8_array,
574 };
575 use datafusion_physical_expr_common::physical_expr::fmt_sql;
576 use insta::assert_snapshot;
577 use std::collections::HashMap;
578
579 fn make_struct_array(fields: Fields, arrays: Vec<ArrayRef>) -> StructArray {
580 StructArray::new(fields, arrays, None)
581 }
582
583 fn cast_struct_array(
584 column: &str,
585 input_field: Field,
586 target_field: Field,
587 input_array: StructArray,
588 ) -> Result<StructArray> {
589 let schema = Arc::new(Schema::new(vec![input_field]));
590 let batch = RecordBatch::try_new(
591 Arc::clone(&schema),
592 vec![Arc::new(input_array) as ArrayRef],
593 )?;
594 let expr = CastExpr::new_with_target_field(
595 col(column, schema.as_ref())?,
596 Arc::new(target_field),
597 None,
598 );
599
600 let result = expr.evaluate(&batch)?.into_array(batch.num_rows())?;
601 Ok(as_struct_array(result.as_ref())?.clone())
602 }
603
604 macro_rules! generic_decimal_to_other_test_cast {
611 ($DECIMAL_ARRAY:ident, $A_TYPE:expr, $TYPEARRAY:ident, $TYPE:expr, $VEC:expr,$CAST_OPTIONS:expr) => {{
612 let schema = Schema::new(vec![Field::new("a", $A_TYPE, true)]);
613 let batch = RecordBatch::try_new(
614 Arc::new(schema.clone()),
615 vec![Arc::new($DECIMAL_ARRAY)],
616 )?;
617 let expression =
619 cast_with_options(col("a", &schema)?, &schema, $TYPE, $CAST_OPTIONS)?;
620
621 assert_eq!(format!("CAST(a@0 AS {})", $TYPE), format!("{}", expression));
623
624 assert_eq!(expression.data_type(&schema)?, $TYPE);
626
627 let result = expression
629 .evaluate(&batch)?
630 .into_array(batch.num_rows())
631 .expect("Failed to convert to array");
632
633 assert_eq!(*result.data_type(), $TYPE);
635
636 let result = result
638 .as_any()
639 .downcast_ref::<$TYPEARRAY>()
640 .expect("failed to downcast");
641
642 for (i, x) in $VEC.iter().enumerate() {
644 match x {
645 Some(x) => assert_eq!(result.value(i), *x),
646 None => assert!(result.is_null(i)),
647 }
648 }
649 }};
650 }
651
652 macro_rules! generic_test_cast {
659 ($A_ARRAY:ident, $A_TYPE:expr, $A_VEC:expr, $TYPEARRAY:ident, $TYPE:expr, $VEC:expr, $CAST_OPTIONS:expr) => {{
660 let schema = Schema::new(vec![Field::new("a", $A_TYPE, true)]);
661 let a_vec_len = $A_VEC.len();
662 let a = $A_ARRAY::from($A_VEC);
663 let batch =
664 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(a)])?;
665
666 let expression =
668 cast_with_options(col("a", &schema)?, &schema, $TYPE, $CAST_OPTIONS)?;
669
670 assert_eq!(format!("CAST(a@0 AS {})", $TYPE), format!("{}", expression));
672
673 assert_eq!(expression.data_type(&schema)?, $TYPE);
675
676 let result = expression
678 .evaluate(&batch)?
679 .into_array(batch.num_rows())
680 .expect("Failed to convert to array");
681
682 assert_eq!(*result.data_type(), $TYPE);
684
685 assert_eq!(result.len(), a_vec_len);
687
688 let result = result
690 .as_any()
691 .downcast_ref::<$TYPEARRAY>()
692 .expect("failed to downcast");
693
694 for (i, x) in $VEC.iter().enumerate() {
696 match x {
697 Some(x) => assert_eq!(result.value(i), *x),
698 None => assert!(result.is_null(i)),
699 }
700 }
701 }};
702 }
703
704 #[test]
705 fn test_cast_decimal_to_decimal() -> Result<()> {
706 let array = vec![
707 Some(1234),
708 Some(2222),
709 Some(3),
710 Some(4000),
711 Some(5000),
712 None,
713 ];
714
715 let decimal_array = array
716 .clone()
717 .into_iter()
718 .collect::<Decimal128Array>()
719 .with_precision_and_scale(10, 3)?;
720
721 generic_decimal_to_other_test_cast!(
722 decimal_array,
723 Decimal128(10, 3),
724 Decimal128Array,
725 Decimal128(20, 6),
726 [
727 Some(1_234_000),
728 Some(2_222_000),
729 Some(3_000),
730 Some(4_000_000),
731 Some(5_000_000),
732 None
733 ],
734 None
735 );
736
737 let decimal_array = array
738 .into_iter()
739 .collect::<Decimal128Array>()
740 .with_precision_and_scale(10, 3)?;
741
742 generic_decimal_to_other_test_cast!(
743 decimal_array,
744 Decimal128(10, 3),
745 Decimal128Array,
746 Decimal128(10, 2),
747 [Some(123), Some(222), Some(0), Some(400), Some(500), None],
748 None
749 );
750
751 Ok(())
752 }
753
754 #[test]
755 fn test_cast_decimal_to_decimal_overflow() -> Result<()> {
756 let array = vec![Some(123456789)];
757
758 let decimal_array = array
759 .clone()
760 .into_iter()
761 .collect::<Decimal128Array>()
762 .with_precision_and_scale(10, 3)?;
763
764 let schema = Schema::new(vec![Field::new("a", Decimal128(10, 3), false)]);
765 let batch = RecordBatch::try_new(
766 Arc::new(schema.clone()),
767 vec![Arc::new(decimal_array)],
768 )?;
769 let expression =
770 cast_with_options(col("a", &schema)?, &schema, Decimal128(6, 2), None)?;
771 let e = expression.evaluate(&batch).unwrap_err().strip_backtrace(); assert_snapshot!(e, @"Arrow error: Invalid argument error: 123456.79 is too large to store in a Decimal128 of precision 6. Max is 9999.99");
773 let expression_safe = cast_with_options(
775 col("a", &schema)?,
776 &schema,
777 Decimal128(6, 2),
778 Some(DEFAULT_SAFE_CAST_OPTIONS),
779 )?;
780 let result_safe = expression_safe
781 .evaluate(&batch)?
782 .into_array(batch.num_rows())
783 .expect("failed to convert to array");
784
785 assert!(result_safe.is_null(0));
786
787 Ok(())
788 }
789
790 #[test]
791 fn test_cast_decimal_to_numeric() -> Result<()> {
792 let array = vec![Some(1), Some(2), Some(3), Some(4), Some(5), None];
793 let decimal_array = array
795 .clone()
796 .into_iter()
797 .collect::<Decimal128Array>()
798 .with_precision_and_scale(10, 0)?;
799 generic_decimal_to_other_test_cast!(
800 decimal_array,
801 Decimal128(10, 0),
802 Int8Array,
803 Int8,
804 [
805 Some(1_i8),
806 Some(2_i8),
807 Some(3_i8),
808 Some(4_i8),
809 Some(5_i8),
810 None
811 ],
812 None
813 );
814
815 let decimal_array = array
817 .clone()
818 .into_iter()
819 .collect::<Decimal128Array>()
820 .with_precision_and_scale(10, 0)?;
821 generic_decimal_to_other_test_cast!(
822 decimal_array,
823 Decimal128(10, 0),
824 Int16Array,
825 Int16,
826 [
827 Some(1_i16),
828 Some(2_i16),
829 Some(3_i16),
830 Some(4_i16),
831 Some(5_i16),
832 None
833 ],
834 None
835 );
836
837 let decimal_array = array
839 .clone()
840 .into_iter()
841 .collect::<Decimal128Array>()
842 .with_precision_and_scale(10, 0)?;
843 generic_decimal_to_other_test_cast!(
844 decimal_array,
845 Decimal128(10, 0),
846 Int32Array,
847 Int32,
848 [
849 Some(1_i32),
850 Some(2_i32),
851 Some(3_i32),
852 Some(4_i32),
853 Some(5_i32),
854 None
855 ],
856 None
857 );
858
859 let decimal_array = array
861 .into_iter()
862 .collect::<Decimal128Array>()
863 .with_precision_and_scale(10, 0)?;
864 generic_decimal_to_other_test_cast!(
865 decimal_array,
866 Decimal128(10, 0),
867 Int64Array,
868 Int64,
869 [
870 Some(1_i64),
871 Some(2_i64),
872 Some(3_i64),
873 Some(4_i64),
874 Some(5_i64),
875 None
876 ],
877 None
878 );
879
880 let array = vec![
882 Some(1234),
883 Some(2222),
884 Some(3),
885 Some(4000),
886 Some(5000),
887 None,
888 ];
889 let decimal_array = array
890 .clone()
891 .into_iter()
892 .collect::<Decimal128Array>()
893 .with_precision_and_scale(10, 3)?;
894 generic_decimal_to_other_test_cast!(
895 decimal_array,
896 Decimal128(10, 3),
897 Float32Array,
898 Float32,
899 [
900 Some(1.234_f32),
901 Some(2.222_f32),
902 Some(0.003_f32),
903 Some(4.0_f32),
904 Some(5.0_f32),
905 None
906 ],
907 None
908 );
909
910 let decimal_array = array
912 .into_iter()
913 .collect::<Decimal128Array>()
914 .with_precision_and_scale(20, 6)?;
915 generic_decimal_to_other_test_cast!(
916 decimal_array,
917 Decimal128(20, 6),
918 Float64Array,
919 Float64,
920 [
921 Some(0.001234_f64),
922 Some(0.002222_f64),
923 Some(0.000003_f64),
924 Some(0.004_f64),
925 Some(0.005_f64),
926 None
927 ],
928 None
929 );
930 Ok(())
931 }
932
933 #[test]
934 fn test_cast_numeric_to_decimal() -> Result<()> {
935 generic_test_cast!(
937 Int8Array,
938 Int8,
939 vec![1, 2, 3, 4, 5],
940 Decimal128Array,
941 Decimal128(3, 0),
942 [Some(1), Some(2), Some(3), Some(4), Some(5)],
943 None
944 );
945
946 generic_test_cast!(
948 Int16Array,
949 Int16,
950 vec![1, 2, 3, 4, 5],
951 Decimal128Array,
952 Decimal128(5, 0),
953 [Some(1), Some(2), Some(3), Some(4), Some(5)],
954 None
955 );
956
957 generic_test_cast!(
959 Int32Array,
960 Int32,
961 vec![1, 2, 3, 4, 5],
962 Decimal128Array,
963 Decimal128(10, 0),
964 [Some(1), Some(2), Some(3), Some(4), Some(5)],
965 None
966 );
967
968 generic_test_cast!(
970 Int64Array,
971 Int64,
972 vec![1, 2, 3, 4, 5],
973 Decimal128Array,
974 Decimal128(20, 0),
975 [Some(1), Some(2), Some(3), Some(4), Some(5)],
976 None
977 );
978
979 generic_test_cast!(
981 Int64Array,
982 Int64,
983 vec![1, 2, 3, 4, 5],
984 Decimal128Array,
985 Decimal128(20, 2),
986 [Some(100), Some(200), Some(300), Some(400), Some(500)],
987 None
988 );
989
990 generic_test_cast!(
992 Float32Array,
993 Float32,
994 vec![1.5, 2.5, 3.0, 1.123_456_8, 5.50],
995 Decimal128Array,
996 Decimal128(10, 2),
997 [Some(150), Some(250), Some(300), Some(112), Some(550)],
998 None
999 );
1000
1001 generic_test_cast!(
1003 Float64Array,
1004 Float64,
1005 vec![1.5, 2.5, 3.0, 1.123_456_8, 5.50],
1006 Decimal128Array,
1007 Decimal128(20, 4),
1008 [
1009 Some(15000),
1010 Some(25000),
1011 Some(30000),
1012 Some(11235),
1013 Some(55000)
1014 ],
1015 None
1016 );
1017 Ok(())
1018 }
1019
1020 #[test]
1021 fn test_cast_i32_u32() -> Result<()> {
1022 generic_test_cast!(
1023 Int32Array,
1024 Int32,
1025 vec![1, 2, 3, 4, 5],
1026 UInt32Array,
1027 UInt32,
1028 [
1029 Some(1_u32),
1030 Some(2_u32),
1031 Some(3_u32),
1032 Some(4_u32),
1033 Some(5_u32)
1034 ],
1035 None
1036 );
1037 Ok(())
1038 }
1039
1040 #[test]
1041 fn test_cast_i32_utf8() -> Result<()> {
1042 generic_test_cast!(
1043 Int32Array,
1044 Int32,
1045 vec![1, 2, 3, 4, 5],
1046 StringArray,
1047 Utf8,
1048 [Some("1"), Some("2"), Some("3"), Some("4"), Some("5")],
1049 None
1050 );
1051 Ok(())
1052 }
1053
1054 #[test]
1055 fn test_cast_i64_t64() -> Result<()> {
1056 let original = vec![1, 2, 3, 4, 5];
1057 let expected: Vec<Option<i64>> = original
1058 .iter()
1059 .map(|i| Some(Time64NanosecondArray::from(vec![*i]).value(0)))
1060 .collect();
1061 generic_test_cast!(
1062 Int64Array,
1063 Int64,
1064 original,
1065 TimestampNanosecondArray,
1066 Timestamp(TimeUnit::Nanosecond, None),
1067 expected,
1068 None
1069 );
1070 Ok(())
1071 }
1072
1073 #[test]
1077 fn invalid_cast() {
1078 let schema = Schema::new(vec![Field::new("a", Int32, false)]);
1080
1081 let result = cast(
1082 col("a", &schema).unwrap(),
1083 &schema,
1084 Interval(IntervalUnit::MonthDayNano),
1085 );
1086 result.expect_err("expected Invalid CAST");
1087 }
1088
1089 #[test]
1090 fn invalid_cast_with_options_error() -> Result<()> {
1091 let schema = Schema::new(vec![Field::new("a", Utf8, false)]);
1093 let a = StringArray::from(vec!["9.1"]);
1094 let batch = RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(a)])?;
1095 let expression = cast_with_options(col("a", &schema)?, &schema, Int32, None)?;
1096 let result = expression.evaluate(&batch);
1097
1098 match result {
1099 Ok(_) => panic!("expected error"),
1100 Err(e) => {
1101 assert!(
1102 e.to_string()
1103 .contains("Cannot cast string '9.1' to value of Int32 type")
1104 )
1105 }
1106 }
1107 Ok(())
1108 }
1109
1110 #[test]
1111 fn field_aware_cast_preserves_target_field_semantics() -> Result<()> {
1112 let metadata = HashMap::from([("target_meta".to_string(), "1".to_string())]);
1114
1115 for (child_nullable, target_nullable) in [(true, false), (false, true)] {
1116 let schema = Schema::new(vec![Field::new("a", Int32, child_nullable)]);
1117 let target_field = Arc::new(
1118 Field::new("cast_target", Int64, target_nullable)
1119 .with_metadata(metadata.clone()),
1120 );
1121 let expr = CastExpr::new_with_target_field(
1122 col("a", &schema)?,
1123 Arc::clone(&target_field),
1124 None,
1125 );
1126
1127 let field = expr.return_field(&schema)?;
1128 assert_eq!(field.name(), "a");
1130 assert_eq!(field.data_type(), &Int64);
1131 assert_eq!(field.is_nullable(), target_nullable);
1133 assert_eq!(
1135 field.metadata().get("target_meta"),
1136 Some(&"1".to_string()),
1137 "Target metadata should be preserved exactly"
1138 );
1139 assert_eq!(expr.nullable(&schema)?, child_nullable || target_nullable);
1140 }
1141
1142 Ok(())
1143 }
1144
1145 #[test]
1146 fn target_field_accessor_returns_the_constructed_field() -> Result<()> {
1147 let schema = Schema::new(vec![Field::new("a", Int32, true)]);
1148 let metadata = HashMap::from([("target_meta".to_string(), "1".to_string())]);
1149 let target_field =
1150 Arc::new(Field::new("cast_target", Int64, false).with_metadata(metadata));
1151
1152 let expr = CastExpr::new_with_target_field(
1153 col("a", &schema)?,
1154 Arc::clone(&target_field),
1155 None,
1156 );
1157
1158 assert_eq!(expr.target_field(), &target_field);
1160 assert_eq!(expr.cast_type(), &Int64);
1161 assert_eq!(expr.target_metadata(), Some(target_field.metadata()));
1162 assert_eq!(expr.target_nullable(), Some(false));
1163 assert!(expr.has_explicit_metadata());
1164 assert!(expr.has_explicit_nullability());
1165
1166 let type_only = CastExpr::new(col("a", &schema)?, Int64, None);
1168 assert_eq!(type_only.cast_type(), &Int64);
1169 assert_eq!(type_only.target_metadata(), None);
1170 assert_eq!(type_only.target_nullable(), None);
1171 assert!(!type_only.has_explicit_metadata());
1172 assert!(!type_only.has_explicit_nullability());
1173
1174 Ok(())
1175 }
1176
1177 #[test]
1178 fn type_only_cast_preserves_legacy_field_name_and_nullability() -> Result<()> {
1179 let schema = Schema::new(vec![Field::new("a", Int32, false)]);
1180 let expr = CastExpr::new(col("a", &schema)?, Int64, None);
1181
1182 let field = expr.return_field(&schema)?;
1183
1184 assert_eq!(field.name(), "a");
1185 assert_eq!(field.data_type(), &Int64);
1186 assert!(!field.is_nullable());
1187 assert!(!expr.nullable(&schema)?);
1188
1189 Ok(())
1190 }
1191
1192 #[test]
1193 fn struct_cast_validation_uses_nested_target_fields() -> Result<()> {
1194 let source_type = Struct(Fields::from(vec![
1195 Arc::new(Field::new("x", Int32, true)),
1196 Arc::new(Field::new("y", Utf8, true)),
1197 ]));
1198 let schema = Schema::new(vec![Field::new("a", source_type.clone(), true)]);
1199
1200 let valid_target = Struct(Fields::from(vec![
1201 Arc::new(Field::new("y", Utf8, true)),
1202 Arc::new(Field::new("x", Int64, true)),
1203 ]));
1204 cast_with_options(col("a", &schema)?, &schema, valid_target, None)?;
1205
1206 let invalid_target = Struct(Fields::from(vec![
1207 Arc::new(Field::new("y", Utf8, true)),
1208 Arc::new(Field::new("missing", Int64, false)),
1209 ]));
1210 let err = cast_with_options(col("a", &schema)?, &schema, invalid_target, None)
1211 .expect_err("missing required struct field should fail");
1212
1213 assert!(err.to_string().contains("Unsupported CAST"));
1214
1215 Ok(())
1216 }
1217
1218 #[test]
1219 fn field_aware_cast_struct_array_missing_child() -> Result<()> {
1220 let source_a = Field::new("a", Int32, true);
1221 let source_b = Field::new("b", Utf8, true);
1222 let target_field = Field::new(
1223 "s",
1224 Struct(
1225 vec![
1226 Arc::new(Field::new("a", Int64, true)),
1227 Arc::new(Field::new("c", Utf8, true)),
1228 ]
1229 .into(),
1230 ),
1231 true,
1232 );
1233
1234 let struct_array = cast_struct_array(
1235 "s",
1236 Field::new(
1237 "s",
1238 Struct(
1239 vec![Arc::new(source_a.clone()), Arc::new(source_b.clone())].into(),
1240 ),
1241 true,
1242 ),
1243 target_field,
1244 make_struct_array(
1245 vec![Arc::new(source_a), Arc::new(source_b)].into(),
1246 vec![
1247 Arc::new(Int32Array::from(vec![Some(1), None])) as ArrayRef,
1248 Arc::new(StringArray::from(vec![Some("alpha"), Some("beta")]))
1249 as ArrayRef,
1250 ],
1251 ),
1252 )?;
1253 let cast_a = as_int64_array(struct_array.column_by_name("a").unwrap().as_ref())?;
1254 assert_eq!(cast_a.value(0), 1);
1255 assert!(cast_a.is_null(1));
1256
1257 let cast_c = as_string_array(struct_array.column_by_name("c").unwrap().as_ref())?;
1258 assert!(cast_c.is_null(0));
1259 assert!(cast_c.is_null(1));
1260 Ok(())
1261 }
1262
1263 #[test]
1264 fn field_aware_cast_nested_struct_array() -> Result<()> {
1265 let inner_source = Field::new(
1266 "inner",
1267 Struct(vec![Arc::new(Field::new("x", Int32, true))].into()),
1268 true,
1269 );
1270 let inner_target = Field::new(
1271 "inner",
1272 Struct(
1273 vec![
1274 Arc::new(Field::new("x", Int64, true)),
1275 Arc::new(Field::new("y", Boolean, true)),
1276 ]
1277 .into(),
1278 ),
1279 true,
1280 );
1281 let target_field =
1282 Field::new("root", Struct(vec![Arc::new(inner_target)].into()), true);
1283
1284 let inner_struct = make_struct_array(
1285 vec![Arc::new(Field::new("x", Int32, true))].into(),
1286 vec![Arc::new(Int32Array::from(vec![Some(7), None])) as ArrayRef],
1287 );
1288 let outer_struct = make_struct_array(
1289 vec![Arc::new(inner_source.clone())].into(),
1290 vec![Arc::new(inner_struct) as ArrayRef],
1291 );
1292 let struct_array = cast_struct_array(
1293 "root",
1294 Field::new("root", Struct(vec![Arc::new(inner_source)].into()), true),
1295 target_field,
1296 outer_struct,
1297 )?;
1298 let inner =
1299 as_struct_array(struct_array.column_by_name("inner").unwrap().as_ref())?;
1300 let x = as_int64_array(inner.column_by_name("x").unwrap().as_ref())?;
1301 assert_eq!(x.value(0), 7);
1302 assert!(x.is_null(1));
1303 let y = as_boolean_array(inner.column_by_name("y").unwrap().as_ref())?;
1304 assert!(y.is_null(0));
1305 assert!(y.is_null(1));
1306 Ok(())
1307 }
1308
1309 #[test]
1310 fn field_aware_cast_struct_scalar() -> Result<()> {
1311 let source_field = Field::new("a", Int32, true);
1312 let target_field = Field::new(
1313 "s",
1314 Struct(vec![Arc::new(Field::new("a", UInt8, true))].into()),
1315 true,
1316 );
1317
1318 let schema = Arc::new(Schema::new(vec![Field::new(
1319 "s",
1320 Struct(vec![Arc::new(source_field.clone())].into()),
1321 true,
1322 )]));
1323 let scalar_struct = make_struct_array(
1324 vec![Arc::new(source_field)].into(),
1325 vec![Arc::new(Int32Array::from(vec![Some(9)])) as ArrayRef],
1326 );
1327 let literal = Arc::new(crate::expressions::Literal::new(ScalarValue::Struct(
1328 Arc::new(scalar_struct),
1329 )));
1330 let target_field = Arc::new(target_field);
1331 let expr = CastExpr::new_with_target_field(literal, target_field, None);
1332
1333 let batch = RecordBatch::new_empty(schema);
1334 let result = expr.evaluate(&batch)?;
1335 let ColumnarValue::Scalar(ScalarValue::Struct(array)) = result else {
1336 panic!("expected struct scalar");
1337 };
1338 let casted = as_uint8_array(array.column_by_name("a").unwrap().as_ref())?;
1339 assert_eq!(casted.value(0), 9);
1340 Ok(())
1341 }
1342
1343 #[test]
1344 #[ignore] fn test_cast_decimal() -> Result<()> {
1346 let schema = Schema::new(vec![Field::new("a", Int64, false)]);
1347 let a = Int64Array::from(vec![100]);
1348 let batch = RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(a)])?;
1349 let expression =
1350 cast_with_options(col("a", &schema)?, &schema, Decimal128(38, 38), None)?;
1351 expression.evaluate(&batch)?;
1352 Ok(())
1353 }
1354
1355 #[test]
1356 fn test_fmt_sql() -> Result<()> {
1357 let schema = Schema::new(vec![Field::new("a", Int32, true)]);
1358
1359 let expr = cast(col("a", &schema)?, &schema, Int64)?;
1361 let display_string = expr.to_string();
1362 assert_eq!(display_string, "CAST(a@0 AS Int64)");
1363 let sql_string = fmt_sql(expr.as_ref()).to_string();
1364 assert_eq!(sql_string, "CAST(a AS Int64)");
1365
1366 let schema = Schema::new(vec![Field::new("b", Utf8, true)]);
1368 let expr = cast(col("b", &schema)?, &schema, Int32)?;
1369 let display_string = expr.to_string();
1370 assert_eq!(display_string, "CAST(b@0 AS Int32)");
1371 let sql_string = fmt_sql(expr.as_ref()).to_string();
1372 assert_eq!(sql_string, "CAST(b AS Int32)");
1373
1374 Ok(())
1375 }
1376
1377 #[test]
1378 fn type_only_cast_strips_extension_metadata() -> Result<()> {
1379 let source_meta = HashMap::from([
1381 (
1382 EXTENSION_TYPE_NAME_KEY.to_string(),
1383 "arrow.uuid".to_string(),
1384 ),
1385 ("custom_key".to_string(), "custom_value".to_string()),
1386 ]);
1387 let schema = Schema::new(vec![
1388 Field::new("a", FixedSizeBinary(16), false).with_metadata(source_meta),
1389 ]);
1390
1391 let expr = CastExpr::new(col("a", &schema)?, Utf8, None);
1392
1393 let field = expr.return_field(&schema)?;
1394 assert!(
1395 field.metadata().get(EXTENSION_TYPE_NAME_KEY).is_none(),
1396 "Type-only cast should strip extension type name from source"
1397 );
1398 assert_eq!(
1399 field.metadata().get("custom_key"),
1400 Some(&"custom_value".to_string()),
1401 "Type-only cast should preserve non-extension metadata"
1402 );
1403
1404 Ok(())
1405 }
1406
1407 #[test]
1408 fn field_aware_cast_uses_exact_target_metadata() -> Result<()> {
1409 let source_meta = HashMap::from([
1411 (
1412 EXTENSION_TYPE_NAME_KEY.to_string(),
1413 "source.type".to_string(),
1414 ),
1415 ("source_key".to_string(), "source_value".to_string()),
1416 ]);
1417 let target_meta = HashMap::from([
1418 (
1419 EXTENSION_TYPE_NAME_KEY.to_string(),
1420 "target.type".to_string(),
1421 ),
1422 (
1423 EXTENSION_TYPE_METADATA_KEY.to_string(),
1424 "target_ext_meta".to_string(),
1425 ),
1426 ("target_key".to_string(), "target_value".to_string()),
1427 ]);
1428 let schema = Schema::new(vec![
1429 Field::new("a", FixedSizeBinary(16), false).with_metadata(source_meta),
1430 ]);
1431
1432 let target_field =
1433 Arc::new(Field::new("b", Utf8, true).with_metadata(target_meta));
1434 let expr = CastExpr::new_with_target_field(
1435 col("a", &schema)?,
1436 Arc::clone(&target_field),
1437 None,
1438 );
1439
1440 let field = expr.return_field(&schema)?;
1441 assert_eq!(
1442 field.metadata().get(EXTENSION_TYPE_NAME_KEY),
1443 Some(&"target.type".to_string()),
1444 "Field-aware cast should use target's extension type name"
1445 );
1446 assert_eq!(
1447 field.metadata().get(EXTENSION_TYPE_METADATA_KEY),
1448 Some(&"target_ext_meta".to_string()),
1449 "Field-aware cast should use target's extension type metadata"
1450 );
1451 assert!(
1452 field.metadata().get("source_key").is_none(),
1453 "Field-aware cast should NOT preserve source metadata"
1454 );
1455 assert_eq!(
1456 field.metadata().get("target_key"),
1457 Some(&"target_value".to_string()),
1458 "Field-aware cast should preserve target's non-extension metadata"
1459 );
1460
1461 Ok(())
1462 }
1463
1464 #[test]
1465 fn test_check_bigger_cast_precision_loss() {
1466 use DataType::*;
1467
1468 assert!(CastExpr::check_bigger_cast(&Int16, &Int8));
1470 assert!(CastExpr::check_bigger_cast(&Int64, &Int32));
1471 assert!(CastExpr::check_bigger_cast(&Float32, &Int16));
1472 assert!(CastExpr::check_bigger_cast(&Float32, &UInt16));
1473 assert!(CastExpr::check_bigger_cast(&Float64, &Int32));
1474 assert!(CastExpr::check_bigger_cast(&Float64, &UInt32));
1475 assert!(CastExpr::check_bigger_cast(&LargeUtf8, &Utf8));
1476
1477 assert!(!CastExpr::check_bigger_cast(&Float32, &Int32));
1479 assert!(!CastExpr::check_bigger_cast(&Float32, &UInt32));
1480 assert!(!CastExpr::check_bigger_cast(&Float64, &Int64));
1481 assert!(!CastExpr::check_bigger_cast(&Float64, &UInt64));
1482
1483 assert!(!CastExpr::check_bigger_cast(&UInt16, &Int8));
1485 assert!(!CastExpr::check_bigger_cast(&UInt32, &Int16));
1486 assert!(!CastExpr::check_bigger_cast(&Int16, &UInt8));
1487 }
1488}
1489
1490#[cfg(all(test, feature = "proto"))]
1492mod proto_tests {
1493 use super::*;
1494 use crate::expressions::{Column, col};
1495 use crate::proto_test_util::{
1496 StubDecoder, StubEncoder, UnreachableDecoder, column_node,
1497 };
1498 use arrow::datatypes::Field;
1499 use datafusion_common::DataFusionError;
1500 use datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecodeCtx;
1501 use datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncodeCtx;
1502 use datafusion_proto_models::datafusion_common::ArrowType;
1503 use datafusion_proto_models::protobuf::{
1504 PhysicalCastNode, PhysicalExprNode, physical_expr_node,
1505 };
1506
1507 fn proto_cast_fixture() -> CastExpr {
1509 let schema = Schema::new(vec![Field::new("a", Int32, false)]);
1510 CastExpr::new(col("a", &schema).unwrap(), Int64, None)
1511 }
1512
1513 fn proto_int64_arrow_type() -> ArrowType {
1514 (&Int64).try_into().unwrap()
1515 }
1516
1517 fn proto_cast_node(
1519 expr: Option<Box<PhysicalExprNode>>,
1520 arrow_type: Option<ArrowType>,
1521 ) -> PhysicalExprNode {
1522 PhysicalExprNode {
1523 expr_id: None,
1524 expr_type: Some(physical_expr_node::ExprType::Cast(Box::new(
1525 PhysicalCastNode { expr, arrow_type },
1526 ))),
1527 }
1528 }
1529
1530 #[test]
1531 fn try_to_proto_encodes_cast_expr() {
1532 let cast = proto_cast_fixture();
1533 let encoder = StubEncoder::ok();
1534 let ctx = PhysicalExprEncodeCtx::new(&encoder);
1535
1536 let node = cast
1537 .try_to_proto(&ctx)
1538 .unwrap()
1539 .expect("CastExpr should encode to Some(node)");
1540
1541 assert!(node.expr_id.is_none());
1542 let cast_node = match node.expr_type {
1543 Some(physical_expr_node::ExprType::Cast(cast_node)) => *cast_node,
1544 other => panic!("expected a Cast node, got {other:?}"),
1545 };
1546 assert!(cast_node.expr.is_some());
1547
1548 let arrow_type = cast_node
1549 .arrow_type
1550 .as_ref()
1551 .expect("cast type should be encoded");
1552 let data_type: DataType = arrow_type.try_into().unwrap();
1553 assert_eq!(data_type, Int64);
1554 }
1555
1556 #[test]
1557 fn try_to_proto_propagates_child_encode_error() {
1558 let cast = proto_cast_fixture();
1559 let encoder = StubEncoder::failing_on(1);
1560 let ctx = PhysicalExprEncodeCtx::new(&encoder);
1561
1562 let err = cast.try_to_proto(&ctx).unwrap_err();
1563 assert!(matches!(
1564 err,
1565 DataFusionError::Internal(msg) if msg.contains("call 1")
1566 ));
1567 }
1568
1569 #[test]
1570 fn try_from_proto_decodes_cast_expr() {
1571 let node = proto_cast_node(
1572 Some(Box::new(column_node("a"))),
1573 Some(proto_int64_arrow_type()),
1574 );
1575 let schema = Schema::empty();
1576 let decoder = StubDecoder::ok();
1577 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
1578
1579 let decoded = CastExpr::try_from_proto(&node, &ctx).unwrap();
1580 let cast = decoded
1581 .downcast_ref::<CastExpr>()
1582 .expect("decoded expr should be a CastExpr");
1583
1584 assert_eq!(cast.cast_type(), &Int64);
1585 assert!(cast.expr().downcast_ref::<Column>().is_some());
1586 }
1587
1588 #[test]
1589 fn try_from_proto_rejects_non_cast_node() {
1590 let node = column_node("a");
1591 let schema = Schema::empty();
1592 let decoder = UnreachableDecoder;
1593 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
1594
1595 let err = CastExpr::try_from_proto(&node, &ctx).unwrap_err();
1596 assert!(matches!(
1597 err,
1598 DataFusionError::Internal(msg)
1599 if msg.contains("PhysicalExprNode is not a CastExpr")
1600 ));
1601 }
1602
1603 #[test]
1604 fn try_from_proto_rejects_missing_expr() {
1605 let node = proto_cast_node(None, Some(proto_int64_arrow_type()));
1606 let schema = Schema::empty();
1607 let decoder = UnreachableDecoder;
1608 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
1609
1610 let err = CastExpr::try_from_proto(&node, &ctx).unwrap_err();
1611 assert!(matches!(
1612 err,
1613 DataFusionError::Internal(msg)
1614 if msg.contains("CastExpr is missing required field 'expr'")
1615 ));
1616 }
1617
1618 #[test]
1619 fn try_from_proto_rejects_missing_arrow_type() {
1620 let node = proto_cast_node(Some(Box::new(column_node("a"))), None);
1621 let schema = Schema::empty();
1622 let decoder = StubDecoder::ok();
1623 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
1624
1625 let err = CastExpr::try_from_proto(&node, &ctx).unwrap_err();
1626 assert!(matches!(
1627 err,
1628 DataFusionError::Internal(msg)
1629 if msg.contains("CastExpr is missing required field 'arrow_type'")
1630 ));
1631 }
1632
1633 #[test]
1634 fn try_from_proto_propagates_child_decode_error() {
1635 let node = proto_cast_node(
1636 Some(Box::new(column_node("a"))),
1637 Some(proto_int64_arrow_type()),
1638 );
1639 let schema = Schema::empty();
1640 let decoder = StubDecoder::failing_on(1);
1641 let ctx = PhysicalExprDecodeCtx::new(&schema, &decoder);
1642
1643 let err = CastExpr::try_from_proto(&node, &ctx).unwrap_err();
1644 assert!(matches!(
1645 err,
1646 DataFusionError::Internal(msg) if msg.contains("call 1")
1647 ));
1648 }
1649}