Skip to content

Commit 61b5fed

Browse files
author
James Leigh
committed
Fix #79: Use null binding for unbound variable solutions
Signed-off-by: James Leigh <james.leigh@ontotext.com>
1 parent d5e7815 commit 61b5fed

6 files changed

Lines changed: 77 additions & 5 deletions

File tree

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/QueryBindingSet.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ public void setBinding(Binding binding) {
8686
}
8787

8888
public void setBinding(String name, Value value) {
89-
assert value != null : "null value for variable " + name;
9089
bindings.put(name, value);
9190
}
9291

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/StrictEvaluationStrategy.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(Service
348348
if (serviceRef.hasValue())
349349
serviceUri = serviceRef.getValue().stringValue();
350350
else {
351-
if (bindings != null && bindings.hasBinding(serviceRef.getName())) {
351+
if (bindings != null && bindings.getValue(serviceRef.getName()) != null) {
352352
serviceUri = bindings.getBinding(serviceRef.getName()).getValue().stringValue();
353353
}
354354
else {
@@ -469,6 +469,13 @@ public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(Stateme
469469
CloseableIteration<? extends Statement, QueryEvaluationException> stIter3 = null;
470470
ConvertingIteration<Statement, BindingSet, QueryEvaluationException> result = null;
471471

472+
if (isUnbound(subjVar, bindings) || isUnbound(predVar, bindings) || isUnbound(objVar, bindings)
473+
|| isUnbound(conVar, bindings))
474+
{
475+
// the variable must remain unbound for this solution see https://www.w3.org/TR/sparql11-query/#assignment
476+
return new EmptyIteration<BindingSet, QueryEvaluationException>();
477+
}
478+
472479
boolean allGood = false;
473480
try {
474481
try {
@@ -653,6 +660,15 @@ protected BindingSet convert(Statement st) {
653660
}
654661
}
655662

663+
protected boolean isUnbound(Var var, BindingSet bindings) {
664+
if (var == null) {
665+
return false;
666+
}
667+
else {
668+
return bindings.hasBinding(var.getName()) && bindings.getValue(var.getName()) == null;
669+
}
670+
}
671+
656672
protected Value getVarValue(Var var, BindingSet bindings) {
657673
if (var == null) {
658674
return null;

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/ExtensionIterator.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ public BindingSet convert(BindingSet sourceBindings)
5757
}
5858
catch (ValueExprEvaluationException e) {
5959
// silently ignore type errors in extension arguments. They should not cause the
60-
// query to fail but just result in no additional binding.
60+
// query to fail but result in no bindings for this solution
61+
// see https://www.w3.org/TR/sparql11-query/#assignment
62+
// use null as place holder for unbound variables that must remain so
63+
targetBindings.setBinding(extElem.getName(), null);
6164
}
6265
}
6366
}

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/iterator/PathIteration.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,11 @@ private void createIteration()
261261
throws QueryEvaluationException
262262
{
263263

264-
if (currentLength == 0L) {
264+
if (isUnbound(startVar, bindings) || isUnbound(endVar, bindings)) {
265+
// the variable must remain unbound for this solution see https://www.w3.org/TR/sparql11-query/#assignment
266+
currentIter = new EmptyIteration<BindingSet, QueryEvaluationException>();
267+
}
268+
else if (currentLength == 0L) {
265269
ZeroLengthPath zlp = new ZeroLengthPath(scope, startVar, endVar, contextVar);
266270
currentIter = this.evaluationStrategyImpl.evaluate(zlp, bindings);
267271
currentLength++;
@@ -334,6 +338,15 @@ else if (currentLength == 1) {
334338
}
335339
}
336340

341+
protected boolean isUnbound(Var var, BindingSet bindings) {
342+
if (var == null) {
343+
return false;
344+
}
345+
else {
346+
return bindings.hasBinding(var.getName()) && bindings.getValue(var.getName()) == null;
347+
}
348+
}
349+
337350
protected Set<ValuePair> makeSet() {
338351
return new HashSet<ValuePair>(64, 0.9f);
339352
}

core/repository/sparql/src/main/java/org/eclipse/rdf4j/repository/sparql/query/SPARQLQueryBindingSet.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ public void setBinding(Binding binding) {
8888
}
8989

9090
public void setBinding(String name, Value value) {
91-
assert value != null : "null value for variable " + name;
9291
bindings.put(name, value);
9392
}
9493

testsuites/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/ComplexSPARQLQueryTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,6 +2333,48 @@ public void testSES1979MinMaxInf()
23332333

23342334
}
23352335

2336+
@Test
2337+
public void testSES2250BindErrors() throws Exception {
2338+
StringBuilder ub = new StringBuilder();
2339+
ub.append("insert data { <urn:test:subj> <urn:test:pred> _:blank }");
2340+
2341+
conn.prepareUpdate(QueryLanguage.SPARQL, ub.toString()).execute();
2342+
2343+
StringBuilder qb = new StringBuilder();
2344+
qb.append("SELECT * {\n"
2345+
+ " ?s1 ?p1 ?blank . "
2346+
+ " FILTER(isBlank(?blank))"
2347+
+ " BIND (iri(?blank) as ?biri)"
2348+
+ " ?biri ?p2 ?o2 ."
2349+
+ "}");
2350+
2351+
TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, qb.toString());
2352+
try (TupleQueryResult evaluate = tq.evaluate();) {
2353+
assertFalse("The query should not return a result", evaluate.hasNext());
2354+
}
2355+
}
2356+
2357+
@Test
2358+
public void testSES2250BindErrorsInPath() throws Exception {
2359+
StringBuilder ub = new StringBuilder();
2360+
ub.append("insert data { <urn:test:subj> <urn:test:pred> _:blank }");
2361+
2362+
conn.prepareUpdate(QueryLanguage.SPARQL, ub.toString()).execute();
2363+
2364+
StringBuilder qb = new StringBuilder();
2365+
qb.append("SELECT * {\n"
2366+
+ " ?s1 ?p1 ?blank . "
2367+
+ " FILTER(isBlank(?blank))"
2368+
+ " BIND (iri(?blank) as ?biri)"
2369+
+ " ?biri <urn:test:pred>* ?o2 ."
2370+
+ "}");
2371+
2372+
TupleQuery tq = conn.prepareTupleQuery(QueryLanguage.SPARQL, qb.toString());
2373+
try (TupleQueryResult evaluate = tq.evaluate();) {
2374+
assertFalse("The query should not return a result", evaluate.hasNext());
2375+
}
2376+
}
2377+
23362378
private boolean containsSolution(List<BindingSet> result, Binding... solution) {
23372379
final MapBindingSet bs = new MapBindingSet();
23382380
for (Binding b : solution) {

0 commit comments

Comments
 (0)