| /* |
| * 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.index.rules; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Stopwatch; |
| import org.apache.calcite.plan.RelOptRule; |
| import org.apache.calcite.plan.RelOptRuleCall; |
| import org.apache.calcite.plan.RelOptRuleOperand; |
| import org.apache.calcite.rel.RelCollationTraitDef; |
| import org.apache.drill.exec.physical.base.DbGroupScan; |
| import org.apache.drill.exec.planner.index.IndexPhysicalPlanCallContext; |
| import org.apache.drill.exec.planner.index.IndexProperties; |
| import org.apache.drill.exec.planner.index.IndexSelector; |
| import org.apache.drill.exec.planner.index.IndexPlanUtils; |
| import org.apache.drill.exec.planner.index.IndexCollection; |
| import org.apache.drill.exec.planner.index.IndexDescriptor; |
| import org.apache.drill.exec.planner.index.FunctionalIndexInfo; |
| import org.apache.drill.exec.planner.index.generators.AbstractIndexPlanGenerator; |
| import org.apache.drill.exec.planner.index.generators.CoveringPlanNoFilterGenerator; |
| import org.apache.drill.exec.planner.logical.RelOptHelper; |
| import org.apache.drill.exec.planner.physical.Prule; |
| import org.apache.drill.exec.planner.physical.PlannerSettings; |
| import org.apache.drill.exec.planner.physical.PrelUtil; |
| import org.apache.drill.exec.planner.physical.ExchangePrel; |
| import org.apache.drill.exec.planner.physical.ProjectPrel; |
| import org.apache.drill.exec.planner.physical.ScanPrel; |
| import org.apache.drill.exec.planner.common.OrderedRel; |
| import org.apache.drill.exec.planner.physical.Prel; |
| import org.apache.drill.exec.planner.physical.HashToRandomExchangePrel; |
| import org.apache.calcite.rel.RelNode; |
| import com.google.common.collect.Lists; |
| |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| |
| public class DbScanSortRemovalRule extends Prule { |
| static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DbScanSortRemovalRule.class); |
| |
| public static final RelOptRule INDEX_SORT_EXCHANGE_SCAN = |
| new DbScanSortRemovalRule(RelOptHelper.some(OrderedRel.class, |
| RelOptHelper.some(HashToRandomExchangePrel.class, |
| RelOptHelper.any(ScanPrel.class))), "DbScanSortRemovalRule:sort_exchange_Scan", new MatchSES()); |
| |
| public static final RelOptRule INDEX_SORT_SCAN = |
| new DbScanSortRemovalRule(RelOptHelper.some(OrderedRel.class, |
| RelOptHelper.any(ScanPrel.class)), "DbScanSortRemovalRule:Sort_Scan", new MatchSS()); |
| |
| public static final RelOptRule INDEX_SORT_PROJ_SCAN = |
| new DbScanSortRemovalRule(RelOptHelper.some(OrderedRel.class, |
| RelOptHelper.some(ProjectPrel.class, |
| RelOptHelper.any(ScanPrel.class))), "DbScanSortRemovalRule:Sort_Proj_Scan", new MatchSPS()); |
| |
| public static final RelOptRule INDEX_SORT_EXCHANGE_PROJ_SCAN = |
| new DbScanSortRemovalRule(RelOptHelper.some(OrderedRel.class, |
| RelOptHelper.some(HashToRandomExchangePrel.class, |
| RelOptHelper.some(ProjectPrel.class, |
| RelOptHelper.any(ScanPrel.class)))), "DbScanSortRemovalRule:sort_exchange_proj_Scan", new MatchSEPS()); |
| |
| final private MatchFunction match; |
| |
| private DbScanSortRemovalRule(RelOptRuleOperand operand, |
| String description, |
| MatchFunction match) { |
| super(operand, description); |
| this.match = match; |
| } |
| |
| private static boolean isRemovableRel(OrderedRel node) { |
| return node.canBeDropped(); |
| } |
| |
| private static class MatchSES extends AbstractMatchFunction<IndexPhysicalPlanCallContext> { |
| |
| public boolean match(RelOptRuleCall call) { |
| final OrderedRel sort = call.rel(0); |
| final ScanPrel scan = call.rel(2); |
| return sort instanceof Prel && checkScan(scan.getGroupScan()) && isRemovableRel(sort); |
| } |
| |
| public IndexPhysicalPlanCallContext onMatch(RelOptRuleCall call) { |
| final ScanPrel scan = call.rel(2); |
| final OrderedRel sort = call.rel(0); |
| final ExchangePrel exch = call.rel(1); |
| return new IndexPhysicalPlanCallContext(call, sort, null, scan, exch); |
| } |
| } |
| |
| private static class MatchSS extends AbstractMatchFunction<IndexPhysicalPlanCallContext> { |
| |
| public boolean match(RelOptRuleCall call) { |
| final OrderedRel sort = call.rel(0); |
| final ScanPrel scan = call.rel(1); |
| return sort instanceof Prel && checkScan(scan.getGroupScan()) && isRemovableRel(sort); |
| } |
| |
| public IndexPhysicalPlanCallContext onMatch(RelOptRuleCall call) { |
| final ScanPrel scan = call.rel(1); |
| final OrderedRel sort = call.rel(0); |
| return new IndexPhysicalPlanCallContext(call, sort, null, scan, null); |
| } |
| } |
| |
| private static class MatchSPS extends AbstractMatchFunction<IndexPhysicalPlanCallContext> { |
| |
| public boolean match(RelOptRuleCall call) { |
| final OrderedRel sort = call.rel(0); |
| final ScanPrel scan = call.rel(2); |
| return sort instanceof Prel && checkScan(scan.getGroupScan()) && isRemovableRel(sort); |
| } |
| |
| public IndexPhysicalPlanCallContext onMatch(RelOptRuleCall call) { |
| final ScanPrel scan = call.rel(2); |
| final ProjectPrel proj = call.rel(1); |
| final OrderedRel sort = call.rel(0); |
| return new IndexPhysicalPlanCallContext(call, sort, proj, scan, null); |
| } |
| } |
| |
| private static class MatchSEPS extends AbstractMatchFunction<IndexPhysicalPlanCallContext> { |
| |
| public boolean match(RelOptRuleCall call) { |
| final OrderedRel sort = call.rel(0); |
| final ScanPrel scan = call.rel(3); |
| return sort instanceof Prel && checkScan(scan.getGroupScan()) && isRemovableRel(sort); |
| } |
| |
| public IndexPhysicalPlanCallContext onMatch(RelOptRuleCall call) { |
| final ScanPrel scan = call.rel(3); |
| final OrderedRel sort = call.rel(0); |
| final ProjectPrel proj = call.rel(2); |
| final ExchangePrel exch = call.rel(1); |
| return new IndexPhysicalPlanCallContext(call, sort, proj, scan, exch); |
| } |
| } |
| |
| @Override |
| public boolean matches(RelOptRuleCall call) { |
| return match.match(call); |
| } |
| |
| @Override |
| public void onMatch(RelOptRuleCall call) { |
| doOnMatch((IndexPhysicalPlanCallContext) match.onMatch(call)); |
| } |
| |
| private void doOnMatch(IndexPhysicalPlanCallContext indexContext) { |
| Stopwatch indexPlanTimer = Stopwatch.createStarted(); |
| final PlannerSettings settings = PrelUtil.getPlannerSettings(indexContext.call.getPlanner()); |
| |
| DbGroupScan groupScan = (DbGroupScan)indexContext.scan.getGroupScan(); |
| boolean isIndexScan = groupScan.isIndexScan(); |
| if (!isIndexScan) { |
| // This case generates the index scan and removes the sort if possible. |
| final IndexCollection indexCollection = groupScan.getSecondaryIndexCollection(indexContext.scan); |
| if (indexCollection == null) { |
| return; |
| } |
| if (settings.isStatisticsEnabled()) { |
| groupScan.getStatistics().initialize(null, indexContext.scan, indexContext); |
| } |
| IndexPlanUtils.updateSortExpression(indexContext, indexContext.getSort() != null ? |
| indexContext.getCollation().getFieldCollations() : null); |
| IndexSelector selector = new IndexSelector(indexContext); |
| for (IndexDescriptor indexDesc : indexCollection) { |
| indexDesc.getIndexGroupScan().setStatistics(groupScan.getStatistics()); |
| FunctionalIndexInfo functionInfo = indexDesc.getFunctionalInfo(); |
| if (IndexPlanUtils.isCoveringIndex(indexContext, functionInfo)) { |
| selector.addIndex(indexDesc, true, |
| indexContext.lowerProject != null ? indexContext.lowerProject.getRowType().getFieldCount() : |
| indexContext.scan.getRowType().getFieldCount()); |
| } |
| } |
| |
| IndexProperties idxProp = selector.getBestIndexNoFilter(); |
| if (idxProp != null) { |
| try { |
| //generate a covering plan |
| CoveringPlanNoFilterGenerator planGen = |
| new CoveringPlanNoFilterGenerator(indexContext, idxProp.getIndexDesc().getFunctionalInfo(), |
| false, settings); |
| if (planGen.convertChild() != null) { |
| indexContext.getCall().transformTo(planGen.convertChild()); |
| } else { |
| logger.debug("Not able to generate index plan in {}", this.getClass().toString()); |
| } |
| } catch (Exception e) { |
| logger.warn("Exception while trying to generate indexscan to remove sort", e); |
| } |
| } |
| } else { |
| Preconditions.checkNotNull(indexContext.getSort()); |
| //This case tries to use the already generated index to see if a sort can be removed. |
| if (indexContext.scan.getTraitSet().getTrait(RelCollationTraitDef.INSTANCE).getFieldCollations().size() == 0) { |
| return; |
| } |
| try { |
| RelNode finalRel = indexContext.scan.copy(indexContext.scan.getTraitSet(), indexContext.scan.getInputs()); |
| if (indexContext.lowerProject != null) { |
| List<RelNode> inputs = Lists.newArrayList(); |
| inputs.add(finalRel); |
| finalRel = indexContext.lowerProject.copy(indexContext.lowerProject.getTraitSet(), inputs); |
| } |
| |
| finalRel = AbstractIndexPlanGenerator.getSortNode(indexContext, finalRel, true,false, |
| indexContext.exch != null); |
| |
| if (finalRel == null) { |
| logger.debug("Not able to generate index plan in {}", this.getClass().toString()); |
| return; |
| } |
| |
| finalRel = Prule.convert(finalRel, finalRel.getTraitSet().plus(Prel.DRILL_PHYSICAL)); |
| indexContext.getCall().transformTo(finalRel); |
| } catch (Exception e) { |
| logger.warn("Exception while trying to use the indexscan to remove the sort", e); |
| } |
| } |
| |
| indexPlanTimer.stop(); |
| logger.debug("Index Planning took {} ms", indexPlanTimer.elapsed(TimeUnit.MILLISECONDS)); |
| } |
| } |