Skip to content

Commit 89122c1

Browse files
committed
more potential optimisations
1 parent d1b1d53 commit 89122c1

9 files changed

Lines changed: 786 additions & 5 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
// Some portions generated by Codex
12+
package org.eclipse.rdf4j.query.algebra.evaluation.optimizer;
13+
14+
import java.util.HashSet;
15+
import java.util.Set;
16+
17+
import org.eclipse.rdf4j.query.BindingSet;
18+
import org.eclipse.rdf4j.query.Dataset;
19+
import org.eclipse.rdf4j.query.algebra.And;
20+
import org.eclipse.rdf4j.query.algebra.BinaryValueOperator;
21+
import org.eclipse.rdf4j.query.algebra.Bound;
22+
import org.eclipse.rdf4j.query.algebra.Coalesce;
23+
import org.eclipse.rdf4j.query.algebra.Exists;
24+
import org.eclipse.rdf4j.query.algebra.Filter;
25+
import org.eclipse.rdf4j.query.algebra.If;
26+
import org.eclipse.rdf4j.query.algebra.Join;
27+
import org.eclipse.rdf4j.query.algebra.LeftJoin;
28+
import org.eclipse.rdf4j.query.algebra.NAryValueOperator;
29+
import org.eclipse.rdf4j.query.algebra.Not;
30+
import org.eclipse.rdf4j.query.algebra.Or;
31+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
32+
import org.eclipse.rdf4j.query.algebra.UnaryValueOperator;
33+
import org.eclipse.rdf4j.query.algebra.ValueConstant;
34+
import org.eclipse.rdf4j.query.algebra.ValueExpr;
35+
import org.eclipse.rdf4j.query.algebra.Var;
36+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
37+
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
38+
39+
/**
40+
* Rewrites Filter(LeftJoin(...)) into Filter(Join(...)) when the filter condition depends on optional-only variables in
41+
* a strict way, making the OPTIONAL mandatory.
42+
*/
43+
public class OptionalFilterJoinOptimizer implements QueryOptimizer {
44+
45+
@Override
46+
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
47+
tupleExpr.visit(new OptionalFilterVisitor());
48+
}
49+
50+
private static final class OptionalFilterVisitor extends AbstractSimpleQueryModelVisitor<RuntimeException> {
51+
@Override
52+
public void meet(Filter filter) {
53+
super.meet(filter);
54+
if (!(filter.getArg() instanceof LeftJoin)) {
55+
return;
56+
}
57+
LeftJoin leftJoin = (LeftJoin) filter.getArg();
58+
if (leftJoin.getCondition() != null) {
59+
return;
60+
}
61+
Set<String> rightOnly = new HashSet<>(leftJoin.getRightArg().getBindingNames());
62+
rightOnly.removeAll(leftJoin.getLeftArg().getBindingNames());
63+
if (rightOnly.isEmpty()) {
64+
return;
65+
}
66+
if (!requiresRightVars(filter.getCondition(), rightOnly)) {
67+
return;
68+
}
69+
Join join = new Join(leftJoin.getLeftArg(), leftJoin.getRightArg());
70+
filter.setArg(join);
71+
}
72+
}
73+
74+
private static boolean requiresRightVars(ValueExpr expr, Set<String> rightOnly) {
75+
if (expr == null || rightOnly.isEmpty()) {
76+
return false;
77+
}
78+
if (expr instanceof Var) {
79+
return rightOnly.contains(((Var) expr).getName());
80+
}
81+
if (expr instanceof ValueConstant) {
82+
return false;
83+
}
84+
if (expr instanceof Bound) {
85+
return rightOnly.contains(((Bound) expr).getArg().getName());
86+
}
87+
if (expr instanceof If || expr instanceof Coalesce || expr instanceof Exists) {
88+
return false;
89+
}
90+
if (expr instanceof Or) {
91+
Or or = (Or) expr;
92+
return requiresRightVars(or.getLeftArg(), rightOnly) && requiresRightVars(or.getRightArg(), rightOnly);
93+
}
94+
if (expr instanceof And) {
95+
And and = (And) expr;
96+
return requiresRightVars(and.getLeftArg(), rightOnly) || requiresRightVars(and.getRightArg(), rightOnly);
97+
}
98+
if (expr instanceof Not) {
99+
return requiresRightVars(((Not) expr).getArg(), rightOnly);
100+
}
101+
if (expr instanceof UnaryValueOperator) {
102+
return requiresRightVars(((UnaryValueOperator) expr).getArg(), rightOnly);
103+
}
104+
if (expr instanceof BinaryValueOperator) {
105+
BinaryValueOperator binary = (BinaryValueOperator) expr;
106+
return requiresRightVars(binary.getLeftArg(), rightOnly)
107+
|| requiresRightVars(binary.getRightArg(), rightOnly);
108+
}
109+
if (expr instanceof NAryValueOperator) {
110+
for (ValueExpr arg : ((NAryValueOperator) expr).getArguments()) {
111+
if (requiresRightVars(arg, rightOnly)) {
112+
return true;
113+
}
114+
}
115+
return false;
116+
}
117+
return false;
118+
}
119+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class SparqlUoOptimizer implements QueryOptimizer {
4444
private final BeTreeBuilder builder;
4545
private final BeTreeSerializer serializer = new BeTreeSerializer();
4646
private final BeTreeTransformer transformer;
47+
private final OptionalFilterJoinOptimizer optionalFilterJoinOptimizer = new OptionalFilterJoinOptimizer();
4748

4849
public SparqlUoOptimizer(EvaluationStatistics evaluationStatistics) {
4950
this(evaluationStatistics, SparqlUoConfig.fromSystemProperties());
@@ -65,6 +66,7 @@ public SparqlUoOptimizer(EvaluationStatistics evaluationStatistics, SparqlUoConf
6566
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
6667
PARENT_REFERENCE_CLEANER.optimize(tupleExpr, dataset, bindings);
6768
tupleExpr.visit(new SparqlUoVisitor());
69+
optionalFilterJoinOptimizer.optimize(tupleExpr, dataset, bindings);
6870
}
6971

7072
private class SparqlUoVisitor extends AbstractQueryModelVisitor<RuntimeException> {

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ public class SparqlUoQueryOptimizerPipeline implements QueryOptimizerPipeline {
2626
private final QueryOptimizerPipeline delegate;
2727
private final SparqlUoOptimizer sparqlUoOptimizer;
2828
private final BindingSetAssignmentUnionOptimizer bindingSetAssignmentUnionOptimizer;
29+
private final UnionCommonStatementPatternOptimizer unionCommonStatementPatternOptimizer;
2930
private final UnionCommonFilterBindingSetOptimizer unionCommonFilterBindingSetOptimizer;
31+
private final OptionalFilterJoinOptimizer optionalFilterJoinOptimizer;
3032
private final QueryJoinOptimizer joinOptimizer;
3133

3234
public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource tripleSource,
@@ -40,7 +42,9 @@ public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource
4042
this.sparqlUoOptimizer = new SparqlUoOptimizer(evaluationStatistics, config);
4143
this.bindingSetAssignmentUnionOptimizer = new BindingSetAssignmentUnionOptimizer(
4244
config.maxBindingSetAssignmentUnionSize());
45+
this.unionCommonStatementPatternOptimizer = new UnionCommonStatementPatternOptimizer(evaluationStatistics);
4346
this.unionCommonFilterBindingSetOptimizer = new UnionCommonFilterBindingSetOptimizer();
47+
this.optionalFilterJoinOptimizer = new OptionalFilterJoinOptimizer();
4448
this.joinOptimizer = new QueryJoinOptimizer(evaluationStatistics, strategy.isTrackResultSize(), tripleSource,
4549
false);
4650
}
@@ -50,6 +54,7 @@ public Iterable<QueryOptimizer> getOptimizers() {
5054
List<QueryOptimizer> optimizers = new ArrayList<>();
5155
boolean inserted = false;
5256
boolean bindingSetUnionInserted = false;
57+
boolean statementPatternInserted = false;
5358
for (QueryOptimizer optimizer : delegate.getOptimizers()) {
5459
if (optimizer instanceof QueryJoinOptimizer) {
5560
if (!inserted) {
@@ -59,14 +64,21 @@ public Iterable<QueryOptimizer> getOptimizers() {
5964
optimizers.add(joinOptimizer);
6065
continue;
6166
}
67+
if (optimizer instanceof FilterOptimizer) {
68+
optimizers.add(optimizer);
69+
optimizers.add(unionCommonFilterBindingSetOptimizer);
70+
optimizers.add(optionalFilterJoinOptimizer);
71+
if (!statementPatternInserted) {
72+
optimizers.add(unionCommonStatementPatternOptimizer);
73+
statementPatternInserted = true;
74+
}
75+
continue;
76+
}
6277
optimizers.add(optimizer);
6378
if (optimizer instanceof IterativeEvaluationOptimizer) {
6479
optimizers.add(bindingSetAssignmentUnionOptimizer);
6580
bindingSetUnionInserted = true;
6681
}
67-
if (optimizer instanceof FilterOptimizer) {
68-
optimizers.add(unionCommonFilterBindingSetOptimizer);
69-
}
7082
}
7183
if (!inserted) {
7284
optimizers.add(sparqlUoOptimizer);
@@ -75,6 +87,9 @@ public Iterable<QueryOptimizer> getOptimizers() {
7587
if (!bindingSetUnionInserted) {
7688
optimizers.add(bindingSetAssignmentUnionOptimizer);
7789
}
90+
if (!statementPatternInserted) {
91+
optimizers.add(unionCommonStatementPatternOptimizer);
92+
}
7893
return optimizers;
7994
}
8095
}

0 commit comments

Comments
 (0)