Skip to content

Commit 99d7ba3

Browse files
committed
GH-5227: fix binding assigner optimizer in FedX
The federation optimizer was missing to execute the binding assigner (which injects external bindings into the statement pattern). The consequence was potentially incorrect results (due to source source selection with partial knowledge) as well as sub-optimal source selection Issue is covered with a unit test, which is failing in two places prior to this change.
1 parent fbf6bea commit 99d7ba3

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
import org.eclipse.rdf4j.query.algebra.ValueExpr;
108108
import org.eclipse.rdf4j.query.algebra.Var;
109109
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
110+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
110111
import org.eclipse.rdf4j.query.algebra.evaluation.QueryValueEvaluationStep;
111112
import org.eclipse.rdf4j.query.algebra.evaluation.ValueExprEvaluationException;
112113
import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedService;
@@ -118,6 +119,7 @@
118119
import org.eclipse.rdf4j.query.algebra.evaluation.iterator.HashJoinIteration;
119120
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.ConstantOptimizer;
120121
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.DisjunctiveConstraintOptimizer;
122+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.StandardQueryOptimizerPipeline;
121123
import org.eclipse.rdf4j.query.algebra.evaluation.util.QueryEvaluationUtil;
122124
import org.eclipse.rdf4j.query.algebra.helpers.TupleExprs;
123125
import org.eclipse.rdf4j.query.algebra.helpers.collectors.VarNameCollector;
@@ -147,6 +149,14 @@ public abstract class FederationEvalStrategy extends StrictEvaluationStrategy {
147149

148150
protected FederationContext federationContext;
149151

152+
/**
153+
* List of standard {@link QueryOptimizer}s applicable to federation
154+
*/
155+
private static final List<QueryOptimizer> standardOptimizers = List.of(
156+
StandardQueryOptimizerPipeline.BINDING_ASSIGNER,
157+
StandardQueryOptimizerPipeline.BINDING_SET_ASSIGNMENT_INLINER,
158+
StandardQueryOptimizerPipeline.DISJUNCTIVE_CONSTRAINT_OPTIMIZER);
159+
150160
public FederationEvalStrategy(FederationContext federationContext) {
151161
super(new org.eclipse.rdf4j.query.algebra.evaluation.TripleSource() {
152162

@@ -209,9 +219,11 @@ public TupleExpr optimize(TupleExpr expr, EvaluationStatistics evaluationStatist
209219
}
210220

211221
/* original RDF4J optimizers */
212-
new ConstantOptimizer(this).optimize(query, dataset, bindings); // maybe remove this optimizer later
222+
for (QueryOptimizer optimizer : standardOptimizers) {
223+
optimizer.optimize(query, dataset, bindings);
224+
}
213225

214-
new DisjunctiveConstraintOptimizer().optimize(query, dataset, bindings);
226+
new ConstantOptimizer(this).optimize(query, dataset, bindings); // maybe remove this optimizer later
215227

216228
/*
217229
* TODO add some generic optimizers: - FILTER ?s=1 && ?s=2 => EmptyResult - Remove variables that are not

tools/federation/src/test/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategyTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,23 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.federated.evaluation;
1212

13+
import java.util.Arrays;
1314
import java.util.List;
15+
import java.util.Set;
16+
import java.util.stream.Collectors;
1417

1518
import org.eclipse.rdf4j.federated.SPARQLBaseTest;
19+
import org.eclipse.rdf4j.federated.cache.SourceSelectionCache;
20+
import org.eclipse.rdf4j.federated.cache.SourceSelectionCache.StatementSourceAssurance;
21+
import org.eclipse.rdf4j.federated.endpoint.Endpoint;
22+
import org.eclipse.rdf4j.federated.structures.SubQuery;
23+
import org.eclipse.rdf4j.model.util.Values;
24+
import org.eclipse.rdf4j.model.vocabulary.FOAF;
25+
import org.eclipse.rdf4j.model.vocabulary.OWL;
26+
import org.eclipse.rdf4j.model.vocabulary.RDF;
27+
import org.eclipse.rdf4j.query.TupleQuery;
28+
import org.eclipse.rdf4j.repository.Repository;
29+
import org.eclipse.rdf4j.repository.RepositoryConnection;
1630
import org.junit.jupiter.api.Assertions;
1731
import org.junit.jupiter.api.Test;
1832

@@ -46,4 +60,50 @@ public void testOptimize_SingleMember_Service() throws Exception {
4660

4761
Assertions.assertTrue(queryPlan.startsWith("QueryRoot"));
4862
}
63+
64+
@Test
65+
public void testSourceSelectionCache_setBindings() throws Exception {
66+
67+
var bob = Values.iri("http://example.com/bob");
68+
69+
List<Endpoint> endpoints = prepareTest(
70+
Arrays.asList("/tests/basic/data_emptyStore.ttl", "/tests/basic/data_emptyStore.ttl"));
71+
72+
Repository repo1 = getRepository(1);
73+
Repository repo2 = getRepository(2);
74+
75+
String repo1Id = endpoints.get(0).getId();
76+
77+
try (RepositoryConnection con = repo1.getConnection()) {
78+
con.add(bob, RDF.TYPE, FOAF.PERSON);
79+
}
80+
81+
try (RepositoryConnection con = repo2.getConnection()) {
82+
con.add(FOAF.PERSON, RDF.TYPE, OWL.CLASS);
83+
}
84+
85+
Repository fedxRepo = fedxRule.getRepository();
86+
87+
fedxRule.enableDebug();
88+
89+
try (var conn = fedxRepo.getConnection()) {
90+
91+
TupleQuery tq = conn.prepareTupleQuery("SELECT * WHERE { ?s a ?type }");
92+
tq.setBinding("s", bob);
93+
94+
try (var tqr = tq.evaluate()) {
95+
// just consume the result
96+
Assertions.assertEquals(Set.of(FOAF.PERSON),
97+
tqr.stream().map(bs -> bs.getValue("type")).collect(Collectors.toSet()));
98+
}
99+
}
100+
101+
SourceSelectionCache cache = federationContext().getSourceSelectionCache();
102+
103+
var assurance = cache.getAssurance(new SubQuery(bob, RDF.TYPE, null),
104+
federationContext().getEndpointManager().getEndpoint(repo1Id));
105+
106+
// we expect that the source selection cache can assure statements
107+
Assertions.assertEquals(StatementSourceAssurance.HAS_REMOTE_STATEMENTS, assurance);
108+
}
49109
}

0 commit comments

Comments
 (0)