[SPARK-52818][SQL] Fix MergeSubplans creating nested WithCTE with cross-scope CTE references#55338
Open
cloud-fan wants to merge 1 commit intoapache:masterfrom
Open
[SPARK-52818][SQL] Fix MergeSubplans creating nested WithCTE with cross-scope CTE references#55338cloud-fan wants to merge 1 commit intoapache:masterfrom
cloud-fan wants to merge 1 commit intoapache:masterfrom
Conversation
615846e to
7d915e1
Compare
…ss-scope CTE references ### What changes were proposed in this pull request? When a non-deterministic CTE (e.g. using `monotonically_increasing_id()`) is referenced in scalar subqueries and the result is displayed with `.show()` (which adds a `Limit`), `MergeSubplans` can create an outer `WithCTE` whose CTE defs reference CTE defs from an inner `WithCTE`. This causes `ReplaceCTERefWithRepartition` to crash with `NoSuchElementException` because it processes the outer CTE defs before the inner CTE defs have been added to the map. The root cause: `MergeSubplans.apply` checks `case _: WithCTE => plan` to skip plans with CTEs, but this only matches when `WithCTE` is the **top-level** node. When `.show()` wraps the plan in `GlobalLimit(LocalLimit(WithCTE(...)))`, the top-level node is `GlobalLimit`, so `MergeSubplans` runs and creates a new outer `WithCTE` around the existing inner one — producing nested `WithCTE` nodes with cross-scope references. The fix has two parts: 1. **`MergeSubplans`**: Change `case _: WithCTE => plan` to `case _ if plan.containsPattern(CTE) => plan` to skip plans that contain `WithCTE` **anywhere**, not just at the top level. 2. **`ReplaceCTERefWithRepartition`**: Add a defensive guard `if cteMap.contains(ref.cteId)` so that orphaned `CTERelationRef` nodes don't crash with `NoSuchElementException`. ### Why are the changes needed? Bug fix. The query crashes with `java.util.NoSuchElementException: key not found` in `ReplaceCTERefWithRepartition`. ### Does this PR introduce _any_ user-facing change? Yes, queries that previously crashed now work correctly. ### How was this patch tested? New tests in `CTEInlineSuite` and `InlineCTESuite`. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code
7d915e1 to
abd393e
Compare
Contributor
Author
|
cc @peter-toth |
peter-toth
approved these changes
Apr 14, 2026
Contributor
peter-toth
left a comment
There was a problem hiding this comment.
LGTM, thanks for the fix.
dongjoon-hyun
approved these changes
Apr 14, 2026
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.
What changes were proposed in this pull request?
When a non-deterministic CTE (e.g. using
monotonically_increasing_id()) is referenced in scalar subqueries and the result is displayed with.show()(which adds aLimit),MergeSubplanscan create an outerWithCTEwhose CTE defs reference CTE defs from an innerWithCTE. This causesReplaceCTERefWithRepartitionto crash withNoSuchElementExceptionbecause it processes the outer CTE defs before the inner CTE defs have been added to the map.The root cause:
MergeSubplans.applycheckscase _: WithCTE => planto skip plans with CTEs, but this only matches whenWithCTEis the top-level node. When.show()wraps the plan inGlobalLimit(LocalLimit(WithCTE(...))), the top-level node isGlobalLimit, soMergeSubplansruns and creates a new outerWithCTEaround the existing inner one — producing nestedWithCTEnodes with cross-scope references.Repro:
Calling
.show()on this crashes. Calling.collect()works (noLimitwrapper).The fix has two parts:
MergeSubplans: Changecase _: WithCTE => plantocase _ if plan.containsPattern(CTE) => planto skip plans that containWithCTEanywhere in the tree, not just at the top level.ReplaceCTERefWithRepartition: improve error messageWhy are the changes needed?
Bug fix. Queries with non-deterministic CTEs referenced in scalar subqueries crash with
java.util.NoSuchElementException: key not foundwhen using.show().Does this PR introduce any user-facing change?
Yes, queries that previously crashed now work correctly.
How was this patch tested?
New tests added:
CTEInlineSuite: end-to-end test with the exact repro query using.show()InlineCTESuite: unit test verifyingReplaceCTERefWithRepartitionhandles orphanedCTERelationRefgracefullyWas this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code