11
11
12
12
package org .eclipse .rdf4j .query .algebra .evaluation .optimizer ;
13
13
14
+ import java .util .Objects ;
14
15
import java .util .Set ;
15
16
16
17
import org .eclipse .rdf4j .query .BindingSet ;
31
32
import org .eclipse .rdf4j .query .algebra .StatementPattern ;
32
33
import org .eclipse .rdf4j .query .algebra .TupleExpr ;
33
34
import org .eclipse .rdf4j .query .algebra .Union ;
35
+ import org .eclipse .rdf4j .query .algebra .VariableScopeChange ;
34
36
import org .eclipse .rdf4j .query .algebra .evaluation .QueryOptimizer ;
35
37
import org .eclipse .rdf4j .query .algebra .helpers .AbstractQueryModelVisitor ;
36
38
import org .eclipse .rdf4j .query .algebra .helpers .AbstractSimpleQueryModelVisitor ;
@@ -78,9 +80,49 @@ public class FilterOptimizer implements QueryOptimizer {
78
80
79
81
@ Override
80
82
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
+ }
84
126
}
85
127
86
128
private static class FilterUnMerger extends AbstractSimpleQueryModelVisitor <RuntimeException > {
@@ -95,6 +137,7 @@ public void meet(Filter filter) {
95
137
And and = (And ) filter .getCondition ();
96
138
filter .setCondition (and .getLeftArg ().clone ());
97
139
Filter newFilter = new Filter (filter .getArg ().clone (), and .getRightArg ().clone ());
140
+ transferScopeChange (filter , newFilter ); // preserve scope flag
98
141
filter .replaceChildNode (filter .getArg (), newFilter );
99
142
}
100
143
super .meet (filter );
@@ -117,6 +160,7 @@ public void meet(Filter filter) {
117
160
And merge = new And (childFilter .getCondition ().clone (), filter .getCondition ().clone ());
118
161
119
162
Filter newFilter = new Filter (childFilter .getArg ().clone (), merge );
163
+ transferScopeChange (filter , newFilter ); // both have same scope flag
120
164
parent .replaceChildNode (filter , newFilter );
121
165
}
122
166
}
@@ -190,6 +234,7 @@ public void meet(LeftJoin leftJoin) {
190
234
public void meet (Union union ) {
191
235
Filter clone = new Filter ();
192
236
clone .setCondition (filter .getCondition ().clone ());
237
+ transferScopeChange (filter , clone );
193
238
194
239
relocate (filter , union .getLeftArg ());
195
240
relocate (clone , union .getRightArg ());
@@ -202,6 +247,7 @@ public void meet(Union union) {
202
247
public void meet (Difference node ) {
203
248
Filter clone = new Filter ();
204
249
clone .setCondition (filter .getCondition ().clone ());
250
+ transferScopeChange (filter , clone );
205
251
206
252
relocate (filter , node .getLeftArg ());
207
253
relocate (clone , node .getRightArg ());
@@ -214,6 +260,7 @@ public void meet(Difference node) {
214
260
public void meet (Intersection node ) {
215
261
Filter clone = new Filter ();
216
262
clone .setCondition (filter .getCondition ().clone ());
263
+ transferScopeChange (filter , clone );
217
264
218
265
relocate (filter , node .getLeftArg ());
219
266
relocate (clone , node .getRightArg ());
@@ -278,5 +325,4 @@ private void relocate(Filter filter, TupleExpr newFilterArg) {
278
325
}
279
326
}
280
327
}
281
-
282
328
}
0 commit comments