blob: ded425cea23146767fd09c91951c8687deb27b2b [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;
import org.apache.drill.exec.util.Utilities;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexCorrelVariable;
import org.apache.calcite.rex.RexDynamicParam;
import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexOver;
import org.apache.calcite.rex.RexRangeRef;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexVisitorImpl;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.NlsString;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.expression.PathSegment;
import java.util.List;
import java.util.Map;
/**
* Rewrite RexNode with these policies:
* 1) field renamed. The input field was named differently in index table,
* 2) field is in different position of underlying rowtype
*
* TODO: 3) certain operator needs rewriting. e.g. CAST function
* This class for now applies to only filter on scan, for filter-on-project-on-scan. A stack of
* rowType is required.
*/
public class SimpleRexRemap {
final RelNode origRel;
final RelDataType origRowType;
final RelDataType newRowType;
private RexBuilder builder;
private Map<LogicalExpression, LogicalExpression> destExprMap;
public SimpleRexRemap(RelNode origRel,
RelDataType newRowType, RexBuilder builder) {
super();
this.origRel = origRel;
this.origRowType = origRel.getRowType();
this.newRowType = newRowType;
this.builder = builder;
this.destExprMap = Maps.newHashMap();
}
/**
* Set the map of src expression to target expression, expressions not in the map do not have assigned destinations
* @param exprMap
* @return
*/
public SimpleRexRemap setExpressionMap(Map<LogicalExpression, LogicalExpression> exprMap) {
destExprMap.putAll(exprMap);
return this;
}
public RexNode rewriteEqualOnCharToLike(RexNode expr,
Map<RexNode, LogicalExpression> equalOnCastCharExprs) {
Map<RexNode, RexNode> srcToReplace = Maps.newIdentityHashMap();
for (Map.Entry<RexNode, LogicalExpression> entry: equalOnCastCharExprs.entrySet()) {
RexNode equalOp = entry.getKey();
LogicalExpression opInput = entry.getValue();
final List<RexNode> operands = ((RexCall)equalOp).getOperands();
RexLiteral newLiteral = null;
RexNode input = null;
if (operands.size() == 2 ) {
RexLiteral oplit = null;
if (operands.get(0) instanceof RexLiteral) {
oplit = (RexLiteral) operands.get(0);
if (oplit.getTypeName() == SqlTypeName.CHAR) {
newLiteral = builder.makeLiteral(((NlsString) oplit.getValue()).getValue() + "%");
input = operands.get(1);
}
}
else if (operands.get(1) instanceof RexLiteral) {
oplit = (RexLiteral) operands.get(1);
if (oplit.getTypeName() == SqlTypeName.CHAR) {
newLiteral = builder.makeLiteral(((NlsString) oplit.getValue()).getValue() + "%");
input = operands.get(0);
}
}
}
if (newLiteral != null) {
srcToReplace.put(equalOp, builder.makeCall(SqlStdOperatorTable.LIKE, input, newLiteral));
}
}
if (srcToReplace.size() > 0) {
RexReplace replacer = new RexReplace(srcToReplace);
RexNode resultRex = expr.accept(replacer);
return resultRex;
}
return expr;
}
/**
*
* @param srcRex the source RexNode to be rewritten
* @param mapRexToExpr a map of rex->logical expression to guide what rex to rewrite
* @return the RexNode after rewriting
*/
public RexNode rewriteWithMap(RexNode srcRex, Map<RexNode, LogicalExpression> mapRexToExpr) {
Map<RexNode, RexNode> destNodeMap = Maps.newHashMap();
for (Map.Entry<RexNode, LogicalExpression> entry: mapRexToExpr.entrySet()) {
LogicalExpression entryExpr = entry.getValue();
LogicalExpression destExpr = destExprMap.get(entryExpr);
// then build rexNode from the path
RexNode destRex = buildRexForField(destExpr==null?entryExpr : destExpr, newRowType);
destNodeMap.put(entry.getKey(), destRex);
}
// Visit through the nodes, if destExprMap has an entry to provide substitute to replace a rexNode, replace the rexNode
RexReplace replacer = new RexReplace(destNodeMap);
RexNode resultRex = srcRex.accept(replacer);
return resultRex;
}
public RexNode rewrite(RexNode expr) {
IndexableExprMarker marker = new IndexableExprMarker(origRel);
expr.accept(marker);
return rewriteWithMap(expr, marker.getIndexableExpression());
}
private RexNode buildRexForField(LogicalExpression expr, RelDataType newRowType) {
ExprToRex toRex = new ExprToRex(origRel, newRowType, builder);
return expr.accept(toRex, null);
}
public static String getFullPath(PathSegment pathSeg) {
PathSegment.NameSegment nameSeg = (PathSegment.NameSegment)pathSeg;
if (nameSeg.isLastPath()) {
return nameSeg.getPath();
}
return String.format("%s.%s",
nameSeg.getPath(),
getFullPath(nameSeg.getChild()));
}
/**
* This class go through the RexNode, collect all the fieldNames, mark starting positions(RexNode) of fields
* so this information can be used later e,.g. replaced with a substitute node
*/
public static class FieldsMarker extends RexVisitorImpl<PathSegment> {
final List<String> fieldNames;
final List<RelDataTypeField> fields;
final Map<RexNode, String> desiredFields = Maps.newHashMap();
int stackDepth;
public FieldsMarker(RelDataType rowType) {
super(true);
this.fieldNames = rowType.getFieldNames();
this.fields = rowType.getFieldList();
this.stackDepth = 0;
}
private PathSegment newPath(PathSegment segment, RexNode node) {
if (stackDepth == 0) {
desiredFields.put(node, getFullPath(segment));
}
return segment;
}
private PathSegment newPath(String path, RexNode node) {
PathSegment segment = new PathSegment.NameSegment(path);
if (stackDepth == 0) {
desiredFields.put(node, getFullPath(segment));
}
return segment;
}
public Map<RexNode, String> getFieldAndPos() {
return ImmutableMap.copyOf(desiredFields);
}
@Override
public PathSegment visitInputRef(RexInputRef inputRef) {
int index = inputRef.getIndex();
String name = fieldNames.get(index);
return newPath(name, inputRef);
}
@Override
public PathSegment visitCall(RexCall call) {
if ("ITEM".equals(call.getOperator().getName())) {
stackDepth++;
PathSegment mapOrArray = call.operands.get(0).accept(this);
stackDepth--;
if (mapOrArray != null) {
if (call.operands.get(1) instanceof RexLiteral) {
PathSegment newFieldPath = newPath(
mapOrArray.cloneWithNewChild(Utilities.convertLiteral((RexLiteral) call.operands.get(1))),
call);
return newFieldPath;
}
return mapOrArray;
}
} else {
for (RexNode operand : call.operands) {
operand.accept(this);
}
}
return null;
}
}
public static class RexReplace extends RexShuttle {
final Map<RexNode, RexNode> rexMap;
public RexReplace( Map<RexNode, RexNode> rexMap) {
this.rexMap = rexMap;
}
boolean toReplace(RexNode node) {
return rexMap.containsKey(node);
}
RexNode replace(RexNode node) {
return rexMap.get(node);
}
public RexNode visitOver(RexOver over) {
return toReplace(over) ? replace(over) : super.visitOver(over);
}
public RexNode visitCall(final RexCall call) {
return toReplace(call) ? replace(call) : super.visitCall(call);
}
public RexNode visitCorrelVariable(RexCorrelVariable variable) {
return variable;
}
public RexNode visitFieldAccess(RexFieldAccess fieldAccess) {
return toReplace(fieldAccess) ? replace(fieldAccess) : super.visitFieldAccess(fieldAccess);
}
public RexNode visitInputRef(RexInputRef inputRef) {
return toReplace(inputRef) ? replace(inputRef) : super.visitInputRef(inputRef);
}
public RexNode visitLocalRef(RexLocalRef localRef) {
return toReplace(localRef) ? replace(localRef) : super.visitLocalRef(localRef);
}
public RexNode visitLiteral(RexLiteral literal) {
return literal;
}
public RexNode visitDynamicParam(RexDynamicParam dynamicParam) {
return dynamicParam;
}
public RexNode visitRangeRef(RexRangeRef rangeRef) {
return toReplace(rangeRef) ? replace(rangeRef) : super.visitRangeRef(rangeRef);
}
}
}