blob: fb6a415945c6450d165e3b90001c82fc57443507 [file] [log] [blame]
/*
* 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.planner.logical;
import org.apache.drill.exec.planner.common.DrillProjectRelBase;
import com.google.common.collect.Lists;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.logical.LogicalFilter;
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.tools.RelBuilderFactory;
import org.apache.calcite.util.Pair;
import org.apache.drill.exec.planner.common.DrillRelOptUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class DrillPushFilterPastProjectRule extends RelOptRule {
public final static RelOptRule LOGICAL = new DrillPushFilterPastProjectRule(
LogicalFilter.class, LogicalProject.class, DrillRelFactories.LOGICAL_BUILDER, "DrillPushFilterPastProjectRule:logical");
public final static RelOptRule DRILL_INSTANCE = new DrillPushFilterPastProjectRule(
DrillFilterRel.class, DrillProjectRelBase.class, DrillRelFactories.LOGICAL_BUILDER, "DrillPushFilterPastProjectRule:drill_logical");
private static final Collection<String> BANNED_OPERATORS;
static {
BANNED_OPERATORS = new ArrayList<>(2);
BANNED_OPERATORS.add("flatten");
BANNED_OPERATORS.add("item");
}
private DrillPushFilterPastProjectRule(Class<? extends Filter> filter,
Class<? extends Project> project, RelBuilderFactory relBuilderFactory, String description) {
super(operand(filter, operand(project, any())), relBuilderFactory,description);
}
//~ Methods ----------------------------------------------------------------
// implement RelOptRule
public void onMatch(RelOptRuleCall call) {
Filter filterRel = call.rel(0);
Project projRel = call.rel(1);
RelBuilder builder = call.builder();
// get a conjunctions of the filter condition. For each conjunction, if it refers to ITEM or FLATTEN expression
// then we could not pushed down. Otherwise, it's qualified to be pushed down.
final List<RexNode> predList = RelOptUtil.conjunctions(filterRel.getCondition());
final List<RexNode> qualifiedPredList = Lists.newArrayList();
final List<RexNode> unqualifiedPredList = Lists.newArrayList();
for (final RexNode pred : predList) {
if (DrillRelOptUtil.findOperators(pred, projRel.getProjects(), BANNED_OPERATORS) == null) {
qualifiedPredList.add(pred);
} else {
unqualifiedPredList.add(pred);
}
}
final RexNode qualifedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), qualifiedPredList, true);
if (qualifedPred == null) {
return;
}
// convert the filter to one that references the child of the project
RexNode newCondition =
RelOptUtil.pushPastProject(qualifedPred, projRel);
RelNode newFilterRel =
builder
.push(projRel.getInput())
.filter(newCondition)
.build();
RelNode newProjRel =
builder
.push(newFilterRel)
.projectNamed(Pair.left(projRel.getNamedProjects()), Pair.right(projRel.getNamedProjects()), true)
.build();
final RexNode unqualifiedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), unqualifiedPredList, true);
if (unqualifiedPred == null) {
call.transformTo(newProjRel);
} else {
// if there are filters not qualified to be pushed down, then we have to put those filters on top of
// the new Project operator.
// Filter -- unqualified filters
// \
// Project
// \
// Filter -- qualified filters
RelNode filterNotPushed =
builder
.push(newProjRel)
.filter(unqualifiedPred)
.build();
call.transformTo(filterNotPushed);
}
}
}