Fix EXISTS with select-list window functions (row-index error) - #3682
Open
vishnujayvel wants to merge 1 commit into
Open
Fix EXISTS with select-list window functions (row-index error)#3682vishnujayvel wants to merge 1 commit into
vishnujayvel wants to merge 1 commit into
Conversation
simplifyPartialJoinParents discarded Project/GroupBy/Sort/etc. when building the right side of a decorrelated EXISTS semi-join, but left *plan.Window in place. The Window shrinks the right-side schema to the window outputs while join-filter GetField indices still refer to pre-window columns, causing "unable to find field with index N in row of M columns" (dolt#11421). Treat *plan.Window like Project: existence does not depend on select-list window outputs. Filter/Having remain unstripped so QUALIFY-like predicates above a Window are preserved. Add enginetest coverage for EXISTS/NOT EXISTS with ROW_NUMBER() OVER (), multi-window silent-corruption shapes, uncorrelated NOT EXISTS + window (panic on main), and the SELECT 1 control. Fixes dolthub/dolt#11421
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: dolthub/dolt#11421
Reported by @Yibo-Dong.
Problem
A correlated
EXISTSwhose select list is a window expression (e.g.ROW_NUMBER() OVER ()) fails with an internal error:The same query with
SELECT 1(or any non-window projection) works. MySQL accepts the window form and returns the expected existence result.This is not crash-only. Multi-window
EXISTSbodies (two or more window functions with distinct partition schemes) return silently wrong rows on main — for example, withu2(x,y)rows(1,10),(1,20),(2,30)andtids1,2,3:NOT EXISTSof the same shape returns no rows on main instead of row3. Silent wrong results are the highest-priority maintainer concern for this fix.Separately, the uncorrelated
NOT EXISTS ... OVER ()shape panics the server on main (nil-pointer SIGSEGV atsql/core.go:432viajoin_iters.go:433). Panic outranks internal error.How to reproduce (crash shape)
Root cause
simplifyPartialJoinParentsinsql/analyzer/unnest_exists_subqueries.gostrips nodes that do not affect existence (Project,GroupBy,Sort,Distinct,TopN,Limit) from the decorrelated EXISTS body before building the semi-join right side. It did not strip*plan.Window.The
Windownode shrinks the right-side schema to the window outputs, while join-filterGetFieldindices still refer to pre-window column ids. Evaluation then hits an out-of-boundsGetFieldinsql/expression/get_field.govia the exists iterator — or, with multiple windows, can produce silently incorrect existence results.Fix
Treat
*plan.Windowlike*plan.ProjectinsimplifyPartialJoinParents: strip it when simplifying the EXISTS body. Window functions are 1:1 row-preserving (empty child → empty output), so existence does not depend on select-list window outputs.Filteris not in the strip list, so the loop halts at any survivingFilter; correlated Filters are hoisted bydecorrelateOuterColsand re-attached above the simplified node (unnest_exists_subqueries.go:416-417). A Filter with zero correlated conjuncts is left untouched (:314-317returnsSameTree).Havingbodies are separately rejected earlier (case *plan.Having: return nil, false). Stripping is therefore limited to pure select-list window projection wrappers.Tests
Enginetest script in
JoinScriptTests(enginetest/queries/join_queries.go):EXISTS (SELECT 1 FROM u WHERE u.x = t.a)→ rows1, 2EXISTS (SELECT ROW_NUMBER() OVER () FROM u WHERE u.x = t.a)→ rows1, 2NOT EXISTSvariant with the same window select list → row3u2) → EXISTS1, 2; NOT EXISTS3NOT EXISTS (SELECT ROW_NUMBER() OVER () FROM u)→ empty result (0 rows)Test plan
JoinQueries multi-window subtests (verbatim
-v):Limitations / follow-ups
HAVINGabove the Window still refuse unnest (Havingreturnsfalse); that path was already conservative and is unchanged.unnest_exists_subqueries.go:114-115, dolt#10493). With Window now stripped,EXISTS(...window... LIMIT 0)reaches that pre-existing hole (base hard-errors; patched returns rows where MySQL says empty) — same behavior as the existingSELECT 1 LIMIT 0shape; nothing new introduced, noted for completeness.Checklist
*plan.Windowonly)