You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2020/05/07 09:45:28 UTC

[calcite] branch master updated: [CALCITE-3926] CannotPlanException when an empty LogicalValues requires a certain collation

This is an automated email from the ASF dual-hosted git repository.

rubenql pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new e3fe745  [CALCITE-3926] CannotPlanException when an empty LogicalValues requires a certain collation
e3fe745 is described below

commit e3fe745a4e8e5e15ae5e04345975c98ab737b31b
Author: rubenada <ru...@gmail.com>
AuthorDate: Wed Apr 15 19:20:05 2020 +0200

    [CALCITE-3926] CannotPlanException when an empty LogicalValues requires a certain collation
---
 .../adapter/enumerable/EnumerableValues.java       |  2 +-
 .../adapter/enumerable/EnumerableValuesRule.java   |  9 +++++---
 .../apache/calcite/rel/logical/LogicalValues.java  |  6 ++++-
 .../apache/calcite/rel/rules/PruneEmptyRules.java  | 26 +++++++++++++++++-----
 .../org/apache/calcite/test/RelBuilderTest.java    | 19 ++++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.java   | 25 +++++++++++++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml    | 17 +++++++++++---
 7 files changed, 91 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
index 497e820..97b6554 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
@@ -67,7 +67,7 @@ public class EnumerableValues extends Values implements EnumerableRel {
 
   @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
     assert inputs.isEmpty();
-    return create(getCluster(), rowType, tuples);
+    return new EnumerableValues(getCluster(), rowType, tuples, traitSet);
   }
 
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
index 6a71ac0..c981a8b 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
@@ -41,8 +41,11 @@ public class EnumerableValuesRule extends ConverterRule {
   }
 
   @Override public RelNode convert(RelNode rel) {
-    LogicalValues values = (LogicalValues) rel;
-    return EnumerableValues.create(values.getCluster(), values.getRowType(),
-        values.getTuples());
+    final LogicalValues logicalValues = (LogicalValues) rel;
+    final EnumerableValues enumerableValues = EnumerableValues.create(
+        logicalValues.getCluster(), logicalValues.getRowType(), logicalValues.getTuples());
+    return enumerableValues.copy(
+        logicalValues.getTraitSet().replace(EnumerableConvention.INSTANCE),
+        enumerableValues.getInputs());
   }
 }
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
index edbea5d..32b9850 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalValues.java
@@ -20,11 +20,13 @@ import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelDistributionTraitDef;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.metadata.RelMdCollation;
+import org.apache.calcite.rel.metadata.RelMdDistribution;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexLiteral;
@@ -83,7 +85,9 @@ public class LogicalValues extends Values {
     final RelMetadataQuery mq = cluster.getMetadataQuery();
     final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
         .replaceIfs(RelCollationTraitDef.INSTANCE,
-            () -> RelMdCollation.values(mq, rowType, tuples));
+            () -> RelMdCollation.values(mq, rowType, tuples))
+        .replaceIf(RelDistributionTraitDef.INSTANCE,
+            () -> RelMdDistribution.values(rowType, tuples));
     return new LogicalValues(cluster, traitSet, rowType, tuples);
   }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
index 1fb6b26..88d408d 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
@@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptRuleOperand;
 import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.plan.SubstitutionRule;
 import org.apache.calcite.plan.hep.HepRelVertex;
 import org.apache.calcite.plan.volcano.RelSubset;
@@ -42,6 +43,7 @@ import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Predicate;
 
@@ -266,7 +268,7 @@ public abstract class PruneEmptyRules {
    * <p>Examples:
    *
    * <ul>
-   * <li>Sort(Empty) becomes Empty
+   * <li>Sort[fetch=0] becomes Empty
    * </ul>
    */
   public static final RelOptRule SORT_FETCH_ZERO_INSTANCE =
@@ -277,7 +279,14 @@ public abstract class PruneEmptyRules {
           if (sort.fetch != null
               && !(sort.fetch instanceof RexDynamicParam)
               && RexLiteral.intValue(sort.fetch) == 0) {
-            call.transformTo(call.builder().push(sort).empty().build());
+            RelNode emptyValues = call.builder().push(sort).empty().build();
+            RelTraitSet traits = sort.getTraitSet();
+            // propagate all traits (except convention) from the original sort into the empty values
+            if (emptyValues.getConvention() != null) {
+              traits = traits.replace(emptyValues.getConvention());
+            }
+            emptyValues = emptyValues.copy(traits, Collections.emptyList());
+            call.transformTo(emptyValues);
           }
         }
       };
