Version
jena 6.1.0
What happened?
This issue is similar to the closed issue at #3659
Jena ARQ rejects an invalid triple-term predicate, but not an invalid triple-term subject
Jena ARQ generally rejects inserting an invalid triple into the graph. Per RDF 1.2 (ttSubject ::= iri | BlankNode, verb ::= iri), a triple term's subject must be an IRI or blank node and its predicate must be an IRI - never a Literal in either position. ARQ correctly enforces this for predicate - inserting a triple term with a Literal predicate is rejected outright, at parse time, with an HTTP 400 syntax error (from Fuseki, the HTTP surface used to reach it here). It does not enforce the identical rule for subject - the same shape with a Literal subject is accepted and written to the graph.
Reproduction (pure Python standard library, no dependencies beyond a running Fuseki endpoint at localhost:3030 with an in-memory dataset named repro - create with: docker run -d --name fuseki-repro -p 3030:3030 atomgraph/fuseki:latest --update --mem --ping /repro):
import json
import urllib.parse
import urllib.request
import urllib.error
BASE = "http://localhost:3030/repro"
def sparql_update(update):
req = urllib.request.Request(
f"{BASE}/update",
data=urllib.parse.urlencode({"update": update}).encode(),
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
urllib.request.urlopen(req).read()
def sparql_query(query):
url = f"{BASE}/query?" + urllib.parse.urlencode({"query": query})
req = urllib.request.Request(url, headers={"Accept": "application/sparql-results+json"})
return json.load(urllib.request.urlopen(req))
sparql_update("CLEAR ALL")
# Invalid Literal in PREDICATE position of a triple term
try:
sparql_update('PREFIX : <http://example/> INSERT DATA { :claim :about <<( :s "bad predicate" :o )>> . }')
print("predicate case: accepted (unexpected)")
except urllib.error.HTTPError as e:
print(f"predicate case: rejected, HTTP {e.code}")
# Invalid Literal in SUBJECT position of a triple term
try:
sparql_update('PREFIX : <http://example/> INSERT DATA { :claim :about <<( "bad subject" :p :o )>> . }')
print("subject case: accepted (unexpected bug)")
except urllib.error.HTTPError as e:
print(f"subject case: rejected, HTTP {e.code}")
# Query out everything actually stored
result = sparql_query("SELECT * { ?s ?p ?o }")
print("stored triples:")
for row in result["results"]["bindings"]:
print(" ", row)
Output:
predicate case: rejected, HTTP 400
subject case: accepted (unexpected bug)
stored triples:
{'s': {'type': 'uri', 'value': 'http://example/claim'}, 'p': {'type': 'uri', 'value': 'http://example/about'}, 'o': {'type': 'triple', 'value': {'subject': {'type': 'literal', 'value': 'bad subject'}, 'predicate': {'type': 'uri', 'value': 'http://example/p'}, 'object': {'type': 'uri', 'value': 'http://example/o'}}}}
The predicate case is rejected before the triple ever reaches storage - ARQ's own SPARQL parser has no grammar production for a Literal there at all (Encountered "bad predicate" ... Was expecting <IRIref> ...). The subject case is accepted, and the stored triple's object (o) is <<( "bad subject" :p :o )>> - a triple term with a Literal subject, not a valid RDF 1.2 term under any reading of the grammar. This is real, persisted data corruption, not a query-time artifact: any later read of this graph, by any client, gets this invalid term back.
Oxigraph, a second independent RDF 1.2 engine, correctly rejects both cases (HTTP 400 either way, nothing written) on the identical update text - confirming this is a genuine ARQ bug, not an ambiguous spec reading.
Also confirmed via TRIPLE() (the SPARQL function, as opposed to inserting the literal <<( )>> syntax directly): TRIPLE(:s, "bad predicate", :o) correctly leaves its result unbound, while TRIPLE("bad subject", :p, :o) wrongly binds one - so this isn't specific to the INSERT DATA parser path, the same asymmetry holds for TRIPLE()'s own evaluation-time validation too.
Relevant output and stacktrace
Are you interested in making a pull request?
None
Version
jena 6.1.0
What happened?
This issue is similar to the closed issue at #3659
Jena ARQ rejects an invalid triple-term predicate, but not an invalid triple-term subject
Jena ARQ generally rejects inserting an invalid triple into the graph. Per RDF 1.2 (
ttSubject ::= iri | BlankNode,verb ::= iri), a triple term'ssubjectmust be an IRI or blank node and itspredicatemust be an IRI - never a Literal in either position. ARQ correctly enforces this forpredicate- inserting a triple term with a Literal predicate is rejected outright, at parse time, with an HTTP 400 syntax error (from Fuseki, the HTTP surface used to reach it here). It does not enforce the identical rule forsubject- the same shape with a Literal subject is accepted and written to the graph.Reproduction (pure Python standard library, no dependencies beyond a running Fuseki endpoint at
localhost:3030with an in-memory dataset namedrepro- create with:docker run -d --name fuseki-repro -p 3030:3030 atomgraph/fuseki:latest --update --mem --ping /repro):Output:
The predicate case is rejected before the triple ever reaches storage - ARQ's own SPARQL parser has no grammar production for a Literal there at all (
Encountered "bad predicate" ... Was expecting <IRIref> ...). The subject case is accepted, and the stored triple's object (o) is<<( "bad subject" :p :o )>>- a triple term with a Literal subject, not a valid RDF 1.2 term under any reading of the grammar. This is real, persisted data corruption, not a query-time artifact: any later read of this graph, by any client, gets this invalid term back.Oxigraph, a second independent RDF 1.2 engine, correctly rejects both cases (HTTP 400 either way, nothing written) on the identical update text - confirming this is a genuine ARQ bug, not an ambiguous spec reading.
Also confirmed via
TRIPLE()(the SPARQL function, as opposed to inserting the literal<<( )>>syntax directly):TRIPLE(:s, "bad predicate", :o)correctly leaves its result unbound, whileTRIPLE("bad subject", :p, :o)wrongly binds one - so this isn't specific to theINSERT DATAparser path, the same asymmetry holds forTRIPLE()'s own evaluation-time validation too.Relevant output and stacktrace
Are you interested in making a pull request?
None