History | Log In     View a printable version of the current page. Get help!  
Issue Details [XML]

Key: SES-677
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Arjohn Kampman
Reporter: Ken Wenzel
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Sesame

SPARQL ASK with UNION always returns true when executed against a federation

Created: 04/Aug/09 04:54 PM   Updated: 19/Feb/10 07:11 PM
Component/s: None
Affects Version/s: None
Fix Version/s: 3.0


 Description   
If an ASK query containing the UNION operator is executed against a federation then it always evaluates to true.

Below is an example that may be used to reproduce this behavior.

// the federation
Repository repository;

RepositoryConnection conn = repository.getConnection();
try {
BooleanQuery query = conn
.prepareBooleanQuery(
QueryLanguage.SPARQL,
"PREFIX example: <http://www.example.org/test#> "
+ "ASK {{example:s1 example:p1 example:o1} UNION {example:s2 example:p2 example:o2}}");

if (query.ask()) {
throw new Exception(
"Test failed - expected FALSE as return value");
}

} finally {
conn.close();
}

 All   Comments   Change History      Sort Order:
Comment by Ken Wenzel [04/Aug/09 05:16 PM]
I've also discovered that a SELECT query doesn't work either.

PREFIX example: <http://www.example.org/test#> select ?s {{?s example:p1 example:o1} UNION {?s example:p2 example:o2}}

The query returns one result where "s" is <null> instead of an empty result set.

Comment by Ken Wenzel [20/Aug/09 01:31 PM]
I did a bit of investigation on this issue.

FederationConnection.optimize(query, ...) applies EmptyPatternOptimizer which replaces statement patterns by empty sets if they do not match any triples.

A given query AST Union (SP1 SP2) is replaced by Union (EmptySet EmptySet) if SP1 and SP2 do not match any triples.

QueryModelPruner then transforms Union (EmptySet EmptySet) to SingletonSet. The method meet(Union union) removes each union argument that is an EmptySet but does not check if union is empty afterwards. Hence the node Union is replaced by SingletonSet.

Proposed fix for QueryModelPruner.meet() is:
@Override
public void meet(Union union) {
super.meet(union);

for (TupleExpr arg : union.getArgs()) {
if (arg instanceof EmptySet) {
union.removeArg(arg);
}
}

int numberOfArguments = union.getNumberOfArguments();
if (numberOfArguments == 0) {
union.replaceWith(new EmptySet());
} else if (numberOfArguments == 1) {
union.replaceWith(union.getArg(0));
}
else {
for (TupleExpr arg : union.getArgs()) {
if (!(arg instanceof SingletonSet)) {
return;
}
}
union.replaceWith(new SingletonSet());
}
}

FederationQueryTest can be extended by the following test:
public void testUnionUnsatisfiable()
throws Exception
{
assertQuery("{ {?person a:nonExistentProperty ?child} UNION {?person a:nonExistentProperty one:teacher}}");
}

Patches are below:

### Eclipse Workspace Patch 1.0
#P sesame-store-compliance
Index: src/test/java/org/openrdf/sail/federation/FederationQueryTest.java
===================================================================
--- src/test/java/org/openrdf/sail/federation/FederationQueryTest.java (revision 9594)
+++ src/test/java/org/openrdf/sail/federation/FederationQueryTest.java (working copy)
@@ -165,6 +165,12 @@
  {
  assertQuery("{ {?person a:parentOf ?child} UNION {?person c:job one:teacher}}");
  }
+
+ public void testUnionUnsatisfiable()
+ throws Exception
+ {
+ assertQuery("{ {?person a:nonExistentProperty ?child} UNION {?person a:nonExistentProperty one:teacher}}");
+ }
 
  private void assertQuery(String qry)
  throws Exception
#P sesame-queryalgebra-evaluation
Index: src/main/java/org/openrdf/query/algebra/evaluation/impl/QueryModelPruner.java
===================================================================
--- src/main/java/org/openrdf/query/algebra/evaluation/impl/QueryModelPruner.java (revision 9594)
+++ src/main/java/org/openrdf/query/algebra/evaluation/impl/QueryModelPruner.java (working copy)
@@ -108,7 +108,10 @@
  }
  }
 
- if (union.getNumberOfArguments() == 1) {
+ int numberOfArguments = union.getNumberOfArguments();
+ if (numberOfArguments == 0) {
+ union.replaceWith(new EmptySet());
+ } else if (numberOfArguments == 1) {
  union.replaceWith(union.getArg(0));
  }
  else {

Comment by Arjohn Kampman [21/Aug/09 10:48 AM]
Patch has been applied. Also, similar "corner case"(?) issues with Join and Intersection have been fixed.

Comment by Arjohn Kampman [21/Aug/09 02:16 PM]
Fix has been verified by Ken Wenzel.