adriangb opened a new issue, #25834:
URL: https://github.com/apache/datafusion/issues/25834

   ### Describe the bug
   
   A correlated filter that sits below the **right side of an `ASOF JOIN`** 
inside a subquery is pulled out of the subquery and attached to the 
decorrelated join. The `ASOF JOIN` then picks the nearest row from all rows of 
the right input, instead of from the rows that match the outer row, so the 
query gives wrong results.
   
   This affects `LATERAL` and `EXISTS` subqueries.
   
   ### To Reproduce
   
   ```sql
   CREATE TABLE o(k INT) AS VALUES (1), (2);
   CREATE TABLE l(id INT, ts INT) AS VALUES (1, 10);
   CREATE TABLE r(ts INT, val INT) AS VALUES (5, 1), (8, 2);
   ```
   
   For each row of `o`, the subquery keeps only the rows of `r` where `r.val = 
o.k`, then finds the latest of those rows with `r.ts <= l.ts`. For `o.k = 1` 
that is `ts = 5`, and for `o.k = 2` it is `ts = 8`.
   
   **`LATERAL` form**
   
   ```sql
   SELECT o.k, s.rts
   FROM o, LATERAL (
     SELECT r.ts AS rts
     FROM l ASOF JOIN (SELECT * FROM r WHERE r.val = o.k) AS r
       MATCH_CONDITION (l.ts >= r.ts)) AS s
   ORDER BY o.k;
   ```
   
   | DataFusion | Correct |
   | --- | --- |
   | `(2, 8)` | `(1, 5)`, `(2, 8)` |
   
   **`EXISTS` form**
   
   ```sql
   SELECT o.k FROM o
   WHERE EXISTS (
     SELECT 1
     FROM l ASOF JOIN (SELECT * FROM r WHERE r.val = o.k) AS r
       MATCH_CONDITION (l.ts >= r.ts)
     WHERE r.ts IS NOT NULL)
   ORDER BY o.k;
   ```
   
   | DataFusion | Correct |
   | --- | --- |
   | `2` | `1`, `2` |
   
   The same query without the correlation gives the correct row. With `r.val = 
1` instead of `r.val = o.k`, DataFusion returns `ts = 5`.
   
   **Reference results.** PostgreSQL has no `ASOF JOIN`. This equivalent query, 
with `ORDER BY ... LIMIT 1` in place of the `ASOF JOIN`, returns `(1, 5)`, `(2, 
8)` on PostgreSQL 17.11 and on DuckDB 1.5.2:
   
   ```sql
   SELECT o.k, s.rts FROM o, LATERAL (
     SELECT m.ts AS rts FROM l LEFT JOIN LATERAL (
       SELECT r2.ts FROM (SELECT * FROM r WHERE r.val = o.k) r2
       WHERE l.ts >= r2.ts ORDER BY r2.ts DESC LIMIT 1) m ON true) s
   ORDER BY o.k;
   ```
   
   Note: DuckDB 1.5.2 gives the same wrong result as DataFusion for the `ASOF 
JOIN` form (`l ASOF JOIN ... ON l.ts >= r.ts`). It gives the correct result for 
the uncorrelated form.
   
   ### Expected behavior
   
   The correct results above: `(1, 5)`, `(2, 8)` for the `LATERAL` form and 
`1`, `2` for the `EXISTS` form. If DataFusion cannot decorrelate the subquery, 
it should fail with a not-implemented error. It must not return wrong rows.
   
   ### Additional context
   
   The plan shows the cause. `r.val = o.k` is gone from below the `AsOf Join` 
and is now the condition of the join that replaces the `LATERAL` subquery:
   
   ```
   Projection: o.k, s.rts
     Inner Join: o.k = s.val
       TableScan: o projection=[k]
       SubqueryAlias: s
         Projection: r.ts AS rts, r.val
           AsOf Join: match=[l.ts >= r.ts], constraint=On
             TableScan: l projection=[ts]
             SubqueryAlias: r
               TableScan: r projection=[ts, val]
   ```
   
   `PullUpCorrelatedExpr::f_down` in `datafusion/optimizer/src/decorrelate.rs` 
has no arm for `LogicalPlan::AsOfJoin`, so the default arm pulls the filter 
through it. `push_down_filter` handles `AsOfJoin` correctly in the other 
direction: it moves only left-only predicates below the join.
   
   Found on `main` at 871058cd73.
   
   This is the same class of bug as these, each for a different plan node:
   
   - https://github.com/apache/datafusion/issues/25507 (`Join`, fixed by 
https://github.com/apache/datafusion/pull/25764)
   - https://github.com/apache/datafusion/issues/25792 (`Window`, 
https://github.com/apache/datafusion/pull/25810)
   - https://github.com/apache/datafusion/issues/25808 (`Aggregate` used as 
`DISTINCT`)
   - https://github.com/apache/datafusion/issues/25283 (`Limit` with an 
`OFFSET`)
   
   The default arm of `PullUpCorrelatedExpr::f_down` pulls a correlated filter 
through every plan node that the rule does not know. The default in 
`push_down_filter` is the opposite: the filter stays where it is. A change that 
makes `PullUpCorrelatedExpr` allow only the nodes it knows are safe would fix 
this bug and prevent the next one of this class.
   


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