Skip to content

fix(cypher): carry the real node through WITH, not a name-only stub (#2208) - #2351

Merged
DeusData merged 1 commit into
mainfrom
fix/issue-2208
Sep 30, 2026
Merged

DeusData merged 1 commit into
mainfrom
fix/issue-2208

Conversation

@DeusData

Copy link
Copy Markdown
Owner

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_name returned "f", f.start_line "0", and aggregate WITH merged same-named nodes into one row.

  • A bare node WITH item now carries a deep copy of the node (simple and aggregate paths), so all properties resolve exactly as without WITH.
  • Aggregate WITH groups node items by id, not name.
  • Scalar WITH aliases no longer expose their own name as qualified_name / keys().
  • WITH f AS g ORDER BY g.<prop> sorts on the carried node's property.
  • The now-dead id-stub re-fetch in node_prop is 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 real qualified_name / file_path / start_line, one row per function. make lint-ci passes (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

…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>
@DeusData
DeusData merged commit 372aff2 into main Sep 30, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

query_graph: property access on a WITH-carried (in-scope) node returns wrong values, not the real property

1 participant