22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
44use vortex_error:: VortexResult ;
5+ use vortex_error:: vortex_ensure;
56use vortex_error:: vortex_err;
67
78use crate :: ArrayRef ;
@@ -27,7 +28,12 @@ pub(super) const PARENT_RULES: ParentRuleSet<StructVTable> = ParentRuleSet::new(
2728 ParentRuleSet :: lift ( & StructGetItemRule ) ,
2829] ) ;
2930
30- /// Rule to push down cast into struct fields
31+ /// Rule to push down cast into struct fields.
32+ ///
33+ /// TODO(joe/rob): should be have this in casts.
34+ ///
35+ /// This rule supports schema evolution by allowing new nullable fields to be added
36+ /// at the end of the struct, filled with null values.
3137#[ derive( Debug ) ]
3238struct StructCastPushDownRule ;
3339impl ArrayParentReduceRule < StructVTable > for StructCastPushDownRule {
@@ -44,10 +50,38 @@ impl ArrayParentReduceRule<StructVTable> for StructCastPushDownRule {
4450 _child_idx : usize ,
4551 ) -> VortexResult < Option < ArrayRef > > {
4652 let target_fields = parent. options . as_struct_fields ( ) ;
53+ let source_field_count = array. fields . len ( ) ;
54+ let target_field_count = target_fields. nfields ( ) ;
4755
48- let mut new_fields = Vec :: with_capacity ( target_fields. nfields ( ) ) ;
49- for ( field_array, field_dtype) in array. fields . iter ( ) . zip ( target_fields. fields ( ) ) {
50- new_fields. push ( field_array. cast ( field_dtype) ?)
56+ // Target must have at least as many fields as source
57+ vortex_ensure ! (
58+ target_field_count >= source_field_count,
59+ "Cannot cast struct: target has fewer fields ({}) than source ({})" ,
60+ target_field_count,
61+ source_field_count
62+ ) ;
63+
64+ let mut new_fields = Vec :: with_capacity ( target_field_count) ;
65+
66+ // Cast existing source fields to target types
67+ for ( field_array, field_dtype) in array
68+ . fields
69+ . iter ( )
70+ . zip ( target_fields. fields ( ) . take ( source_field_count) )
71+ {
72+ new_fields. push ( field_array. cast ( field_dtype) ?) ;
73+ }
74+
75+ // Add null arrays for any extra target fields (schema evolution)
76+ for field_dtype in target_fields. fields ( ) . skip ( source_field_count) {
77+ vortex_ensure ! (
78+ field_dtype. is_nullable( ) ,
79+ "Cannot add non-nullable field during struct cast (schema evolution only supports nullable fields)"
80+ ) ;
81+ new_fields. push (
82+ ConstantArray :: new ( vortex_scalar:: Scalar :: null ( field_dtype) , array. len ( ) )
83+ . into_array ( ) ,
84+ ) ;
5185 }
5286
5387 let validity = if parent. options . is_nullable ( ) {
0 commit comments