Fix: MERGE followed by CREATE creates endless nodes (#4080)
Closes #1333 **Root cause:** `GenMerge` plans `merge_match` with `storage::View::NEW` so MERGE can see vertices created by earlier clauses in the same query. The underlying skip-list iterator has a `nullptr` end-sentinel, so a downstream `CREATE` in the same query kept feeding new nodes back into the running scan — producing unbounded node creation and OOM on `MERGE (n0) MERGE (n1) CREATE (c0)`. This is fundamentally an iterator-semantic problem: `View::NEW` is correct for cross-clause visibility, but bulk iteration should be scoped to entries that existed when the iterable opened — same guarantee most MVCC systems provide for cursors. **Fix:** Bound every in-memory vertex iterable by a GID high-water mark captured at construction. `storage->vertex_id_.load(acquire)` at that moment is the "next GID to be assigned" and therefore an exclusive upper bound on every vertex already in storage. Anything appended after that has `gid >= max_gid_` and is filtered out by the iterator's advance loop. Per-iterable behaviour: - `AllVerticesIterable` (used by `ScanAll`) — vertex skip-list is GID-ordered, so the iterator terminates on the first out-of-scope entry. - `InMemoryLabelIndex::Iterable` + `ChunkedIterable` (used by `ScanAllByLabel`) — index is not GID-ordered, so out-of-scope entries are skipped individually. - `InMemoryLabelPropertyIndex::Iterable<Entry/DescEntry>` + `ChunkedIterable<EntryT>` (used by `ScanAllByLabelProperties`) — same skip-and-continue. Cross-clause `View::NEW` visibility is preserved: any vertex created by an earlier clause has `gid < max_gid_` and remains visible. The watermark only filters writes that happen *after* the iterable opens. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
D
David Ivekovic committed
9173bf03fc0d2269b6961dcc2ea27ae970cba2ec
Parent: 487a349
Committed by GitHub <noreply@github.com>
on 5/19/2026, 8:49:57 AM