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..a716d415f2e 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 @@ -85,4 +85,5 @@ protected void meetNode(QueryModelNode node) throws RuntimeException { super.meetNode(node); } } + } diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java index 37c1bf3e540..90ec4eecaee 100644 --- a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java @@ -19,7 +19,9 @@ import java.util.List; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.query.algebra.BindingSetAssignment; import org.eclipse.rdf4j.query.algebra.Extension; +import org.eclipse.rdf4j.query.algebra.Join; import org.eclipse.rdf4j.query.algebra.Order; import org.eclipse.rdf4j.query.algebra.Projection; import org.eclipse.rdf4j.query.algebra.Service; @@ -82,6 +84,29 @@ public void testAskQuerySolutionModifiers() { } + @Test + public void testValuesOptionalQuery() throws Exception { + String query = "select ?parent ?child\n" + + "where {\n" + + " values ?type {}\n" + + " ?parent ?type .\n" + + " optional {\n" + + " ?child ?parent ;\n" + + " ?type .\n" + + " }\n" + + "}"; + + TupleExprBuilder builder = new TupleExprBuilder(SimpleValueFactory.getInstance()); + ASTQueryContainer qc = SyntaxTreeBuilder.parseQuery(query); + TupleExpr result = builder.visit(qc, null); + + assertThat(result).isInstanceOf(Projection.class); + Join topJoin = (Join) ((Projection) result).getArg(); + + assertThat(topJoin.getLeftArg()).isInstanceOf(BindingSetAssignment.class); + + } + @Test public void testNegatedPathWithFixedObject() { String query = "ASK WHERE { ?s ! . }"; diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/ValuesClauseTest.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/ValuesClauseTest.java new file mode 100644 index 00000000000..287ffe355e4 --- /dev/null +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/ValuesClauseTest.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2022 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.sail.nativerdf; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.rdf4j.model.util.Values.iri; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.eclipse.rdf4j.common.transaction.IsolationLevels; +import org.eclipse.rdf4j.model.Model; +import org.eclipse.rdf4j.model.util.ModelBuilder; +import org.eclipse.rdf4j.model.util.Values; +import org.eclipse.rdf4j.model.vocabulary.OWL; +import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.query.explanation.Explanation; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +/** + * @author Jeen Broekstra + */ +public class ValuesClauseTest { + + private static SailRepository repository; + + public static TemporaryFolder tempDir = new TemporaryFolder(); + + private static final String query1; + private static final String query2; + + static { + try { + query1 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query-values.qr"), StandardCharsets.UTF_8); + query2 = IOUtils.toString(getResourceAsStream("benchmarkFiles/query-without-values.qr"), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @BeforeClass + public static void beforeClass() throws IOException { + tempDir.create(); + File file = tempDir.newFolder(); + + repository = new SailRepository(new NativeStore(file, "spoc,posc,cspo,opsc")); + + int numberOfItems = 2; + int numberOfChildren = 2; + int numberOfTypeOwlClassStatements = 500; + int numberOfSubClassOfStatements = 10_000; + + try (var conn = repository.getConnection()) { + conn.begin(IsolationLevels.NONE); + for (int i = 0; i < numberOfItems; i++) { + + var parent = iri("http://example.org/parent_" + i); + + Model m = new ModelBuilder().setNamespace(OWL.NS) + .setNamespace("ex", "http://example.org/") + .subject(parent) + .add(RDF.TYPE, OWL.CLASS) + .add(RDF.TYPE, RDFS.CLASS) + .add(RDFS.LABEL, "parent " + i) + .build(); + conn.add(m); + if (i % 2 == 0) { + for (int j = 0; j < numberOfChildren; j++) { + m = new ModelBuilder().setNamespace(OWL.NS) + .setNamespace("ex", "http://example.org/") + .subject("ex:child_" + i + "_" + j) + .add(RDF.TYPE, OWL.CLASS) + .add(RDF.TYPE, RDFS.CLASS) + .add(RDFS.SUBCLASSOF, parent) + .add(RDFS.LABEL, "child of " + i) + .build(); + conn.add(m); + } + } + + } + for (int i = 0; i < numberOfTypeOwlClassStatements; i++) { + conn.add(Values.bnode(), RDF.TYPE, OWL.CLASS); + } + + for (int i = 0; i < numberOfSubClassOfStatements; i++) { + conn.add(Values.bnode(), RDFS.SUBCLASSOF, Values.bnode()); + } + conn.commit(); + } + } + + @AfterClass + public static void afterClass() throws IOException { + tempDir.delete(); + repository.shutDown(); + tempDir = null; + repository = null; + } + + @Test + public void valuesOptionalQuery() { + try (SailRepositoryConnection connection = repository.getConnection()) { + System.out.println(connection.prepareTupleQuery(query1).explain(Explanation.Level.Executed)); + assertThat(connection.prepareTupleQuery(query1).evaluate().stream().count()).isEqualTo(505); + } + } + + @Test + public void simpleEquivalentQuery() { + try (SailRepositoryConnection connection = repository.getConnection()) { + System.out.println(connection.prepareTupleQuery(query2).explain(Explanation.Level.Executed)); + assertThat(connection.prepareTupleQuery(query2).evaluate().stream().count()).isEqualTo(505); + } + } + + private static InputStream getResourceAsStream(String name) { + return ValuesClauseTest.class.getClassLoader().getResourceAsStream(name); + } +} diff --git a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/SPARQLValuesClauseBenchmark.java b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/SPARQLValuesClauseBenchmark.java index 4917ef0341c..d3560e6ca92 100644 --- a/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/SPARQLValuesClauseBenchmark.java +++ b/core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/SPARQLValuesClauseBenchmark.java @@ -153,6 +153,19 @@ public void afterClass() { @Benchmark public long valuesOptionalQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { +// +// System.out.println("ONLY PARSED:"); +// System.out.println(connection.prepareTupleQuery(query_with_values_clause)); +// System.out.println("\nUNOPTIMIZED:"); +// System.out.println( +// connection.prepareTupleQuery(query_with_values_clause).explain(Explanation.Level.Unoptimized)); +// +// System.out.println(); +// System.out.println("OPTIMIZED:"); +// System.out.println( +// connection.prepareTupleQuery(query_with_values_clause).explain(Explanation.Level.Optimized)); + +// return 0L; return connection .prepareTupleQuery(query_with_values_clause) .evaluate() @@ -164,6 +177,7 @@ public long valuesOptionalQuery() { @Benchmark public long simpleEquivalentQuery() { try (SailRepositoryConnection connection = repository.getConnection()) { + return connection .prepareTupleQuery(query_without_values_clause) .evaluate() diff --git a/core/sail/nativerdf/src/test/resources/benchmarkFiles/query-values.qr b/core/sail/nativerdf/src/test/resources/benchmarkFiles/query-values.qr index 6fe73e0a498..b8f417a553f 100644 --- a/core/sail/nativerdf/src/test/resources/benchmarkFiles/query-values.qr +++ b/core/sail/nativerdf/src/test/resources/benchmarkFiles/query-values.qr @@ -7,7 +7,7 @@ where { values ?type {owl:Class} ?parent rdf:type ?type . optional { - ?child rdfs:subClassOf ?parent ; - rdf:type ?type . + ?child rdfs:subClassOf ?parent . + ?child rdf:type ?type . } } \ No newline at end of file