Skip to content

fix: reject correlated IN/NOT IN over a groupless aggregate - #26677

Open
mohammadnaqvi04 wants to merge 3 commits into
apache:mainfrom
mohammadnaqvi04:in-not-in-groupless-agg-count-bug
Open

mohammadnaqvi04 wants to merge 3 commits into
apache:mainfrom
mohammadnaqvi04:in-not-in-groupless-agg-count-bug

Conversation

@mohammadnaqvi04

@mohammadnaqvi04 mohammadnaqvi04 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Part of #24960. This PR adds a safe rejection until IN/NOT IN count-bug compensation is implemented.

Rationale for this change

A correlated IN/NOT IN over an aggregate without GROUP BY returns wrong rows when an outer row has no match (see #24960 (comment)). #25391 fixed EXISTS/NOT EXISTS. IN/NOT IN will be covered in a follow-up, so until then these queries should error instead of returning wrong results.

What changes are included in this PR?

build_join in decorrelate_predicate_subquery.rs now keeps these subqueries correlated, so they fail to plan with a not-implemented error. The exception is an IN in a WHERE over an aggregate that is NULL on empty input (e.g. max, sum), which the current rewrite already answers correctly.

What is the testing strategy for this PR?

  • The existing IN over max test still passes unchanged.
  • The existing NOT IN over count test now expects the error.
  • New tests expect the error for IN over count in a filter and in a projection, NOT IN over max, and IN over max in a projection.

Are there any user-facing changes?

These queries now return a not-implemented error instead of wrong results.

@mohammadnaqvi04 mohammadnaqvi04 changed the title reject correlated IN/NOT IN over a groupless aggregate fix: reject correlated IN/NOT IN over a groupless aggregate Oct 23, 2026
@mohammadnaqvi04

mohammadnaqvi04 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

@jayzhan211 this addresses your comment (#24960 (comment)) similar to how #26544 handles its no row for an unmatched outer row case.

@mohammadnaqvi04
mohammadnaqvi04 marked this pull request as ready for review October 23, 2026 20:29
if !pull_up.can_pull_up {
return Ok(None);
}
// Stopgap until a follow-up adds count-bug compensation for `IN`/`NOT IN`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could this point at the tracking issue, so whoever adds the IN count-bug compensation knows to remove the block?

-    // Stopgap until a follow-up adds count-bug compensation for `IN`/`NOT IN`.
+    // TODO(#24960): stopgap until count-bug compensation covers `IN`/`NOT IN`.

// Stopgap until a follow-up adds count-bug compensation for `IN`/`NOT IN`.
// The subquery stays correlated unless an outer row with no match gets the
// right answer anyway.
if !exists_sub_query {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the extra rewrite(true) only finds something when a groupless aggregate was pulled up, and the first rewrite already records that. Gating on it skips the second pass for every other IN subquery. Optimizer tests and subquery.slt still pass with this:

-    if !exists_sub_query {
+    if !exists_sub_query && pull_up.pulled_up_scalar_agg {

@@ -2124,14 +2124,35 @@ WHERE t1_int + 2 IN (SELECT max(t2_int) FROM t2 WHERE t2.t2_id = t1.t1_id)
----
11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the LeftMark branch that is allowed through (an IN under OR in a WHERE) has no test. Something like this would pin it down:

SELECT t1_id FROM t1
WHERE t1_int + 2 IN (SELECT max(t2_int) FROM t2 WHERE t2.t2_id = t1.t1_id) OR t1_id = 33
ORDER BY t1_id;
-- 11
-- 33

@jayzhan211

Copy link
Copy Markdown
Contributor

@mohammadnaqvi04 Thanks for the fix! I left a few nits inline that are still open, so please take a look when you get a chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants