Skip to content

Commit 33c5d45

Browse files
committed
wip
1 parent 2a930c6 commit 33c5d45

7 files changed

Lines changed: 603 additions & 17 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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.Set;
15+
16+
import org.eclipse.rdf4j.query.BindingSet;
17+
import org.eclipse.rdf4j.query.Dataset;
18+
import org.eclipse.rdf4j.query.algebra.Extension;
19+
import org.eclipse.rdf4j.query.algebra.ExtensionElem;
20+
import org.eclipse.rdf4j.query.algebra.LeftJoin;
21+
import org.eclipse.rdf4j.query.algebra.SingletonSet;
22+
import org.eclipse.rdf4j.query.algebra.TupleExpr;
23+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
24+
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
25+
26+
/**
27+
* Rewrites OPTIONAL blocks that only contain BIND expressions into direct Extensions.
28+
*/
29+
public class OptionalBindLeftJoinOptimizer implements QueryOptimizer {
30+
31+
@Override
32+
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
33+
tupleExpr.visit(new OptionalBindVisitor());
34+
}
35+
36+
private static final class OptionalBindVisitor extends AbstractSimpleQueryModelVisitor<RuntimeException> {
37+
@Override
38+
public void meet(LeftJoin leftJoin) {
39+
super.meet(leftJoin);
40+
if (leftJoin.getCondition() != null) {
41+
return;
42+
}
43+
if (!(leftJoin.getRightArg() instanceof Extension)) {
44+
return;
45+
}
46+
Extension extension = (Extension) leftJoin.getRightArg();
47+
if (!(extension.getArg() instanceof SingletonSet)) {
48+
return;
49+
}
50+
Set<String> leftBindings = leftJoin.getLeftArg().getBindingNames();
51+
for (ExtensionElem elem : extension.getElements()) {
52+
if (leftBindings.contains(elem.getName())) {
53+
return;
54+
}
55+
}
56+
Extension rewritten = new Extension(leftJoin.getLeftArg());
57+
for (ExtensionElem elem : extension.getElements()) {
58+
rewritten.addElement(elem.clone());
59+
}
60+
leftJoin.replaceWith(rewritten);
61+
}
62+
}
63+
}

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

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,35 @@ private static final class OptionalFilterVisitor extends AbstractSimpleQueryMode
5252
@Override
5353
public void meet(Filter filter) {
5454
super.meet(filter);
55-
if (!(filter.getArg() instanceof LeftJoin)) {
56-
return;
57-
}
58-
LeftJoin leftJoin = (LeftJoin) filter.getArg();
59-
if (leftJoin.getCondition() != null) {
60-
return;
61-
}
62-
Set<String> rightOnly = new HashSet<>(leftJoin.getRightArg().getBindingNames());
63-
rightOnly.removeAll(leftJoin.getLeftArg().getBindingNames());
64-
if (rightOnly.isEmpty()) {
65-
return;
66-
}
67-
if (!requiresRightVars(filter.getCondition(), rightOnly)) {
68-
return;
69-
}
70-
Join join = new Join(leftJoin.getLeftArg(), leftJoin.getRightArg());
71-
filter.setArg(join);
55+
rewriteOptionalJoins(filter.getCondition(), filter.getArg());
7256
}
7357
}
7458

