Skip to content

Commit cb28bc5

Browse files
author
James Leigh
authored
Merge pull request #825 from jamesrdf/issues/#380-bind-join
Fix #380: Always evaluate Extension/BIND before join
2 parents db900d0 + 37d177f commit cb28bc5

2 files changed

Lines changed: 65 additions & 9 deletions

File tree

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryJoinOptimizer.java

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.eclipse.rdf4j.query.BindingSet;
1818
import org.eclipse.rdf4j.query.Dataset;
19+
import org.eclipse.rdf4j.query.algebra.Extension;
1920
import org.eclipse.rdf4j.query.algebra.Join;
2021
import org.eclipse.rdf4j.query.algebra.LeftJoin;
2122
import org.eclipse.rdf4j.query.algebra.StatementPattern;
@@ -88,9 +89,18 @@ public void meet(Join node) {
8889
// Reorder the (recursive) join arguments to a more optimal sequence
8990
List<TupleExpr> orderedJoinArgs = new ArrayList<TupleExpr>(joinArgs.size());
9091

92+
// Reorder the subselects and extensions to a more optimal sequence
93+
List<TupleExpr> priorityArgs = new ArrayList<TupleExpr>(joinArgs.size());
94+
9195
// first get all subselects and order them
9296
List<TupleExpr> orderedSubselects = reorderSubselects(getSubSelects(joinArgs));
9397
joinArgs.removeAll(orderedSubselects);
98+
priorityArgs.addAll(orderedSubselects);
99+
100+
// second get all extensions (BIND clause)
101+
List<Extension> orderedExtensions = getExtensions(joinArgs);
102+
joinArgs.removeAll(orderedExtensions);
103+
priorityArgs.addAll(orderedExtensions);
94104

95105
// We order all remaining join arguments based on cardinality and
96106
// variable frequency statistics
@@ -131,11 +141,11 @@ public void meet(Join node) {
131141
}
132142

133143
// Build new join hierarchy
134-
TupleExpr subselectJoins = null;
135-
if (orderedSubselects.size() > 0) {
136-
subselectJoins = orderedSubselects.get(0);
137-
for (int i = 1; i < orderedSubselects.size(); i++) {
138-
subselectJoins = new Join(subselectJoins, orderedSubselects.get(i));
144+
TupleExpr priorityJoins = null;
145+
if (priorityArgs.size() > 0) {
146+
priorityJoins = priorityArgs.get(0);
147+
for (int i = 1; i < priorityArgs.size(); i++) {
148+
priorityJoins = new Join(priorityJoins, priorityArgs.get(i));
139149
}
140150
}
141151

@@ -149,17 +159,17 @@ public void meet(Join node) {
149159
replacement = new Join(orderedJoinArgs.get(i), replacement);
150160
}
151161

152-
if (subselectJoins != null) {
153-
replacement = new Join(subselectJoins, replacement);
162+
if (priorityJoins != null) {
163+
replacement = new Join(priorityJoins, replacement);
154164
}
155165

156166
// Replace old join hierarchy
157167
node.replaceWith(replacement);
158168

159169
}
160170
else {
161-
// only subselect joins involved in this query.
162-
node.replaceWith(subselectJoins);
171+
// only subselect/priority joins involved in this query.
172+
node.replaceWith(priorityJoins);
163173
}
164174
}
165175
finally {
@@ -198,6 +208,16 @@ protected <M extends Map<Var, Integer>> M getVarFreqMap(List<Var> varList, M var
198208
return varFreqMap;
199209
}
200210

211+
protected List<Extension> getExtensions(List<TupleExpr> expressions) {
212+
List<Extension> extensions = new ArrayList<Extension>();
213+
for (TupleExpr expr : expressions) {
214+
if (expr instanceof Extension) {
215+
extensions.add((Extension)expr);
216+
}
217+
}
218+
return extensions;
219+
}
220+
201221
protected List<TupleExpr> getSubSelects(List<TupleExpr> expressions) {
202222
List<TupleExpr> subselects = new ArrayList<TupleExpr>();
203223

core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryJoinOptimizerTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@
1313
import org.eclipse.rdf4j.query.MalformedQueryException;
1414
import org.eclipse.rdf4j.query.QueryLanguage;
1515
import org.eclipse.rdf4j.query.UnsupportedQueryLanguageException;
16+
import org.eclipse.rdf4j.query.algebra.BinaryTupleOperator;
17+
import org.eclipse.rdf4j.query.algebra.Extension;
1618
import org.eclipse.rdf4j.query.algebra.QueryModelNode;
1719
import org.eclipse.rdf4j.query.algebra.QueryRoot;
20+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
21+
import org.eclipse.rdf4j.query.algebra.UnaryTupleOperator;
1822
import org.eclipse.rdf4j.query.parser.ParsedQuery;
1923
import org.eclipse.rdf4j.query.parser.QueryParserUtil;
24+
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
25+
import org.junit.Assert;
2026
import org.junit.Test;
2127

2228
/**
@@ -59,6 +65,36 @@ public void testContextOptimization()
5965
testOptimizer(expectedQuery, query);
6066
}
6167

68+
@Test
69+
public void testSES2116JoinBind()
70+
throws Exception
71+
{
72+
73+
StringBuilder qb = new StringBuilder();
74+
qb.append("SELECT ?subject ?name ?row {\n"
75+
+ " ?subject <http://localhost/table_1> ?uri .\n" + " BIND(STR(?uri) AS ?name)\n"
76+
+ " ?table <http://linked.opendata.cz/ontology/odcs/tabular/hasRow> ?row .\n"
77+
+ " ?table <http://linked.opendata.cz/ontology/odcs/tabular/symbolicName> ?name .\n" + "}");
78+
79+
SPARQLParser parser = new SPARQLParser();
80+
ParsedQuery q = parser.parseQuery(qb.toString(), null);
81+
QueryJoinOptimizer opt = new QueryJoinOptimizer();
82+
QueryRoot optRoot = new QueryRoot(q.getTupleExpr());
83+
opt.optimize(optRoot, null, null);
84+
TupleExpr leaf = findLeaf(optRoot);
85+
Assert.assertTrue("Extension must be evaluated before StatementPattern", leaf.getParentNode() instanceof Extension);
86+
}
87+
88+
private TupleExpr findLeaf(TupleExpr expr) {
89+
if (expr instanceof UnaryTupleOperator) {
90+
return findLeaf(((UnaryTupleOperator)expr).getArg());
91+
} else if (expr instanceof BinaryTupleOperator) {
92+
return findLeaf(((BinaryTupleOperator)expr).getLeftArg());
93+
} else {
94+
return expr;
95+
}
96+
}
97+
6298
private void testOptimizer(String expectedQuery, String actualQuery)
6399
throws MalformedQueryException, UnsupportedQueryLanguageException
64100
{

0 commit comments

Comments
 (0)