@@ -159,12 +159,19 @@ fn evaluate_transform_expression(
159159 }
160160
161161 // Extract the input path, if any
162- let source_data = transform
162+ let source_array = transform
163163 . input_path ( )
164164 . map ( |path| extract_column ( batch, path) )
165165 . transpose ( ) ?;
166166
167- let source_data: & dyn ProvidesColumnByName = match source_data {
167+ // For nested transforms, get the source struct's null bitmap to preserve null rows
168+ let source_null_buffer = source_array. as_ref ( ) . and_then ( |arr| {
169+ arr. as_any ( )
170+ . downcast_ref :: < StructArray > ( )
171+ . and_then ( |s| s. nulls ( ) . cloned ( ) )
172+ } ) ;
173+
174+ let source_data: & dyn ProvidesColumnByName = match source_array {
168175 Some ( ref array) => array
169176 . as_any ( )
170177 . downcast_ref :: < StructArray > ( )
@@ -204,7 +211,7 @@ fn evaluate_transform_expression(
204211 return Err ( Error :: generic ( "Too many fields in output schema" ) ) ;
205212 }
206213
207- // Build the final struct
214+ // Build the final struct, preserving null bitmap for nested transforms
208215 let output_fields: Vec < ArrowField > = output_cols
209216 . iter ( )
210217 . zip ( output_schema. fields ( ) )
@@ -216,7 +223,7 @@ fn evaluate_transform_expression(
216223 )
217224 } )
218225 . collect ( ) ;
219- let data = StructArray :: try_new ( output_fields. into ( ) , output_cols, None ) ?;
226+ let data = StructArray :: try_new ( output_fields. into ( ) , output_cols, source_null_buffer ) ?;
220227 Ok ( Arc :: new ( data) )
221228}
222229
0 commit comments