/** * 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 org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollationTraitDef; import org.apache.calcite.rel.RelCollations; import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.JoinRelType; import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rex.RexLiteral; import org.apache.hadoop.hive.ql.optimizer.calcite.HiveCalciteUtil; import org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveJoin; import org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveSortLimit; /** * Planner rule that pushes * a {@link org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveSortLimit} * past a {@link org.apache.hadoop.hive.ql.optimizer.calcite.reloperators.HiveJoin}. */ public class HiveSortJoinReduceRule extends RelOptRule { public static final HiveSortJoinReduceRule INSTANCE = new HiveSortJoinReduceRule(); //~ Constructors ----------------------------------------------------------- private HiveSortJoinReduceRule() { super( operand( HiveSortLimit.class, operand(HiveJoin.class, any()))); } //~ Methods ---------------------------------------------------------------- @Override public boolean matches(RelOptRuleCall call) { final HiveSortLimit sortLimit = call.rel(0); final HiveJoin join = call.rel(1); // If sort does not contain a limit operation or limit is 0, we bail out if (!HiveCalciteUtil.limitRelNode(sortLimit) || RexLiteral.intValue(sortLimit.fetch) == 0) { return false; } // 1) If join is not a left or right outer, we bail out // 2) If any sort column is not part of the input where the // sort is pushed, we bail out RelNode reducedInput; if (join.getJoinType() == JoinRelType.LEFT) { reducedInput = join.getLeft(); if (sortLimit.getCollation() != RelCollations.EMPTY) { for (RelFieldCollation relFieldCollation : sortLimit.getCollation().getFieldCollations()) { if (relFieldCollation.getFieldIndex() >= join.getLeft().getRowType().getFieldCount()) { return false; } } } } else if (join.getJoinType() == JoinRelType.RIGHT) { reducedInput = join.getRight(); if (sortLimit.getCollation() != RelCollations.EMPTY) { for (RelFieldCollation relFieldCollation : sortLimit.getCollation().getFieldCollations()) { if (relFieldCollation.getFieldIndex() < join.getLeft().getRowType().getFieldCount()) { return false; } } } } else { return false; } // Finally, if we do not reduce the input size, we bail out final int offset = sortLimit.offset == null ? 0 : RexLiteral.intValue(sortLimit.offset); if (offset + RexLiteral.intValue(sortLimit.fetch) >= RelMetadataQuery.instance().getRowCount(reducedInput)) { return false; } return true; } @Override public void onMatch(RelOptRuleCall call) { final HiveSortLimit sortLimit = call.rel(0); final HiveJoin join = call.rel(1); RelNode inputLeft = join.getLeft(); RelNode inputRight = join.getRight(); // We create a new sort operator on the corresponding input if (join.getJoinType() == JoinRelType.LEFT) { inputLeft = sortLimit.copy(sortLimit.getTraitSet(), inputLeft, sortLimit.getCollation(), sortLimit.offset, sortLimit.fetch); ((HiveSortLimit) inputLeft).setRuleCreated(true); } else { // Adjust right collation final RelCollation rightCollation = RelCollationTraitDef.INSTANCE.canonize( RelCollations.shift(sortLimit.getCollation(), -join.getLeft().getRowType().getFieldCount())); inputRight = sortLimit.copy(sortLimit.getTraitSet().replace(rightCollation), inputRight, rightCollation, sortLimit.offset, sortLimit.fetch); ((HiveSortLimit) inputRight).setRuleCreated(true); } // We copy the join and the top sort operator RelNode result = join.copy(join.getTraitSet(), join.getCondition(), inputLeft, inputRight, join.getJoinType(), join.isSemiJoinDone()); result = sortLimit.copy(sortLimit.getTraitSet(), result, sortLimit.getCollation(), sortLimit.offset, sortLimit.fetch); call.transformTo(result); } }