blob: de2a911cd349125bfaee2e2a3480437a952703a9 [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.physical.impl.project;
import com.google.common.base.Preconditions;
import org.apache.drill.common.expression.FunctionHolderExpression;
import org.apache.drill.common.expression.IfExpression;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.expression.TypedNullConstant;
import org.apache.drill.common.expression.ValueExpressions;
import org.apache.drill.common.types.TypeProtos.MajorType;
import org.apache.drill.common.types.Types;
import org.apache.drill.exec.expr.AbstractExecExprVisitor;
import org.apache.drill.exec.expr.DrillFuncHolderExpr;
import org.apache.drill.exec.expr.ValueVectorReadExpression;
import org.apache.drill.exec.expr.ValueVectorWriteExpression;
import org.apache.drill.exec.expr.annotations.FunctionTemplate;
import org.apache.drill.exec.expr.fn.DrillFuncHolder;
import org.apache.drill.exec.expr.fn.output.OutputWidthCalculator;
import org.apache.drill.exec.physical.impl.project.OutputWidthExpression.FixedLenExpr;
import org.apache.drill.exec.physical.impl.project.OutputWidthExpression.FunctionCallExpr;
import org.apache.drill.exec.physical.impl.project.OutputWidthExpression.VarLenReadExpr;
import org.apache.drill.exec.physical.impl.project.OutputWidthExpression.IfElseWidthExpr;
import org.apache.drill.common.expression.ValueExpressions.VarDecimalExpression;
import org.apache.drill.exec.record.RecordBatchSizer;
import org.apache.drill.exec.record.TypedFieldId;
import java.util.ArrayList;
public class OutputWidthVisitor extends
AbstractExecExprVisitor<OutputWidthExpression,
OutputWidthVisitorState, RuntimeException> {
@Override
public OutputWidthExpression visitVarDecimalConstant(VarDecimalExpression varDecimalExpression,
OutputWidthVisitorState state) throws RuntimeException {
Preconditions.checkArgument(varDecimalExpression.getMajorType().hasPrecision());
return new FixedLenExpr(varDecimalExpression.getMajorType().getPrecision());
}
/**
* Records the {@link IfExpression} as a {@link IfElseWidthExpr}.
* IfElseWidthExpr will be reduced to a {@link FixedLenExpr} by taking the max
* of the if-expr-width and the else-expr-width.
*/
@Override
public OutputWidthExpression visitIfExpression(IfExpression ifExpression, OutputWidthVisitorState state)
throws RuntimeException {
IfExpression.IfCondition condition = ifExpression.ifCondition;
LogicalExpression ifExpr = condition.expression;
LogicalExpression elseExpr = ifExpression.elseExpression;
OutputWidthExpression ifWidthExpr = ifExpr.accept(this, state);
OutputWidthExpression elseWidthExpr = null;
if (elseExpr != null) {
elseWidthExpr = elseExpr.accept(this, state);
}
return new IfElseWidthExpr(ifWidthExpr, elseWidthExpr);
}
/**
* Handles a {@link FunctionHolderExpression}. Functions that produce
* fixed-width output are trivially converted to a {@link FixedLenExpr}. For
* functions that produce variable width output, the output width calculator
* annotation is looked-up and recorded in a {@link FunctionCallExpr}. This
* calculator will later be used to convert the FunctionCallExpr to a
* {@link FixedLenExpr} expression
*/
@Override
public OutputWidthExpression visitFunctionHolderExpression(FunctionHolderExpression holderExpr,
OutputWidthVisitorState state) throws RuntimeException {
OutputWidthExpression fixedWidth = getFixedLenExpr(holderExpr.getMajorType());
if (fixedWidth != null) { return fixedWidth; }
// Only Drill functions can be handled. Non-drill Functions, like HiveFunctions
// will default to a fixed value
if (!(holderExpr instanceof DrillFuncHolderExpr)) {
// We currently only know how to handle DrillFuncs.
// Use a default if this is not a DrillFunc
return new FixedLenExpr(OutputSizeEstimateConstants.NON_DRILL_FUNCTION_OUTPUT_SIZE_ESTIMATE);
}
final DrillFuncHolder holder = ((DrillFuncHolderExpr) holderExpr).getHolder();
// If the user has provided a size estimate, use it
int estimate = holder.variableOutputSizeEstimate();
if (estimate != FunctionTemplate.OUTPUT_SIZE_ESTIMATE_DEFAULT) {
return new FixedLenExpr(estimate);
}
// Use the calculator provided by the user or use the default
OutputWidthCalculator widthCalculator = holder.getOutputWidthCalculator();
final int argSize = holderExpr.args.size();
ArrayList<OutputWidthExpression> arguments = null;
if (argSize != 0) {
arguments = new ArrayList<>(argSize);
for (LogicalExpression expr : holderExpr.args) {
arguments.add(expr.accept(this, state));
}
}
return new FunctionCallExpr(holderExpr, widthCalculator, arguments);
}
/**
* Records a variable width write expression. This will be converted to a
* {@link FixedLenExpr} expression by walking the tree of expression attached
* to the write expression.
*/
@Override
public OutputWidthExpression visitValueVectorWriteExpression(ValueVectorWriteExpression writeExpr,
OutputWidthVisitorState state) throws RuntimeException {
TypedFieldId fieldId = writeExpr.getFieldId();
ProjectMemoryManager manager = state.getManager();
OutputWidthExpression outputExpr;
if (manager.isFixedWidth(fieldId)) {
outputExpr = getFixedLenExpr(fieldId.getFinalType());
} else {
LogicalExpression writeArg = writeExpr.getChild();
outputExpr = writeArg.accept(this, state);
}
return outputExpr;
}
/**
* Records a variable width read expression as a {@link VarLenReadExpr}. This
* will be converted to a {@link FixedLenExpr} expression by getting the size
* for the corresponding column from the {@link RecordBatchSizer}.
*/
@Override
public OutputWidthExpression visitValueVectorReadExpression(ValueVectorReadExpression readExpr,
OutputWidthVisitorState state) throws RuntimeException {
return new VarLenReadExpr(readExpr);
}
@Override
public OutputWidthExpression visitQuotedStringConstant(ValueExpressions.QuotedString quotedString,
OutputWidthVisitorState state) throws RuntimeException {
return new FixedLenExpr(quotedString.getString().length());
}
@Override
public OutputWidthExpression visitUnknown(LogicalExpression logicalExpression, OutputWidthVisitorState state) {
OutputWidthExpression fixedLenExpr = getFixedLenExpr(logicalExpression.getMajorType());
if (fixedLenExpr != null) {
return fixedLenExpr;
}
throw new IllegalStateException("Unknown variable width expression: " + logicalExpression);
}
@Override
public OutputWidthExpression visitNullConstant(TypedNullConstant nullConstant, OutputWidthVisitorState state)
throws RuntimeException {
int width;
if (nullConstant.getMajorType().hasPrecision()) {
width = nullConstant.getMajorType().getPrecision();
} else {
width = 0;
}
return new FixedLenExpr(width);
}
@Override
public OutputWidthExpression visitFixedLenExpr(FixedLenExpr fixedLenExpr, OutputWidthVisitorState state)
throws RuntimeException {
return fixedLenExpr;
}
/**
* Converts the {@link VarLenReadExpr} to a {@link FixedLenExpr} by getting
* the size for the corresponding column from the RecordBatchSizer.
*/
@Override
public OutputWidthExpression visitVarLenReadExpr(VarLenReadExpr varLenReadExpr, OutputWidthVisitorState state)
throws RuntimeException {
String columnName = varLenReadExpr.getInputColumnName();
if (columnName == null) {
TypedFieldId fieldId = varLenReadExpr.getReadExpression().getTypedFieldId();
columnName = TypedFieldId.getPath(fieldId, state.manager.incomingBatch());
}
final RecordBatchSizer.ColumnSize columnSize = state.manager.getColumnSize(columnName);
int columnWidth = columnSize.getDataSizePerEntry();
return new FixedLenExpr(columnWidth);
}
/**
* Converts a {@link FunctionCallExpr} to a {@link FixedLenExpr} by passing
* the the args of the function to the width calculator for this function.
*/
@Override
public OutputWidthExpression visitFunctionCallExpr(FunctionCallExpr functionCallExpr, OutputWidthVisitorState state)
throws RuntimeException {
ArrayList<OutputWidthExpression> args = functionCallExpr.getArgs();
ArrayList<FixedLenExpr> estimatedArgs = null;
if (args != null && args.size() != 0) {
estimatedArgs = new ArrayList<>(args.size());
for (OutputWidthExpression expr : args) {
// Once the args are visited, they will all become FixedWidthExpr
FixedLenExpr fixedLenExpr = (FixedLenExpr) expr.accept(this, state);
estimatedArgs.add(fixedLenExpr);
}
}
OutputWidthCalculator estimator = functionCallExpr.getCalculator();
int estimatedSize = estimator.getOutputWidth(estimatedArgs);
return new FixedLenExpr(estimatedSize);
}
/**
* Converts the {@link IfElseWidthExpr} to a {@link FixedLenExpr} by taking
* the max of the if-expr-width and the else-expr-width.
*/
@Override
public OutputWidthExpression visitIfElseWidthExpr(IfElseWidthExpr ifElseWidthExpr, OutputWidthVisitorState state)
throws RuntimeException {
OutputWidthExpression ifReducedExpr = ifElseWidthExpr.expressions[0].accept(this, state);
assert ifReducedExpr instanceof FixedLenExpr;
int ifWidth = ((FixedLenExpr)ifReducedExpr).getDataWidth();
int elseWidth = -1;
if (ifElseWidthExpr.expressions[1] != null) {
OutputWidthExpression elseReducedExpr = ifElseWidthExpr.expressions[1].accept(this, state);
assert elseReducedExpr instanceof FixedLenExpr;
elseWidth = ((FixedLenExpr)elseReducedExpr).getDataWidth();
}
int outputWidth = Math.max(ifWidth, elseWidth);
return new FixedLenExpr(outputWidth);
}
private OutputWidthExpression getFixedLenExpr(MajorType majorType) {
MajorType type = majorType;
if (Types.isFixedWidthType(type)) {
// Use only the width of the data. Metadata width will be accounted for at the end
// This is to avoid using metadata size in intermediate calculations
int fixedDataWidth = ProjectMemoryManager.getFixedWidth(type);
return new OutputWidthExpression.FixedLenExpr(fixedDataWidth);
}
return null;
}
}