Version
codebase-memory-mcp dev — built from source at main 997d087b.
Platform
macOS (Apple Silicon)
Install channel
Built from source
Binary variant
standard
What happened, and what did you expect?
A RETURN may name a variable that the WITH before it dropped. The query
runs, exits clean, prints a column for that variable, and fills the column with
empty strings. Nothing reports an error.
Real Cypher refuses this query and names the offending variable. Here the
caller cannot tell a wrong query from a graph that holds no such data.
The empty column is the harmful part. A reader — a person or an agent — sees a
column of nothing and reads it as "the graph has no data here". The true answer
is "your query named something that is out of scope".
Reproduction
The repository's own cypher test fixture reproduces it, so no outside code is
needed. setup_cypher_store() in tests/test_cypher.c builds a store with
three CALLS edges.
-
Code being indexed: the fixture built by setup_cypher_store() in
tests/test_cypher.c.
-
The query:
MATCH (f:Function)-[:CALLS]->(g) WITH f.name AS caller RETURN caller, g.name
g does not survive the WITH. A plain MATCH does this as readily as an
OPTIONAL MATCH, so the optional half is not the cause.
-
Result: three rows. Column g.name is present and every value is "".
Return code 0. No error text anywhere.
Expected: a parse or execution error naming g as out of scope.
To see it without writing a test, run the same query through query_graph
against any indexed project, using a variable the WITH did not carry.
Logs
PROBE rc=0 col_count=2 row_count=3
PROBE column[0]="caller"
PROBE column[1]="g.name"
PROBE row[0] = ["HandleOrder"] [""]
PROBE row[1] = ["HandleOrder"] [""]
PROBE row[2] = ["ValidateOrder"] [""]
Captured by a throwaway test against the fixture above, on main 997d087b
plus the one commit in #1918. That commit only touches RETURN *, so it does
not affect this path.
Where it comes from
Nothing checks a projected name against the live scope. An unbound variable
resolves to NULL and renders as "", and that is deliberate:
src/cypher/cypher.c:2568 — return NULL; /* unbound variable */
src/cypher/cypher.c:3345 and :3350 — an unmatched OPTIONAL MATCH target
stays unbound on purpose, and the projection renders it "".
That convention is right for the OPTIONAL MATCH case. Its cost is that a name
which was never carried through looks exactly like an optional row that did not
match, so no error can be raised at projection time.
A search for not in scope, unknown variable, undefined variable and
scope check across src/cypher/cypher.c at 997d087b returns nothing.
Suggested fix
Add one check after parsing, before execution: every variable a RETURN or
WITH item names must be either a pattern variable still in scope, or an alias
the previous WITH created. Report the name that fails.
The check belongs at parse time, which keeps the "" convention for an
unmatched OPTIONAL MATCH target exactly as it is. The check is about names
that were never carried through, not about rows that did not match.
Related
Confirmations
Version
codebase-memory-mcp dev— built from source atmain997d087b.Platform
macOS (Apple Silicon)
Install channel
Built from source
Binary variant
standard
What happened, and what did you expect?
A
RETURNmay name a variable that theWITHbefore it dropped. The queryruns, exits clean, prints a column for that variable, and fills the column with
empty strings. Nothing reports an error.
Real Cypher refuses this query and names the offending variable. Here the
caller cannot tell a wrong query from a graph that holds no such data.
The empty column is the harmful part. A reader — a person or an agent — sees a
column of nothing and reads it as "the graph has no data here". The true answer
is "your query named something that is out of scope".
Reproduction
The repository's own cypher test fixture reproduces it, so no outside code is
needed.
setup_cypher_store()intests/test_cypher.cbuilds a store withthree
CALLSedges.Code being indexed: the fixture built by
setup_cypher_store()intests/test_cypher.c.The query:
gdoes not survive theWITH. A plainMATCHdoes this as readily as anOPTIONAL MATCH, so the optional half is not the cause.Result: three rows. Column
g.nameis present and every value is"".Return code 0. No error text anywhere.
Expected: a parse or execution error naming
gas out of scope.To see it without writing a test, run the same query through
query_graphagainst any indexed project, using a variable the
WITHdid not carry.Logs
Captured by a throwaway test against the fixture above, on
main997d087bplus the one commit in #1918. That commit only touches
RETURN *, so it doesnot affect this path.
Where it comes from
Nothing checks a projected name against the live scope. An unbound variable
resolves to NULL and renders as
"", and that is deliberate:src/cypher/cypher.c:2568—return NULL; /* unbound variable */src/cypher/cypher.c:3345and:3350— an unmatchedOPTIONAL MATCHtargetstays unbound on purpose, and the projection renders it
"".That convention is right for the
OPTIONAL MATCHcase. Its cost is that a namewhich was never carried through looks exactly like an optional row that did not
match, so no error can be raised at projection time.
A search for
not in scope,unknown variable,undefined variableandscope checkacrosssrc/cypher/cypher.cat997d087breturns nothing.Suggested fix
Add one check after parsing, before execution: every variable a
RETURNorWITHitem names must be either a pattern variable still in scope, or an aliasthe previous
WITHcreated. Report the name that fails.The check belongs at parse time, which keeps the
""convention for anunmatched
OPTIONAL MATCHtarget exactly as it is. The check is about namesthat were never carried through, not about rows that did not match.
Related
UNWINDis parsed but silently ignored and should error. Same shape:a query that is wrong runs quietly instead of failing.
RETURN *after aWITHansweredevery value empty. That one is the star projection reading the query pattern
instead of the live bindings. This issue is the named-column path and is a
separate fix, kept out of that PR on the one-issue-per-PR rule.
Confirmations