Skip to content

Commit 47d096d

Browse files
xiedeyantumihaibudiu
authored andcommitted
[CALCITE-6335] Quidem tests should allow specifying optimization passes to apply to programs
1 parent aa11fd6 commit 47d096d

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# planner.iq - planner tests can customizable optimization rules
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one or more
4+
# contributor license agreements. See the NOTICE file distributed with
5+
# this work for additional information regarding copyright ownership.
6+
# The ASF licenses this file to you under the Apache License, Version 2.0
7+
# (the "License"); you may not use this file except in compliance with
8+
# the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
!use post
19+
!set outputformat mysql
20+
21+
# The following line is used to update the default planner rules.
22+
# "+" means add a rule, "-" means remove a rule. Only the "CoreRules." can be omitted.
23+
!set planner-rules "-INTERSECT_TO_DISTINCT,-EnumerableRules.ENUMERABLE_INTERSECT_RULE,+CoreRules.INTERSECT_TO_SEMI_JOIN"
24+
25+
# Test INTERSECT_TO_SEMI_JOIN
26+
with t (i) as (values (0), (1))
27+
select * from t as t1
28+
intersect
29+
select * from t as t2 where t2.i > 0;
30+
+---+
31+
| I |
32+
+---+
33+
| 1 |
34+
+---+
35+
(1 row)
36+
37+
!ok
38+
39+
EnumerableNestedLoopJoin(condition=[IS NOT DISTINCT FROM($0, $1)], joinType=[semi])
40+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
41+
EnumerableCalc(expr#0=[{inputs}], expr#1=[0], expr#2=[>($t0, $t1)], EXPR$0=[$t0], $condition=[$t2])
42+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
43+
!plan
44+
45+
# Test that rules set by "!set planner-rules" are sticky
46+
with t (i) as (values (0), (1))
47+
select * from t as t1
48+
intersect
49+
select * from t as t2 where t2.i > 0;
50+
+---+
51+
| I |
52+
+---+
53+
| 1 |
54+
+---+
55+
(1 row)
56+
57+
!ok
58+
59+
EnumerableNestedLoopJoin(condition=[IS NOT DISTINCT FROM($0, $1)], joinType=[semi])
60+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
61+
EnumerableCalc(expr#0=[{inputs}], expr#1=[0], expr#2=[>($t0, $t1)], EXPR$0=[$t0], $condition=[$t2])
62+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
63+
!plan
64+
65+
# This command is used to reset the planner rules to the original state.
66+
# Before running this command, the planner rules by "!set planner-rules" are sticky.
67+
!set planner-rules original
68+
69+
# Test that rules set by "!set planner-rules original" are reset
70+
with t (i) as (values (0), (1))
71+
select * from t as t1
72+
intersect
73+
select * from t as t2 where t2.i > 0;
74+
+---+
75+
| I |
76+
+---+
77+
| 1 |
78+
+---+
79+
(1 row)
80+
81+
!ok
82+
83+
EnumerableIntersect(all=[false])
84+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
85+
EnumerableCalc(expr#0=[{inputs}], expr#1=[0], expr#2=[>($t0, $t1)], EXPR$0=[$t0], $condition=[$t2])
86+
EnumerableValues(tuples=[[{ 0 }, { 1 }]])
87+
!plan
88+
89+
# End planner.iq

testkit/src/main/java/org/apache/calcite/test/QuidemTest.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616
*/
1717
package org.apache.calcite.test;
1818

19+
import org.apache.calcite.adapter.enumerable.EnumerableRules;
1920
import org.apache.calcite.avatica.AvaticaUtils;
2021
import org.apache.calcite.config.CalciteConnectionProperty;
2122
import org.apache.calcite.jdbc.CalciteConnection;
23+
import org.apache.calcite.plan.RelOptPlanner;
24+
import org.apache.calcite.plan.RelOptRule;
2225
import org.apache.calcite.prepare.Prepare;
26+
import org.apache.calcite.rel.rules.CoreRules;
2327
import org.apache.calcite.rel.type.RelDataType;
2428
import org.apache.calcite.rel.type.RelDataTypeFactory;
2529
import org.apache.calcite.runtime.Hook;
@@ -56,7 +60,9 @@
5660
import java.util.ArrayList;
5761
import java.util.Collection;
5862
import java.util.List;
63+
import java.util.function.Consumer;
5964
import java.util.function.Function;
65+
import java.util.regex.Matcher;
6066
import java.util.regex.Pattern;
6167

6268
import static org.junit.jupiter.api.Assertions.fail;
@@ -71,6 +77,9 @@ public abstract class QuidemTest {
7177

7278
private static final Pattern PATTERN = Pattern.compile("\\.iq$");
7379

80+
// Saved original planner rules
81+
private static @Nullable List<RelOptRule> originalRules;
82+
7483
private static @Nullable Object getEnv(String varName) {
7584
switch (varName) {
7685
case "jdk18":
@@ -171,6 +180,26 @@ protected void checkRun(String path) throws Exception {
171180
int thresholdValue = ((BigDecimal) value).intValue();
172181
closer.add(Prepare.THREAD_INSUBQUERY_THRESHOLD.push(thresholdValue));
173182
}
183+
// Configures query planner rules via "!set planner-rules" command.
184+
// The value can be set as follows:
185+
// - Add rule: "+EnumerableRules.ENUMERABLE_INTERSECT_RULE"
186+
// - Remove rule: "-CoreRules.INTERSECT_TO_DISTINCT"
187+
// - Short form: "+INTERSECT_TO_DISTINCT" (CoreRules prefix may be omitted)
188+
// - Reset defaults: "original"
189+
if (propertyName.equals("planner-rules")) {
190+
if (value.equals("original")) {
191+
closer.add(Hook.PLANNER.addThread(this::resetPlanner));
192+
} else {
193+
closer.add(
194+
Hook.PLANNER.addThread((Consumer<RelOptPlanner>)
195+
planner -> {
196+
if (originalRules == null) {
197+
originalRules = planner.getRules();
198+
}
199+
updatePlanner(planner, (String) value);
200+
}));
201+
}
202+
}
174203
})
175204
.withEnv(QuidemTest::getEnv)
176205
.build();
@@ -187,6 +216,57 @@ protected void checkRun(String path) throws Exception {
187216
}
188217
}
189218

219+
private void updatePlanner(RelOptPlanner planner, String value) {
220+
List<RelOptRule> rulesAdd = new ArrayList<>();
221+
List<RelOptRule> rulesRemove = new ArrayList<>();
222+
parseRules(value, rulesAdd, rulesRemove);
223+
rulesRemove.forEach(planner::removeRule);
224+
rulesAdd.forEach(planner::addRule);
225+
}
226+
227+
private void resetPlanner(RelOptPlanner planner) {
228+
if (originalRules != null) {
229+
planner.getRules().forEach(planner::removeRule);
230+
originalRules.forEach(planner::addRule);
231+
}
232+
}
233+
234+
private void parseRules(String value, List<RelOptRule> rulesAdd, List<RelOptRule> rulesRemove) {
235+
Pattern pattern = Pattern.compile("([+-])((CoreRules|EnumerableRules)\\.)?(\\w+)");
236+
Matcher matcher = pattern.matcher(value);
237+
238+
while (matcher.find()) {
239+
char operation = matcher.group(1).charAt(0);
240+
String ruleSource = matcher.group(3);
241+
String ruleName = matcher.group(4);
242+
243+
try {
244+
if (ruleSource == null || ruleSource.equals("CoreRules")) {
245+
Object rule = CoreRules.class.getField(ruleName).get(null);
246+
setRules(operation, (RelOptRule) rule, rulesAdd, rulesRemove);
247+
} else if (ruleSource.equals("EnumerableRules")) {
248+
Object rule = EnumerableRules.class.getField(ruleName).get(null);
249+
setRules(operation, (RelOptRule) rule, rulesAdd, rulesRemove);
250+
} else {
251+
throw new RuntimeException("Unknown rule: " + ruleName);
252+
}
253+
} catch (NoSuchFieldException | IllegalAccessException e) {
254+
throw new RuntimeException("set rules failed: " + e.getMessage(), e);
255+
}
256+
}
257+
}
258+
259+
private void setRules(char operation, RelOptRule rule,
260+
List<RelOptRule> rulesAdd, List<RelOptRule> rulesRemove) {
261+
if (operation == '+') {
262+
rulesAdd.add(rule);
263+
} else if (operation == '-') {
264+
rulesRemove.add(rule);
265+
} else {
266+
throw new RuntimeException("unknown operation '" + operation + "'");
267+
}
268+
}
269+
190270
/** Returns a file, replacing one directory with another.
191271
*
192272
* <p>For example, {@code replaceDir("/abc/str/astro.txt", "str", "xyz")}

0 commit comments

Comments
 (0)