Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.eclipse.rdf4j.query.algebra;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -148,7 +149,7 @@ public ProjectionElemList clone() {
clone.elements[i].setParentNode(clone);
}

clone.elementsList = List.of(clone.elements);
clone.elementsList = Arrays.asList(clone.elements);

return clone;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

package org.eclipse.rdf4j.query.algebra.helpers.collectors;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

import org.eclipse.rdf4j.query.algebra.Filter;
Expand Down Expand Up @@ -50,33 +52,32 @@ public void meet(Filter node) {

@Override
public void meet(Join node) throws RuntimeException {
TupleExpr leftArg = node.getLeftArg();
TupleExpr rightArg = node.getRightArg();

// INSERT clause is often a deeply nested join. Recursive approach may cause stack overflow. Attempt
// non-recursive (or at least less-recursive) approach first.
while (true) {
if (leftArg instanceof Join && !(rightArg instanceof Join)) {
rightArg.visit(this);
if (!(node.getLeftArg() instanceof Join || node.getRightArg() instanceof Join)) {
super.meet(node);
return;
}

Join join = (Join) leftArg;
leftArg = join.getLeftArg();
rightArg = join.getRightArg();
Deque<TupleExpr> stack = new ArrayDeque<>();
TupleExpr current = node;

} else if (rightArg instanceof Join && !(leftArg instanceof Join)) {
leftArg.visit(this);
while (true) {
// Drill down the leftmost spine, pushing right branches onto the stack
while (current instanceof Join) {
Join join = (Join) current;
stack.push(join.getRightArg()); // defer right side
current = join.getLeftArg(); // continue with left side
}

Join join = (Join) rightArg;
leftArg = join.getLeftArg();
rightArg = join.getRightArg();
// current is a leaf (not a Join)
current.visit(this);

} else {
leftArg.visit(this);
rightArg.visit(this);
// When the stack is empty, we have visited every deferred right branch
if (stack.isEmpty()) {
return;
}
// Pop the next right branch to process
current = stack.pop();
}

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.rdf4j.model.vocabulary.FOAF;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.RDFS;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.Query;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.explanation.Explanation;
Expand Down Expand Up @@ -101,6 +102,60 @@ public class QueryPlanRetrievalTest {
" }\n" +
"} GROUP BY ?countryID ?year";

public static final String CONSTRUCT = "PREFIX epo: <http://data.europa.eu/a4g/ontology#>\n" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
"PREFIX legal: <https://www.w3.org/ns/legal#>\n" +
"PREFIX dcterms: <http://purl.org/dc/terms#>\n" +
"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n" +
"PREFIX dc: <http://purl.org/dc/elements/1.1/>\n" +
"\n" +
"CONSTRUCT {" +
"\n" +
" ?proc a epo:Procedure .\n" +
" ?proc epo:hasProcedureType ?p .\n" +
" ?stat epo:concernsSubmissionsForLot ?lot .\n" +
" ?stat a epo:SubmissionStatisticalInformation .\n" +
" ?stat epo:hasReceivedTenders ?bidders .\n" +
" ?resultnotice a epo:ResultNotice .\n" +
" ?resultnotice epo:hasDispatchDate ?ddate .\n" +
" ?proc epo:hasProcurementScopeDividedIntoLot ?lot .\n" +
" ?resultnotice epo:refersToRole ?buyerrole .\n" +
" ?resultnotice epo:refersToProcedure ?proc .\n" +
"}" +
" WHERE {\n"
+
"\n" +
" ?proc a epo:Procedure .\n" +
" ?proc epo:hasProcedureType ?p .\n" +
" ?stat epo:concernsSubmissionsForLot ?lot .\n" +
" ?stat a epo:SubmissionStatisticalInformation .\n" +
" ?stat epo:hasReceivedTenders ?bidders .\n" +
" ?resultnotice a epo:ResultNotice .\n" +
" ?resultnotice epo:hasDispatchDate ?ddate .\n" +
" ?proc epo:hasProcurementScopeDividedIntoLot ?lot .\n" +
" ?resultnotice epo:refersToRole ?buyerrole .\n" +
" ?resultnotice epo:refersToProcedure ?proc .\n" +
"\n" +
" \tFILTER ( ?p != <http://publications.europa.eu/resource/authority/procurement-procedure-type/neg-wo-call>)\n"
+
"\t\tBIND(year(xsd:dateTime(?ddate)) AS ?year) .\n" +
"\n" +
"\n" +
" {\n" +
" SELECT DISTINCT ?buyerrole ?countryID WHERE {\n" +
" ?org epo:hasBuyerType ?buytype .\n" +
" FILTER (?buytype != <http://publications.europa.eu/resource/authority/buyer-legal-type/eu-int-org> )\n"
+
"\n" +
" ?buyerrole epo:playedBy ?org .\n" +
" ?org legal:registeredAddress ?orgaddress .\n" +
" ?orgaddress epo:hasCountryCode ?countrycode .\n" +
" ?countrycode dc:identifier ?countryID .\n" +
"\n" +
" }\n" +
" }\n" +
"} GROUP BY ?countryID ?year";

public static final String UNION_QUERY = "select ?a where {?a a ?type. {?a ?b ?c, ?c2. {?c2 a ?type1}UNION{?c2 a ?type2}} UNION {?type ?d ?c}}";

ValueFactory vf = SimpleValueFactory.getInstance();
Expand Down Expand Up @@ -1534,6 +1589,211 @@ public void testArbitraryLengthPath() {

}

@Test
public void constructQueryTest() {

String expected = "Reduced\n" +
" MultiProjection\n" +
" ProjectionElemList\n" +
" ProjectionElem \"proc\" AS \"subject\"\n" +
" ProjectionElem \"_const_f5e5585a_uri\" AS \"predicate\"\n" +
" ProjectionElem \"_const_be18ee7b_uri\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"proc\" AS \"subject\"\n" +
" ProjectionElem \"_const_9c756f6b_uri\" AS \"predicate\"\n" +
" ProjectionElem \"p\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"stat\" AS \"subject\"\n" +
" ProjectionElem \"_const_25686184_uri\" AS \"predicate\"\n" +
" ProjectionElem \"lot\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"stat\" AS \"subject\"\n" +
" ProjectionElem \"_const_f5e5585a_uri\" AS \"predicate\"\n" +
" ProjectionElem \"_const_ea79e75_uri\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"stat\" AS \"subject\"\n" +
" ProjectionElem \"_const_98c73a3c_uri\" AS \"predicate\"\n" +
" ProjectionElem \"bidders\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"resultnotice\" AS \"subject\"\n" +
" ProjectionElem \"_const_f5e5585a_uri\" AS \"predicate\"\n" +
" ProjectionElem \"_const_77e914ad_uri\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"resultnotice\" AS \"subject\"\n" +
" ProjectionElem \"_const_1b0b00ca_uri\" AS \"predicate\"\n" +
" ProjectionElem \"ddate\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"proc\" AS \"subject\"\n" +
" ProjectionElem \"_const_9c3f1eec_uri\" AS \"predicate\"\n" +
" ProjectionElem \"lot\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"resultnotice\" AS \"subject\"\n" +
" ProjectionElem \"_const_6aa9a9c_uri\" AS \"predicate\"\n" +
" ProjectionElem \"buyerrole\" AS \"object\"\n" +
" ProjectionElemList\n" +
" ProjectionElem \"resultnotice\" AS \"subject\"\n" +
" ProjectionElem \"_const_183bd06d_uri\" AS \"predicate\"\n" +
" ProjectionElem \"proc\" AS \"object\"\n" +
" Extension\n" +
" Group (countryID, year)\n" +
" Join (HashJoinIteration)\n" +
" ╠══ Extension [left]\n" +
" ║ ├── Join (JoinIterator)\n" +
" ║ │ ╠══ StatementPattern (costEstimate=0.71, resultSizeEstimate=0) [left]\n" +
" ║ │ ║ s: Var (name=resultnotice)\n" +
" ║ │ ║ p: Var (name=_const_183bd06d_uri, value=http://data.europa.eu/a4g/ontology#refersToProcedure, anonymous)\n"
+
" ║ │ ║ o: Var (name=proc)\n" +
" ║ │ ╚══ Join (JoinIterator) [right]\n" +
" ║ │ ├── StatementPattern (costEstimate=1.00, resultSizeEstimate=0) [left]\n" +
" ║ │ │ s: Var (name=proc)\n" +
" ║ │ │ p: Var (name=_const_f5e5585a_uri, value=http://www.w3.org/1999/02/22-rdf-syntax-ns#type, anonymous)\n"
+
" ║ │ │ o: Var (name=_const_be18ee7b_uri, value=http://data.europa.eu/a4g/ontology#Procedure, anonymous)\n"
+
" ║ │ └── Join (JoinIterator) [right]\n" +
" ║ │ ╠══ StatementPattern (costEstimate=1.00, resultSizeEstimate=0) [left]\n" +
" ║ │ ║ s: Var (name=resultnotice)\n" +
" ║ │ ║ p: Var (name=_const_f5e5585a_uri, value=http://www.w3.org/1999/02/22-rdf-syntax-ns#type, anonymous)\n"
+
" ║ │ ║ o: Var (name=_const_77e914ad_uri, value=http://data.europa.eu/a4g/ontology#ResultNotice, anonymous)\n"
+
" ║ │ ╚══ Join (JoinIterator) [right]\n" +
" ║ │ ├── StatementPattern (costEstimate=1.12, resultSizeEstimate=0) [left]\n" +
" ║ │ │ s: Var (name=proc)\n" +
" ║ │ │ p: Var (name=_const_9c3f1eec_uri, value=http://data.europa.eu/a4g/ontology#hasProcurementScopeDividedIntoLot, anonymous)\n"
+
" ║ │ │ o: Var (name=lot)\n" +
" ║ │ └── Join (JoinIterator) [right]\n" +
" ║ │ ╠══ StatementPattern (costEstimate=0.75, resultSizeEstimate=0) [left]\n"
+
" ║ │ ║ s: Var (name=stat)\n" +
" ║ │ ║ p: Var (name=_const_25686184_uri, value=http://data.europa.eu/a4g/ontology#concernsSubmissionsForLot, anonymous)\n"
+
" ║ │ ║ o: Var (name=lot)\n" +
" ║ │ ╚══ Join (JoinIterator) [right]\n" +
" ║ │ ├── StatementPattern (costEstimate=1.00, resultSizeEstimate=0) [left]\n"
+
" ║ │ │ s: Var (name=stat)\n" +
" ║ │ │ p: Var (name=_const_f5e5585a_uri, value=http://www.w3.org/1999/02/22-rdf-syntax-ns#type, anonymous)\n"
+
" ║ │ │ o: Var (name=_const_ea79e75_uri, value=http://data.europa.eu/a4g/ontology#SubmissionStatisticalInformation, anonymous)\n"
+
" ║ │ └── Join (JoinIterator) [right]\n" +
" ║ │ ╠══ Filter [left]\n" +
" ║ │ ║ ├── Compare (!=)\n" +
" ║ │ ║ │ Var (name=p)\n" +
" ║ │ ║ │ ValueConstant (value=http://publications.europa.eu/resource/authority/procurement-procedure-type/neg-wo-call)\n"
+
" ║ │ ║ └── StatementPattern (costEstimate=2.24, resultSizeEstimate=0)\n"
+
" ║ │ ║ s: Var (name=proc)\n" +
" ║ │ ║ p: Var (name=_const_9c756f6b_uri, value=http://data.europa.eu/a4g/ontology#hasProcedureType, anonymous)\n"
+
" ║ │ ║ o: Var (name=p)\n" +
" ║ │ ╚══ Join (JoinIterator) [right]\n" +
" ║ │ ├── StatementPattern (costEstimate=2.24, resultSizeEstimate=0) [left]\n"
+
" ║ │ │ s: Var (name=stat)\n" +
" ║ │ │ p: Var (name=_const_98c73a3c_uri, value=http://data.europa.eu/a4g/ontology#hasReceivedTenders, anonymous)\n"
+
" ║ │ │ o: Var (name=bidders)\n" +
" ║ │ └── Join (JoinIterator) [right]\n" +
" ║ │ ╠══ StatementPattern (costEstimate=2.24, resultSizeEstimate=0) [left]\n"
+
" ║ │ ║ s: Var (name=resultnotice)\n" +
" ║ │ ║ p: Var (name=_const_1b0b00ca_uri, value=http://data.europa.eu/a4g/ontology#hasDispatchDate, anonymous)\n"
+
" ║ │ ║ o: Var (name=ddate)\n" +
" ║ │ ╚══ StatementPattern (costEstimate=2.24, resultSizeEstimate=0) [right]\n"
+
" ║ │ s: Var (name=resultnotice)\n" +
" ║ │ p: Var (name=_const_6aa9a9c_uri, value=http://data.europa.eu/a4g/ontology#refersToRole, anonymous)\n"
+
" ║ │ o: Var (name=buyerrole)\n" +
" ║ └── ExtensionElem (year)\n" +
" ║ FunctionCall (http://www.w3.org/2005/xpath-functions#year-from-dateTime)\n" +
" ║ FunctionCall (http://www.w3.org/2001/XMLSchema#dateTime)\n" +
" ║ Var (name=ddate)\n" +
" ╚══ Distinct (new scope) [right]\n" +
" Projection\n" +
" ╠══ ProjectionElemList\n" +
" ║ ProjectionElem \"buyerrole\"\n" +
" ║ ProjectionElem \"countryID\"\n" +
" ╚══ Join (JoinIterator)\n" +
" ├── StatementPattern (costEstimate=1.25, resultSizeEstimate=0) [left]\n" +
" │ s: Var (name=org)\n" +
" │ p: Var (name=_const_beb18915_uri, value=https://www.w3.org/ns/legal#registeredAddress, anonymous)\n"
+
" │ o: Var (name=orgaddress)\n" +
" └── Join (JoinIterator) [right]\n" +
" ╠══ StatementPattern (costEstimate=1.12, resultSizeEstimate=0) [left]\n" +
" ║ s: Var (name=orgaddress)\n" +
" ║ p: Var (name=_const_2f7de0e1_uri, value=http://data.europa.eu/a4g/ontology#hasCountryCode, anonymous)\n"
+
" ║ o: Var (name=countrycode)\n" +
" ╚══ Join (JoinIterator) [right]\n" +
" ├── Filter [left]\n" +
" │ ╠══ Compare (!=)\n" +
" │ ║ Var (name=buytype)\n" +
" │ ║ ValueConstant (value=http://publications.europa.eu/resource/authority/buyer-legal-type/eu-int-org)\n"
+
" │ ╚══ StatementPattern (costEstimate=2.24, resultSizeEstimate=0)\n" +
" │ s: Var (name=org)\n" +
" │ p: Var (name=_const_1abd8d4b_uri, value=http://data.europa.eu/a4g/ontology#hasBuyerType, anonymous)\n"
+
" │ o: Var (name=buytype)\n" +
" └── Join (JoinIterator) [right]\n" +
" ╠══ StatementPattern (costEstimate=2.24, resultSizeEstimate=0) [left]\n"
+
" ║ s: Var (name=buyerrole)\n" +
" ║ p: Var (name=_const_beb855c2_uri, value=http://data.europa.eu/a4g/ontology#playedBy, anonymous)\n"
+
" ║ o: Var (name=org)\n" +
" ╚══ StatementPattern (costEstimate=2.24, resultSizeEstimate=0) [right]\n"
+
" s: Var (name=countrycode)\n" +
" p: Var (name=_const_a825a5f4_uri, value=http://purl.org/dc/elements/1.1/identifier, anonymous)\n"
+
" o: Var (name=countryID)\n" +
" ExtensionElem (_const_f5e5585a_uri)\n" +
" ValueConstant (value=http://www.w3.org/1999/02/22-rdf-syntax-ns#type)\n" +
" ExtensionElem (_const_be18ee7b_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#Procedure)\n" +
" ExtensionElem (_const_9c756f6b_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#hasProcedureType)\n" +
" ExtensionElem (_const_25686184_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#concernsSubmissionsForLot)\n" +
" ExtensionElem (_const_ea79e75_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#SubmissionStatisticalInformation)\n"
+
" ExtensionElem (_const_98c73a3c_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#hasReceivedTenders)\n" +
" ExtensionElem (_const_77e914ad_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#ResultNotice)\n" +
" ExtensionElem (_const_1b0b00ca_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#hasDispatchDate)\n" +
" ExtensionElem (_const_9c3f1eec_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#hasProcurementScopeDividedIntoLot)\n"
+
" ExtensionElem (_const_6aa9a9c_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#refersToRole)\n" +
" ExtensionElem (_const_183bd06d_uri)\n" +
" ValueConstant (value=http://data.europa.eu/a4g/ontology#refersToProcedure)\n";
SailRepository sailRepository = new SailRepository(new MemoryStore());
addData(sailRepository);

try (SailRepositoryConnection connection = sailRepository.getConnection()) {
GraphQuery query = connection.prepareGraphQuery(CONSTRUCT);
String actual = query.explain(Explanation.Level.Optimized).toString();

assertThat(actual).isEqualToNormalizingNewlines(expected);

}
sailRepository.shutDown();

}

@Test
public void testHaving() {

Expand Down
Loading