Skip to content

Commit 8a3a131

Browse files
committed
fix for failing tests
1 parent 6df1ac1 commit 8a3a131

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,11 +603,31 @@ protected QueryEvaluationStep prepare(Slice node, QueryEvaluationContext context
603603
protected QueryEvaluationStep prepare(Extension node, QueryEvaluationContext context)
604604
throws QueryEvaluationException {
605605
QueryEvaluationStep arg = precompile(node.getArg(), context);
606+
boolean setNullOnError = !isWithinMinusRightArg(node);
606607
Consumer<MutableBindingSet> consumer = ExtensionIterator.buildLambdaToEvaluateTheExpressions(node, this,
607-
context);
608+
context, setNullOnError);
608609
return new ExtensionQueryEvaluationStep(arg, consumer, context);
609610
}
610611

612+
private static boolean isWithinMinusRightArg(QueryModelNode node) {
613+
QueryModelNode child = node;
614+
QueryModelNode parent = node.getParentNode();
615+
while (parent != null) {
616+
if (parent instanceof Difference) {
617+
Difference diff = (Difference) parent;
618+
if (diff.getRightArg() == child) {
619+
return true;
620+
}
621+
if (diff.getLeftArg() == child) {
622+
return false;
623+
}
624+
}
625+
child = parent;
626+
parent = parent.getParentNode();
627+
}
628+
return false;
629+
}
630+
611631
protected QueryEvaluationStep prepare(Service service, QueryEvaluationContext context)
612632
throws QueryEvaluationException {
613633
Var serviceRef = service.getServiceRef();
@@ -1321,7 +1341,18 @@ protected QueryValueEvaluationStep prepare(CompareAll node, QueryEvaluationConte
13211341

13221342
protected QueryValueEvaluationStep prepare(Exists node, QueryEvaluationContext context)
13231343
throws QueryEvaluationException {
1324-
QueryEvaluationStep subquery = precompile(node.getSubQuery(), context);
1344+
// Optimization/semantic shortcut: EXISTS { OPTIONAL { ... } } is always true.
1345+
// In algebra, OPTIONAL is a LeftJoin. With a SingletonSet left-arg, LeftJoin
1346+
// always yields at least the input binding set. Therefore EXISTS evaluates to TRUE.
1347+
TupleExpr subQuery = node.getSubQuery();
1348+
if (subQuery instanceof LeftJoin) {
1349+
LeftJoin leftJoin = (LeftJoin) subQuery;
1350+
if (leftJoin.getLeftArg() instanceof SingletonSet) {
1351+
return bindings -> BooleanLiteral.TRUE;
1352+
}
1353+
}
1354+
1355+
QueryEvaluationStep subquery = precompile(subQuery, context);
13251356
return new ExistsQueryValueEvaluationStep(subquery);
13261357
}
13271358

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public ExtensionIterator(Extension extension, CloseableIteration<BindingSet> ite
3737
EvaluationStrategy strategy, QueryEvaluationContext context) throws QueryEvaluationException {
3838
super(iter);
3939
this.context = context;
40-
this.setter = buildLambdaToEvaluateTheExpressions(extension, strategy, context);
40+
this.setter = buildLambdaToEvaluateTheExpressions(extension, strategy, context, true);
4141
}
4242

4343
public ExtensionIterator(CloseableIteration<BindingSet> iter,
@@ -48,14 +48,16 @@ public ExtensionIterator(CloseableIteration<BindingSet> iter,
4848
}
4949

5050
public static Consumer<MutableBindingSet> buildLambdaToEvaluateTheExpressions(Extension extension,
51-
EvaluationStrategy strategy, QueryEvaluationContext context) {
51+
EvaluationStrategy strategy, QueryEvaluationContext context, boolean setNullOnError) {
5252
Consumer<MutableBindingSet> consumer = null;
5353
for (ExtensionElem extElem : extension.getElements()) {
5454
ValueExpr expr = extElem.getExpr();
5555
if (!(expr instanceof AggregateOperator)) {
5656
QueryValueEvaluationStep prepared = strategy.precompile(extElem.getExpr(), context);
5757
BiConsumer<Value, MutableBindingSet> setBinding = context.setBinding(extElem.getName());
58-
consumer = andThen(consumer, targetBindings -> setValue(setBinding, prepared, targetBindings));
58+
boolean setNullOnErrorLocal = setNullOnError;
59+
consumer = andThen(consumer,
60+
targetBindings -> setValue(setBinding, prepared, targetBindings, setNullOnErrorLocal));
5961
}
6062
}
6163
if (consumer == null) {
@@ -67,7 +69,7 @@ public static Consumer<MutableBindingSet> buildLambdaToEvaluateTheExpressions(Ex
6769
}
6870

6971
private static void setValue(BiConsumer<Value, MutableBindingSet> setBinding, QueryValueEvaluationStep prepared,
70-
MutableBindingSet targetBindings) {
72+
MutableBindingSet targetBindings, boolean setNullOnError) {
7173
try {
7274
// we evaluate each extension element over the targetbindings, so that bindings from
7375
// a previous extension element in this same extension can be used by other extension elements.
@@ -79,11 +81,13 @@ private static void setValue(BiConsumer<Value, MutableBindingSet> setBinding, Qu
7981
setBinding.accept(targetValue, targetBindings);
8082
}
8183
} catch (ValueExprEvaluationException e) {
82-
// silently ignore type errors in extension arguments. They should not cause the
83-
// query to fail but result in no bindings for this solution
84-
// see https://www.w3.org/TR/sparql11-query/#assignment
85-
// use null as place holder for unbound variables that must remain so
86-
setBinding.accept(null, targetBindings);
84+
// Silently ignore type errors in extension arguments. Whether to install a
85+
// placeholder null-binding (variable must remain unbound) or leave it unset
86+
// depends on context. By default we set a null-binding to enforce the SPARQL
87+
// assignment rule; in special contexts (e.g., MINUS RHS), we may skip it.
88+
if (setNullOnError) {
89+
setBinding.accept(null, targetBindings);
90+
}
8791
}
8892
}
8993

0 commit comments

Comments
 (0)