Skip to content

Commit 5e4b1f5

Browse files
committed
wip
1 parent eda66c4 commit 5e4b1f5

8 files changed

Lines changed: 539 additions & 19 deletions

File tree

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

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ public void meet(Filter filter) {
112112
if (!hasStatementPatternCoveringVars(subQuery, shared)) {
113113
return;
114114
}
115+
if (shouldKeepExistsAsFilter(subQuery, shared)) {
116+
return;
117+
}
115118
TupleExpr right = buildDistinctProjection(subQuery.clone(), shared);
116119
if (!shouldRewrite(arg, right)) {
117120
return;
@@ -125,6 +128,14 @@ public void meet(Filter filter) {
125128
filter.setCondition(extraction.remainingCondition);
126129
}
127130

131+
private boolean shouldKeepExistsAsFilter(TupleExpr subQuery, Set<String> joinVars) {
132+
StatementPattern statementPattern = singleStatementPatternOrNull(subQuery);
133+
if (statementPattern == null) {
134+
return false;
135+
}
136+
return countUnboundNonJoinVars(statementPattern, joinVars) == 0;
137+
}
138+
128139
private boolean shouldRewrite(TupleExpr left, TupleExpr right) {
129140
if (allowNonImprovingTransforms) {
130141
return true;
@@ -145,8 +156,10 @@ private static TupleExpr buildDistinctProjection(TupleExpr subQuery, Set<String>
145156
for (String name : ordered) {
146157
projectionElemList.addElement(new ProjectionElem(name));
147158
}
148-
Projection projection = new Projection(subQuery, projectionElemList);
149-
return new Distinct(projection);
159+
Projection projection = new Projection(subQuery, projectionElemList, false);
160+
Distinct distinct = new Distinct(projection);
161+
distinct.setVariableScopeChange(true);
162+
return distinct;
150163
}
151164

152165
private static boolean containsService(TupleExpr subQuery) {
@@ -214,6 +227,32 @@ private static Map<String, String> collectAliasMap(TupleExpr expr) {
214227
return aliasMap;
215228
}
216229

230+
private static StatementPattern singleStatementPatternOrNull(TupleExpr expr) {
231+
StatementPattern[] match = { null };
232+
int[] count = { 0 };
233+
expr.visit(new StopAtScopeChange(true) {
234+
@Override
235+
public void meet(StatementPattern node) {
236+
count[0]++;
237+
match[0] = node;
238+
}
239+
});
240+
return count[0] == 1 ? match[0] : null;
241+
}
242+
243+
private static int countUnboundNonJoinVars(StatementPattern statementPattern, Set<String> joinVars) {
244+
int count = 0;
245+
count += isUnboundNonJoinVar(statementPattern.getSubjectVar(), joinVars) ? 1 : 0;
246+
count += isUnboundNonJoinVar(statementPattern.getPredicateVar(), joinVars) ? 1 : 0;
247+
count += isUnboundNonJoinVar(statementPattern.getObjectVar(), joinVars) ? 1 : 0;
248+
count += isUnboundNonJoinVar(statementPattern.getContextVar(), joinVars) ? 1 : 0;
249+
return count;
250+
}
251+
252+
private static boolean isUnboundNonJoinVar(Var var, Set<String> joinVars) {
253+
return var != null && !var.hasValue() && !joinVars.contains(var.getName());
254+
}
255+
217256
private static boolean hasStatementPatternCoveringVars(TupleExpr expr, Set<String> joinVars) {
218257
if (joinVars.isEmpty()) {
219258
return false;

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ public void meet(Filter filter) {
107107
if (!hasStatementPatternCoveringVars(subQuery, shared)) {
108108
return;
109109
}
110+
if (isExactJoinVarMatch(subQuery, shared)) {
111+
return;
112+
}
110113
TupleExpr leftForEstimate = arg;
111114
if (extraction.remainingCondition != null) {
112115
leftForEstimate = new Filter(arg.clone(), extraction.remainingCondition.clone());
@@ -146,7 +149,7 @@ private static TupleExpr buildDistinctProjection(TupleExpr subQuery, Set<String>
146149
for (String name : ordered) {
147150
projectionElemList.addElement(new ProjectionElem(name));
148151
}
149-
Projection projection = new Projection(subQuery, projectionElemList);
152+
Projection projection = new Projection(subQuery, projectionElemList, false);
150153
return new Distinct(projection);
151154
}
152155

@@ -215,6 +218,13 @@ private static Map<String, String> collectAliasMap(TupleExpr expr) {
215218
return aliasMap;
216219
}
217220

221+
private static boolean isExactJoinVarMatch(TupleExpr expr, Set<String> joinVars) {
222+
if (joinVars.isEmpty()) {
223+
return false;
224+
}
225+
return collectUnboundVarNames(expr).equals(joinVars);
226+
}
227+
218228
private static boolean hasStatementPatternCoveringVars(TupleExpr expr, Set<String> joinVars) {
219229
if (joinVars.isEmpty()) {
220230
return false;

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource
5252
public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource tripleSource,
5353
EvaluationStatistics evaluationStatistics, SparqlUoConfig config) {
5454
this.delegate = new StandardQueryOptimizerPipeline(strategy, tripleSource, evaluationStatistics);
55-
this.sparqlUoOptimizer = new SparqlUoOptimizer(evaluationStatistics, config);
55+
this.sparqlUoOptimizer = new SparqlUoOptimizer(evaluationStatistics, disableOptionalFilterJoin(config));
5656
this.bindingSetAssignmentUnionOptimizer = new BindingSetAssignmentUnionOptimizer(
5757
config.maxBindingSetAssignmentUnionSize());
5858
this.unionCommonStatementPatternOptimizer = new UnionCommonStatementPatternOptimizer(evaluationStatistics);
@@ -89,28 +89,22 @@ public Iterable<QueryOptimizer> getOptimizers() {
8989
optimizers.add(minusOptimizer);
9090
optimizers.add(preJoinFilterOptimizer);
9191
optimizers.add(sparqlUoOptimizer);
92-
optimizers.add(existsSemiJoinOptimizer);
93-
optimizers.add(notExistsSemiJoinOptimizer);
9492
inserted = true;
9593
}
94+
optimizers.add(joinOptimizer);
9695
if (enableOptionalFilterJoin && !optionalFilterJoinInserted) {
9796
optimizers.add(optionalFilterJoinOptimizer);
9897
optimizers.add(optionalNotBoundFilterOptimizer);
9998
optimizers.add(optionalBindLeftJoinOptimizer);
10099
optionalFilterJoinInserted = true;
101100
}
102-
optimizers.add(joinOptimizer);
101+
optimizers.add(existsSemiJoinOptimizer);
102+
optimizers.add(notExistsSemiJoinOptimizer);
103103
continue;
104104
}
105105
if (optimizer instanceof FilterOptimizer) {
106106
optimizers.add(optimizer);
107107
optimizers.add(unionCommonFilterBindingSetOptimizer);
108-
if (enableOptionalFilterJoin && !optionalFilterJoinInserted) {
109-
optimizers.add(optionalFilterJoinOptimizer);
110-
optimizers.add(optionalNotBoundFilterOptimizer);
111-
optimizers.add(optionalBindLeftJoinOptimizer);
112-
optionalFilterJoinInserted = true;
113-
}
114108
if (!statementPatternInserted) {
115109
if (enableUnionCommonPullUp) {
116110
optimizers.add(unionCommonStatementPatternOptimizer);
@@ -132,15 +126,15 @@ public Iterable<QueryOptimizer> getOptimizers() {
132126
optimizers.add(minusOptimizer);
133127
optimizers.add(preJoinFilterOptimizer);
134128
optimizers.add(sparqlUoOptimizer);
135-
optimizers.add(existsSemiJoinOptimizer);
136-
optimizers.add(notExistsSemiJoinOptimizer);
129+
optimizers.add(joinOptimizer);
137130
if (enableOptionalFilterJoin && !optionalFilterJoinInserted) {
138131
optimizers.add(optionalFilterJoinOptimizer);
139132
optimizers.add(optionalNotBoundFilterOptimizer);
140133
optimizers.add(optionalBindLeftJoinOptimizer);
141134
optionalFilterJoinInserted = true;
142135
}
143-
optimizers.add(joinOptimizer);
136+
optimizers.add(existsSemiJoinOptimizer);
137+
optimizers.add(notExistsSemiJoinOptimizer);
144138
}
145139
if (!bindingSetUnionInserted) {
146140
optimizers.add(bindingSetAssignmentUnionOptimizer);
@@ -154,6 +148,24 @@ public Iterable<QueryOptimizer> getOptimizers() {
154148
return optimizers;
155149
}
156150

151+
private static SparqlUoConfig disableOptionalFilterJoin(SparqlUoConfig config) {
152+
if (!config.enableOptionalFilterJoin()) {
153+
return config;
154+
}
155+
return SparqlUoConfig.builder()
156+
.allowNonImprovingTransforms(config.allowNonImprovingTransforms())
157+
.assumedVarDomainCardinality(config.assumedVarDomainCardinality())
158+
.optionalMatchRate(config.optionalMatchRate())
159+
.optionalMultiplicity(config.optionalMultiplicity())
160+
.debugLogging(config.debugLogging())
161+
.simulateJoinOrder(config.simulateJoinOrder())
162+
.maxBindingSetAssignmentUnionSize(config.maxBindingSetAssignmentUnionSize())
163+
.enableMinusUnionSplit(config.enableMinusUnionSplit())
164+
.enableOptionalFilterJoin(false)
165+
.enableUnionCommonPrefixPullUp(config.enableUnionCommonPrefixPullUp())
166+
.build();
167+
}
168+
157169
private static final class LimitAwareFilterOptimizer extends FilterOptimizer {
158170
@Override
159171
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*
99
* SPDX-License-Identifier: BSD-3-Clause
1010
*******************************************************************************/
11+
// Some portions generated by Codex
1112

1213
package org.eclipse.rdf4j.query.algebra.evaluation.optimizer;
1314

@@ -18,6 +19,7 @@
1819
import org.eclipse.rdf4j.query.algebra.Projection;
1920
import org.eclipse.rdf4j.query.algebra.TupleExpr;
2021
import org.eclipse.rdf4j.query.algebra.Union;
22+
import org.eclipse.rdf4j.query.algebra.VariableScopeChange;
2123
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
2224
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
2325

@@ -34,6 +36,22 @@ public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings)
3436
tupleExpr.visit(new UnionScopeChangeFixer());
3537
}
3638

39+
private static void clearRedundantScopeChangeOnUnionArg(TupleExpr arg) {
40+
if (arg == null || !(arg instanceof VariableScopeChange)
41+
|| !((VariableScopeChange) arg).isVariableScopeChange()) {
42+
return;
43+
}
44+
45+
// If the union itself is no longer a scope change (because its args contain no BIND/VALUES),
46+
// then any scope-change flag on a plain graph pattern group root is redundant. Avoid clearing
47+
// on nodes that are known to be genuine scope-change boundaries.
48+
if (arg instanceof Union || arg instanceof Projection) {
49+
return;
50+
}
51+
52+
((VariableScopeChange) arg).setVariableScopeChange(false);
53+
}
54+
3755
private static class UnionScopeChangeFixer extends AbstractSimpleQueryModelVisitor<RuntimeException> {
3856

3957
private UnionScopeChangeFixer() {
@@ -59,6 +77,8 @@ public void meet(Union union) {
5977
// Neither argument of the union contains a BIND or VALUES clause, we can safely ignore scope change
6078
// for binding injection
6179
union.setVariableScopeChange(false);
80+
clearRedundantScopeChangeOnUnionArg(union.getLeftArg());
81+
clearRedundantScopeChangeOnUnionArg(union.getRightArg());
6282
}
6383
}
6484
}

0 commit comments

Comments
 (0)