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


##########
datafusion/optimizer/src/decorrelate.rs:
##########
@@ -182,6 +195,11 @@ impl TreeNodeRewriter for PullUpCorrelatedExpr {
                         .iter()
                         .filter(|e| e.contains_outer())
                         .all(|&e| can_pullup_over_aggregation(e));
+                for expr in &subquery_filter_exprs {
+                    if expr.contains_outer() && 
!self.correlated_filters.contains(expr) {
+                        self.correlated_filters.push((*expr).clone());

Review Comment:
   `correlated_filters` records a conjunct regardless of what sits above the 
`Filter`. A LEFT JOIN (filter on the nullable side) or a `ROLLUP` above it puts 
NULLs back into the key, but `filter_rejects_null` still reads `u.y = o.x` as 
"never NULL", so the `NOT IN` anti join loses `null_aware`. `main` returns the 
correct result for both queries below:
   
   ```sql
   CREATE TABLE o(x INT) AS VALUES (1),(3),(NULL);
   CREATE TABLE t(k INT) AS VALUES (1),(2);
   CREATE TABLE u(y INT, k INT) AS VALUES (1,1);
   SELECT x FROM o WHERE x NOT IN (
     SELECT u.y FROM t LEFT JOIN (SELECT * FROM u WHERE u.y = o.x) AS u ON t.k 
= u.k);
   -- expected (and main): no rows; this PR: 3, NULL
   
   -- co/ci from subquery_projection.slt
   SELECT co.id, co.k FROM co WHERE co.k NOT IN (
     SELECT ci.k FROM ci WHERE ci.k = co.k GROUP BY ROLLUP(ci.k));
   -- expected (and main): no rows; this PR: (NULL,3), (9,9), (5,NULL)
   ```
   
   Fix: drop the recorded filters when the pull up passes such a node, and add 
both queries to the slt:
   
   ```diff
        fn f_up(&mut self, plan: LogicalPlan) -> 
Result<Transformed<LogicalPlan>> {
   +        // A node that null-extends or regroups rows can put a NULL back 
into
   +        // a column that a correlated filter below it rejected.
   +        if may_reintroduce_nulls(&plan) {
   +            self.correlated_filters.clear();
   +        }
            let subquery_schema = plan.schema();
   ```
   
   ```rs
   fn may_reintroduce_nulls(plan: &LogicalPlan) -> bool {
       match plan {
           LogicalPlan::Join(join) => matches!(
               join.join_type,
               JoinType::Left | JoinType::Right | JoinType::Full
           ),
           LogicalPlan::Union(_) => true,
           LogicalPlan::Aggregate(aggregate) => aggregate
               .group_expr
               .iter()
               .any(|e| matches!(e, Expr::GroupingSet(_))),
           _ => false,
       }
   }
   ```



-- 
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