blob: 6b48fe40e0ee44083cb7e5bf8162ab0e9d12af66 [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.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));
}
}