This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 3e940a9128 Fix logical vs physical schema mismatch for UNION where 
some inputs are constants (#12954)
3e940a9128 is described below

commit 3e940a91285562a4351074dd63c9b8706ff8c397
Author: wiedld <[email protected]>
AuthorDate: Wed Oct 23 16:08:07 2024 -0700

    Fix logical vs physical schema mismatch for UNION where some inputs are 
constants (#12954)
    
    * test(12733): reproducer of when metadata from the left side is not 
transferred to the right side
    
    * fix(12733): because either the left or right fields may be chosen, add 
metadata from both to each other
    
    * test(12733): update regression test to show that fix works
    
    * Add extra test to fix other issue with schema metadata
    
    * Fix union_schema to merge metadatas for both fields and schema
    
    * fmt
    
    ---------
    
    Co-authored-by: itsjunetime <[email protected]>
---
 datafusion/physical-plan/src/union.rs           | 48 ++++++++++++++-----------
 datafusion/sqllogictest/src/test_context.rs     | 21 +++++++++--
 datafusion/sqllogictest/test_files/metadata.slt | 27 ++++++++++++++
 3 files changed, 72 insertions(+), 24 deletions(-)

diff --git a/datafusion/physical-plan/src/union.rs 
b/datafusion/physical-plan/src/union.rs
index 108e42e7be..433dda870d 100644
--- a/datafusion/physical-plan/src/union.rs
+++ b/datafusion/physical-plan/src/union.rs
@@ -468,35 +468,41 @@ pub fn can_interleave<T: Borrow<Arc<dyn ExecutionPlan>>>(
 }
 
 fn union_schema(inputs: &[Arc<dyn ExecutionPlan>]) -> SchemaRef {
-    let fields: Vec<Field> = (0..inputs[0].schema().fields().len())
+    let first_schema = inputs[0].schema();
+
+    let fields = (0..first_schema.fields().len())
         .map(|i| {
             inputs
                 .iter()
-                .filter_map(|input| {
-                    if input.schema().fields().len() > i {
-                        let field = input.schema().field(i).clone();
-                        let right_hand_metdata = inputs
-                            .get(1)
-                            .map(|right_input| {
-                                
right_input.schema().field(i).metadata().clone()
-                            })
-                            .unwrap_or_default();
-                        let mut metadata = field.metadata().clone();
-                        metadata.extend(right_hand_metdata);
-                        Some(field.with_metadata(metadata))
-                    } else {
-                        None
-                    }
+                .enumerate()
+                .map(|(input_idx, input)| {
+                    let field = input.schema().field(i).clone();
+                    let mut metadata = field.metadata().clone();
+
+                    let other_metadatas = inputs
+                        .iter()
+                        .enumerate()
+                        .filter(|(other_idx, _)| *other_idx != input_idx)
+                        .flat_map(|(_, other_input)| {
+                            
other_input.schema().field(i).metadata().clone().into_iter()
+                        });
+
+                    metadata.extend(other_metadatas);
+                    field.with_metadata(metadata)
                 })
-                .find_or_first(|f| f.is_nullable())
+                .find_or_first(Field::is_nullable)
+                // We can unwrap this because if inputs was empty, this 
would've already panic'ed when we
+                // indexed into inputs[0].
                 .unwrap()
         })
+        .collect::<Vec<_>>();
+
+    let all_metadata_merged = inputs
+        .iter()
+        .flat_map(|i| i.schema().metadata().clone().into_iter())
         .collect();
 
-    Arc::new(Schema::new_with_metadata(
-        fields,
-        inputs[0].schema().metadata().clone(),
-    ))
+    Arc::new(Schema::new_with_metadata(fields, all_metadata_merged))
 }
 
 /// CombinedRecordBatchStream can be used to combine a Vec of 
SendableRecordBatchStreams into one
diff --git a/datafusion/sqllogictest/src/test_context.rs 
b/datafusion/sqllogictest/src/test_context.rs
index 2143b3089e..deeacb1b88 100644
--- a/datafusion/sqllogictest/src/test_context.rs
+++ b/datafusion/sqllogictest/src/test_context.rs
@@ -319,17 +319,27 @@ pub async fn register_metadata_tables(ctx: 
&SessionContext) {
             String::from("metadata_key"),
             String::from("the l_name field"),
         )]));
+
     let ts = Field::new("ts", DataType::Timestamp(TimeUnit::Nanosecond, None), 
false)
         .with_metadata(HashMap::from([(
             String::from("metadata_key"),
             String::from("ts non-nullable field"),
         )]));
 
-    let schema =
-        Schema::new(vec![id, name, l_name, ts]).with_metadata(HashMap::from([(
+    let nonnull_name =
+        Field::new("nonnull_name", DataType::Utf8, 
false).with_metadata(HashMap::from([
+            (
+                String::from("metadata_key"),
+                String::from("the nonnull_name field"),
+            ),
+        ]));
+
+    let schema = Schema::new(vec![id, name, l_name, ts, 
nonnull_name]).with_metadata(
+        HashMap::from([(
             String::from("metadata_key"),
             String::from("the entire schema"),
-        )]));
+        )]),
+    );
 
     let batch = RecordBatch::try_new(
         Arc::new(schema),
@@ -342,6 +352,11 @@ pub async fn register_metadata_tables(ctx: 
&SessionContext) {
                 1599572549190855123,
                 1599572549190855123,
             ])) as _,
+            Arc::new(StringArray::from(vec![
+                Some("no_foo"),
+                Some("no_bar"),
+                Some("no_baz"),
+            ])) as _,
         ],
     )
     .unwrap();
diff --git a/datafusion/sqllogictest/test_files/metadata.slt 
b/datafusion/sqllogictest/test_files/metadata.slt
index 588a36e3d5..8f787254c0 100644
--- a/datafusion/sqllogictest/test_files/metadata.slt
+++ b/datafusion/sqllogictest/test_files/metadata.slt
@@ -123,7 +123,34 @@ ORDER BY id, name, l_name;
 NULL bar NULL
 NULL NULL l_bar
 
+# Regression test: missing field metadata from left side of the union when 
right side is chosen
+query T
+select name from (
+  SELECT nonnull_name as name FROM "table_with_metadata"
+  UNION ALL
+  SELECT NULL::string as name
+) group by name order by name;
+----
+no_bar
+no_baz
+no_foo
+NULL
 
+# Regression test: missing schema metadata from union when schema with 
metadata isn't the first one
+# and also ensure it works fine with multiple unions
+query T
+select name from (
+  SELECT NULL::string as name
+  UNION ALL
+  SELECT nonnull_name as name FROM "table_with_metadata"
+  UNION ALL
+  SELECT NULL::string as name
+) group by name order by name;
+----
+no_bar
+no_baz
+no_foo
+NULL
 
 query P rowsort
 SELECT ts


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to