From 7f26b87b1d1bbdb87f69256b4a46bd6a0cac148d Mon Sep 17 00:00:00 2001 From: Andreas Schwarte Date: Mon, 6 Apr 2020 01:19:52 +0200 Subject: [PATCH] GH-2006: fix handling of external bindings in FedX SingleSourceQuery (#2067) This change correctly set externally passed binding to single source queries, i.e. where the query is relevant at a single owner (and thus the original query string is passed as-is) Signed-off-by: Andreas Schwarte --- .../evaluation/FederationEvalStrategy.java | 6 ++- .../federated/evaluation/TripleSource.java | 22 ++++++++++ .../evaluation/TripleSourceBase.java | 15 ++++++- .../eclipse/rdf4j/federated/BasicTests.java | 40 +++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java index 5bc46616c18..e47305d0fb9 100644 --- a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java +++ b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/FederationEvalStrategy.java @@ -409,7 +409,8 @@ public CloseableIteration evaluateSingleSo try { Endpoint source = query.getSource(); return source.getTripleSource() - .getStatements(query.getQueryString(), query.getQueryInfo().getQueryType(), query.getQueryInfo()); + .getStatements(query.getQueryString(), bindings, query.getQueryInfo().getQueryType(), + query.getQueryInfo()); } catch (RepositoryException | MalformedQueryException e) { throw new QueryEvaluationException(e); } @@ -689,7 +690,8 @@ protected CloseableIteration evaluateAtSta Endpoint ownedEndpoint = federationContext.getEndpointManager() .getEndpoint(statementSources.get(0).getEndpointID()); org.eclipse.rdf4j.federated.evaluation.TripleSource t = ownedEndpoint.getTripleSource(); - result = t.getStatements(preparedQuery, EmptyBindingSet.getInstance(), null, queryInfo); + result = t.getStatements(preparedQuery, EmptyBindingSet.getInstance(), (FilterValueExpr) null, + queryInfo); } else { diff --git a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSource.java b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSource.java index d504a41a8c4..706f7c9f0aa 100644 --- a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSource.java +++ b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSource.java @@ -80,8 +80,30 @@ public CloseableIteration getStatements(St * @throws RepositoryException * @throws MalformedQueryException * @throws QueryEvaluationException + * @Deprecated will be removed in 4.0. Replaced with + * {@link #getStatements(String, BindingSet, QueryType, QueryInfo)} + */ + @Deprecated + public default CloseableIteration getStatements(String preparedQuery, + QueryType queryType, QueryInfo queryInfo) + throws RepositoryException, MalformedQueryException, QueryEvaluationException { + return getStatements(preparedQuery, EmptyBindingSet.getInstance(), queryType, queryInfo); + } + + /** + * Evaluate a given SPARQL query of the provided query type at the given source. + * + * @param preparedQuery + * @param queryBindings optional query bindings, use {@link EmptyBindingSet} if there are none + * @param queryType + * @param queryInfo + * @return the statements + * @throws RepositoryException + * @throws MalformedQueryException + * @throws QueryEvaluationException */ public CloseableIteration getStatements(String preparedQuery, + BindingSet queryBindings, QueryType queryType, QueryInfo queryInfo) throws RepositoryException, MalformedQueryException, QueryEvaluationException; diff --git a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSourceBase.java b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSourceBase.java index e88aaef138f..4584534acdc 100644 --- a/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSourceBase.java +++ b/tools/federation/src/main/java/org/eclipse/rdf4j/federated/evaluation/TripleSourceBase.java @@ -27,6 +27,7 @@ import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.query.Binding; import org.eclipse.rdf4j.query.BindingSet; import org.eclipse.rdf4j.query.BooleanQuery; import org.eclipse.rdf4j.query.GraphQuery; @@ -59,7 +60,7 @@ public TripleSourceBase(FederationContext federationContext, Endpoint endpoint) @Override public CloseableIteration getStatements( - String preparedQuery, QueryType queryType, QueryInfo queryInfo) + String preparedQuery, BindingSet queryBindings, QueryType queryType, QueryInfo queryInfo) throws RepositoryException, MalformedQueryException, QueryEvaluationException { @@ -68,6 +69,7 @@ public CloseableIteration getStatements( case SELECT: monitorRemoteRequest(); TupleQuery tQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, preparedQuery); + applyBindings(tQuery, queryBindings); applyMaxExecutionTimeUpperBound(tQuery); configureInference(tQuery, queryInfo); resultHolder.set(tQuery.evaluate()); @@ -75,6 +77,7 @@ public CloseableIteration getStatements( case CONSTRUCT: monitorRemoteRequest(); GraphQuery gQuery = conn.prepareGraphQuery(QueryLanguage.SPARQL, preparedQuery); + applyBindings(gQuery, queryBindings); applyMaxExecutionTimeUpperBound(gQuery); configureInference(gQuery, queryInfo); resultHolder.set(new GraphToBindingSetConversionIteration(gQuery.evaluate())); @@ -84,6 +87,7 @@ public CloseableIteration getStatements( boolean hasResults = false; try (RepositoryConnection _conn = conn) { BooleanQuery bQuery = _conn.prepareBooleanQuery(QueryLanguage.SPARQL, preparedQuery); + applyBindings(bQuery, queryBindings); applyMaxExecutionTimeUpperBound(bQuery); configureInference(bQuery, queryInfo); hasResults = bQuery.evaluate(); @@ -96,6 +100,15 @@ public CloseableIteration getStatements( }); } + private void applyBindings(Operation operation, BindingSet queryBindings) { + if (queryBindings == null) { + return; + } + for (Binding b : queryBindings) { + operation.setBinding(b.getName(), b.getValue()); + } + } + @Override public boolean hasStatements(Resource subj, IRI pred, Value obj, QueryInfo queryInfo, Resource... contexts) throws RepositoryException { diff --git a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java index f630f42345b..1c4a917e077 100644 --- a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java +++ b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/BasicTests.java @@ -11,10 +11,14 @@ import java.util.List; import java.util.Set; +import org.eclipse.rdf4j.common.iteration.Iterations; import org.eclipse.rdf4j.federated.endpoint.Endpoint; import org.eclipse.rdf4j.federated.structures.FedXDataset; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.BooleanQuery; +import org.eclipse.rdf4j.query.GraphQuery; +import org.eclipse.rdf4j.query.GraphQueryResult; import org.eclipse.rdf4j.query.QueryResults; import org.eclipse.rdf4j.query.TupleQuery; import org.eclipse.rdf4j.query.TupleQueryResult; @@ -23,6 +27,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import com.google.common.collect.Sets; + public class BasicTests extends SPARQLBaseTest { @Test @@ -207,4 +213,38 @@ public void testBeginTransaction() throws Exception { Repositories.tupleQuery(fedxRule.repository, "SELECT ?person WHERE { ?person ?p 'Alan' }", it -> QueryResults.asList(it)).size()); } + + @Test + public void testSingleSource_SetBinding() throws Exception { + + /* test a single source select query where we set a binding */ + prepareTest(Arrays.asList("/tests/basic/data01endpoint1.ttl", "/tests/basic/data01endpoint2.ttl")); + + try (RepositoryConnection conn = fedxRule.getRepository().getConnection()) { + + // SELECT query + TupleQuery tq = conn + .prepareTupleQuery("SELECT ?person WHERE { ?person ?name }"); + tq.setBinding("name", l("Alan")); + TupleQueryResult tqr = tq.evaluate(); + List res = Iterations.asList(tqr); + assertContainsAll(res, "person", Sets.newHashSet(iri("http://example.org/", "a"))); + + // CONSTRUCT query + GraphQuery gq = conn.prepareGraphQuery( + "CONSTRUCT { ?person ?name } WHERE { ?person ?name }"); + gq.setBinding("name", l("Alan")); + GraphQueryResult gqr = gq.evaluate(); + List stmts = Iterations.asList(gqr); + Assertions.assertEquals(1, stmts.size()); + Assertions.assertEquals(iri("http://example.org/", "a"), stmts.get(0).getSubject()); + + // BOOLEAN query + BooleanQuery bq = conn.prepareBooleanQuery("ASK { ?person ?name }"); + bq.setBinding("name", l("non-existing-name")); + Assertions.assertEquals(false, bq.evaluate()); + + } + + } }