Split out of the review of #6326, where the SET and REMOVE label paths were unified. This one is pre-existing and was deliberately left out of that PR: it changes REMOVE's observable semantics rather than fixing a crash, and it is unrelated to #6311/#6312/#6313.
SetStep calls propagateUpdateToSameNodeAliases() after writing a property, so every alias in the row that binds the same record is pointed at the updated MutableDocument and all of them observe the write. RemoveStep.removeProperty() does not: it replaces only the alias it was given.
MATCH (n {name:'a'}) MATCH (m {name:'a'})
REMOVE n.p
RETURN n.p AS throughN, m.p AS throughM
throughN is null, throughM still reports the removed value - the same query written with SET n.p = null answers null for both. Neo4j has one binding per node per row, so both aliases must see the same state.
Suggested fix: give the two steps one shared helper for pointing a row's aliases at an updated record, the way #6326 gave them one shared LabelReplacements for pointing them at a replaced one, and call it from removeProperty(). A regression test should cover both the two-alias case and the same node reached again on a later row.
Split out of the review of #6326, where the SET and REMOVE label paths were unified. This one is pre-existing and was deliberately left out of that PR: it changes REMOVE's observable semantics rather than fixing a crash, and it is unrelated to #6311/#6312/#6313.
SetStepcallspropagateUpdateToSameNodeAliases()after writing a property, so every alias in the row that binds the same record is pointed at the updatedMutableDocumentand all of them observe the write.RemoveStep.removeProperty()does not: it replaces only the alias it was given.throughNis null,throughMstill reports the removed value - the same query written withSET n.p = nullanswers null for both. Neo4j has one binding per node per row, so both aliases must see the same state.Suggested fix: give the two steps one shared helper for pointing a row's aliases at an updated record, the way #6326 gave them one shared
LabelReplacementsfor pointing them at a replaced one, and call it fromremoveProperty(). A regression test should cover both the two-alias case and the same node reached again on a later row.