fix(planner): bind by-value set once in correlated-optional unnest to avoid hanging MERGE/OPTIONAL MATCH with pattern predicates - #1083
Merged
adsharma merged 1 commit intoOct 1, 2026
Conversation
planOptionalMatch's inner-selectivity gate built constFilteredVars with
constFilteredVars.insert(collector.getVarNames().begin(),
collector.getVarNames().end());
Because getVarNames() returns by value, begin() and end() came from two
different temporaries. The insert loop then compared iterators across two
containers and never terminated (or walked freed memory): planning hung
on one core, or crashed with SIGSEGV depending on heap layout.
Every MERGE / OPTIONAL MATCH whose pattern carried a property predicate
against a non-empty outer plan hit this, e.g.
MATCH (a:person), (b:person) WHERE a.ID = 0 AND b.ID = 5
MERGE (a)-[r:knows {date: date('2022-02-02')}]->(b);
The same shape without a pattern property planned fine, and an empty
table could appear to pass, which made the failure look data-dependent.
Binding the temporary once restores a well-defined iterator range.
Introduced in 58c10a4.
Contributor
|
Thanks! |
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.
Hi! Thanks a lot for 0.21.1 — we've been building on it and hit one planner hang that turned out to come from upstream, so we'd like to contribute a fix. Hopefully this is helpful.
Summary
planOptionalMatch's inner-selectivity gate (introduced in 58c10a4) buildsconstFilteredVarswith:constFilteredVars.insert(collector.getVarNames().begin(), collector.getVarNames().end());Since
getVarNames()returns by value,begin()andend()come from two different temporaries. The insert loop then compares iterators across two distinct containers and never terminates (or walks freed memory): planning spins on one core indefinitely, or crashes with SIGSEGV depending on heap layout.Reproduction
Hangs at planning time (
EXPLAINis enough — no execution needed) whenever the pattern carries a property predicate and the left plan is non-empty:Also reproduces with a node property map on the MERGE pattern:
Telling details that cost us some time:
MERGE (a)-[r:knows]->(b)) plans fine — the loop body only runs when the clause has pattern predicates.MERGEwith no precedingMATCHalso plans fine — the gate is skipped for an empty left plan.We bisected this to the 58c10a4 window and confirmed the bug is present on current
main(file blob9b269c4).Fix
Bind the by-value temporary once before taking the iterator range (plus a brief comment so it doesn't come back):
auto predVarNames = collector.getVarNames(); constFilteredVars.insert(predVarNames.begin(), predVarNames.end());Testing
EXPLAINand without).dml_rel/merge,dml_node/merge, and transaction merge e2e suites pass (26 tests, previously hanging on the pattern-property cases).Happy to adjust anything — thanks for taking a look!