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

   ### Describe the bug
   
   ### Describe the bug
   
   In `datafusion/optimizer/src/decorrelate.rs:276-309`, 
`PullUpCorrelatedExpr::f_down` reasons that "for `EXISTS`
   only emptiness matters" and deletes any `Limit` with `SkipType::Literal(0)` 
and non-zero fetch:
   
   ```rust
   match (limit.get_skip_type()?, fetch) {
       (SkipType::Literal(0), FetchType::Literal(_)) => {
           self.column_refs_above.pop();
           let mut t = self.f_down((*limit.input).clone())?;
           t.transformed = true;
           Ok(t)
       }
       _ => self.stop_pull_up(plan),
   }
   ```
   
   Removing `LIMIT k` preserves emptiness only when the `Limit` is the 
**outermost** operator of the `EXISTS`
   subquery. When the `LIMIT` sits *below* an `Aggregate` (`HAVING COUNT(*) > 
1`) or a `Filter`
   (`WHERE sub.v = 200`), deleting it changes the rows seen by the enclosing 
operator and the result is no longer
   equivalent.
   
   
   ### To Reproduce
   
   
   ```sql
   CREATE TABLE a(k INT, x INT) AS VALUES (1, 10), (2, 20);
   CREATE TABLE b(k INT, v INT) AS VALUES (1, 100), (1, 200), (2, 300);
   
   -- Case 1: LIMIT 1 below HAVING COUNT(*) > 1 (can never be > 1 -> must 
return 0 rows)
   SELECT a.k FROM a
   WHERE EXISTS (
       SELECT 1 FROM (SELECT b.k, b.v FROM b WHERE b.k = a.k LIMIT 1) sub
       HAVING COUNT(*) > 1
   );
   
   -- Case 2: LIMIT 1 below WHERE sub.v = 200 (first row for k=1 has v=100 -> 
must return 0 rows)
   SELECT a.k FROM a
   WHERE EXISTS (
       SELECT 1 FROM (SELECT b.k, b.v FROM b WHERE b.k = a.k LIMIT 1) sub
       WHERE sub.v = 200
   );
   ```
   
   ### Expected behavior
   
   - Case 1: expected `[]`; DataFusion `55.0.0` returns `[(1,)]`.
   - Case 2: expected `[]`; DataFusion `55.0.0` returns `[(1,)]`.
   
   The optimized plan shows the inner `Limit` gone while `HAVING COUNT(*) > 1` 
/ `Filter b.v = 200` remain.
   
   ### Additional context
   
   ### Root cause
   
   `decorrelate.rs:276-309` applies the "emptiness only" argument without 
checking the position of the `Limit`
   relative to aggregates and filters. The condition must be: the `Limit` is 
the top-level operator of the `EXISTS`
   subquery (or every operator between the `Limit` and the `EXISTS` root is 
row-count-preserving and cannot observe
   the difference).
   
   ### Suggested fix
   
   Only strip `LIMIT` when it is the root of the `EXISTS` subquery (possibly 
under row-preserving
   `Projection`/`SubqueryAlias`). Otherwise stop pull-up (`stop_pull_up`) and 
decorrelate using the
   `row_number()`-based limit decorrelation already implemented for correlated 
`LIMIT` (as used for scalar
   subqueries). Also handle `LIMIT 0` explicitly: `EXISTS (... LIMIT 0)` is 
always false and must not return rows.
   
   ### Willingness to contribute
   
   None


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