59+
private static void rewriteOptionalJoins(ValueExpr condition, TupleExpr arg) {
60+
if (condition == null || arg == null) {
61+
return;
62+
}
63+
arg.visit(new AbstractSimpleQueryModelVisitor<RuntimeException>() {
64+
@Override
65+
public void meet(LeftJoin leftJoin) {
66+
super.meet(leftJoin);
67+
if (leftJoin.getCondition() != null) {
68+
return;
69+
}
70+
Set<String> rightOnly = new HashSet<>(leftJoin.getRightArg().getBindingNames());
71+
rightOnly.removeAll(leftJoin.getLeftArg().getBindingNames());
72+
if (rightOnly.isEmpty()) {
73+
return;
74+
}
75+
if (!requiresRightVars(condition, rightOnly)) {
76+
return;
77+
}
78+
Join join = new Join(leftJoin.getLeftArg(), leftJoin.getRightArg());
79+
leftJoin.replaceWith(join);
80+
}
81+
});
82+
}
83+
7584
private static boolean requiresRightVars(ValueExpr expr, Set<String> rightOnly) {
7685
if (expr == null || rightOnly.isEmpty()) {
7786
return false;

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
@@ -46,6 +46,7 @@ public class SparqlUoOptimizer implements QueryOptimizer {
4646
private final BeTreeTransformer transformer;
4747
private final OptionalFilterJoinOptimizer optionalFilterJoinOptimizer = new OptionalFilterJoinOptimizer();
4848
private final OptionalNotBoundFilterOptimizer optionalNotBoundFilterOptimizer = new OptionalNotBoundFilterOptimizer();
49+
private final OptionalBindLeftJoinOptimizer optionalBindLeftJoinOptimizer = new OptionalBindLeftJoinOptimizer();
4950

5051
public SparqlUoOptimizer(EvaluationStatistics evaluationStatistics) {
5152
this(evaluationStatistics, SparqlUoConfig.fromSystemProperties());
@@ -70,6 +71,7 @@ public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings)
7071
if (config.enableOptionalFilterJoin()) {
7172
optionalFilterJoinOptimizer.optimize(tupleExpr, dataset, bindings);
7273
optionalNotBoundFilterOptimizer.optimize(tupleExpr, dataset, bindings);
74+
optionalBindLeftJoinOptimizer.optimize(tupleExpr, dataset, bindings);
7375
}
7476
}
7577

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ public class SparqlUoQueryOptimizerPipeline implements QueryOptimizerPipeline {
2727
private final SparqlUoOptimizer sparqlUoOptimizer;
2828
private final BindingSetAssignmentUnionOptimizer bindingSetAssignmentUnionOptimizer;
2929
private final UnionCommonStatementPatternOptimizer unionCommonStatementPatternOptimizer;
30+
private final UnionCommonJoinFactorOptimizer unionCommonJoinFactorOptimizer;
3031
private final UnionCommonFilterBindingSetOptimizer unionCommonFilterBindingSetOptimizer;
3132
private final OptionalFilterJoinOptimizer optionalFilterJoinOptimizer;
3233
private final OptionalNotBoundFilterOptimizer optionalNotBoundFilterOptimizer;
34+
private final OptionalBindLeftJoinOptimizer optionalBindLeftJoinOptimizer;
3335
private final MinusOptimizer minusOptimizer;
3436
private final ExistsConstantOptimizer existsConstantOptimizer;
3537
private final QueryJoinOptimizer joinOptimizer;
@@ -47,9 +49,12 @@ public SparqlUoQueryOptimizerPipeline(EvaluationStrategy strategy, TripleSource
4749
this.bindingSetAssignmentUnionOptimizer = new BindingSetAssignmentUnionOptimizer(
4850
config.maxBindingSetAssignmentUnionSize());
4951
this.unionCommonStatementPatternOptimizer = new UnionCommonStatementPatternOptimizer(evaluationStatistics);
52+
this.unionCommonJoinFactorOptimizer = new UnionCommonJoinFactorOptimizer(evaluationStatistics,
53+
config.allowNonImprovingTransforms());
5054
this.unionCommonFilterBindingSetOptimizer = new UnionCommonFilterBindingSetOptimizer();
5155
this.optionalFilterJoinOptimizer = new OptionalFilterJoinOptimizer();
5256
this.optionalNotBoundFilterOptimizer = new OptionalNotBoundFilterOptimizer();
57+
this.optionalBindLeftJoinOptimizer = new OptionalBindLeftJoinOptimizer();
5358
this.minusOptimizer = new MinusOptimizer(config.enableMinusUnionSplit());
5459
this.existsConstantOptimizer = new ExistsConstantOptimizer();
5560
this.joinOptimizer = new QueryJoinOptimizer(evaluationStatistics, strategy.isTrackResultSize(), tripleSource,
@@ -63,6 +68,7 @@ public Iterable<QueryOptimizer> getOptimizers() {
6368
boolean inserted = false;
6469
boolean bindingSetUnionInserted = false;
6570
boolean statementPatternInserted = false;
71+
boolean commonFactorInserted = false;
6672
for (QueryOptimizer optimizer : delegate.getOptimizers()) {
6773
if (optimizer instanceof QueryJoinOptimizer) {
6874
if (!inserted) {
@@ -80,10 +86,13 @@ public Iterable<QueryOptimizer> getOptimizers() {
8086
if (enableOptionalFilterJoin) {
8187
optimizers.add(optionalFilterJoinOptimizer);
8288
optimizers.add(optionalNotBoundFilterOptimizer);
89+
optimizers.add(optionalBindLeftJoinOptimizer);
8390
}
8491
if (!statementPatternInserted) {
8592
optimizers.add(unionCommonStatementPatternOptimizer);
93+
optimizers.add(unionCommonJoinFactorOptimizer);
8694
statementPatternInserted = true;
95+
commonFactorInserted = true;
8796
}
8897
continue;
8998
}
@@ -105,6 +114,9 @@ public Iterable<QueryOptimizer> getOptimizers() {
105114
if (!statementPatternInserted) {
106115
optimizers.add(unionCommonStatementPatternOptimizer);
107116
}
117+
if (!commonFactorInserted) {
118+
optimizers.add(unionCommonJoinFactorOptimizer);
119+
}
108120
return optimizers;
109121
}
110122
}

0 commit comments

Comments
 (0)