fornwall opened a new pull request, #25284:
URL: https://github.com/apache/datafusion/pull/25284

   ## Which issue does this PR close?
   
   Closes #25283.
   
   ## Rationale for this change
   
   A correlated `EXISTS` subquery with an `OFFSET` returns wrong results: the 
`OFFSET` is silently dropped when the subquery is rewritten into a semi join.
   
   ```sql
   CREATE TABLE t1(k INT) AS VALUES (1), (2), (3);
   CREATE TABLE t2(v INT) AS VALUES (1), (1), (3);
   
   SELECT k FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t2.v = t1.k OFFSET 1);
   ```
   
   Only `k = 1` has two matching rows in `t2`, so only for `k = 1` is a row 
left after skipping one. The expected result is a single row with `1`, which is 
also what SQLite returns. DataFusion returns `1` and `3`. The `NOT EXISTS` form 
is wrong in the same way: it returns only `2`, but `2` and `3` are expected.
   
   `EXPLAIN` shows why. The subquery becomes a plain `LeftSemi` join with no 
`Limit` node at all:
   
   ```
   LeftSemi Join: t1.k = __correlated_sq_1.v
     TableScan: t1 projection=[k]
     SubqueryAlias: __correlated_sq_1
       TableScan: t2 projection=[v]
   ```
   
   To rewrite the subquery into a semi join, `PullUpCorrelatedExpr` removes the 
`LIMIT` that sits above the correlated filter, on the grounds that `EXISTS` 
only asks whether any row exists and a `LIMIT n` with `n > 0` cannot change 
that. The same plan node carries the `OFFSET`, which was dropped along with it, 
although an offset can turn a non-empty subquery into an empty one.
   
   ## What changes are included in this PR?
   
   `PullUpCorrelatedExpr` now only removes the limit of a correlated `EXISTS` 
subquery when it cannot change whether the subquery is empty: a zero offset 
with a literal fetch. A zero fetch still becomes an empty relation. A positive 
offset, or an offset or fetch that is not a literal, marks the subquery as one 
that cannot be pulled up, so it stays correlated in the plan and is reported as 
unsupported rather than answered wrongly.
   
   ## What is the testing strategy for this PR?
   
   New sqllogictest cases in `subquery.slt`:
   
   - `exists_subquery_with_offset0` checks that `LIMIT 1 OFFSET 0` is still 
decorrelated.
   - `exists_subquery_with_offset` and `not_exists_subquery_with_offset` check 
that `OFFSET 1` keeps the subquery correlated, and that running the query fails 
with a not-implemented error. The explain cases fail without the fix.
   
   ## Are there any user-facing changes?
   
   A correlated `EXISTS` or `NOT EXISTS` subquery with a positive `OFFSET` now 
fails with a not-implemented error instead of returning wrong results. Queries 
with a zero offset are unaffected.
   
   A correlated `EXISTS` subquery whose `LIMIT` is not a literal (which could 
evaluate to zero) previously had that limit dropped as well; it now fails with 
the same not-implemented error.
   
   Disclaimer: Created with fable 5.1 in claude code. I have reviewed the code.


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