fix(cypher): carry the real node through WITH, not a name-only stub (#2208) - #2351
Merged
Merged
Conversation
…2208) A node variable carried in scope through WITH (`WITH f`, `WITH f AS g`, `WITH f, count(r) AS refs`) was projected as a scalar virtual var: the node's name in .name and the variable name in .qualified_name (used as the owned storage for the binding's name), start_line 0. RETURN f.qualified_name therefore returned the literal variable name "f" and f.start_line "0"; only f.name was right. The aggregate path tagged the stub with the node id so node_prop could re-fetch some fields, but the re-fetch only ran for empty string fields, so the non-empty qualified_name and the numeric start_line never reached it. The aggregate path also grouped a node by its display name, so several same-named functions collapsed into one row carrying one node's properties and the summed count. Fix: - A bare, bound node item in WITH is now carried as a deep copy of the node, bound under its AST-owned alias, on both the simple and the aggregate path; every property then resolves exactly as it does without the WITH. Unbound OPTIONAL vars and scalar items stay scalar. - Aggregate WITH groups node items by node id, not name. - Scalar virtual vars keep their owned alias string in .project (never exposed as a property) instead of .qualified_name, so n.qualified_name / keys(n) no longer return the variable name. - WITH ... ORDER BY g.<prop> resolves the property on the carried node. - The node_prop stub re-fetch heuristic is removed: nothing produces id-tagged stubs anymore. Tests (cypher suite): cypher_issue2208_with_agg_carried_node_props, cypher_issue2208_with_agg_groups_by_node_identity, cypher_issue2208_with_simple_carried_node_props. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This was referenced Sep 25, 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.
A node carried through WITH was a placeholder that held only the node's name, with the variable name stored in
qualified_name.f.qualified_namereturned "f",f.start_line"0", and aggregate WITH merged same-named nodes into one row.qualified_name/keys().WITH f AS g ORDER BY g.<prop>sorts on the carried node's property.node_propis removed.Tests:
cypher_issue2208_with_agg_carried_node_props,cypher_issue2208_with_agg_groups_by_node_identity,cypher_issue2208_with_simple_carried_node_props(RED on main, green with the fix, RED again when reverted). Checked end to end on a fixture index with two same-named functions: the reporter's query now returns the realqualified_name/file_path/start_line, one row per function.make lint-cipasses (memory-core: none grew).Known separate bug, not in this PR:
count(r)over an unbound OPTIONAL MATCH variable returns 1 instead of 0 (also on the v0.11.0 release).Fixes #2208