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 dbc8e50412..94bccbfa04 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 @@ -333,7 +333,7 @@ protected Set performSourceSelection(FedXArbitraryLengthPath pathExpr, identifiedMembers = new HashSet<>(members); } else { StatementPattern checkStmt = new StatementPattern(stmt.getScope(), new Var("subject"), - stmt.getPredicateVar().clone(), new Var("object"), stmt.getContextVar()); + clone(stmt.getPredicateVar()), new Var("object"), clone(stmt.getContextVar())); @SuppressWarnings("unused") // only used as artificial parent HolderNode holderParent = new HolderNode(checkStmt); @@ -364,6 +364,13 @@ protected Set performSourceSelection(FedXArbitraryLengthPath pathExpr, return identifiedMembers; } + private Var clone(Var var) { + if (var == null) { + return null; + } + return var.clone(); + } + protected void optimizeJoinOrder(TupleExpr query, QueryInfo queryInfo, GenericInfoOptimizer info) { // optimize statement groups and join order new StatementGroupAndJoinOptimizer(queryInfo, DefaultFedXCostModel.INSTANCE).optimize(query); diff --git a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/PropertyPathTests.java b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/PropertyPathTests.java index 041374bb01..fc83c2bf10 100644 --- a/tools/federation/src/test/java/org/eclipse/rdf4j/federated/PropertyPathTests.java +++ b/tools/federation/src/test/java/org/eclipse/rdf4j/federated/PropertyPathTests.java @@ -301,4 +301,52 @@ public void testZeroLengthPath_length2_crossRepository() throws Exception { } + @Test + public void testPropertyPath_sourceSelection_crossRepository() throws Exception { + + prepareTest(Arrays.asList("/tests/basic/data_emptyStore.ttl", "/tests/basic/data_emptyStore.ttl")); + + Repository repo1 = getRepository(1); + Repository repo2 = getRepository(2); + + try (RepositoryConnection con = repo1.getConnection()) { + con.add(Values.iri("http://example.org/A"), RDFS.SUBCLASSOF, Values.iri("http://example.org/B"), + Values.iri("http://example.org/graph1")); + } + + try (RepositoryConnection con = repo2.getConnection()) { + con.add(Values.iri("http://example.org/B"), RDFS.SUBCLASSOF, Values.iri("http://example.org/C"), + Values.iri("http://example.org/graph2")); + } + + Repository fedxRepo = fedxRule.getRepository(); + + // 1a: bound (matching) object + try (RepositoryConnection con = fedxRepo.getConnection()) { + TupleQuery tupleQuery = con.prepareTupleQuery( + "PREFIX rdfs: " + + "SELECT * WHERE { " + + " ?subClass (rdfs:subClassOf+) . " + + " } " + ); + + Assertions.assertEquals(2, QueryResults.asSet(tupleQuery.evaluate()).size()); + } + + // 1b: with named graph + try (RepositoryConnection con = fedxRepo.getConnection()) { + TupleQuery tupleQuery = con.prepareTupleQuery( + "PREFIX rdfs: " + + "SELECT * WHERE { " + + " GRAPH {" + + " ?subClass (rdfs:subClassOf+) . " + + " }" + + "} " + ); + + Assertions.assertEquals(1, QueryResults.asSet(tupleQuery.evaluate()).size()); + } + + } + }