Fix duplicate MERGE default projection#520
Merged
Merged
Conversation
9132b90 to
79aed5c
Compare
79aed5c to
9b85d90
Compare
9b85d90 to
3a7f8bb
Compare
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: #170
Summary
This fix separates the responsibilities more cleanly:
Planner /
LogicalMerge:MERGErows are user-visible.suppressDuplicateCreatedOutputfrom the logical child schema, merge keys, inserted pattern expressions, and ON CREATE / ON MATCH state.Mapper /
map_merge:logicalMerge.suppressesDuplicateCreatedOutput()and passes that decision into physicalMergeInfo.DataPos, and local table layout.Executor / persistent
Merge:The important semantic invariant is:
MERGEcreates at most one physical pattern per merge key, but duplicate-created output rows are preserved only when they carry user-visible upstream payload.That gives the desired behavior for both cases:
UNWIND [1, 1] AS i MERGE (a {stuff: i}) RETURN a.idreturns one row.MATCH ... WITH ..., age MERGE (...) RETURN u.ID, agestill returns one row per upstream payload row.The UUID/default part is fixed separately but consistently: when a duplicate-created row must still be output, we read the already-created node’s projected properties from storage instead of re-evaluating default expressions. That prevents volatile defaults like
gen_random_uuid()from producing a fresh value for a row that was not actually inserted.Root Cause
The unwind dedup changes let duplicate no-match MERGE rows continue through the merge output path. The duplicate-created path skipped the physical insert but still evaluated insert defaults again, so projected defaults such as gen_random_uuid() differed from the row that was actually stored.
Validation
issue.unwind_merge_rel_after_rel_match_dedup:issueissue.unwind_merge_with_projection_dedup:issue~issue.unwind_merge_with_projection_preserves_match_expansion'