Two related but distinct concepts from the SPARQL literature are relevant
here.
Query containment is a semantic property: query Q1 is contained in Q2
(written Q1 ⊑ Q2) if, for every possible RDF graph, the answers to Q1 are a
subset of the answers to Q2.
Query subsumption is a structural witness for containment: a mapping from
the variables of one query to the terms of another that demonstrates
containment holds without evaluating over any actual data.

In practice, we use a subsumption check as a lightweight proxy for
containment. A SPARQL query can be compiled into an algebra tree — a
structured representation of its joins, filters, optional patterns, unions
and so on. Given a pre-approved template query and a client-supplied query,
we walk both algebra trees in parallel and check, operator by operator,
whether the client query is a valid specialisation of the template. The
rules per operator are:
subsumes(template, client):

  BGP, BGP         → every template triple matches some client triple
                     under a consistent substitution of template variables
  JOIN, JOIN        → subsumes(left, left) ∧ subsumes(right, right)
  FILTER, FILTER    → template filters ⊆ client filters ∧ subsumes(inner,
inner)
  FILTER, _         → false  (client dropped a template filter)
  _, FILTER         → subsumes(template, inner)  (client added a filter —
ok)
  OPTIONAL, OPTIONAL → subsumes(left, left) ∧ subsumes(right, right)
  OPTIONAL, JOIN    → subsumes(left, left) ∧ subsumes(right, right)
 (optional→required ok)
  _, OPTIONAL       → false  (client introduced OPTIONAL not in template)
  UNION, UNION      → subsumes(left, left) ∧ subsumes(right, right)
  UNION, _          → subsumes(left, client) ∨ subsumes(right, client)
  _, UNION          → false  (client introduced UNION not in template)
  MINUS, MINUS      → subsumes(left, left)
  _, MINUS          → subsumes(template, left)  (client added MINUS — ok)
  MINUS, _          → false  (client dropped a MINUS)
  SLICE, SLICE      → client limit ≤ template limit ∧ subsumes(inner, inner)
  PROJECT, PROJECT  → subsumes(inner, inner)

This check is sound but intentionally incomplete. Sound means that if the
check passes, containment is guaranteed — the client query cannot return
results outside the scope of the template. Incomplete means it may reject
queries that are semantically equivalent but expressed in a syntactically
different form. For a security gate this is the correct trade-off: false
negatives (over-rejection) are safe, false positives (under-rejection)
would not be.

On Sat, Mar 7, 2026 at 2:55 PM Andy Seaborne <[email protected]> wrote:

>  > Claude calls this "pattern containment" or "query subsumption".
>
> Ask Claude what it thinks these terms mean.
>
> Seems like "will pattern T1, return a superset of the triples returned
> by T2". i.e variable names don't matter but they must be consistently
> used if they occur more than once.
>
>     Andy
>
>
> On 05/03/2026 23:53, Justin Dowdy wrote:
> > It seems like one query is "isomorphic" to another query if and only if
> > they produce the same bindings when evaluated against all possible data
> > sets.
> >
> > If you want to do this in the general case you might need a way to
> simulate
> > all possible data sets then evaluate the queries against them, right?
> >
> > On Thu, Mar 5, 2026, 2:37 PM Martynas Jusevičius <[email protected]
> >
> > wrote:
> >
> >> Hi,
> >>
> >> What would be the way to check programatically using Jena that
> >>
> >> { <http://localhost/> rdf:type ?x }
> >>
> >> is a substitution of a binding (?s = <http://localhost/>) or VALUES
> (?s)
> >> { <
> >> http://localhost/> }
> >>
> >> on
> >>
> >> { ?s rdf:type ?type }
> >>
> >> but
> >>
> >> { <http://localhost/> another:property ?x }
> >>
> >> is not?
> >>
> >> Basically check if a query is "isomorphic" to a given query template?
> >>
> >> Claude calls this "pattern containment" or "query subsumption".
> >>
> >>
> >> Martynas
> >>
> >
>
>

Reply via email to