Skip to content

Commit d78001b

Browse files
authored
GH-4872 FilterOptimizer retains scoping of filters (#5316)
2 parents 158af44 + 96cdedf commit d78001b

File tree

3 files changed

+434
-8
lines changed

3 files changed

+434
-8
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void meet(Filter filter) {
5656
TupleExpr filterArg = filter.getArg();
5757

5858
for (int i = conjunctiveConstraints.size() - 1; i >= 1; i--) {
59-
Filter newFilter = new Filter(filterArg, conjunctiveConstraints.get(i));
59+
Filter newFilter = new Filter(filterArg, conjunctiveConstraints.get(i).clone());
6060
filterArg = newFilter;
6161
}
6262

@@ -77,11 +77,11 @@ public void meet(LeftJoin node) {
7777

7878
for (ValueExpr constraint : conjunctiveConstraints) {
7979
if (isWithinBindingScope(constraint, arg)) {
80-
arg = new Filter(arg, constraint);
80+
arg = new Filter(arg, constraint.clone());
8181
} else if (condition == null) {
8282
condition = constraint;
8383
} else {
84-
condition = new And(condition, constraint);
84+
condition = new And(condition, constraint.clone());
8585
}
8686
}
8787

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

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
package org.eclipse.rdf4j.query.algebra.evaluation.optimizer;
1313

14+
import java.util.Objects;
1415
import java.util.Set;
1516

1617
import org.eclipse.rdf4j.query.BindingSet;
@@ -31,6 +32,7 @@
3132
import org.eclipse.rdf4j.query.algebra.StatementPattern;
3233
import org.eclipse.rdf4j.query.algebra.TupleExpr;
3334
import org.eclipse.rdf4j.query.algebra.Union;
35+
import org.eclipse.rdf4j.query.algebra.VariableScopeChange;
3436
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
3537
import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
3638
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
@@ -78,9 +80,49 @@ public class FilterOptimizer implements QueryOptimizer {
7880

7981
@Override
8082
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
81-
tupleExpr.visit(new FilterUnMerger());
82-
tupleExpr.visit(new FilterOrganizer());
83-
tupleExpr.visit(new FilterMerger());
83+
Objects.requireNonNull(tupleExpr, "tupleExpr must not be null");
84+
optimizeScope(tupleExpr);
85+
}
86+
87+
/**
88+
* Bottom‑up optimisation: optimise each child subtree that starts a new variable scope, then run the three filter
89+
* passes on the current subtree.
90+
*/
91+
private void optimizeScope(TupleExpr expr) {
92+
// 1) recurse into nested scopes first
93+
expr.visit(new AbstractQueryModelVisitor<>() {
94+
final TupleExpr current = expr;
95+
96+
@Override
97+
protected void meetNode(QueryModelNode node) {
98+
if (node != current && node instanceof TupleExpr && node instanceof VariableScopeChange
99+
&& ((VariableScopeChange) node).isVariableScopeChange()) {
100+
101+
optimizeScope(((TupleExpr) node));
102+
// do NOT traverse further into that subtree with this visitor
103+
} else {
104+
super.meetNode(node);
105+
}
106+
}
107+
108+
});
109+
110+
// 2) run the three filter passes for *this* scope
111+
expr.visit(new FilterUnMerger());
112+
expr.visit(new FilterOrganizer());
113+
expr.visit(new FilterMerger());
114+
}
115+
116+
/**
117+
* Copies the {@code isVariableScopeChange} flag from one node to another if both implement
118+
* {@link VariableScopeChange}.
119+
*/
120+
private static void transferScopeChange(QueryModelNode source, QueryModelNode target) {
121+
if (source instanceof VariableScopeChange && target instanceof VariableScopeChange) {
122+
VariableScopeChange src = (VariableScopeChange) source;
123+
VariableScopeChange tgt = (VariableScopeChange) target;
124+
tgt.setVariableScopeChange(src.isVariableScopeChange());
125+
}
84126
}
85127

86128
private static class FilterUnMerger extends AbstractSimpleQueryModelVisitor<RuntimeException> {
@@ -95,6 +137,7 @@ public void meet(Filter filter) {
95137
And and = (And) filter.getCondition();
96138
filter.setCondition(and.getLeftArg().clone());
97139
Filter newFilter = new Filter(filter.getArg().clone(), and.getRightArg().clone());
140+
transferScopeChange(filter, newFilter); // preserve scope flag
98141
filter.replaceChildNode(filter.getArg(), newFilter);
99142
}
100143
super.meet(filter);
@@ -117,6 +160,7 @@ public void meet(Filter filter) {
117160
And merge = new And(childFilter.getCondition().clone(), filter.getCondition().clone());
118161

119162
Filter newFilter = new Filter(childFilter.getArg().clone(), merge);
163+
transferScopeChange(filter, newFilter); // both have same scope flag
120164
parent.replaceChildNode(filter, newFilter);
121165
}
122166
}
@@ -190,6 +234,7 @@ public void meet(LeftJoin leftJoin) {
190234
public void meet(Union union) {
191235
Filter clone = new Filter();
192236
clone.setCondition(filter.getCondition().clone());
237+
transferScopeChange(filter, clone);
193238

194239
relocate(filter, union.getLeftArg());
195240
relocate(clone, union.getRightArg());
@@ -202,6 +247,7 @@ public void meet(Union union) {
202247
public void meet(Difference node) {
203248
Filter clone = new Filter();
204249
clone.setCondition(filter.getCondition().clone());
250+
transferScopeChange(filter, clone);
205251

206252
relocate(filter, node.getLeftArg());
207253
relocate(clone, node.getRightArg());
@@ -214,6 +260,7 @@ public void meet(Difference node) {
214260
public void meet(Intersection node) {
215261
Filter clone = new Filter();
216262
clone.setCondition(filter.getCondition().clone());
263+
transferScopeChange(filter, clone);
217264

218265
relocate(filter, node.getLeftArg());
219266
relocate(clone, node.getRightArg());
@@ -278,5 +325,4 @@ private void relocate(Filter filter, TupleExpr newFilterArg) {
278325
}
279326
}
280327
}
281-
282328
}

0 commit comments

Comments
 (0)