Skip to content

GFQL: WITH re-entry mutates the caller's Plottable, silently corrupting the next query #1786

Description

@lmeyerov

Found while adversarially reviewing #1781. Pre-existing on master (84835988), engine-independent — wrong on pandas too, so it is not that PR's regression, but it is a silent wrong answer in ordinary two-query user code.

Repro

import graphistry, pandas as pd
N = pd.DataFrame({"id": list(range(7)), "kind": ["a","b"]*3 + ["a"]})
E = pd.DataFrame([(0,3),(1,2),(4,6),(3,5),(5,6),(0,6),(3,4)], columns=["s","d"])
g = graphistry.nodes(N, "id").edges(E, "s", "d")

g.gfql("MATCH (a {kind:'a'}) WITH a MATCH (a)-[*]->(b) RETURN count(*) AS c")  # 5  (correct)
g.gfql("MATCH (a)-[*]->(b) RETURN count(*) AS c")                              # 5  <-- WRONG

The second query is unrelated to the first and should return 12. It returns the first query's answer instead.

Cause

WITH re-entry sets _gfql_start_nodes on the caller's Plottable and never clears it, so the next query on the same g is answered against the leaked seed. The native polars path does the same thing at graphistry/compute/gfql/lazy/engine/polars/chain.py:378-380.

Why it matters

  • Violates the repo's pure-functional policy — g is supposed to be immutable across queries, and users reasonably reuse one Plottable for many queries.
  • Fails open: no error, just a wrong count. The two queries need not be related in any way.
  • Engine-independent, so an engine A/B will not surface it.

Suggested fix

Carry the re-entry seed on the per-execution context rather than assigning it to the caller's object, or restore the previous value in a finally. Worth a regression test that runs two independent queries against the same Plottable and asserts the second is unaffected by the first.

🤖 Generated with Claude Code

https://claude.ai/code/session_015YsqAZQLbqjSDrYSFz2GoB

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions