blob: 67e40947e5674bbc7f12b463796de204cf1d95f5 [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.store.mapr.db;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptRuleOperand;
import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.rules.ProjectRemoveRule;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexNode;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.exec.planner.common.DrillRelOptUtil;
import org.apache.drill.exec.planner.logical.RelOptHelper;
import org.apache.drill.exec.planner.physical.Prel;
import org.apache.drill.exec.planner.physical.ProjectPrel;
import org.apache.drill.exec.planner.physical.ScanPrel;
import org.apache.drill.exec.store.StoragePluginOptimizerRule;
import org.apache.drill.exec.store.mapr.db.json.JsonTableGroupScan;
import org.apache.drill.exec.util.Utilities;
import java.util.List;
import java.util.stream.Collectors;
/**
* Push a physical Project into Scan. Currently, this rule is only doing projection pushdown for MapRDB-JSON tables
* since it was needed for the secondary index feature which only applies to Json tables.
* For binary tables, note that the DrillPushProjectIntoScanRule is still applicable during the logical
* planning phase.
*/
public abstract class MapRDBPushProjectIntoScan extends StoragePluginOptimizerRule {
private MapRDBPushProjectIntoScan(RelOptRuleOperand operand, String description) {
super(operand, description);
}
public static final StoragePluginOptimizerRule PROJECT_ON_SCAN = new MapRDBPushProjectIntoScan(
RelOptHelper.some(ProjectPrel.class, RelOptHelper.any(ScanPrel.class)), "MapRDBPushProjIntoScan:Proj_On_Scan") {
@Override
public void onMatch(RelOptRuleCall call) {
final ProjectPrel project = call.rel(0);
final ScanPrel scan = call.rel(1);
if (scan.getGroupScan() instanceof JsonTableGroupScan) {
JsonTableGroupScan groupScan = (JsonTableGroupScan) scan.getGroupScan();
doPushProjectIntoGroupScan(call, project, scan, groupScan);
}
}
@Override
public boolean matches(RelOptRuleCall call) {
final ScanPrel scan = call.rel(1);
// See class level comments above for why only JsonGroupScan is considered
if (scan.getGroupScan() instanceof JsonTableGroupScan) {
return super.matches(call);
}
return false;
}
};
protected void doPushProjectIntoGroupScan(RelOptRuleCall call,
ProjectPrel project, ScanPrel scan, JsonTableGroupScan groupScan) {
try {
DrillRelOptUtil.ProjectPushInfo columnInfo =
DrillRelOptUtil.getFieldsInformation(scan.getRowType(), project.getProjects());
if (columnInfo == null || Utilities.isStarQuery(columnInfo.getFields())
|| !groupScan.canPushdownProjects(columnInfo.getFields())) {
return;
}
RelTraitSet newTraits = call.getPlanner().emptyTraitSet();
// Clear out collation trait
for (RelTrait trait : scan.getTraitSet()) {
if (!(trait instanceof RelCollation)) {
newTraits.plus(trait);
}
}
final ScanPrel newScan = new ScanPrel(scan.getCluster(), newTraits.plus(Prel.DRILL_PHYSICAL),
groupScan.clone(columnInfo.getFields()),
columnInfo.createNewRowType(project.getInput().getCluster().getTypeFactory()), scan.getTable());
List<RexNode> newProjects = project.getProjects().stream()
.map(n -> n.accept(columnInfo.getInputReWriter()))
.collect(Collectors.toList());
final ProjectPrel newProj =
new ProjectPrel(project.getCluster(),
project.getTraitSet().plus(Prel.DRILL_PHYSICAL),
newScan,
newProjects,
project.getRowType());
if (ProjectRemoveRule.isTrivial(newProj) &&
// the old project did not involve any column renaming
sameRowTypeProjectionsFields(project.getRowType(), newScan.getRowType())) {
call.transformTo(newScan);
} else {
call.transformTo(newProj);
}
} catch (Exception e) {
throw new DrillRuntimeException(e);
}
}
private boolean sameRowTypeProjectionsFields(RelDataType oldRowType, RelDataType newRowType) {
for (RelDataTypeField oldField : oldRowType.getFieldList()) {
String oldProjName = oldField.getName();
boolean match = false;
for (RelDataTypeField newField : newRowType.getFieldList()) {
if (oldProjName.equals(newField.getName())) {
match = true;
break;
}
}
if (!match) {
return false;
}
}
return true;
}
}