/** * 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.views; import java.util.List; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.tools.RelBuilderFactory; import com.google.common.collect.ImmutableList; /** * Extension to {@link SubstitutionVisitor}. * * TODO: Remove when we upgrade to Calcite version using builders. */ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor { private static final ImmutableList<UnifyRule> EXTENDED_RULES = ImmutableList.<UnifyRule>builder() .addAll(DEFAULT_RULES) .add(ProjectToProjectUnifyRule1.INSTANCE) .add(FilterToFilterUnifyRule1.INSTANCE) .add(FilterToProjectUnifyRule1.INSTANCE) .build(); public MaterializedViewSubstitutionVisitor(RelNode target_, RelNode query_) { super(target_, query_, EXTENDED_RULES); } public MaterializedViewSubstitutionVisitor(RelNode target_, RelNode query_, RelBuilderFactory relBuilderFactory) { super(target_, query_, EXTENDED_RULES, relBuilderFactory); } public List<RelNode> go(RelNode replacement_) { return super.go(replacement_); } /** * Implementation of {@link SubstitutionVisitor.UnifyRule} that matches a * {@link SubstitutionVisitor.MutableProject} to a * {@link SubstitutionVisitor.MutableProject} where the condition of the target * relation is weaker. * * <p>Example: target has a weaker condition and contains all columns selected * by query</p> * <ul> * <li>query: Project(projects: [$2, $0]) * Filter(condition: >($1, 20)) * Scan(table: [hr, emps])</li> * <li>target: Project(projects: [$0, $1, $2]) * Filter(condition: >($1, 10)) * Scan(table: [hr, emps])</li> * </ul> */ private static class ProjectToProjectUnifyRule1 extends AbstractUnifyRule { public static final ProjectToProjectUnifyRule1 INSTANCE = new ProjectToProjectUnifyRule1(); private ProjectToProjectUnifyRule1() { super(operand(MutableProject.class, query(0)), operand(MutableProject.class, target(0)), 1); } @Override protected UnifyResult apply(UnifyRuleCall call) { final MutableProject query = (MutableProject) call.query; final List<RelDataTypeField> oldFieldList = query.getInput().getRowType().getFieldList(); final List<RelDataTypeField> newFieldList = call.target.getRowType().getFieldList(); List<RexNode> newProjects; try { newProjects = transformRex(query.getProjects(), oldFieldList, newFieldList); } catch (MatchFailed e) { return null; } final MutableProject newProject = MutableProject.of( query.getRowType(), call.target, newProjects); final MutableRel newProject2 = MutableRels.strip(newProject); return call.result(newProject2); } @Override protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query, MutableRel target) { assert query instanceof MutableProject && target instanceof MutableProject; if (queryOperand.matches(visitor, query)) { if (targetOperand.matches(visitor, target)) { return null; } else if (targetOperand.isWeaker(visitor, target)) { final MutableProject queryProject = (MutableProject) query; if (queryProject.getInput() instanceof MutableFilter) { final MutableFilter innerFilter = (MutableFilter) queryProject.getInput(); RexNode newCondition; try { newCondition = transformRex(innerFilter.getCondition(), innerFilter.getInput().getRowType().getFieldList(), target.getRowType().getFieldList()); } catch (MatchFailed e) { return null; } final MutableFilter newFilter = MutableFilter.of(target, newCondition); return visitor.new UnifyRuleCall(this, query, newFilter, copy(visitor.slots, slotCount)); } } } return null; } } /** * Implementation of {@link SubstitutionVisitor.UnifyRule} that matches a * {@link SubstitutionVisitor.MutableFilter} to a * {@link SubstitutionVisitor.MutableFilter} where the condition of the target * relation is weaker. * * <p>Example: target has a weaker condition</p> * <ul> * <li>query: Filter(condition: >($1, 20)) * Scan(table: [hr, emps])</li> * <li>target: Filter(condition: >($1, 10)) * Scan(table: [hr, emps])</li> * </ul> */ private static class FilterToFilterUnifyRule1 extends AbstractUnifyRule { public static final FilterToFilterUnifyRule1 INSTANCE = new FilterToFilterUnifyRule1(); private FilterToFilterUnifyRule1() { super(operand(MutableFilter.class, query(0)), operand(MutableFilter.class, target(0)), 1); } public UnifyResult apply(UnifyRuleCall call) { final MutableFilter query = (MutableFilter) call.query; final MutableFilter target = (MutableFilter) call.target; final MutableFilter newFilter = MutableFilter.of(target, query.getCondition()); return call.result(newFilter); } @Override protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query, MutableRel target) { if (queryOperand.matches(visitor, query)) { if (targetOperand.matches(visitor, target)) { if (visitor.isWeaker(query, target)) { return visitor.new UnifyRuleCall(this, query, target, copy(visitor.slots, slotCount)); } } } return null; } } /** * Implementation of {@link SubstitutionVisitor.UnifyRule} that matches a * {@link SubstitutionVisitor.MutableFilter} to a * {@link SubstitutionVisitor.MutableProject} on top of a * {@link SubstitutionVisitor.MutableFilter} where the condition of the target * relation is weaker. * * <p>Example: target has a weaker condition and is a permutation projection of * its child relation</p> * <ul> * <li>query: Filter(condition: >($1, 20)) * Scan(table: [hr, emps])</li> * <li>target: Project(projects: [$1, $0, $2, $3, $4]) * Filter(condition: >($1, 10)) * Scan(table: [hr, emps])</li> * </ul> */ private static class FilterToProjectUnifyRule1 extends AbstractUnifyRule { public static final FilterToProjectUnifyRule1 INSTANCE = new FilterToProjectUnifyRule1(); private FilterToProjectUnifyRule1() { super( operand(MutableFilter.class, query(0)), operand(MutableProject.class, operand(MutableFilter.class, target(0))), 1); } public UnifyResult apply(UnifyRuleCall call) { final MutableRel query = call.query; final List<RelDataTypeField> oldFieldList = query.getRowType().getFieldList(); final List<RelDataTypeField> newFieldList = call.target.getRowType().getFieldList(); List<RexNode> newProjects; try { newProjects = transformRex( (List<RexNode>) call.getCluster().getRexBuilder().identityProjects( query.getRowType()), oldFieldList, newFieldList); } catch (MatchFailed e) { return null; } final MutableProject newProject = MutableProject.of( query.getRowType(), call.target, newProjects); final MutableRel newProject2 = MutableRels.strip(newProject); return call.result(newProject2); } @Override protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query, MutableRel target) { assert query instanceof MutableFilter && target instanceof MutableProject; if (queryOperand.matches(visitor, query)) { if (targetOperand.matches(visitor, target)) { if (visitor.isWeaker(query, ((MutableProject) target).getInput())) { final MutableFilter filter = (MutableFilter) query; RexNode newCondition; try { newCondition = transformRex(filter.getCondition(), filter.getInput().getRowType().getFieldList(), target.getRowType().getFieldList()); } catch (MatchFailed e) { return null; } final MutableFilter newFilter = MutableFilter.of(target, newCondition); return visitor.new UnifyRuleCall(this, query, newFilter, copy(visitor.slots, slotCount)); } } } return null; } } private static RexNode transformRex(RexNode node, final List<RelDataTypeField> oldFields, final List<RelDataTypeField> newFields) { List<RexNode> nodes = transformRex(ImmutableList.of(node), oldFields, newFields); return nodes.get(0); } private static List<RexNode> transformRex( List<RexNode> nodes, final List<RelDataTypeField> oldFields, final List<RelDataTypeField> newFields) { RexShuttle shuttle = new RexShuttle() { @Override public RexNode visitInputRef(RexInputRef ref) { RelDataTypeField f = oldFields.get(ref.getIndex()); for (int index = 0; index < newFields.size(); index++) { RelDataTypeField newf = newFields.get(index); if (f.getKey().equals(newf.getKey()) && f.getValue() == newf.getValue()) { return new RexInputRef(index, f.getValue()); } } throw MatchFailed.INSTANCE; } }; return shuttle.apply(nodes); } } // End MaterializedViewSubstitutionVisitor.java