Skip to content

Commit a4d815a

Browse files
committed
wip (+14 squashed commits)
Squashed commits: [fda9de2] wip [bb1798e] wip [5316527] wip [05483e0] wip [bfe4194] wip [543ebb8] wip [a94797a] wip [0e84556] wip [82dc4fb] wip [b40e73f] wip [1dc8888] wip [a675c99] wip [c3613cd] wip [442321b] initial implementation # Conflicts: # scripts/checkCopyrightPresent.sh
1 parent 84113ca commit a4d815a

27 files changed

Lines changed: 2647 additions & 32 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ public DefaultEvaluationStrategy(TripleSource tripleSource, Dataset dataset,
285285
this.dataset = dataset;
286286
this.serviceResolver = serviceResolver;
287287
this.iterationCacheSyncThreshold = iterationCacheSyncTreshold;
288-
this.pipeline = new org.eclipse.rdf4j.query.algebra.evaluation.optimizer.StandardQueryOptimizerPipeline(this,
288+
this.pipeline = new org.eclipse.rdf4j.query.algebra.evaluation.optimizer.SparqlUoQueryOptimizerPipeline(this,
289289
tripleSource, evaluationStatistics);
290290
this.trackResultSize = trackResultSize;
291291
this.tupleFuncRegistry = tupleFunctionRegistry;

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

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.eclipse.rdf4j.query.BindingSet;
1818
import org.eclipse.rdf4j.query.Dataset;
1919
import org.eclipse.rdf4j.query.algebra.And;
20+
import org.eclipse.rdf4j.query.algebra.BindingSetAssignment;
2021
import org.eclipse.rdf4j.query.algebra.Difference;
2122
import org.eclipse.rdf4j.query.algebra.EmptySet;
2223
import org.eclipse.rdf4j.query.algebra.Filter;
@@ -86,14 +87,18 @@ public void meet(Join join) {
8687
newUnion.setVariableScopeChange(union.isVariableScopeChange());
8788
join.replaceWith(newUnion);
8889
newUnion.visit(this);
89-
} else if (leftArg instanceof LeftJoin && isWellDesigned((LeftJoin) leftArg)) {
90+
} else if (leftArg instanceof LeftJoin && isWellDesigned((LeftJoin) leftArg)
91+
&& !bindsOptionalVars(rightArg, (LeftJoin) leftArg)
92+
&& !containsBindingSetAssignment(rightArg)) {
9093
// sort left join above normal joins
9194
LeftJoin leftJoin = (LeftJoin) leftArg;
9295
join.replaceWith(leftJoin);
9396
join.setLeftArg(leftJoin.getLeftArg());
9497
leftJoin.setLeftArg(join);
9598
leftJoin.visit(this);
96-
} else if (rightArg instanceof LeftJoin && isWellDesigned((LeftJoin) rightArg)) {
99+
} else if (rightArg instanceof LeftJoin && isWellDesigned((LeftJoin) rightArg)
100+
&& !bindsOptionalVars(leftArg, (LeftJoin) rightArg)
101+
&& !containsBindingSetAssignment(leftArg)) {
97102
// sort left join above normal joins
98103
LeftJoin leftJoin = (LeftJoin) rightArg;
99104
join.replaceWith(leftJoin);
@@ -244,6 +249,50 @@ private boolean isWellDesigned(LeftJoin leftJoin) {
244249
return checkAgainstParent(leftJoin, problemVars);
245250
}
246251

252+
private boolean bindsOptionalVars(TupleExpr otherArg, LeftJoin leftJoin) {
253+
Set<String> optionalVars = VarNameCollector.process(leftJoin.getRightArg());
254+
if (leftJoin.hasCondition()) {
255+
optionalVars = new HashSet<>(optionalVars);
256+
optionalVars.addAll(VarNameCollector.process(leftJoin.getCondition()));
257+
}
258+
259+
optionalVars = retainAll(optionalVars, leftJoin.getLeftArg().getBindingNames());
260+
if (optionalVars.isEmpty()) {
261+
return false;
262+
}
263+
264+
Set<String> otherBindingNames = otherArg.getBindingNames();
265+
for (String var : optionalVars) {
266+
if (otherBindingNames.contains(var)) {
267+
return true;
268+
}
269+
}
270+
return false;
271+
}
272+
273+
private boolean containsBindingSetAssignment(TupleExpr tupleExpr) {
274+
BindingSetAssignmentFinder finder = new BindingSetAssignmentFinder();
275+
tupleExpr.visit(finder);
276+
return finder.found;
277+
}
278+
279+
private static class BindingSetAssignmentFinder extends AbstractQueryModelVisitor<RuntimeException> {
280+
281+
private boolean found;
282+
283+
@Override
284+
public void meet(BindingSetAssignment node) {
285+
found = true;
286+
}
287+
288+
@Override
289+
protected void meetNode(QueryModelNode node) {
290+
if (!found) {
291+
super.meetNode(node);
292+
}
293+
}
294+
}
295+
247296
private Set<String> retainAll(Set<String> problemVars, Set<String> leftBindingNames) {
248297
if (!leftBindingNames.isEmpty() && !problemVars.isEmpty()) {
249298
if (leftBindingNames.size() > problemVars.size()) {
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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 org.eclipse.rdf4j.query.BindingSet;
15+
import org.eclipse.rdf4j.query.Dataset;
16+
import org.eclipse.rdf4j.query.algebra.Exists;
17+
import org.eclipse.rdf4j.query.algebra.Join;
18+
import org.eclipse.rdf4j.query.algebra.LeftJoin;
19+
import org.eclipse.rdf4j.query.algebra.Not;
20+
import org.eclipse.rdf4j.query.algebra.Projection;
21+
import org.eclipse.rdf4j.query.algebra.QueryRoot;
22+
import org.eclipse.rdf4j.query.algebra.Service;
23+
import org.eclipse.rdf4j.query.algebra.StatementPattern;
24+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
25+
import org.eclipse.rdf4j.query.algebra.Union;
26+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
27+
import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics;
28+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.BeCostEstimator;
29+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.BeGroupNode;
30+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.BeTreeBuilder;
31+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.BeTreeSerializer;
32+
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.sparqluo.BeTreeTransformer;
33+
import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
34+
35+
public class SparqlUoOptimizer implements QueryOptimizer {
36+
37+
private static final ParentReferenceCleaner PARENT_REFERENCE_CLEANER = new ParentReferenceCleaner();
38+
39+
private final BeTreeBuilder builder = new BeTreeBuilder();
40+
private final BeTreeSerializer serializer = new BeTreeSerializer();
41+
private final BeTreeTransformer transformer;
42+
43+
public SparqlUoOptimizer(EvaluationStatistics evaluationStatistics) {
44+
this(evaluationStatistics, false);
45+
}
46+
47+
public SparqlUoOptimizer(EvaluationStatistics evaluationStatistics, boolean allowNonImprovingTransforms) {
48+
this.transformer = new BeTreeTransformer(new BeCostEstimator(evaluationStatistics),
49+
allowNonImprovingTransforms);
50+
}
51+
52+
@Override
53+
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
54+
PARENT_REFERENCE_CLEANER.optimize(tupleExpr, dataset, bindings);
55+
tupleExpr.visit(new SparqlUoVisitor());
56+
}
57+
58+
private class SparqlUoVisitor extends AbstractQueryModelVisitor<RuntimeException> {
59+
60+
@Override
61+
public void meet(Join node) {
62+
if (isGroupRoot(node)) {
63+
rewrite(node);
64+
return;
65+
}
66+
super.meet(node);
67+
}
68+
69+
@Override
70+
public void meet(LeftJoin node) {
71+
if (node.getCondition() == null && isGroupRoot(node)) {
72+
rewrite(node);
73+
return;
74+
}
75+
super.meet(node);
76+
}
77+
78+
@Override
79+
public void meet(Union node) {
80+
if (isGroupRoot(node)) {
81+
rewrite(node);
82+
return;
83+
}
84+
super.meet(node);
85+
}
86+
87+
@Override
88+
public void meet(StatementPattern node) {
89+
if (isGroupRoot(node)) {
90+
rewrite(node);
91+
}
92+
}
93+
94+
@Override
95+
public void meet(Service node) {
96+
// Skip SERVICE subtrees to avoid changing remote semantics.
97+
}
98+
99+
@Override
100+
public void meet(Projection node) {
101+
if (node.isSubquery() && !(node.getParentNode() instanceof QueryRoot)) {
102+
return;
103+
}
104+
super.meet(node);
105+
}
106+
107+
@Override
108+
public void meet(Exists node) {
109+
// Skip EXISTS subqueries.
110+
}
111+
112+
@Override
113+
public void meet(Not node) {
114+
// Skip NOT EXISTS subqueries.
115+
}
116+
117+
private boolean isGroupRoot(TupleExpr node) {
118+
if (node.getParentNode() == null) {
119+
return false;
120+
}
121+
return !(node.getParentNode() instanceof Join
122+
|| node.getParentNode() instanceof LeftJoin
123+
|| node.getParentNode() instanceof Union);
124+
}
125+
126+
private void rewrite(TupleExpr node) {
127+
BeGroupNode group = builder.build(node);
128+
transformer.transform(group);
129+
TupleExpr replacement = serializer.serialize(group);
130+
node.replaceWith(replacement);
131+
}
132+
}
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.ArrayList;
15+
import java.util.List;
16+
17+
import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategy;
18+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
19+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizerPipeline;
20+
import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource;
21+
import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics;
22+
23+
public class SparqlUoQueryOptimizerPipeline implements QueryOptimizerPipeline {
24+
25+
private final QueryOptimizerPipeline delegate;
26+
private final SparqlUoOptimizer sparqlUoOptimizer;
27+
28+
public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource tripleSource,
29+
EvaluationStatistics evaluationStatistics) {
30+
this.delegate = new StandardQueryOptimizerPipeline(strategy, tripleSource, evaluationStatistics);
31+
this.sparqlUoOptimizer = new SparqlUoOptimizer(evaluationStatistics);
32+
}
33+
34+
@Override
35+
public Iterable<QueryOptimizer> getOptimizers() {
36+
List<QueryOptimizer> optimizers = new ArrayList<>();
37+
boolean inserted = false;
38+
for (QueryOptimizer optimizer : delegate.getOptimizers()) {
39+
if (!inserted && optimizer instanceof QueryJoinOptimizer) {
40+
optimizers.add(sparqlUoOptimizer);
41+
inserted = true;
42+
}
43+
optimizers.add(optimizer);
44+
}
45+
if (!inserted) {
46+
optimizers.add(sparqlUoOptimizer);
47+
}
48+
return optimizers;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.sparqluo;
13+
14+
public abstract class AbstractBeNode implements BeNode {
15+
private BeNode parent;
16+
17+
@Override
18+
public BeNode getParent() {
19+
return parent;
20+
}
21+
22+
@Override
23+
public void setParent(BeNode parent) {
24+
this.parent = parent;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.sparqluo;
13+
14+
import java.util.Objects;
15+
16+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
17+
18+
public class BeBarrierNode extends AbstractBeNode {
19+
private final TupleExpr tupleExpr;
20+
21+
BeBarrierNode(TupleExpr tupleExpr) {
22+
this.tupleExpr = Objects.requireNonNull(tupleExpr, "tupleExpr");
23+
}
24+
25+
@Override
26+
public BeNodeType getType() {
27+
return BeNodeType.BARRIER;
28+
}
29+
30+
TupleExpr getTupleExpr() {
31+
return tupleExpr;
32+
}
33+
}

0 commit comments

Comments
 (0)