This is an automated email from the ASF dual-hosted git repository.
cgivre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git
The following commit(s) were added to refs/heads/master by this push:
new fa2cb0f DRILL-8090: LIMIT clause is pushed down to an invalid
OFFSET-FETCH clause for MS SQL Server (#2420)
fa2cb0f is described below
commit fa2cb0f4937c0d8e797a675d8d6c13c316e48d4c
Author: Volodymyr Vysotskyi <[email protected]>
AuthorDate: Tue Jan 4 00:34:31 2022 +0200
DRILL-8090: LIMIT clause is pushed down to an invalid OFFSET-FETCH clause
for MS SQL Server (#2420)
* DRILL-8090: LIMIT clause is pushed down to an invalid OFFSET-FETCH clause
for MS SQL Server
* DRILL-8090: Changes after code review
---
.../drill/exec/store/jdbc/DrillJdbcConvention.java | 44 +++++++++++-------
.../drill/exec/store/jdbc/rules/JdbcLimitRule.java | 50 ++++++++++++++++++++
.../drill/exec/store/jdbc/rules/JdbcSortRule.java | 49 ++++++++++++++++++++
.../store/phoenix/rules/PhoenixConvention.java | 41 +++++++++-------
.../PhoenixIntermediatePrelConverterRule.java | 54 ++++++++++------------
.../store/enumerable/plan/DrillJdbcRuleBase.java | 2 +-
pom.xml | 2 +-
7 files changed, 177 insertions(+), 65 deletions(-)
diff --git
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/DrillJdbcConvention.java
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/DrillJdbcConvention.java
index 666502a..c469502 100644
---
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/DrillJdbcConvention.java
+++
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/DrillJdbcConvention.java
@@ -17,6 +17,7 @@
*/
package org.apache.drill.exec.store.jdbc;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -25,18 +26,21 @@ import org.apache.calcite.adapter.jdbc.JdbcConvention;
import org.apache.calcite.adapter.jdbc.JdbcRules;
import org.apache.calcite.adapter.jdbc.JdbcRules.JdbcFilterRule;
import org.apache.calcite.adapter.jdbc.JdbcRules.JdbcProjectRule;
-import org.apache.calcite.adapter.jdbc.JdbcRules.JdbcSortRule;
import org.apache.calcite.adapter.jdbc.JdbcToEnumerableConverterRule;
import org.apache.calcite.linq4j.tree.ConstantUntypedNull;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.sql.SqlDialect;
import org.apache.drill.exec.planner.RuleInstance;
import org.apache.drill.exec.planner.logical.DrillRel;
import org.apache.drill.exec.planner.logical.DrillRelFactories;
+import org.apache.drill.exec.planner.physical.Prel;
import org.apache.drill.exec.store.enumerable.plan.DrillJdbcRuleBase;
import org.apache.drill.exec.store.enumerable.plan.VertexDrelConverterRule;
+import org.apache.drill.exec.store.jdbc.rules.JdbcLimitRule;
+import org.apache.drill.exec.store.jdbc.rules.JdbcSortRule;
import org.apache.drill.shaded.guava.com.google.common.collect.ImmutableSet;
/**
@@ -48,7 +52,7 @@ public class DrillJdbcConvention extends JdbcConvention {
* Unwanted Calcite's JdbcRules are filtered out using this set
*/
private static final Set<Class<? extends RelOptRule>> EXCLUDED_CALCITE_RULES
= ImmutableSet.of(
- JdbcToEnumerableConverterRule.class, JdbcFilterRule.class,
JdbcProjectRule.class, JdbcSortRule.class);
+ JdbcToEnumerableConverterRule.class, JdbcFilterRule.class,
JdbcProjectRule.class, JdbcRules.JdbcSortRule.class);
private final ImmutableSet<RelOptRule> rules;
private final JdbcStoragePlugin plugin;
@@ -59,21 +63,27 @@ public class DrillJdbcConvention extends JdbcConvention {
List<RelOptRule> calciteJdbcRules = JdbcRules.rules(this,
DrillRelFactories.LOGICAL_BUILDER).stream()
.filter(rule -> !EXCLUDED_CALCITE_RULES.contains(rule.getClass()))
.collect(Collectors.toList());
- this.rules = ImmutableSet.<RelOptRule>builder()
- .addAll(calciteJdbcRules)
- .add(new JdbcIntermediatePrelConverterRule(this))
- .add(new VertexDrelConverterRule(this))
- .add(new DrillJdbcRuleBase.DrillJdbcProjectRule(Convention.NONE, this))
- .add(new
DrillJdbcRuleBase.DrillJdbcProjectRule(DrillRel.DRILL_LOGICAL, this))
- .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(DrillRel.DRILL_LOGICAL,
this))
- .add(new DrillJdbcRuleBase.DrillJdbcSortRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcSortRule(DrillRel.DRILL_LOGICAL,
this))
- .add(new DrillJdbcRuleBase.DrillJdbcLimitRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcLimitRule(DrillRel.DRILL_LOGICAL,
this))
- .add(RuleInstance.FILTER_SET_OP_TRANSPOSE_RULE)
- .add(RuleInstance.PROJECT_REMOVE_RULE)
- .build();
+
+ List<RelTrait> inputTraits = Arrays.asList(
+ Convention.NONE,
+ DrillRel.DRILL_LOGICAL,
+ Prel.DRILL_PHYSICAL);
+
+ ImmutableSet.Builder<RelOptRule> builder =
ImmutableSet.<RelOptRule>builder()
+ .addAll(calciteJdbcRules)
+ .add(new JdbcIntermediatePrelConverterRule(this))
+ .add(new VertexDrelConverterRule(this))
+ .add(RuleInstance.FILTER_SET_OP_TRANSPOSE_RULE)
+ .add(RuleInstance.PROJECT_REMOVE_RULE);
+ for (RelTrait inputTrait : inputTraits) {
+ builder
+ .add(new DrillJdbcRuleBase.DrillJdbcProjectRule(inputTrait, this))
+ .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(inputTrait, this))
+ .add(new JdbcSortRule(inputTrait, this))
+ .add(new JdbcLimitRule(inputTrait, this));
+ }
+
+ this.rules = builder.build();
}
@Override
diff --git
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcLimitRule.java
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcLimitRule.java
new file mode 100644
index 0000000..dc44c72
--- /dev/null
+++
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcLimitRule.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.store.jdbc.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelTrait;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.sql.dialect.MssqlSqlDialect;
+import org.apache.drill.exec.planner.common.DrillLimitRelBase;
+import org.apache.drill.exec.store.enumerable.plan.DrillJdbcRuleBase;
+import org.apache.drill.exec.store.jdbc.DrillJdbcConvention;
+
+public class JdbcLimitRule extends DrillJdbcRuleBase.DrillJdbcLimitRule {
+ private final DrillJdbcConvention convention;
+
+ public JdbcLimitRule(RelTrait in, DrillJdbcConvention out) {
+ super(in, out);
+ this.convention = out;
+ }
+
+ @Override
+ public boolean matches(RelOptRuleCall call) {
+ DrillLimitRelBase limit = call.rel(0);
+ if (super.matches(call)) {
+ // MS SQL doesn't support either OFFSET or FETCH without ORDER BY.
+ // But for the case of FETCH without OFFSET, Calcite generates TOP N
+ // instead of FETCH, and it is supported by MS SQL.
+ // So do not push down only the limit with both OFFSET and FETCH but
without ORDER BY.
+ return limit.getOffset() == null
+ || !limit.getTraitSet().contains(RelCollations.EMPTY)
+ || !(convention.getPlugin().getDialect() instanceof MssqlSqlDialect);
+ }
+ return false;
+ }
+}
diff --git
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcSortRule.java
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcSortRule.java
new file mode 100644
index 0000000..4b52306
--- /dev/null
+++
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/rules/JdbcSortRule.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.store.jdbc.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelTrait;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.sql.dialect.MssqlSqlDialect;
+import org.apache.drill.exec.store.enumerable.plan.DrillJdbcRuleBase;
+import org.apache.drill.exec.store.jdbc.DrillJdbcConvention;
+
+public class JdbcSortRule extends DrillJdbcRuleBase.DrillJdbcSortRule {
+ private final DrillJdbcConvention convention;
+
+ public JdbcSortRule(RelTrait in, DrillJdbcConvention out) {
+ super(in, out);
+ this.convention = out;
+ }
+
+ @Override
+ public boolean matches(RelOptRuleCall call) {
+ Sort sort = call.rel(0);
+ if (super.matches(call)) {
+ // MS SQL doesn't support either OFFSET or FETCH without ORDER BY.
+ // But for the case of FETCH without OFFSET, Calcite generates TOP N
+ // instead of FETCH, and it is supported by MS SQL.
+ // So do not push down only the limit with both OFFSET and FETCH but
without ORDER BY.
+ return sort.offset == null
+ || !sort.getCollation().getFieldCollations().isEmpty()
+ || !(convention.getPlugin().getDialect() instanceof MssqlSqlDialect);
+ }
+ return false;
+ }
+}
diff --git
a/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixConvention.java
b/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixConvention.java
index 09ce9be..6d4ff48 100644
---
a/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixConvention.java
+++
b/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixConvention.java
@@ -17,6 +17,7 @@
*/
package org.apache.drill.exec.store.phoenix.rules;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -32,10 +33,12 @@ import org.apache.calcite.linq4j.tree.ConstantUntypedNull;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.sql.SqlDialect;
import org.apache.drill.exec.planner.RuleInstance;
import org.apache.drill.exec.planner.logical.DrillRel;
import org.apache.drill.exec.planner.logical.DrillRelFactories;
+import org.apache.drill.exec.planner.physical.Prel;
import org.apache.drill.exec.store.enumerable.plan.DrillJdbcRuleBase;
import org.apache.drill.exec.store.enumerable.plan.VertexDrelConverterRule;
import org.apache.drill.exec.store.phoenix.PhoenixStoragePlugin;
@@ -60,22 +63,28 @@ public class PhoenixConvention extends JdbcConvention {
.rules(this, DrillRelFactories.LOGICAL_BUILDER).stream()
.filter(rule -> !EXCLUDED_CALCITE_RULES.contains(rule.getClass()))
.collect(Collectors.toList());
- this.rules = ImmutableSet.<RelOptRule>builder()
- .addAll(calciteJdbcRules)
- .add(PhoenixIntermediatePrelConverterRule.INSTANCE)
- .add(new VertexDrelConverterRule(this))
- .add(new DrillJdbcRuleBase.DrillJdbcProjectRule(Convention.NONE, this))
- .add(new
DrillJdbcRuleBase.DrillJdbcProjectRule(DrillRel.DRILL_LOGICAL, this))
- .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(DrillRel.DRILL_LOGICAL,
this))
- .add(new DrillJdbcRuleBase.DrillJdbcSortRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcSortRule(DrillRel.DRILL_LOGICAL,
this))
- .add(new DrillJdbcRuleBase.DrillJdbcLimitRule(Convention.NONE, this))
- .add(new DrillJdbcRuleBase.DrillJdbcLimitRule(DrillRel.DRILL_LOGICAL,
this))
- .add(RuleInstance.FILTER_SET_OP_TRANSPOSE_RULE)
- .add(RuleInstance.PROJECT_REMOVE_RULE)
- .add(new PhoenixJoinRule(Convention.NONE, this))
- .build();
+
+ List<RelTrait> inputTraits = Arrays.asList(
+ Convention.NONE,
+ DrillRel.DRILL_LOGICAL,
+ Prel.DRILL_PHYSICAL);
+
+ ImmutableSet.Builder<RelOptRule> builder =
ImmutableSet.<RelOptRule>builder()
+ .addAll(calciteJdbcRules)
+ .add(new PhoenixIntermediatePrelConverterRule(this))
+ .add(new VertexDrelConverterRule(this))
+ .add(RuleInstance.FILTER_SET_OP_TRANSPOSE_RULE)
+ .add(RuleInstance.PROJECT_REMOVE_RULE);
+ for (RelTrait inputTrait : inputTraits) {
+ builder
+ .add(new DrillJdbcRuleBase.DrillJdbcProjectRule(inputTrait, this))
+ .add(new DrillJdbcRuleBase.DrillJdbcFilterRule(inputTrait, this))
+ .add(new DrillJdbcRuleBase.DrillJdbcSortRule(inputTrait, this))
+ .add(new DrillJdbcRuleBase.DrillJdbcLimitRule(inputTrait, this))
+ .add(new PhoenixJoinRule(inputTrait, this));
+ }
+
+ this.rules = builder.build();
}
@Override
diff --git
a/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixIntermediatePrelConverterRule.java
b/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixIntermediatePrelConverterRule.java
index c823c82..c5eaaf1 100644
---
a/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixIntermediatePrelConverterRule.java
+++
b/contrib/storage-phoenix/src/main/java/org/apache/drill/exec/store/phoenix/rules/PhoenixIntermediatePrelConverterRule.java
@@ -17,50 +17,44 @@
*/
package org.apache.drill.exec.store.phoenix.rules;
-import java.util.function.Predicate;
-
-import org.apache.calcite.plan.Convention;
-import org.apache.calcite.plan.ConventionTraitDef;
+import org.apache.calcite.adapter.jdbc.JdbcConvention;
+import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterRule;
import org.apache.drill.exec.planner.logical.DrillRel;
import org.apache.drill.exec.planner.logical.DrillRelFactories;
+import org.apache.drill.exec.planner.logical.RelOptHelper;
import org.apache.drill.exec.planner.physical.Prel;
import org.apache.drill.exec.store.enumerable.plan.VertexDrel;
-final class PhoenixIntermediatePrelConverterRule extends ConverterRule {
+final class PhoenixIntermediatePrelConverterRule extends RelOptRule {
+
+ private final RelTrait inTrait;
+ private final RelTrait outTrait;
- static final PhoenixIntermediatePrelConverterRule INSTANCE = new
PhoenixIntermediatePrelConverterRule();
+ public PhoenixIntermediatePrelConverterRule(JdbcConvention jdbcConvention) {
+ super(
+ RelOptHelper.some(VertexDrel.class, DrillRel.DRILL_LOGICAL,
+ RelOptHelper.any(RelNode.class, jdbcConvention)),
+ DrillRelFactories.LOGICAL_BUILDER, "Phoenix_PREL_Converter" +
jdbcConvention);
- private PhoenixIntermediatePrelConverterRule() {
- super(VertexDrel.class, (Predicate<RelNode>) input -> true,
- DrillRel.DRILL_LOGICAL, Prel.DRILL_PHYSICAL,
DrillRelFactories.LOGICAL_BUILDER, "Phoenix_PREL_Converter");
+ this.inTrait = DrillRel.DRILL_LOGICAL;
+ this.outTrait = Prel.DRILL_PHYSICAL;
}
@Override
- public RelNode convert(RelNode in) {
- return new PhoenixIntermediatePrel(
- in.getCluster(),
- in.getTraitSet().replace(getOutTrait()),
- in.getInput(0));
+ public void onMatch(RelOptRuleCall call) {
+ VertexDrel in = call.rel(0);
+ RelNode intermediatePrel = new PhoenixIntermediatePrel(
+ in.getCluster(),
+ in.getTraitSet().replace(outTrait),
+ in.getInput(0));
+ call.transformTo(intermediatePrel);
}
@Override
- public void onMatch(RelOptRuleCall call) {
- RelNode rel = call.rel(0);
- if (rel.getTraitSet().contains(getInTrait())) {
- Convention c =
rel.getInput(0).getTraitSet().getTrait(ConventionTraitDef.INSTANCE);
- /*
- * only accept the PhoenixConvention
- * need to avoid the NPE or ClassCastException
- */
- if (c != null && c instanceof PhoenixConvention) {
- final RelNode converted = convert(rel);
- if (converted != null) {
- call.transformTo(converted);
- }
- }
- }
+ public boolean matches(RelOptRuleCall call) {
+ return super.matches(call) && call.rel(0).getTraitSet().contains(inTrait);
}
}
diff --git
a/exec/java-exec/src/main/java/org/apache/drill/exec/store/enumerable/plan/DrillJdbcRuleBase.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/store/enumerable/plan/DrillJdbcRuleBase.java
index 6e8a0f4..1a2722d 100644
---
a/exec/java-exec/src/main/java/org/apache/drill/exec/store/enumerable/plan/DrillJdbcRuleBase.java
+++
b/exec/java-exec/src/main/java/org/apache/drill/exec/store/enumerable/plan/DrillJdbcRuleBase.java
@@ -54,7 +54,7 @@ public abstract class DrillJdbcRuleBase extends ConverterRule
{
protected final JdbcConvention out;
- private DrillJdbcRuleBase(Class<? extends RelNode> clazz, RelTrait in,
JdbcConvention out, String description) {
+ protected DrillJdbcRuleBase(Class<? extends RelNode> clazz, RelTrait in,
JdbcConvention out, String description) {
super(clazz, (Predicate<RelNode>) input -> true, in, out,
DrillRelFactories.LOGICAL_BUILDER, description);
this.out = out;
}
diff --git a/pom.xml b/pom.xml
index c4f3618..553ffce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,7 +59,7 @@
avoid_bad_dependencies plugin found in the file.
-->
<calcite.groupId>com.github.vvysotskyi.drill-calcite</calcite.groupId>
- <calcite.version>1.21.0-drill-r5</calcite.version>
+ <calcite.version>1.21.0-drill-r7</calcite.version>
<avatica.version>1.17.0</avatica.version>
<janino.version>3.0.11</janino.version>
<sqlline.version>1.12.0</sqlline.version>