@@ -375,7 +384,7 @@ public abstract class PruneEmptyRules {
     /** Creates a simple RemoveEmptySingleRule. */
     public <R extends SingleRel> RemoveEmptySingleRule(Class<R> clazz,
         String description) {
-      this(clazz, (Predicate<R>) project -> true, RelFactories.LOGICAL_BUILDER,
+      this(clazz, (Predicate<R>) singleRel -> true, RelFactories.LOGICAL_BUILDER,
           description);
     }
 
@@ -399,8 +408,15 @@ public abstract class PruneEmptyRules {
     }
 
     public void onMatch(RelOptRuleCall call) {
-      SingleRel single = call.rel(0);
-      call.transformTo(call.builder().push(single).empty().build());
+      SingleRel singleRel = call.rel(0);
+      RelNode emptyValues = call.builder().push(singleRel).empty().build();
+      RelTraitSet traits = singleRel.getTraitSet();
+      // propagate all traits (except convention) from the original singleRel into the empty values
+      if (emptyValues.getConvention() != null) {
+        traits = traits.replace(emptyValues.getConvention());
+      }
+      emptyValues = emptyValues.copy(traits, Collections.emptyList());
+      call.transformTo(emptyValues);
     }
   }
 }
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index cee5808..f096e39 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -3463,4 +3463,23 @@ public class RelBuilderTest {
             builder.literal(5));
     assertThat(call.toStringRaw(), is("BETWEEN ASYMMETRIC($0, 1, 5)"));
   }
+
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-3926">[CALCITE-3926]
+   * CannotPlanException when an empty LogicalValues requires a certain collation</a>. */
+  @Test void testEmptyValuesWithCollation() throws Exception {
+    final RelBuilder builder = RelBuilder.create(config().build());
+    final RelNode root =
+        builder
+            .scan("DEPT").empty()
+            .sort(
+                builder.field("DNAME"),
+                builder.field("DEPTNO"))
+            .build();
+    try (PreparedStatement preparedStatement = RelRunners.run(root)) {
+      final String result = CalciteAssert.toString(preparedStatement.executeQuery());
+      final String expectedResult = "";
+      assertThat(result, is(expectedResult));
+    }
+  }
 }
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 468f509..4ff8902 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -3430,6 +3430,7 @@ class RelOptRulesTest extends RelOptTestBase {
   @Test void testEmptySort() {
     HepProgram program = new HepProgramBuilder()
         .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .addRuleInstance(PruneEmptyRules.PROJECT_INSTANCE)
         .addRuleInstance(PruneEmptyRules.SORT_INSTANCE)
         .build();
 
@@ -3437,6 +3438,30 @@ class RelOptRulesTest extends RelOptTestBase {
     sql(sql).with(program).check();
   }
 
+  @Test void testEmptySort2() {
+    final RelBuilder relBuilder = RelBuilder.create(RelBuilderTest.config().build());
+    final RelNode relNode = relBuilder
+        .scan("DEPT").empty()
+        .sort(
+            relBuilder.field("DNAME"),
+            relBuilder.field("DEPTNO"))
+        .build();
+
+    final HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(PruneEmptyRules.SORT_INSTANCE)
+        .build();
+
+    final HepPlanner hepPlanner = new HepPlanner(program);
+    hepPlanner.setRoot(relNode);
+    final RelNode output = hepPlanner.findBestExp();
+
+    final String planBefore = NL + RelOptUtil.toString(relNode);
+    final String planAfter = NL + RelOptUtil.toString(output);
+    final DiffRepository diffRepos = getDiffRepos();
+    diffRepos.assertEquals("planBefore", "${planBefore}", planBefore);
+    diffRepos.assertEquals("planAfter", "${planAfter}", planAfter);
+  }
+
   @Test void testEmptySortLimitZero() {
     final String sql = "select * from emp order by deptno limit 0";
     sql(sql).withRule(PruneEmptyRules.SORT_FETCH_ZERO_INSTANCE).check();
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 4b6d286..2685ee7 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3168,9 +3168,20 @@ LogicalSort(sort0=[$7], dir0=[ASC])
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalSort(sort0=[$7], dir0=[ASC])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
-    LogicalValues(tuples=[[]])
+LogicalValues(tuples=[[]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testEmptySort2">
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalSort(sort0=[$1], sort1=[$0], dir0=[ASC], dir1=[ASC])
+  LogicalValues(tuples=[[]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalValues(tuples=[[]])
 ]]>
         </Resource>
     </TestCase>