jayzhan211 commented on code in PR #27347:
URL: https://github.com/apache/datafusion/pull/27347#discussion_r4551779479


##########
datafusion/physical-expr/src/equivalence/properties/union.rs:
##########
@@ -104,6 +102,22 @@ pub fn calculate_union(
         groupings.add_groupings(std::iter::once(grouping.iter().cloned()));
     }
 
+    // An equality holds across the union only if it holds in every input 
partition.

Review Comment:
   @galaxytemple thanks for picking this up! Because the equalities are added 
after the fold, each `calculate_union_binary` step still sees an accumulator 
without `x = y`. So with 3 arms where the arm lacking the equality comes last, 
the same SanityCheckPlan error remains:
   
   ```sql
   CREATE TABLE t(a INT, b INT) AS VALUES (2, 20), (1, 30);
   SELECT a AS x, a AS y FROM t
   UNION ALL SELECT a AS x, a AS y FROM t
   UNION ALL SELECT a AS x, b AS y FROM t
   ORDER BY x, y;
   -- SanityCheckPlan ... Child-0 order: [[x@0 ASC NULLS LAST]]
   ```
   
   Computing the meet inside `calculate_union_binary` fixes it and lets you 
drop the n-ary block, the emptiness guard and the `unwrap`. With this change 
the physical-expr tests and the full sqllogictest suite pass, and the new 
`union.slt` case below fails without it:
   
   <details>
   <summary>Diff for union.rs and union.slt</summary>
   
   ```diff
   @@ fn calculate_union_binary(
        let orderings = orderings.build();
    
   +    // An equality holds at the output only if it holds in both inputs. Keep
   +    // it in the result so the next fold step can still use it.
   +    let mut eq_group = EquivalenceGroup::default();
   +    for class in lhs.eq_group().iter() {
   +        for (i, left) in class.iter().enumerate() {
   +            for right in class.iter().skip(i + 1) {
   +                if rhs.eq_group().exprs_equal(left, right) {
   +                    eq_group.add_equal_conditions(Arc::clone(left), 
Arc::clone(right));
   +                }
   +            }
   +        }
   +    }
   +
        let mut eq_properties = EquivalenceProperties::new(lhs.schema);
   +    eq_properties.add_equivalence_group(eq_group)?;
        eq_properties.add_constants(constants)?;
   @@ pub fn calculate_union(
   -    // An equality holds across the union only if it holds in every input 
partition.
   -    let mut eq_group = EquivalenceGroup::default();
   -    if !eqps.is_empty() && eqps.iter().all(|p| !p.eq_group().is_empty()) {
   -        ...
   -    }
   -
   @@
   -    acc.add_equivalence_group(eq_group)?;
        acc.add_groupings(groupings);
   ```
   
   ```diff
   --- a/datafusion/sqllogictest/test_files/union.slt
   +++ b/datafusion/sqllogictest/test_files/union.slt
   @@ -1170,6 +1170,22 @@ ORDER BY x, y;
    2 2
    2 2
    
   +# The arm without x = y is folded last, after the arms that have it.
   +query II
   +SELECT a AS x, a AS y FROM t_26625
   +UNION ALL
   +SELECT a AS x, a AS y FROM t_26625
   +UNION ALL
   +SELECT a AS x, (a % 2)::INT AS y FROM t_26625
   +ORDER BY x, y;
   +----
   +1 1
   +1 1
   +1 1
   +2 0
   +2 2
   +2 2
   +
    statement ok
    DROP TABLE t_26625;
   ```
   
   The `::INT` matters: without it, `a % 2` is coerced to Int64 and the first 
two arms project `CAST(a AS Int64)`, so `x = y` disappears from them and the 
test passes without the fix.
   
   </details>
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to