blob: b735021ea7c57395cf3ab0e86505631453e5b2b4 [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.expr.fn.output;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.expression.ValueExpressions;
import org.apache.drill.common.types.TypeProtos;
import org.apache.drill.exec.planner.types.decimal.DecimalScalePrecisionAddFunction;
import org.apache.drill.exec.planner.types.decimal.DecimalScalePrecisionDivideFunction;
import org.apache.drill.exec.planner.types.decimal.DecimalScalePrecisionModFunction;
import org.apache.drill.exec.planner.types.decimal.DecimalScalePrecisionMulFunction;
import org.apache.drill.exec.expr.annotations.FunctionTemplate;
import org.apache.drill.exec.expr.fn.FunctionAttributes;
import org.apache.drill.exec.expr.fn.FunctionUtils;
import org.apache.drill.exec.util.DecimalUtility;
import java.util.List;
import static org.apache.drill.exec.planner.types.DrillRelDataTypeSystem.DRILL_REL_DATATYPE_SYSTEM;
public class DecimalReturnTypeInference {
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_ADD_SCALE}.
*/
public static class DecimalAddReturnTypeInference implements ReturnTypeInference {
public static final DecimalAddReturnTypeInference INSTANCE = new DecimalAddReturnTypeInference();
/**
* This return type is used by add and subtract functions for decimal data type.
* DecimalScalePrecisionAddFunction is used to compute the output types' scale and precision.
*
* @param logicalExpressions logical expressions
* @param attributes function attributes
* @return return type
*/
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
assert logicalExpressions.size() == 2;
TypeProtos.MajorType leftMajorType = logicalExpressions.get(0).getMajorType();
TypeProtos.MajorType rightMajorType = logicalExpressions.get(1).getMajorType();
DecimalScalePrecisionAddFunction outputScalePrec =
new DecimalScalePrecisionAddFunction(
DecimalUtility.getDefaultPrecision(leftMajorType.getMinorType(), leftMajorType.getPrecision()),
leftMajorType.getScale(),
DecimalUtility.getDefaultPrecision(rightMajorType.getMinorType(), rightMajorType.getPrecision()),
rightMajorType.getScale());
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(outputScalePrec.getOutputScale())
.setPrecision(outputScalePrec.getOutputPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_AGGREGATE}.
*/
public static class DecimalAggReturnTypeInference implements ReturnTypeInference {
public static final DecimalAggReturnTypeInference INSTANCE = new DecimalAggReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
int scale = 0;
int precision = 0;
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
// Get the max scale and precision from the inputs
for (LogicalExpression e : logicalExpressions) {
scale = Math.max(scale, e.getMajorType().getScale());
precision = Math.max(precision, e.getMajorType().getPrecision());
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(attributes.getReturnValue().getType().getMinorType())
.setScale(scale)
.setPrecision(precision)
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_CAST}.
*/
public static class DecimalCastReturnTypeInference implements ReturnTypeInference {
public static final DecimalCastReturnTypeInference INSTANCE = new DecimalCastReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
if (logicalExpressions.size() != 3) {
StringBuilder err = new StringBuilder();
for (int i = 0; i < logicalExpressions.size(); i++) {
err.append("arg").append(i).append(": ").append(logicalExpressions.get(i).getMajorType().getMinorType());
}
throw new DrillRuntimeException("Decimal cast function invoked with incorrect arguments" + err);
}
int scale = ((ValueExpressions.IntExpression) logicalExpressions.get(logicalExpressions.size() - 1)).getInt();
int precision = ((ValueExpressions.IntExpression) logicalExpressions.get(logicalExpressions.size() - 2)).getInt();
return TypeProtos.MajorType.newBuilder()
.setMinorType(attributes.getReturnValue().getType().getMinorType())
.setScale(scale)
.setPrecision(precision)
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_DIV_SCALE}.
*/
public static class DecimalDivScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalDivScaleReturnTypeInference INSTANCE = new DecimalDivScaleReturnTypeInference();
/**
* Return type is used by divide functions for decimal data type.
* DecimalScalePrecisionDivideFunction is used to compute the output types' scale and precision.
*
* @param logicalExpressions logical expressions
* @param attributes function attributes
* @return return type
*/
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
assert logicalExpressions.size() == 2;
TypeProtos.MajorType leftMajorType = logicalExpressions.get(0).getMajorType();
TypeProtos.MajorType rightMajorType = logicalExpressions.get(1).getMajorType();
DecimalScalePrecisionDivideFunction outputScalePrec =
new DecimalScalePrecisionDivideFunction(
DecimalUtility.getDefaultPrecision(leftMajorType.getMinorType(), leftMajorType.getPrecision()),
leftMajorType.getScale(),
DecimalUtility.getDefaultPrecision(rightMajorType.getMinorType(), rightMajorType.getPrecision()),
rightMajorType.getScale());
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(outputScalePrec.getOutputScale())
.setPrecision(outputScalePrec.getOutputPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_MAX_SCALE}.
*/
public static class DecimalMaxScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalMaxScaleReturnTypeInference INSTANCE = new DecimalMaxScaleReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
int scale = 0;
int precision = 0;
for (LogicalExpression e : logicalExpressions) {
scale = Math.max(scale, e.getMajorType().getScale());
precision = Math.max(precision, e.getMajorType().getPrecision());
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(attributes.getReturnValue().getType().getMinorType())
.setScale(scale)
.setPrecision(precision)
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_MOD_SCALE}.
*/
public static class DecimalModScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalModScaleReturnTypeInference INSTANCE = new DecimalModScaleReturnTypeInference();
/**
* Return type is used by divide functions for decimal data type.
* DecimalScalePrecisionDivideFunction is used to compute the output types' scale and precision.
*
* @param logicalExpressions logical expressions
* @param attributes function attributes
* @return return type
*/
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
assert logicalExpressions.size() == 2;
TypeProtos.MajorType leftMajorType = logicalExpressions.get(0).getMajorType();
TypeProtos.MajorType rightMajorType = logicalExpressions.get(1).getMajorType();
DecimalScalePrecisionModFunction outputScalePrec =
new DecimalScalePrecisionModFunction(
DecimalUtility.getDefaultPrecision(leftMajorType.getMinorType(), leftMajorType.getPrecision()),
leftMajorType.getScale(),
DecimalUtility.getDefaultPrecision(rightMajorType.getMinorType(), rightMajorType.getPrecision()),
rightMajorType.getScale());
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(outputScalePrec.getOutputScale())
.setPrecision(outputScalePrec.getOutputPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SET_SCALE}.
*/
public static class DecimalSetScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalSetScaleReturnTypeInference INSTANCE = new DecimalSetScaleReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = attributes.getReturnValue().getType().getMode();
int scale = 0;
int precision = 0;
if (attributes.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
// if any one of the input types is nullable, then return nullable return type
for (LogicalExpression e : logicalExpressions) {
precision = Math.max(precision, e.getMajorType().getPrecision());
if (e.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
mode = TypeProtos.DataMode.OPTIONAL;
}
}
// Used by functions like round, truncate which specify the scale for the output as the second argument
assert (logicalExpressions.size() == 2) && (logicalExpressions.get(1) instanceof ValueExpressions.IntExpression);
// Get the scale from the second argument which should be a constant
scale = ((ValueExpressions.IntExpression) logicalExpressions.get(1)).getInt();
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(attributes.getReturnValue().getType().getMinorType())
.setScale(Math.max(scale, 0))
.setPrecision(precision)
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SUM_AGGREGATE}.
*/
public static class DecimalSumAggReturnTypeInference implements ReturnTypeInference {
public static final DecimalSumAggReturnTypeInference INSTANCE = new DecimalSumAggReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
int scale = 0;
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
// Get the max scale and precision from the inputs
for (LogicalExpression e : logicalExpressions) {
scale = Math.max(scale, e.getMajorType().getScale());
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(scale)
.setPrecision(DRILL_REL_DATATYPE_SYSTEM.getMaxNumericPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_AVG_AGGREGATE}.
* Resulting scale is calculated as the max of 6 and the scale of input.
* Resulting precision is max allowed numeric precision.
*/
public static class DecimalAvgAggReturnTypeInference implements ReturnTypeInference {
public static final DecimalAvgAggReturnTypeInference INSTANCE = new DecimalAvgAggReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
int scale = 0;
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
// Get the max scale and precision from the inputs
for (LogicalExpression e : logicalExpressions) {
scale = Math.max(scale, e.getMajorType().getScale());
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(Math.min(Math.max(6, scale),
DRILL_REL_DATATYPE_SYSTEM.getMaxNumericScale()))
.setPrecision(DRILL_REL_DATATYPE_SYSTEM.getMaxNumericPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_SUM_SCALE}.
*/
public static class DecimalSumScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalSumScaleReturnTypeInference INSTANCE = new DecimalSumScaleReturnTypeInference();
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
TypeProtos.DataMode mode = FunctionUtils.getReturnTypeDataMode(logicalExpressions, attributes);
assert logicalExpressions.size() == 2;
TypeProtos.MajorType leftMajorType = logicalExpressions.get(0).getMajorType();
TypeProtos.MajorType rightMajorType = logicalExpressions.get(1).getMajorType();
DecimalScalePrecisionMulFunction outputScalePrec =
new DecimalScalePrecisionMulFunction(
DecimalUtility.getDefaultPrecision(leftMajorType.getMinorType(), leftMajorType.getPrecision()),
leftMajorType.getScale(),
DecimalUtility.getDefaultPrecision(rightMajorType.getMinorType(), rightMajorType.getPrecision()),
rightMajorType.getScale());
return TypeProtos.MajorType.newBuilder()
.setMinorType(TypeProtos.MinorType.VARDECIMAL)
.setScale(outputScalePrec.getOutputScale())
.setPrecision(outputScalePrec.getOutputPrecision())
.setMode(mode)
.build();
}
}
/**
* Return type calculation implementation for functions with return type set as
* {@link org.apache.drill.exec.expr.annotations.FunctionTemplate.ReturnType#DECIMAL_ZERO_SCALE}.
*/
public static class DecimalZeroScaleReturnTypeInference implements ReturnTypeInference {
public static final DecimalZeroScaleReturnTypeInference INSTANCE = new DecimalZeroScaleReturnTypeInference();
/**
* Return type is used for functions where we need to remove the scale part.
* For example, truncate and round functions.
*
* @param logicalExpressions logical expressions
* @param attributes function attributes
* @return return type
*/
@Override
public TypeProtos.MajorType getType(List<LogicalExpression> logicalExpressions, FunctionAttributes attributes) {
int precision = 0;
TypeProtos.DataMode mode = attributes.getReturnValue().getType().getMode();
if (attributes.getNullHandling() == FunctionTemplate.NullHandling.NULL_IF_NULL) {
// if any one of the input types is nullable, then return nullable return type
for (LogicalExpression e : logicalExpressions) {
if (e.getMajorType().getMode() == TypeProtos.DataMode.OPTIONAL) {
mode = TypeProtos.DataMode.OPTIONAL;
}
precision = Math.max(precision, e.getMajorType().getPrecision());
}
}
return TypeProtos.MajorType.newBuilder()
.setMinorType(attributes.getReturnValue().getType().getMinorType())
.setScale(0)
.setPrecision(precision)
.setMode(mode)
.build();
}
}
}