diff --git a/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/RepositoryFederatedServiceIntegrationTest.java b/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/RepositoryFederatedServiceIntegrationTest.java index 763d3c2a6d2..3cc47bdc6e9 100644 --- a/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/RepositoryFederatedServiceIntegrationTest.java +++ b/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/RepositoryFederatedServiceIntegrationTest.java @@ -340,6 +340,16 @@ public void test10_consumePartially() { } } + @Test + public void test_ValuesClause() { + + addData(serviceRepo, Lists.newArrayList(vf.createStatement(iri("s1"), RDFS.LABEL, l("val1")))); + + String query = "SELECT ?var WHERE { SERVICE { VALUES ?var { 'val1' 'val2' } ?s ?p ?var } }"; + + assertResultEquals(evaluateQuery(query), "var", Lists.newArrayList(l("val1"))); + } + private void addData(Repository repo, Collection m) { try (RepositoryConnection conn = repo.getConnection()) { conn.add(m); diff --git a/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/SparqlFederatedServiceIntegrationTest.java b/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/SparqlFederatedServiceIntegrationTest.java new file mode 100644 index 00000000000..9d72227fc7c --- /dev/null +++ b/compliance/repository/src/test/java/org/eclipse/rdf4j/repository/sparql/federation/SparqlFederatedServiceIntegrationTest.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + *******************************************************************************/ +package org.eclipse.rdf4j.repository.sparql.federation; + +import org.eclipse.rdf4j.repository.Repository; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class SparqlFederatedServiceIntegrationTest { + + @Test + @Disabled("manual test to demonstrate the original issue of GH-5358") + public void testValues_Wikidata() { + Repository repo = new SailRepository(new MemoryStore()); + try (var conn = repo.getConnection()) { + + var tq = conn.prepareTupleQuery("PREFIX rdf: \n" + + "PREFIX sh: \n" + + "SELECT * WHERE {\n" + + " SERVICE {\n" + + " VALUES ?resource {\n" + + " \n" + + " }\n" + + " ?resource ?website\n" + + " }\n" + + "}"); + + try (var tqr = tq.evaluate()) { + tqr.stream().forEach(bs -> System.out.println(bs)); + } + } + } +} diff --git a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/optimizer/BindingSetAssignmentInlinerOptimizer.java b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/optimizer/BindingSetAssignmentInlinerOptimizer.java index 2d6c903d105..f12e91da8cd 100644 --- a/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/optimizer/BindingSetAssignmentInlinerOptimizer.java +++ b/core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/optimizer/BindingSetAssignmentInlinerOptimizer.java @@ -20,6 +20,7 @@ import org.eclipse.rdf4j.query.algebra.Filter; import org.eclipse.rdf4j.query.algebra.LeftJoin; import org.eclipse.rdf4j.query.algebra.QueryModelNode; +import org.eclipse.rdf4j.query.algebra.Service; import org.eclipse.rdf4j.query.algebra.TupleExpr; import org.eclipse.rdf4j.query.algebra.Var; import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer; @@ -54,6 +55,11 @@ public void meet(BindingSetAssignment bsa) { super.meet(bsa); } + @Override + public void meet(Service node) throws RuntimeException { + // do not inject bindings inside service clauses + } + @Override public void meet(Var var) { if (bindingSet != null && bindingSet.hasBinding(var.getName())) { diff --git a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java index d2de3db28d1..1b8e5f3088f 100644 --- a/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java +++ b/core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/BindingSetAssignmentInlinerTest.java @@ -26,6 +26,7 @@ import org.eclipse.rdf4j.query.algebra.Not; import org.eclipse.rdf4j.query.algebra.Projection; import org.eclipse.rdf4j.query.algebra.QueryRoot; +import org.eclipse.rdf4j.query.algebra.Service; import org.eclipse.rdf4j.query.algebra.StatementPattern; import org.eclipse.rdf4j.query.algebra.TupleExpr; import org.eclipse.rdf4j.query.algebra.Union; @@ -311,6 +312,31 @@ public void testOptimize_Minus() { assertThat(sp.getSubjectVar().getValue()).isNull(); } + @Test + public void testServiceClause() { + String query = "SELECT * WHERE { SERVICE { VALUES ?s1 { } ?s1 ?p1 ?o1 } }"; + + ParsedTupleQuery parsedQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SPARQL, query, null); + + QueryOptimizer optimizer = getOptimizer(); + optimizer.optimize(parsedQuery.getTupleExpr(), new SimpleDataset(), EmptyBindingSet.getInstance()); + + TupleExpr optimizedTreeRoot = parsedQuery.getTupleExpr(); + TupleExpr optimizedTree = ((QueryRoot) optimizedTreeRoot).getArg(); + + assertThat(optimizedTree).isInstanceOf(Projection.class); + + Projection projection = (Projection) optimizedTree; + + Service service = (Service) projection.getArg(); + + Join join = (Join) service.getArg(); + Var s1 = ((StatementPattern) join.getRightArg()).getSubjectVar(); + assertThat(s1.getName()).isEqualTo("s1"); + assertThat(s1.hasValue()).isFalse(); + + } + @Override public QueryOptimizer getOptimizer() { return new BindingSetAssignmentInlinerOptimizer();