@@ -186,8 +186,32 @@ impl CrossJoinExec {
186186 /// operators on the join's children. Check [`super::HashJoinExec::swap_inputs`]
187187 /// for more details.
188188 pub fn swap_inputs ( & self ) -> Result < Arc < dyn ExecutionPlan > > {
189- let new_join =
190- CrossJoinExec :: new ( Arc :: clone ( & self . right ) , Arc :: clone ( & self . left ) ) ;
189+ // Rebuild schema with columns from right to left, preserve existing metadata
190+ let new_columns = self
191+ . right
192+ . schema ( )
193+ . fields
194+ . iter ( )
195+ . chain ( self . left . schema ( ) . fields . iter ( ) )
196+ . cloned ( )
197+ . collect :: < Fields > ( ) ;
198+
199+ let new_schema = Arc :: new (
200+ Schema :: new ( new_columns) . with_metadata ( self . schema . metadata . clone ( ) ) ,
201+ ) ;
202+
203+ let new_cache =
204+ Self :: compute_properties ( & self . right , & self . left , Arc :: clone ( & new_schema) ) ?;
205+
206+ let new_join = CrossJoinExec {
207+ left : Arc :: clone ( & self . right ) ,
208+ right : Arc :: clone ( & self . left ) ,
209+ schema : new_schema,
210+ left_fut : Default :: default ( ) ,
211+ metrics : ExecutionPlanMetricsSet :: default ( ) ,
212+ cache : Arc :: new ( new_cache) ,
213+ } ;
214+
191215 reorder_output_after_swap (
192216 Arc :: new ( new_join) ,
193217 & self . left . schema ( ) ,
@@ -701,7 +725,9 @@ impl<T: BatchTransformer> CrossJoinStream<T> {
701725mod tests {
702726 use super :: * ;
703727 use crate :: common;
704- use crate :: test:: { assert_join_metrics, build_table_scan_i32} ;
728+ use crate :: test:: { TestMemoryExec , assert_join_metrics, build_table_scan_i32} ;
729+ use arrow_schema:: { DataType , Field } ;
730+ use std:: collections:: HashMap ;
705731
706732 use datafusion_common:: { assert_contains, test_util:: batches_to_sort_string} ;
707733 use datafusion_execution:: runtime_env:: RuntimeEnvBuilder ;
@@ -994,6 +1020,38 @@ mod tests {
9941020 Ok ( ( ) )
9951021 }
9961022
1023+ #[ test]
1024+ fn test_swapped_cross_join_schema_on_conflicting_metadata ( ) {
1025+ let input = |field : & str , meta_value : & str | {
1026+ let schema = Arc :: new (
1027+ Schema :: new ( vec ! [ Field :: new( field, DataType :: Int32 , false ) ] )
1028+ . with_metadata ( HashMap :: from ( [ (
1029+ String :: from ( "metadata_key" ) ,
1030+ String :: from ( meta_value) ,
1031+ ) ] ) ) ,
1032+ ) ;
1033+ TestMemoryExec :: try_new_exec ( & [ vec ! [ ] ] , schema, None ) . unwrap ( )
1034+ } ;
1035+ // Conflicting metadata on left and right input, right side wins "metadata_key" -> "right value"
1036+ let join =
1037+ CrossJoinExec :: new ( input ( "a" , "left value" ) , input ( "b" , "right value" ) ) ;
1038+
1039+ let swapped_join = join. swap_inputs ( ) . unwrap ( ) ;
1040+
1041+ // The metadata of the cross-join and the swapped cross-join must be preserved
1042+ assert_eq ! (
1043+ join. schema( )
1044+ . metadata( )
1045+ . get( "metadata_key" )
1046+ . map( String :: as_str) ,
1047+ swapped_join
1048+ . schema( )
1049+ . metadata( )
1050+ . get( "metadata_key" )
1051+ . map( String :: as_str) ,
1052+ ) ;
1053+ }
1054+
9971055 /// Returns the column names on the schema
9981056 fn columns ( schema : & Schema ) -> Vec < String > {
9991057 schema. fields ( ) . iter ( ) . map ( |f| f. name ( ) . clone ( ) ) . collect ( )
0 commit comments