Skip to content

Commit 2bb9466

Browse files
committed
wip
1 parent 75b4a79 commit 2bb9466

2 files changed

Lines changed: 158 additions & 64 deletions

File tree

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/optimizer/BindingSetAssignmentJoinOrderOptimizer.java

Lines changed: 69 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,23 @@
1111
// Some portions generated by Codex
1212
package org.eclipse.rdf4j.query.algebra.evaluation.optimizer;
1313

14-
import java.util.Collection;
1514
import java.util.Set;
1615

1716
import org.eclipse.rdf4j.query.BindingSet;
1817
import org.eclipse.rdf4j.query.Dataset;
1918
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
19+
import org.eclipse.rdf4j.query.algebra.Filter;
2020
import org.eclipse.rdf4j.query.algebra.Join;
2121
import org.eclipse.rdf4j.query.algebra.StatementPattern;
2222
import org.eclipse.rdf4j.query.algebra.TupleExpr;
2323
import org.eclipse.rdf4j.query.algebra.Var;
2424
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
2525
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
26+
import org.eclipse.rdf4j.query.algebra.helpers.collectors.VarNameCollector;
2627

2728
/**
28-
* Reorders joins to avoid an early cartesian product between two {@link BindingSetAssignment}s (VALUES) when the result
29-
* is immediately joined with a {@link StatementPattern} that can be evaluated efficiently with a single bound side.
29+
* Rewrites specific VALUES cross-products so the {@link StatementPattern} is evaluated with both sides bound without
30+
* materializing an explicit cartesian join between two {@link BindingSetAssignment}s.
3031
*/
3132
public class BindingSetAssignmentJoinOrderOptimizer implements QueryOptimizer {
3233

@@ -42,28 +43,26 @@ private static final class BindingSetAssignmentJoinOrderVisitor
4243
extends AbstractSimpleQueryModelVisitor<RuntimeException> {
4344

4445
@Override
45-
public void meet(Join join) throws RuntimeException {
46-
super.meet(join);
47-
48-
StatementPattern statementPattern;
49-
Join bindingSetJoin;
50-
if (join.getLeftArg() instanceof Join && join.getRightArg() instanceof StatementPattern) {
51-
bindingSetJoin = (Join) join.getLeftArg();
52-
statementPattern = (StatementPattern) join.getRightArg();
53-
} else if (join.getRightArg() instanceof Join && join.getLeftArg() instanceof StatementPattern) {
54-
bindingSetJoin = (Join) join.getRightArg();
55-
statementPattern = (StatementPattern) join.getLeftArg();
56-
} else {
46+
public void meet(Filter filter) throws RuntimeException {
47+
super.meet(filter);
48+
49+
if (!(filter.getArg() instanceof Join)) {
50+
return;
51+
}
52+
53+
Join join = (Join) filter.getArg();
54+
StatementPattern statementPattern = statementPattern(join);
55+
if (statementPattern == null) {
5756
return;
5857
}
5958

60-
if (!(bindingSetJoin.getLeftArg() instanceof BindingSetAssignment)
61-
|| !(bindingSetJoin.getRightArg() instanceof BindingSetAssignment)) {
59+
Join valuesJoin = valuesJoin(join);
60+
if (valuesJoin == null) {
6261
return;
6362
}
6463

65-
BindingSetAssignment leftValues = (BindingSetAssignment) bindingSetJoin.getLeftArg();
66-
BindingSetAssignment rightValues = (BindingSetAssignment) bindingSetJoin.getRightArg();
64+
BindingSetAssignment leftValues = (BindingSetAssignment) valuesJoin.getLeftArg();
65+
BindingSetAssignment rightValues = (BindingSetAssignment) valuesJoin.getRightArg();
6766
if (!areDisjoint(leftValues.getBindingNames(), rightValues.getBindingNames())) {
6867
return;
6968
}
@@ -73,17 +72,53 @@ public void meet(Join join) throws RuntimeException {
7372
return;
7473
}
7574

76-
BindingSetAssignment firstValues = chooseFirst(statementPattern, leftValues, rightValues);
77-
BindingSetAssignment secondValues = firstValues == leftValues ? rightValues : leftValues;
75+
Set<String> filterVars = VarNameCollector.process(filter.getCondition());
76+
if (!join.getBindingNames().containsAll(filterVars)) {
77+
return;
78+
}
7879

79-
StatementPattern statementPatternClone = statementPattern.clone();
80-
Join inner = new Join((TupleExpr) firstValues.clone(), statementPatternClone);
81-
Join outer = new Join(inner, (TupleExpr) secondValues.clone());
80+
BindingSetAssignment subjectValues = subjectValues(statementPattern, leftValues, rightValues);
81+
if (subjectValues == null) {
82+
return;
83+
}
84+
BindingSetAssignment objectValues = subjectValues == leftValues ? rightValues : leftValues;
8285

83-
join.replaceWith(outer);
86+
Filter statementPatternFilter = new Filter(statementPattern.clone(), filter.getCondition().clone());
87+
Join inner = new Join((TupleExpr) objectValues.clone(), statementPatternFilter);
88+
Join outer = new Join((TupleExpr) subjectValues.clone(), inner);
89+
90+
filter.replaceWith(outer);
8491
outer.visit(this);
8592
}
8693

94+
private static StatementPattern statementPattern(Join join) {
95+
if (join.getLeftArg() instanceof StatementPattern) {
96+
return (StatementPattern) join.getLeftArg();
97+
}
98+
if (join.getRightArg() instanceof StatementPattern) {
99+
return (StatementPattern) join.getRightArg();
100+
}
101+
return null;
102+
}
103+
104+
private static Join valuesJoin(Join join) {
105+
if (join.getLeftArg() instanceof Join) {
106+
Join candidate = (Join) join.getLeftArg();
107+
if (candidate.getLeftArg() instanceof BindingSetAssignment
108+
&& candidate.getRightArg() instanceof BindingSetAssignment) {
109+
return candidate;
110+
}
111+
}
112+
if (join.getRightArg() instanceof Join) {
113+
Join candidate = (Join) join.getRightArg();
114+
if (candidate.getLeftArg() instanceof BindingSetAssignment
115+
&& candidate.getRightArg() instanceof BindingSetAssignment) {
116+
return candidate;
117+
}
118+
}
119+
return null;
120+
}
121+
87122
private static boolean areDisjoint(Set<String> left, Set<String> right) {
88123
for (String name : left) {
89124
if (right.contains(name)) {
@@ -106,49 +141,20 @@ private static boolean statementPatternUsesAny(StatementPattern statementPattern
106141
return false;
107142
}
108143

109-
private static BindingSetAssignment chooseFirst(StatementPattern statementPattern, BindingSetAssignment left,
144+
private static BindingSetAssignment subjectValues(StatementPattern statementPattern, BindingSetAssignment left,
110145
BindingSetAssignment right) {
111-
int leftScore = positionScore(statementPattern, left.getBindingNames());
112-
int rightScore = positionScore(statementPattern, right.getBindingNames());
113-
if (leftScore != rightScore) {
114-
return leftScore < rightScore ? left : right;
115-
}
116-
117-
int leftSize = estimateSize(left);
118-
int rightSize = estimateSize(right);
119-
if (leftSize != rightSize) {
120-
return leftSize <= rightSize ? left : right;
121-
}
122-
123-
return left;
124-
}
125-
126-
private static int positionScore(StatementPattern statementPattern, Set<String> names) {
127146
Var subject = statementPattern.getSubjectVar();
128-
if (subject != null && !subject.hasValue() && names.contains(subject.getName())) {
129-
return 0;
130-
}
131-
Var object = statementPattern.getObjectVar();
132-
if (object != null && !object.hasValue() && names.contains(object.getName())) {
133-
return 1;
147+
if (subject == null || subject.hasValue()) {
148+
return null;
134149
}
135-
Var predicate = statementPattern.getPredicateVar();
136-
if (predicate != null && !predicate.hasValue() && names.contains(predicate.getName())) {
137-
return 2;
150+
String subjectName = subject.getName();
151+
if (left.getBindingNames().contains(subjectName)) {
152+
return left;
138153
}
139-
Var context = statementPattern.getContextVar();
140-
if (context != null && !context.hasValue() && names.contains(context.getName())) {
141-
return 3;
142-
}
143-
return Integer.MAX_VALUE;
144-
}
145-
146-
private static int estimateSize(BindingSetAssignment assignment) {
147-
Iterable<BindingSet> bindingSets = assignment.getBindingSets();
148-
if (bindingSets instanceof Collection<?>) {
149-
return ((Collection<?>) bindingSets).size();
154+
if (right.getBindingNames().contains(subjectName)) {
155+
return right;
150156
}
151-
return Integer.MAX_VALUE;
157+
return null;
152158
}
153159
}
154160
}

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

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,18 @@
2121
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
2222
import org.eclipse.rdf4j.query.BindingSet;
2323
import org.eclipse.rdf4j.query.algebra.And;
24+
import org.eclipse.rdf4j.query.algebra.BinaryValueOperator;
2425
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
2526
import org.eclipse.rdf4j.query.algebra.Compare;
2627
import org.eclipse.rdf4j.query.algebra.Exists;
2728
import org.eclipse.rdf4j.query.algebra.Filter;
2829
import org.eclipse.rdf4j.query.algebra.Join;
30+
import org.eclipse.rdf4j.query.algebra.NAryValueOperator;
2931
import org.eclipse.rdf4j.query.algebra.QueryRoot;
3032
import org.eclipse.rdf4j.query.algebra.StatementPattern;
3133
import org.eclipse.rdf4j.query.algebra.TupleExpr;
34+
import org.eclipse.rdf4j.query.algebra.UnaryValueOperator;
35+
import org.eclipse.rdf4j.query.algebra.ValueExpr;
3236
import org.eclipse.rdf4j.query.algebra.Var;
3337
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.SparqlUoQueryOptimizerPipeline;
3438
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.SparqlUoConfig;
@@ -63,7 +67,8 @@ void avoidsCrossProductOfValuesBeforeStatementPattern() {
6367

6468
optimize(expr);
6569

66-
assertThat(containsDirectValuesToStatementPatternJoin(expr, follows)).isTrue();
70+
assertThat(containsCartesianJoinBetweenValues(expr)).isFalse();
71+
assertThat(containsExistsFilterOnStatementPattern(expr, follows)).isTrue();
6772
}
6873

6974
private static BindingSetAssignment createValues(String name, String... values) {
@@ -129,4 +134,87 @@ public void meet(Join node) throws RuntimeException {
129134
});
130135
return found.get();
131136
}
137+
138+
private static boolean containsCartesianJoinBetweenValues(TupleExpr expr) {
139+
AtomicBoolean found = new AtomicBoolean(false);
140+
expr.visit(new AbstractQueryModelVisitor<RuntimeException>() {
141+
@Override
142+
public void meet(Join node) throws RuntimeException {
143+
if (found.get()) {
144+
return;
145+
}
146+
147+
if (node.getLeftArg() instanceof BindingSetAssignment
148+
&& node.getRightArg() instanceof BindingSetAssignment) {
149+
BindingSetAssignment left = (BindingSetAssignment) node.getLeftArg();
150+
BindingSetAssignment right = (BindingSetAssignment) node.getRightArg();
151+
if (isDisjoint(left.getBindingNames(), right.getBindingNames())) {
152+
found.set(true);
153+
return;
154+
}
155+
}
156+
157+
super.meet(node);
158+
}
159+
});
160+
return found.get();
161+
}
162+
163+
private static boolean isDisjoint(Set<String> left, Set<String> right) {
164+
for (String name : left) {
165+
if (right.contains(name)) {
166+
return false;
167+
}
168+
}
169+
return true;
170+
}
171+
172+
private static boolean containsExistsFilterOnStatementPattern(TupleExpr expr, IRI predicate) {
173+
AtomicBoolean found = new AtomicBoolean(false);
174+
expr.visit(new AbstractQueryModelVisitor<RuntimeException>() {
175+
@Override
176+
public void meet(Filter node) throws RuntimeException {
177+
if (found.get()) {
178+
return;
179+
}
180+
if (!(node.getArg() instanceof StatementPattern)) {
181+
return;
182+
}
183+
StatementPattern statementPattern = (StatementPattern) node.getArg();
184+
Var predicateVar = statementPattern.getPredicateVar();
185+
if (predicateVar == null || !predicateVar.hasValue() || !predicateVar.getValue().equals(predicate)) {
186+
return;
187+
}
188+
if (!containsExists(node.getCondition())) {
189+
return;
190+
}
191+
found.set(true);
192+
}
193+
});
194+
return found.get();
195+
}
196+
197+
private static boolean containsExists(ValueExpr expr) {
198+
if (expr == null) {
199+
return false;
200+
}
201+
if (expr instanceof Exists) {
202+
return true;
203+
}
204+
if (expr instanceof UnaryValueOperator) {
205+
return containsExists(((UnaryValueOperator) expr).getArg());
206+
}
207+
if (expr instanceof BinaryValueOperator) {
208+
BinaryValueOperator binary = (BinaryValueOperator) expr;
209+
return containsExists(binary.getLeftArg()) || containsExists(binary.getRightArg());
210+
}
211+
if (expr instanceof NAryValueOperator) {
212+
for (ValueExpr arg : ((NAryValueOperator) expr).getArguments()) {
213+
if (containsExists(arg)) {
214+
return true;
215+
}
216+
}
217+
}
218+
return false;
219+
}
132220
}

0 commit comments

Comments
 (0)