Skip to content

Commit b20d11b

Browse files
committed
fix: Preserve output schema and schema-level metadata when swapping cross-join
1 parent bc6e058 commit b20d11b

1 file changed

Lines changed: 61 additions & 3 deletions

File tree

datafusion/physical-plan/src/joins/cross_join.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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> {
701725
mod 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

Comments
 (0)