From 731f1f33adfd81788437b06e162f21cb36684704 Mon Sep 17 00:00:00 2001 From: Harish Butani Date: Fri, 26 Sep 2014 12:37:42 -0700 Subject: [PATCH] OPTIQ-425: Add a PushFilterPastAggRule rule --- .../rel/rules/PushFilterPastAggRule.java | 104 ++++++++++++++++++ .../org/eigenbase/test/RelOptRulesTest.java | 9 ++ .../org/eigenbase/test/RelOptRulesTest.xml | 23 ++++ 3 files changed, 136 insertions(+) create mode 100644 core/src/main/java/org/eigenbase/rel/rules/PushFilterPastAggRule.java diff --git a/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastAggRule.java b/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastAggRule.java new file mode 100644 index 000000000..b9da434b5 --- /dev/null +++ b/core/src/main/java/org/eigenbase/rel/rules/PushFilterPastAggRule.java @@ -0,0 +1,104 @@ +/* + * 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.eigenbase.rel.rules; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import org.eigenbase.rel.*; +import org.eigenbase.relopt.*; +import org.eigenbase.reltype.RelDataTypeField; +import org.eigenbase.rex.*; + +import net.hydromatic.optiq.util.BitSets; + +import com.google.common.collect.ImmutableList; + + +/** + * PushFilterPastAggRule implements the rule for pushing a {@link FilterRelBase} + * past a {@link AggregateRelBase}. + */ +public class PushFilterPastAggRule extends RelOptRule { + + /** The default instance of + * {@link org.eigenbase.rel.rules.PushFilterPastAggRule}. + * + *

It matches any kind of agg. or filter */ + public static final PushFilterPastAggRule INSTANCE = + new PushFilterPastAggRule( + FilterRelBase.class, + RelFactories.DEFAULT_FILTER_FACTORY, + AggregateRelBase.class); + + private final RelFactories.FilterFactory filterFactory; + + //~ Constructors ----------------------------------------------------------- + + /** + * Creates a PushFilterPastAggRule. + * + *

If {@code filterFactory} is null, creates the same kind of filter as + * matched in the rule. Similarly {@code aggregateFactory}.

+ */ + public PushFilterPastAggRule( + Class filterClass, + RelFactories.FilterFactory filterFactory, + Class aggregateClass) { + super( + operand(filterClass, + operand(aggregateClass, any()))); + this.filterFactory = filterFactory; + } + + //~ Methods ---------------------------------------------------------------- + + // implement RelOptRule + public void onMatch(RelOptRuleCall call) { + final FilterRelBase filterRel = call.rel(0); + final AggregateRelBase aggRel = call.rel(1); + + List condtions = + RelOptUtil.conjunctions(filterRel.getCondition()); + BitSet groupKeys = aggRel.getGroupSet(); + RexBuilder rexBuilder = filterRel.getCluster().getRexBuilder(); + List origFields = aggRel.getRowType().getFieldList(); + int[] adjustments = new int[origFields.size()]; + List pushedConds = new ArrayList(); + + for (RexNode cond : condtions) { + BitSet rCols = RelOptUtil.InputFinder.bits(cond); + if (BitSets.contains(groupKeys, rCols)) { + pushedConds.add(cond.accept(new RelOptUtil.RexInputConverter( + rexBuilder, origFields, aggRel.getInput(0).getRowType() + .getFieldList(), adjustments))); + } + } + + RexNode pushedCond = RexUtil.composeConjunction(rexBuilder, pushedConds, + true); + + if (pushedCond != null) { + RelNode newFilterRel = filterFactory.createFilter(aggRel.getInput(0), + pushedCond); + RelNode newAggRel = aggRel.copy(aggRel.getTraitSet(), + ImmutableList.of(newFilterRel)); + call.transformTo(newAggRel); + } + } +} diff --git a/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java b/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java index b6bc20876..b7ce3e96c 100644 --- a/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java +++ b/core/src/test/java/org/eigenbase/test/RelOptRulesTest.java @@ -35,6 +35,7 @@ import org.eigenbase.rel.rules.ProjectToCalcRule; import org.eigenbase.rel.rules.PullConstantsThroughAggregatesRule; import org.eigenbase.rel.rules.PushAggregateThroughUnionRule; +import org.eigenbase.rel.rules.PushFilterPastAggRule; import org.eigenbase.rel.rules.PushFilterPastJoinRule; import org.eigenbase.rel.rules.PushFilterPastProjectRule; import org.eigenbase.rel.rules.PushFilterPastSetOpRule; @@ -163,6 +164,14 @@ protected DiffRepository getDiffRepos() { + " where d.name = 'Charlie'"); } + @Test public void testPushFilterPastAgg() { + checkPlanning( + PushFilterPastAggRule.INSTANCE, + "select dname, c from" + + " (select name dname, count(*) as c from dept group by name) t" + + " where dname = 'Charlie'"); + } + @Test public void testSemiJoinRule() { final HepProgram preProgram = HepProgram.builder() diff --git a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml index 07eeb68b0..701680495 100644 --- a/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/eigenbase/test/RelOptRulesTest.xml @@ -105,6 +105,29 @@ ProjectRel(EXPR$0=[1]) FilterRel(condition=[=($1, 'Charlie')]) TableAccessRel(table=[[CATALOG, SALES, DEPT]]) TableAccessRel(table=[[CATALOG, SALES, EMP]]) +]]> + + + + + + + + + + +