fix(query): route named-graph SPARQL DELETE/INSERT WHERE to the target graph (and make MODIFY durable) - #368
Open
teipsum wants to merge 2 commits into
Open
fix(query): route named-graph SPARQL DELETE/INSERT WHERE to the target graph (and make MODIFY durable)#368teipsum wants to merge 2 commits into
teipsum wants to merge 2 commits into
Conversation
…t graph Named-graph SPARQL Update was silently broken: a GRAPH-wrapped or WITH-scoped DELETE WHERE / MODIFY parsed, executed, and reported success while changing nothing in the designated named graph, and an INSERT-via-MODIFY silently wrote into the default graph instead of the named one (wrong-graph data, no error). Root cause is in the pattern/MODIFY mutation path of the RDF planner: - the translator dropped the GRAPH block when extracting DELETE WHERE templates, so a GRAPH-wrapped DELETE WHERE produced an empty template and a no-op plan; - RdfInsertPatternOperator, RdfDeletePatternOperator and RdfModifyOperator ignored the template graph and called the bare default-store insert/remove; - a WITH-scoped WHERE was planned against the default graph, so an unqualified WHERE bound zero rows and the whole MODIFY no-oped. The concrete-term operators already route through the per-graph sub-store API; this change applies the same idiom to the pattern/template family: - extract_triples_from_pattern descends into GRAPH blocks and carries each triple's graph onto DeleteTripleOp; - the pattern and MODIFY executors resolve the per-template named sub-store and insert/remove there, with matching CDC and WAL graph attribution. The delete path uses the non-creating graph lookup so a zero-match named delete never materialises an empty sub-store; - translate_modify redirects the WHERE clause's default graph to the WITH graph before planning it, so an unqualified WITH-scoped WHERE binds in the named graph; - update variants that cannot be routed fail closed: USING / USING NAMED and variable graph targets (GRAPH ?g) now raise an explicit error instead of silently dropping the clause or mis-targeting. Adds named-graph MODIFY oracles to update_advanced.gtest and a named_graph_modify integration test covering the mis-write, the non-creating delete, and the fail-closed guards, and documents the transaction-handle atomicity contract on Session::execute_sparql. Co-Authored-By: Claude <noreply@anthropic.com>
RdfModifyOperator emitted no WAL record at all, so a DELETE/INSERT-WHERE MODIFY was not durable even on the default graph: under wal-directory storage the mutation was applied in memory but silently lost on the next replay. Emit InsertRdfTriple / DeleteRdfTriple WAL records for the triples a MODIFY inserts and removes, carrying the resolved per-template graph so replay re-materialises named-graph mutations into the correct sub-store (WAL replay already routes by the record's graph field). The MODIFY operator now receives the session WAL handle, threaded through plan_modify the same way the pattern operators already receive it. Adds named_graph_modify_durability, a close/reopen WAL-replay test covering both a named-graph MODIFY and a default-graph MODIFY. Co-Authored-By: Claude <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #367.
What was wrong
Named-graph SPARQL Update worked for the concrete-term forms (
INSERT DATA,DELETE DATA) andDROP GRAPH, but the pattern / MODIFY path ignored thetarget graph entirely. The result was a set of silent failures (no error, no
warning):
GRAPH-wrappedINSERT ... WHEREmis-wrote into the default graph(data corruption);
GRAPH-wrappedDELETE WHEREwas a silent no-op;WITH <g>MODIFY with an unqualified WHERE bound zero rows and no-oped;DELETE/INSERT ... WHEREMODIFY emitted no WAL record, so it was loston
wal-directoryreplay (even on the default graph).See the linked issue for runnable repros.
Root cause (paths/lines at
main, 4ebae02)The concrete-term operators already resolve the per-graph sub-store via the
existing
RdfStore::graph/graph_or_createAPI; the pattern/MODIFY familydid not. Specifically:
SparqlTranslator::extract_triples_from_pattern(
crates/grafeo-engine/src/query/translators/sparql.rs:487) matched onlyBasic/Groupand returnedVec::new()forNamedGraph, so aGRAPH-wrapped
DELETE WHEREproduced an empty template;DeleteTripleOp.graphwas hard-coded
None(sparql.rs:469).RdfInsertPatternOperator(rdf/mod.rs:1850) andRdfDeletePatternOperator(
rdf/mod.rs:2134) calledstore.insert/store.removeon the defaultstore and logged
graph: Noneto WAL/CDC(
rdf/mod.rs:1861/1873,2145/2157).template.graph.RdfModifyOperator::next(rdf/mod.rs:2688) deleted viastore.remove(
:2720) and inserted viastore.insert(:2793) on the default store, andthe struct had no WAL field at all.
WITH.SparqlTranslator::translate_modify(sparql.rs:498) built the WHERE planwith
self.dataset = None, so an unqualified WHERE underWITH <g>scannedthe default graph (contrast
translate_select, which setsself.dataset).USINGwas dropped at dispatch (sparql.rs:334,using_clauses: _).The fix
Two logical commits in one PR.
Commit 1 — routing correctness
extract_triples_from_patterndescends intoGRAPHblocks and tags eachextracted triple with its enclosing graph;
translate_delete_wheresetsDeleteTripleOp.graphaccordingly.RdfInsertPatternOperator,RdfDeletePatternOperatorandRdfModifyOperatorresolve the per-template named sub-store and insert/remove there, with the
resolved graph carried into the CDC events (and the pattern operators' WAL
records). The delete path uses the non-creating
graphlookup so azero-match named delete never materialises an empty sub-store.
translate_modifyredirects the WHERE clause's default graph to theWITHgraph before planning it, mirroring
translate_select, so an unqualifiedWITH-scoped WHERE binds in the named graph. A GRAPH-wrapped WHERE still binds
through the existing graph-context stack and is unaffected.
USING/USING NAMEDand avariable graph target (
GRAPH ?g) now return an explicit error instead ofsilently dropping the clause or mis-targeting.
Session::execute_sparql.Commit 2 — MODIFY durability
RdfModifyOperatornow emitsInsertRdfTriple/DeleteRdfTripleWAL recordsfor the triples it inserts and removes, carrying the resolved per-template
graph. WAL replay already routes by the record's graph field, so this is a
writer-side-only change. The operator receives the session WAL handle through
plan_modify, the same way the pattern operators already do.Tests
tests/spec/rdf/sparql/update_advanced.gtest: named-graph MODIFY oracles —the mis-write (named gets the triple, default stays empty), the fingerprint
trio (named
DELETE WHEREdeletes, default still works,DROP GRAPHstillworks), the GRAPH-wrapped MODIFY rename, and the
WITHread-side binding.crates/grafeo-engine/tests/named_graph_modify.rs: the store-levelmis-write check, the non-creating-on-zero-match-delete invariant, and the
fail-closed
USING/ variable-graph guards (including the zero-binding case).crates/grafeo-engine/tests/named_graph_modify_durability.rs: close/reopenWAL-replay durability for a named-graph and a default-graph MODIFY.
All four touched crates pass (
grafeo-engine,grafeo-core,grafeo-adapters,grafeo-spec-tests);cargo fmtandcargo clippy --all-targets --all-features -- -D warningsare clean.Notes / scope
it on the pattern/MODIFY path.
pre-existing). Honest caveat: happy to split this into more commits/PRs if
preferred.
Summary by cubic
Routes named-graph SPARQL DELETE/INSERT WHERE (MODIFY) to the correct graph and makes MODIFY durable via WAL. Fixes silent no-ops, wrong-graph writes, and data loss on WAL replay.
Bug Fixes
USING/USING NAMEDandGRAPH ?g.Migration
USING/USING NAMEDin MODIFY withWITH <g>or explicitGRAPH <g>.GRAPH ?gin DELETE/INSERT templates; use a concrete graph IRI.Written for commit 417d629. Summary will update on new commits.