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 b4e399c  [CALCITE-4414] RelMdSelectivity#getSelectivity for Calc can 
propagate a predicate with wrong references
b4e399c is described below

commit b4e399cb35224d8c8d55f02b7cf2b9649a3b28a4
Author: rubenada <rube...@gmail.com>
AuthorDate: Fri Nov 20 15:06:46 2020 +0000

    [CALCITE-4414] RelMdSelectivity#getSelectivity for Calc can propagate a 
predicate with wrong references
---
 .../java/org/apache/calcite/plan/RelOptUtil.java   | 22 +++++++
 .../calcite/rel/metadata/RelMdSelectivity.java     |  5 +-
 .../apache/calcite/test/RelMdSelectivityTest.java  | 75 ++++++++++++++++++++++
 3 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java 
b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index eec60ee..70181d6 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -3064,6 +3064,28 @@ public abstract class RelOptUtil {
   }
 
   /**
+   * Converts an expression that is based on the output fields of a
+   * {@link Calc} to an equivalent expression on the Calc's input fields.
+   *
+   * @param node The expression to be converted
+   * @param calc Calc underneath the expression
+   * @return converted expression
+   */
+  public static RexNode pushPastCalc(RexNode node, Calc calc) {
+    return node.accept(pushShuttle(calc));
+  }
+
+  private static RexShuttle pushShuttle(final Calc calc) {
+    final List<RexNode> projects = 
Util.transform(calc.getProgram().getProjectList(),
+        calc.getProgram()::expandLocalRef);
+    return new RexShuttle() {
+      @Override public RexNode visitInputRef(RexInputRef ref) {
+        return projects.get(ref.getIndex());
+      }
+    };
+  }
+
+  /**
    * Creates a new {@link org.apache.calcite.rel.rules.MultiJoin} to reflect
    * projection references from a
    * {@link Project} that is on top of the
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
index 192cca0..b87634a 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdSelectivity.java
@@ -123,10 +123,13 @@ public class RelMdSelectivity
   }
 
   public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
predicate) {
+    if (predicate != null) {
+      predicate = RelOptUtil.pushPastCalc(predicate, rel);
+    }
     final RexProgram rexProgram = rel.getProgram();
     final RexLocalRef programCondition = rexProgram.getCondition();
     if (programCondition == null) {
-      return getSelectivity(rel.getInput(), mq, predicate);
+      return mq.getSelectivity(rel.getInput(), predicate);
     } else {
       return mq.getSelectivity(rel.getInput(),
           RelMdUtil.minusPreds(
diff --git 
a/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
new file mode 100644
index 0000000..a086022
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/RelMdSelectivityTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.calcite.test;
+
+import org.apache.calcite.plan.hep.HepPlanner;
+import org.apache.calcite.plan.hep.HepProgram;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.tools.RelBuilder;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test cases for {@link org.apache.calcite.rel.metadata.RelMdSelectivity}.
+ */
+class RelMdSelectivityTest {
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4414";>[CALCITE-4414]
+   * RelMdSelectivity#getSelectivity for Calc can propagate a predicate with 
wrong reference</a>. */
+  @Test void testCalcSelectivityWithPredicate() {
+    final RelBuilder builder = 
RelBuilder.create(RelBuilderTest.config().build());
+    final RelNode relNode = builder
+        .scan("EMP")
+        .project(
+            builder.field("DEPTNO"))
+        .scan("EMP")
+        .project(
+            builder.field("DEPTNO"))
+        .union(true)
+        .projectPlus(builder.field("DEPTNO"))
+        .filter(
+            builder.equals(
+                builder.field(0),
+                builder.literal(0)))
+        .build();
+
+    // Program to convert Project + Filter into a single Calc
+    final HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(CoreRules.FILTER_TO_CALC)
+        .addRuleInstance(CoreRules.PROJECT_TO_CALC)
+        .addRuleInstance(CoreRules.CALC_MERGE)
+        .build();
+    final HepPlanner hepPlanner = new HepPlanner(program);
+    hepPlanner.setRoot(relNode);
+    RelNode output = hepPlanner.findBestExp();
+
+    // Add filter on the extra field generated by projectPlus (now a Calc 
after hepPlanner)
+    output = builder
+        .push(output)
+        .filter(
+            builder.equals(
+                builder.field(1),
+                builder.literal(0)))
+        .build();
+
+    // Should not fail
+    output.estimateRowCount(output.getCluster().getMetadataQuery());
+  }
+}

Reply via email to