Skip to content

fix(cypher): enforce variable-length path semantics#883

Open
jstar0 wants to merge 2 commits into
DeusData:mainfrom
jstar0:fix/cypher-path-semantics
Open

fix(cypher): enforce variable-length path semantics#883
jstar0 wants to merge 2 commits into
DeusData:mainfrom
jstar0:fix/cypher-path-semantics

Conversation

@jstar0

@jstar0 jstar0 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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:

  • repeated node variables now unify with an existing binding, so MATCH (f)-[:CALLS*1..2]->(f) only matches paths that return to the same node;
  • variable-length traversal now rejects paths that reuse the same edge id, so a single self-loop cannot fabricate longer paths;
  • cbm_store_bfs now caps rows admitted to the recursive CTE, so tracking edge ids cannot enumerate an unbounded number of simple paths before the outer LIMIT.

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

  • Enforce existing target-node bindings when expanding variable-length relationships.
  • Track edge ids in recursive traversal and reject reusing the same edge within one path.
  • Cap recursive CTE rows inside cbm_store_bfs.
  • Add regressions for repeated node variables, self-loop edge reuse, and the BFS recursive row cap.

Verification

make -f Makefile.cbm test
make -f Makefile.cbm lint-ci

Checklist

  • Every commit is signed off (git commit -s) — required, CI rejects
    unsigned commits (DCO, see CONTRIBUTING.md)
  • Tests pass locally (make -f Makefile.cbm test)
  • Lint passes (make -f Makefile.cbm lint-ci)
  • New behavior is covered by a test (reproduce-first for bug fixes)

@jstar0 jstar0 requested a review from DeusData as a code owner July 5, 2026 11:49

@DeusData DeusData left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

jstar0 added 2 commits July 6, 2026 00:30
Signed-off-by: King Star <mcxin.y@gmail.com>
Signed-off-by: King Star <mcxin.y@gmail.com>
@jstar0 jstar0 force-pushed the fix/cypher-path-semantics branch from 313059b to 85c32f3 Compare July 5, 2026 17:03
@DeusData DeusData added bug Something isn't working cypher Cypher query language parser/executor bugs stability/performance Server crashes, OOM, hangs, high CPU/memory priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Jul 5, 2026
@DeusData

DeusData commented Jul 5, 2026

Copy link
Copy Markdown
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cypher Cypher query language parser/executor bugs priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. stability/performance Server crashes, OOM, hangs, high CPU/memory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants