fix(cypher): enforce variable-length path semantics#883
Conversation
DeusData
left a comment
There was a problem hiding this comment.
Thanks for this, @jstar0 — the semantics here are exactly right. The node-variable unification (matching the fixed-length #627 behavior) and the no-repeated-relationship "trail" semantics are both correct, and the two regression tests are genuine guards: they fail on the old executor and pass only on the fix. Security, scope, and DCO all check out.
One blocking issue before we can merge, on the store.c change to cbm_store_bfs.
Pulling edge_path into the recursive CTE row changes the UNION dedup key from (node_id, hop) to (node_id, hop, edge_path). Because edge_path is distinct per path, UNION no longer collapses the many paths that reach a node — the CTE now enumerates every simple path up to max_depth instead of doing a bounded node-BFS. On a hub-heavy graph at depth 10 that's on the order of b^d intermediate rows, each carrying a growing TEXT path, and the outer ORDER BY bfs.hop LIMIT N can't rein it in (SQLite materializes the full CTE before it orders/limits).
Why this reaches beyond var-length Cypher: cbm_store_bfs is shared — it also backs the trace_call_path MCP tool, whose depth is client-controlled (and currently unclamped). So this turns a polynomial traversal into a potential exponential blow-up on a widely-used, untrusted-input path.
The PR notes the expensive-expansion guard is deferred — the catch is that this change is the amplifier that guard is meant to contain, so merging it now ships the blow-up ahead of its bound. Could you fold the bound into this same PR? A hard cap on the number of enumerated paths / CTE rows inside the recursion (or the deferred guard itself) would do it. Happy to think through the shape with you.
Once that's in, this is good to go — the correctness work is solid. Thanks again.
Signed-off-by: King Star <mcxin.y@gmail.com>
Signed-off-by: King Star <mcxin.y@gmail.com>
313059b to
85c32f3
Compare
|
Thanks for the focused #797 fix. Triage: high-priority Cypher/stability bug. Review focus is exactly where the risk sits: variable binding semantics, edge-reuse prevention, and bounding recursive expansion without breaking valid variable-length path queries. The added regressions are the right shape; we will keep this tied to #797 while review finishes. |
What does this PR do?
Part of #797.
This fixes two variable-length Cypher path semantics and bounds the traversal expansion introduced by the trail check:
MATCH (f)-[:CALLS*1..2]->(f)only matches paths that return to the same node;cbm_store_bfsnow caps rows admitted to the recursive CTE, so tracking edge ids cannot enumerate an unbounded number of simple paths before the outerLIMIT.Summary
Variable-length Cypher paths now follow the same binding and simple-path semantics as the fixed-length executor cases covered by the existing tests, while shared BFS traversal keeps a hard bound on recursive row expansion.
Changes
cbm_store_bfs.Verification
make -f Makefile.cbm test make -f Makefile.cbm lint-ciChecklist
git commit -s) — required, CI rejectsunsigned commits (DCO, see CONTRIBUTING.md)
make -f Makefile.cbm test)make -f Makefile.cbm lint-ci)