Skip to content

[mirror] fix: SPARQL IN/NOT IN must use value equality, not term equality - #4

Closed
jdsika wants to merge 3 commits into
mainfrom
fix/sparql-in-value-equality
Closed

jdsika wants to merge 3 commits into
mainfrom
fix/sparql-in-value-equality

Conversation

@jdsika

@jdsika jdsika commented Oct 2, 2026 •

Copy link
Copy Markdown

Mirror of RDFLib#3564. Tracking only, do not merge.

Upstream PR: RDFLib#3564

Why this PR exists

Every fix this organisation depends on is written as a self-contained, single-commit
upstream contribution rather than a local patch, so that a fork is only needed for as
long as its contributions are unmerged.

That convention has one weakness: an upstream PR is easy to lose track of, because it
lives in someone else's repository and closes on someone else's schedule. This mirrored PR
is the counterweight: an open PR in our own fork for every contribution still pending
upstream, so the set of things we are carrying is visible from our own repository list.

It is closed when the upstream PR is merged or rejected, not before.

Contents

A single commit, identical in content to the upstream PR head:

fix/sparql-in-value-equality -> main

Where it is used

Not yet consumed: the pipeline pins released rdflib==7.6.0. Relevant to SHACL sh:sparql
constraints that use IN / NOT IN on literals coerced to xsd:string by a JSON-LD
@context, which is how the defect was found.

@jdsika
jdsika force-pushed the fix/sparql-in-value-equality branch from 7b860d5 to 51c9b4f Compare October 2, 2026 17:44
@jdsika
jdsika force-pushed the fix/sparql-in-value-equality branch from 51c9b4f to a41e95b Compare October 14, 2026 12:13
@jdsika jdsika changed the title fix: SPARQL IN/NOT IN must use value equality, not term equality [mirror] fix: SPARQL IN/NOT IN must use value equality, not term equality Oct 14, 2026
RelationalExpression evaluated IN / NOT IN membership with Python `==`, which
is strict RDF term equality, while `=` and `!=` use Identifier.eq, which is
value equality. SPARQL 1.1 17.4.1.9 defines IN as exactly

    (lhs = expression1) || (lhs = expression2) || ...

("The test is done with "=" operator, which tests for the same value"), and
17.4.1.10 defines NOT IN as its negation, so the two must never disagree.

They did. RDF 1.1 treats a simple literal and an xsd:string-typed literal as
the same term, and XSD type promotion makes 1 and 1.0 equal in value space,
so these evaluated incorrectly while the equivalent `=` comparisons were
correct:

    "x"^^xsd:string IN ("x")                  false, should be true
    1 IN (1.0)                                false, should be true
    2 IN (<http://example/iri>, "str", 2.0)   false, spec example says true

FILTER(?v NOT IN (...)) therefore admitted values that are in the list. It was
found through a SHACL sh:sparql constraint whose data reached the query as
xsd:string-typed literals while the shape listed plain ones.

Membership now uses Identifier.eq, with the error handling `=` already has: a
NotImplemented result (truthy, and a TypeError in a boolean context on Python
3.14) and a comparison TypeError are recorded as errors. Errors follow
17.4.1.9, "Errors in comparisons cause the IN expression to raise an error if
the RDF term being tested is not found elsewhere in the list", and now do so
uniformly: an erroring list entry is an error for an IRI on the left as well
as for a literal, and an erroring left-hand side is an error rather than a
result.

    2 NOT IN (1/0)          true -> error
    <urn:x> NOT IN (1/0)    true -> error
    (1/0) NOT IN (2)        true -> error

With rdflib.DAWG_LITERAL_COLLATION set, `=` raises a type error for literals
that share no value space (RDFterm-equal, 17.4.1.7), and IN now does too.

The W3C SPARQL 1.0 and 1.1 test suites give the same results before and
after.
@jdsika
jdsika force-pushed the fix/sparql-in-value-equality branch from a41e95b to 7084ad3 Compare October 14, 2026 12:54
@jdsika

jdsika commented Oct 16, 2026

Copy link
Copy Markdown
Author

merged upstream

@jdsika jdsika closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants