Observed (v0.6.27, live bench + source-verified)
pgrdf.drop_graph(id, cascade) returns BIGINT = pre-drop quad count. That single number is returned for structurally different outcomes:
- empty graph fully removed (partition + catalog row gone) →
0
- graph already absent (idempotent path: stale catalog row pruned, nothing else existed) →
0
A caller receiving 0 cannot distinguish "I removed the graph" from "there was nothing to remove". Measured live: create empty graph → drop_graph(id, false) → 0, graph verified gone from the census; the same 0 comes back re-running the drop.
cascade adds a third confusion: it reads like "also remove the catalog row" but actually only gates an inferred-rows refusal (see the drop path in src/storage/graphs.rs) — the structural outcome is identical either way. A downstream MCP client formed exactly this wrong mental model and mis-diagnosed a stray catalog row as pgRDF's doing; the engine was innocent (a live probe refuted it) but the report made the wrong story cheap and the right one expensive.
Proposal (additive, non-breaking — no change to existing UDFs)
pgrdf.drop_graph_report(graph_id BIGINT, cascade BOOLEAN DEFAULT true) → JSONB returning {"existed": bool, "quads": N, "partition_removed": bool, "catalog_removed": bool} (+ IRI overload). Same locking/guards as drop_graph.
RAISE NOTICE on both paths of the existing drop_graph naming the outcome ("graph removed, N quads" / "graph already absent") so callers get the truth today without a return-type change.
New SQL surface means version bump + upgrade script in the same PR (house rule since #93). Not for the current release train — this is a v0.6.29+ candidate; nothing here blocks or reopens v0.6.28.
Observed (v0.6.27, live bench + source-verified)
pgrdf.drop_graph(id, cascade)returns BIGINT = pre-drop quad count. That single number is returned for structurally different outcomes:00A caller receiving
0cannot distinguish "I removed the graph" from "there was nothing to remove". Measured live: create empty graph →drop_graph(id, false)→0, graph verified gone from the census; the same0comes back re-running the drop.cascadeadds a third confusion: it reads like "also remove the catalog row" but actually only gates an inferred-rows refusal (see the drop path insrc/storage/graphs.rs) — the structural outcome is identical either way. A downstream MCP client formed exactly this wrong mental model and mis-diagnosed a stray catalog row as pgRDF's doing; the engine was innocent (a live probe refuted it) but the report made the wrong story cheap and the right one expensive.Proposal (additive, non-breaking — no change to existing UDFs)
pgrdf.drop_graph_report(graph_id BIGINT, cascade BOOLEAN DEFAULT true) → JSONBreturning{"existed": bool, "quads": N, "partition_removed": bool, "catalog_removed": bool}(+ IRI overload). Same locking/guards asdrop_graph.RAISE NOTICEon both paths of the existingdrop_graphnaming the outcome ("graph removed, N quads" / "graph already absent") so callers get the truth today without a return-type change.New SQL surface means version bump + upgrade script in the same PR (house rule since #93). Not for the current release train — this is a v0.6.29+ candidate; nothing here blocks or reopens v0.6.28.