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()); + } +}