fix(cypher): RETURN * must read the live scope, not the query pattern - #1918
Open
CaptainMittens wants to merge 1 commit into
Open
fix(cypher): RETURN * must read the live scope, not the query pattern#1918CaptainMittens wants to merge 1 commit into
CaptainMittens wants to merge 1 commit into
Conversation
RETURN * built its columns from the variables the query pattern named, never from the bindings it was about to project. One line caused two separate wrong answers, and neither one reported an error. After a WITH, the pattern's variables are out of scope — the WITH replaced them with the names it made. The old code still asked for the old names, found none of them, and answered a full result of empty strings. This query used to print twelve columns of nothing: MATCH (f:Function) OPTIONAL MATCH (f)-[:CALLS]->(g) WITH f.name AS caller, g.name AS callee RETURN * It now prints two columns, caller and callee, holding their values. A name the WITH made holds one value rather than a node, so it gets one column, not the four a node variable gets. Separately, collect_pattern_vars appended every pattern's variables with no repeat check. A variable named in two patterns got its four columns twice, which the OPTIONAL MATCH above does with f. Two tests cover both faults and fail against the old code: cypher_return_star_dedups_repeated_pattern_var col_count 12, want 8 cypher_return_star_after_with_names_aliases col_count 8, want 2 Cypher suite: 185 passed, 0 failed. clang-format clean on both files. Reported alongside a second fault this does NOT fix: a variable the WITH dropped is still accepted afterwards and renders empty, because nothing checks a projected name against the live scope. See .agents/research/2026-08-29-cypher-return-star-and-with-scope.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joshua Richter <jrichter5781@gmail.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
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.
PROBLEM
RETURN *built its columns from the variables the query pattern named,never from the bindings it was about to project. One line caused two
separate wrong answers, and neither reported an error — so a caller
cannot tell a broken query from an empty graph.
Measured against a real indexed project before the fix:
MATCH (f:Function) RETURN *MATCH (f:Function) OPTIONAL MATCH (f)-[:CALLS]->(g) RETURN *fappears twiceWITH f.name AS caller, g.name AS calleeCAUSE
execute_return_starcalledcollect_pattern_vars(q, ...), which readsthe parse-time pattern. Two consequences fall out of that one call.
After a
WITH, the pattern variables are out of scope — theWITHreplaced them with the names it made, and the live bindings are keyed by
those names. Asking for
fthen finds nothing, and every column iswritten as
"".Separately,
collect_pattern_varsappended each pattern in turn with norepeat check, so a variable named in two patterns got its four columns
twice. The
OPTIONAL MATCHabove namesftwice.FIX
execute_return_star_after_with: when aWITHis present, columnscome from its items. A name a
WITHmakes holds one value rather thana node, so it is ONE column, not the four a node variable gets.
collect_pattern_varsgained a repeat check. It has exactly onecaller, so nothing else is affected.
The query above now answers two columns,
callerandcallee, holdingtheir values.
TESTS
Two, both failing against the old code:
Before: 183 passed, 2 failed. After: 185 passed.
GATES RUN
scripts/lint.sh --ci— exit 0, all linters passedscripts/test.sh --suites cypher— exit 0, 185 passedNOT IN THIS PR
A second fault found alongside this one, kept out to keep the PR to one
change: a variable a
WITHdrops is still accepted afterwards andrenders empty, because nothing checks a projected name against the live
scope.
MATCH (f:Function)-[:CALLS]->(g) WITH f.name AS caller RETURN caller, g.nameruns and prints ag.namecolumn of nothing, where realCypher refuses the query and names
g. Happy to raise it as its ownissue.
🤖 Generated with Claude Code