/** * 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.hadoop.hive.ql.optimizer.calcite.rules; import java.util.HashMap; import java.util.List; import java.util.Map; 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.rex.RexBuilder; import org.apache.calcite.rex.RexCall; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.rex.RexUtil; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.tools.RelBuilderFactory; import org.apache.hadoop.hive.ql.optimizer.calcite.HiveCalciteUtil; import org.apache.hadoop.hive.ql.optimizer.calcite.HiveRelFactories; import org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveFilter; import org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveProject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; /** * Planner rule that infers constant expressions from Filter into * a Project operator. */ public class HiveProjectFilterPullUpConstantsRule extends RelOptRule { protected static final Logger LOG = LoggerFactory.getLogger( HiveProjectFilterPullUpConstantsRule.class); public static final HiveProjectFilterPullUpConstantsRule INSTANCE = new HiveProjectFilterPullUpConstantsRule(HiveProject.class, HiveFilter.class, HiveRelFactories.HIVE_BUILDER); public HiveProjectFilterPullUpConstantsRule( Class<? extends Project> projectClass, Class<? extends Filter> filterClass, RelBuilderFactory relBuilderFactory) { super(operand(projectClass, operand(filterClass, any())), relBuilderFactory, null); } @Override public boolean matches(RelOptRuleCall call) { final Filter filterRel = call.rel(1); RexNode condition = filterRel.getCondition(); if (!HiveCalciteUtil.isDeterministic(condition)) { return false; } return super.matches(call); } public void onMatch(RelOptRuleCall call) { final Project project = call.rel(0); final Filter filter = call.rel(1); final RelBuilder builder = call.builder(); List<RexNode> projects = project.getChildExps(); List<RexNode> newProjects = rewriteProjects(projects, filter.getCondition(), builder); if (newProjects == null) { return; } RelNode newProjRel = builder.push(filter) .project(newProjects, project.getRowType().getFieldNames()).build(); call.transformTo(newProjRel); } // Rewrite projects to replace column references by constants when possible @SuppressWarnings("incomplete-switch") private static List<RexNode> rewriteProjects(List<RexNode> projects, RexNode newPushedCondition, RelBuilder relBuilder) { final List<RexNode> conjunctions = RelOptUtil.conjunctions(newPushedCondition); final Map<String, RexNode> conditions = new HashMap<String, RexNode>(); for (RexNode conjunction: conjunctions) { // 1.1. If it is not a RexCall, we continue if (!(conjunction instanceof RexCall)) { continue; } // 1.2. We extract the information that we need RexCall conjCall = (RexCall) conjunction; switch (conjCall.getOperator().getKind()) { case EQUALS: if (!(RexUtil.isConstant(conjCall.operands.get(0))) && RexUtil.isConstant(conjCall.operands.get(1))) { conditions.put(conjCall.operands.get(0).toString(), conjCall.operands.get(1)); } else if (!(RexUtil.isConstant(conjCall.operands.get(1))) && RexUtil.isConstant(conjCall.operands.get(0))) { conditions.put(conjCall.operands.get(1).toString(), conjCall.operands.get(0)); } break; case IS_NULL: conditions.put(conjCall.operands.get(0).toString(), relBuilder.getRexBuilder().makeNullLiteral( conjCall.operands.get(0).getType().getSqlTypeName())); } } RexReplacer replacer = new RexReplacer(relBuilder.getRexBuilder(), conditions); List<RexNode> newProjects = Lists.newArrayList(projects); replacer.mutate(newProjects); if (replacer.replaced) { return newProjects; } return null; } protected static class RexReplacer extends RexShuttle { private final RexBuilder rexBuilder; private final Map<String, RexNode> replacements; private boolean replaced; RexReplacer( RexBuilder rexBuilder, Map<String, RexNode> replacements) { this.rexBuilder = rexBuilder; this.replacements = replacements; this.replaced = false; } @Override public RexNode visitInputRef(RexInputRef inputRef) { RexNode node = visit(inputRef); if (node == null) { return super.visitInputRef(inputRef); } this.replaced = true; return node; } @Override public RexNode visitCall(RexCall call) { RexNode node = visit(call); if (node != null) { this.replaced = true; return node; } return super.visitCall(call); } private RexNode visit(final RexNode call) { RexNode replacement = replacements.get(call.toString()); if (replacement == null) { return null; } if (replacement.getType().equals(call.getType())) { return replacement; } return rexBuilder.makeCast(call.getType(), replacement, true); } } }