in fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java [1336:2077]
public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
if (isMergeAggFn) {
// This is the function call expr after splitting up to a merge aggregation.
// The function has already been analyzed so just do the minimal sanity
// check here.
Preconditions.checkNotNull(fn);
return;
}
if (fnName.getFunction().equals(FunctionSet.COUNT) && fnParams.isDistinct()) {
// Treat COUNT(DISTINCT ...) special because of how we do the equal.
// There is no version of COUNT() that takes more than 1 argument but after
// the equal, we only need count(*).
// TODO: fix how we equal count distinct.
fn = getBuiltinFunction(fnName.getFunction(), new Type[0], Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
type = fn.getReturnType();
if (children.get(0).type.isComplexType()) {
throw new AnalysisException("The pattern params of " + fnName + " function can not support "
+ children.get(0).type);
}
// Make sure BE doesn't see any TYPE_NULL exprs
for (int i = 0; i < children.size(); ++i) {
if (getChild(i).getType().isNull()) {
uncheckedCastChild(Type.BOOLEAN, i);
}
}
return;
}
Type[] argTypes = new Type[this.children.size()];
for (int i = 0; i < this.children.size(); ++i) {
this.children.get(i).analyze(analyzer);
argTypes[i] = this.children.get(i).getType();
}
analyzeBuiltinAggFunction(analyzer);
analyzeArrayFunction(analyzer);
boolean enableDecimal256 = SessionVariable.getEnableDecimal256();
if (fnName.getFunction().equalsIgnoreCase("sum")) {
if (this.children.isEmpty()) {
throw new AnalysisException("The " + fnName + " function must has one input param");
}
// Prevent the cast type in vector exec engine
Type type = getChild(0).type;
fn = getBuiltinFunction(fnName.getFunction(), new Type[] { type },
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("count_distinct")) {
Type compatibleType = this.children.get(0).getType();
for (int i = 1; i < this.children.size(); ++i) {
Type type = this.children.get(i).getType();
compatibleType = Type.getAssignmentCompatibleType(compatibleType, type, true, enableDecimal256);
if (compatibleType.isInvalid()) {
compatibleType = Type.VARCHAR;
break;
}
}
fn = getBuiltinFunction(fnName.getFunction(), new Type[] { compatibleType },
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase(FunctionSet.WINDOW_FUNNEL)) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 4) {
throw new AnalysisException("The " + fnName + " function must have at least four params");
}
if (!children.get(0).type.isIntegerType()) {
throw new AnalysisException("The window params of " + fnName + " function must be integer");
}
if (!children.get(1).type.isStringType()) {
throw new AnalysisException("The mode params of " + fnName + " function must be string");
}
if (!children.get(2).type.isDateType()) {
throw new AnalysisException("The 3rd param of " + fnName + " function must be DATE or DATETIME");
}
Type[] childTypes = new Type[children.size()];
for (int i = 0; i < 3; i++) {
childTypes[i] = children.get(i).type;
}
for (int i = 3; i < children.size(); i++) {
if (children.get(i).type != Type.BOOLEAN) {
throw new AnalysisException("The 4th and subsequent params of "
+ fnName + " function must be boolean");
}
childTypes[i] = children.get(i).type;
}
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn != null && childTypes[2].isDate()) {
// cast date to datetime
uncheckedCastChild(ScalarType.DATETIME, 2);
} else if (fn != null && childTypes[2].isDateV2()) {
// cast date to datetime
uncheckedCastChild(ScalarType.DATETIMEV2, 2);
}
} else if (fnName.getFunction().equalsIgnoreCase(FunctionSet.RETENTION)) {
if (this.children.isEmpty()) {
throw new AnalysisException("The " + fnName + " function must have at least one param");
}
Type[] childTypes = new Type[children.size()];
for (int i = 0; i < children.size(); i++) {
if (children.get(i).type != Type.BOOLEAN) {
throw new AnalysisException("All params of "
+ fnName + " function must be boolean");
}
childTypes[i] = children.get(i).type;
}
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase(FunctionSet.SEQUENCE_MATCH)
|| fnName.getFunction().equalsIgnoreCase(FunctionSet.SEQUENCE_COUNT)) {
if (fnParams.exprs() == null || fnParams.exprs().size() < 4) {
throw new AnalysisException("The " + fnName + " function must have at least four params");
}
if (!children.get(0).type.isStringType()) {
throw new AnalysisException("The pattern params of " + fnName + " function must be string");
}
if (!children.get(1).type.isDateType()) {
throw new AnalysisException("The timestamp params of " + fnName + " function must be DATE or DATETIME");
}
String pattern = children.get(0).toSql();
int patternLength = pattern.length();
pattern = pattern.substring(1, patternLength - 1);
if (!parsePattern(pattern)) {
throw new AnalysisException("The format of pattern params is wrong");
}
Type[] childTypes = new Type[children.size()];
for (int i = 0; i < 2; i++) {
childTypes[i] = children.get(i).type;
}
for (int i = 2; i < children.size(); i++) {
if (children.get(i).type != Type.BOOLEAN) {
throw new AnalysisException("The 3th and subsequent params of "
+ fnName + " function must be boolean");
}
childTypes[i] = children.get(i).type;
}
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("if")) {
Type[] childTypes = collectChildReturnTypes();
Type assignmentCompatibleType = ScalarType.getAssignmentCompatibleType(childTypes[1], childTypes[2], true,
enableDecimal256);
if (assignmentCompatibleType.isDecimalV3()) {
if (assignmentCompatibleType.isDecimalV3() && !childTypes[1].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, 1);
}
if (assignmentCompatibleType.isDecimalV3() && !childTypes[2].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, 2);
}
}
childTypes[0] = Type.BOOLEAN;
childTypes[1] = assignmentCompatibleType;
childTypes[2] = assignmentCompatibleType;
if (childTypes[1].isDecimalV3() && childTypes[2].isDecimalV3()) {
argTypes[1] = assignmentCompatibleType;
argTypes[2] = assignmentCompatibleType;
}
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (assignmentCompatibleType.isDatetimeV2()) {
fn.setReturnType(assignmentCompatibleType);
}
} else if (fnName.getFunction().equalsIgnoreCase("ifnull")
|| fnName.getFunction().equalsIgnoreCase("nvl")) {
Preconditions.checkArgument(children != null && children.size() == 2,
"The " + fnName + " function must have two params");
Type[] childTypes = collectChildReturnTypes();
Type assignmentCompatibleType = ScalarType.getAssignmentCompatibleType(childTypes[0], childTypes[1], true,
enableDecimal256);
if (assignmentCompatibleType != Type.INVALID) {
if (assignmentCompatibleType.isDecimalV3()) {
if (assignmentCompatibleType.isDecimalV3() && !childTypes[0].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, 0);
}
if (assignmentCompatibleType.isDecimalV3() && !childTypes[1].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, 1);
}
}
childTypes[0] = assignmentCompatibleType;
childTypes[1] = assignmentCompatibleType;
if (childTypes[1].isDecimalV3() && childTypes[0].isDecimalV3()) {
argTypes[1] = assignmentCompatibleType;
argTypes[0] = assignmentCompatibleType;
}
}
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if ((fnName.getFunction().equalsIgnoreCase("coalesce")
|| fnName.getFunction().equalsIgnoreCase("least")
|| fnName.getFunction().equalsIgnoreCase("greatest")) && children.size() > 1) {
Type[] childTypes = collectChildReturnTypes();
Type assignmentCompatibleType = childTypes[0];
for (int i = 1; i < childTypes.length; i++) {
assignmentCompatibleType = ScalarType
.getAssignmentCompatibleType(assignmentCompatibleType, childTypes[i], true, enableDecimal256);
}
if (assignmentCompatibleType.isDecimalV3()) {
for (int i = 0; i < childTypes.length; i++) {
if (assignmentCompatibleType.isDecimalV3()
&& !childTypes[i].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, i);
argTypes[i] = assignmentCompatibleType;
}
}
} else if (assignmentCompatibleType.isDateV2OrDateTimeV2()) {
for (int i = 0; i < childTypes.length; i++) {
if (assignmentCompatibleType.isDateV2OrDateTimeV2()
&& !childTypes[i].equals(assignmentCompatibleType)) {
uncheckedCastChild(assignmentCompatibleType, i);
argTypes[i] = assignmentCompatibleType;
}
}
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("array_apply")
&& ((ArrayType) children.get(0).getType()).getItemType().isDecimalV3()) {
uncheckedCastChild(((ArrayType) children.get(0).getType()).getItemType(), 2);
fn = getBuiltinFunction(fnName.getFunction(), collectChildReturnTypes(),
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (AggregateFunction.SUPPORT_ORDER_BY_AGGREGATE_FUNCTION_NAME_SET.contains(
fnName.getFunction().toLowerCase())) {
// order by elements add as child like windows function. so if we get the
// param of arg, we need remove the order by elements
Type[] childTypes = collectChildReturnTypes();
Type[] newChildTypes = new Type[children.size() - orderByElements.size()];
System.arraycopy(childTypes, 0, newChildTypes, 0, newChildTypes.length);
fn = getBuiltinFunction(fnName.getFunction(), newChildTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (STDDEV_FUNCTION_SET.contains(fnName.getFunction().toLowerCase())
&& collectChildReturnTypes()[0].isDecimalV3()) {
Type[] childrenTypes = collectChildReturnTypes();
Type[] args = new Type[childrenTypes.length];
args[0] = Type.DOUBLE;
System.arraycopy(childrenTypes, 1, args, 1, childrenTypes.length - 1);
fn = getBuiltinFunction(fnName.getFunction(), args, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("bitand")
|| fnName.getFunction().equalsIgnoreCase("bitor")
|| fnName.getFunction().equalsIgnoreCase("bitxor")) {
Type[] childTypes = collectChildReturnTypes();
if (Arrays.stream(childTypes).anyMatch(child -> child.isDecimalV2()
|| child.isDecimalV3() || child.isFloatingPointType())) {
uncheckedCastChild(Type.BIGINT, 0);
uncheckedCastChild(Type.BIGINT, 1);
argTypes[0] = Type.BIGINT;
argTypes[1] = Type.BIGINT;
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("bitnot")) {
if (children.get(0).type.isDecimalV2() || children.get(0).type.isDecimalV3()
|| children.get(0).type.isFloatingPointType()) {
uncheckedCastChild(Type.BIGINT, 0);
argTypes[0] = Type.BIGINT;
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else if (fnName.getFunction().equalsIgnoreCase("date_add")
|| fnName.getFunction().equalsIgnoreCase("days_add")
|| fnName.getFunction().equalsIgnoreCase("adddate")
|| fnName.getFunction().equalsIgnoreCase("date_sub")
|| fnName.getFunction().equalsIgnoreCase("days_sub")
|| fnName.getFunction().equalsIgnoreCase("subdate")) {
Type[] childTypes = collectChildReturnTypes();
argTypes[0] = childTypes[0];
argTypes[1] = childTypes[1];
if (childTypes[1] == Type.TINYINT || childTypes[1] == Type.SMALLINT) {
// be only support second param as int type
uncheckedCastChild(Type.INT, 1);
argTypes[1] = Type.INT;
}
fn = getBuiltinFunction(fnName.getFunction(), argTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else {
// now first find table function in table function sets
if (isTableFnCall) {
Type[] childTypes = collectChildReturnTypes();
// when we call explode<Array<Decimal>> with nested decimal has specific precision and scale,
// collectChildReturnTypes will return specific precision and scale decimal type witch may not match
// builtln func we defined in fe code, because we make array_support_type is actual origin type.here we
// temp write this if to get matched explode function and then set actually decimal type from sql to
// func return type. if we switch nereid would hasn't this problems.
if (fnName.getFunction().equalsIgnoreCase("explode") && childTypes[0].isArrayType()) {
// get origin type to match builtln func
Type[] matchFuncChildTypes = getActualArgTypes(childTypes);
fn = getTableFunction(fnName.getFunction(), matchFuncChildTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
if (fn == null) {
throw new AnalysisException(getFunctionNotFoundError(argTypes) + " in table function");
}
// set param child types
fn.setReturnType(((ArrayType) childTypes[0]).getItemType());
} else {
fn = getTableFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
}
// find user defined functions
if (fn == null) {
fn = findUdf(fnName, analyzer);
if (fn != null) {
FunctionUtil.checkEnableJavaUdf();
if (!fn.isUDTFunction()) {
throw new AnalysisException(getFunctionNotFoundError(argTypes) + " in table function");
}
}
}
if (fn == null) {
throw new AnalysisException(getFunctionNotFoundError(argTypes));
}
} else {
if (ROUND_FUNCTION_SET.contains(fnName.getFunction()) && children.size() == 2
&& children.get(0).getType().isDecimalV3() && children.get(1) instanceof IntLiteral) {
children.get(1).setType(Type.INT);
}
// now first find function in built-in functions
if (Strings.isNullOrEmpty(fnName.getDb())) {
Type[] childTypes = collectChildReturnTypes();
// when we call count<Array<T>> with nested type is not null type which is defined in FunctionSet
// so here aim to make function signature to match builtln func we defined in fe code
if (fnName.getFunction().equalsIgnoreCase("count") && childTypes.length > 0
&& childTypes[0].isComplexType()) {
// get origin type to match builtln func
Type[] matchFuncChildTypes = new Type[1];
if (childTypes[0].isArrayType()) {
matchFuncChildTypes[0] = Type.ARRAY;
} else if (childTypes[0].isMapType()) {
matchFuncChildTypes[0] = Type.MAP;
} else if (childTypes[0].isStructType()) {
matchFuncChildTypes[0] = Type.GENERIC_STRUCT;
}
fn = getBuiltinFunction(fnName.getFunction(), matchFuncChildTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
} else {
fn = getBuiltinFunction(fnName.getFunction(), childTypes,
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
}
}
// find user defined functions
if (fn == null) {
fn = findUdf(fnName, analyzer);
if (analyzer.isReAnalyze() && fn instanceof AliasFunction) {
throw new AnalysisException("a UDF in the original function of a alias function");
}
if (fn != null) {
FunctionUtil.checkEnableJavaUdf();
}
}
}
}
if (fn == null) {
LOG.warn("fn {} not exists", this.toSqlImpl());
throw new AnalysisException(getFunctionNotFoundError(collectChildReturnTypes()));
}
if (fnName.getFunction().equalsIgnoreCase("collect_list")
|| fnName.getFunction().equalsIgnoreCase("collect_set")
|| fnName.getFunction().equalsIgnoreCase("array_agg")) {
fn.setReturnType(new ArrayType(getChild(0).type));
}
if (fnName.getFunction().equalsIgnoreCase("map_agg")) {
fn.setReturnType(new MapType(getChild(0).type, getChild(1).type));
}
if (fnName.getFunction().equalsIgnoreCase("group_uniq_array")
|| fnName.getFunction().equalsIgnoreCase("group_array")) {
fn.setReturnType(new ArrayType(getChild(0).type));
}
if (fnName.getFunction().equalsIgnoreCase("from_unixtime")
|| fnName.getFunction().equalsIgnoreCase("date_format")
|| fnName.getFunction().equalsIgnoreCase("unix_timestamp")) {
// if has only one child, it has default time format: yyyy-MM-dd HH:mm:ss.SSSSSS
if (children.size() > 1) {
final StringLiteral fmtLiteral = (StringLiteral) children.get(1);
if (fmtLiteral.getStringValue().equals("yyyyMMdd")) {
children.set(1, new StringLiteral("%Y%m%d"));
} else if (fmtLiteral.getStringValue().equals("yyyy-MM-dd")) {
children.set(1, new StringLiteral("%Y-%m-%d"));
} else if (fmtLiteral.getStringValue().equals("yyyy-MM-dd HH:mm:ss")) {
children.set(1, new StringLiteral("%Y-%m-%d %H:%i:%s"));
}
}
if (fnName.getFunction().equalsIgnoreCase("unix_timestamp") && children.size() == 1) {
if (getChild(0).type.isDatetimeV2()) {
ScalarType type = (ScalarType) getChild(0).type;
Preconditions.checkArgument(type.getScalarScale() <= 6,
"DatetimeV2's scale shouldn't exceed 6 but meet " + type.getScalarScale());
fn.setReturnType(
ScalarType.createDecimalType(PrimitiveType.DECIMAL64, 10 + type.getScalarScale(),
type.getScalarScale()));
} else if (getChild(0).type.isStringType()) {
// use DATETIME to make scale adaptive
ScalarType type = ((ScalarType) (getChild(0).uncheckedCastTo(ScalarType.DATETIME).type));
if (type.isDatetimeV2()) {
int scale = type.getScalarScale();
fn.setReturnType(
ScalarType.createDecimalType(PrimitiveType.DECIMAL64, 10 + scale, scale));
}
}
}
}
if (fnName.getFunction().equalsIgnoreCase("convert_to")) {
if (children.size() < 2 || !getChild(1).isConstant()) {
throw new AnalysisException(
fnName.getFunction() + " needs two params, and the second is must be a constant: " + this
.toSql());
}
}
if (fnName.getFunction().equalsIgnoreCase("date_trunc")) {
if ((children.size() != 2) || (getChild(1).isConstant() == false)
|| !(getChild(1) instanceof StringLiteral)) {
throw new AnalysisException(
fnName.getFunction() + " needs two params, and the second is must be a string constant: "
+ this.toSql());
}
final String constParam = ((StringLiteral) getChild(1)).getValue().toLowerCase();
if (!Lists.newArrayList("year", "quarter", "month", "week", "day", "hour", "minute", "second")
.contains(constParam)) {
throw new AnalysisException("date_trunc function second param only support argument is "
+ "year|quarter|month|week|day|hour|minute|second");
}
}
if (fnName.getFunction().equalsIgnoreCase("array_range")
|| fnName.getFunction().equalsIgnoreCase("sequence")) {
if (getChild(0) instanceof DateLiteral && !(getChild(2) instanceof StringLiteral)) {
throw new AnalysisException("To generate datetime array, please use interval literal like: "
+ "interval 1 day.");
}
}
if (fnName.getFunction().equalsIgnoreCase("char")) {
if (!getChild(0).isConstant()) {
throw new AnalysisException(
fnName.getFunction() + " charset name must be a constant: " + this
.toSql());
}
LiteralExpr literal = (LiteralExpr) getChild(0);
if (!literal.getStringValue().equalsIgnoreCase("utf8")) {
throw new AnalysisException(
fnName.getFunction() + " function currently only support charset name 'utf8': " + this
.toSql());
}
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("timediff")) {
fn.getReturnType().getPrimitiveType().setTimeType();
ScalarType left = (ScalarType) argTypes[0];
ScalarType right = (ScalarType) argTypes[1];
if (left.isDatetimeV2() || right.isDatetimeV2() || left.isDateV2() || right.isDateV2()) {
Type ret = ScalarType.createTimeV2Type(Math.max(left.getScalarScale(), right.getScalarScale()));
fn.setReturnType(ret);
fn.getReturnType().getPrimitiveType().setTimeType();
}
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("from_microsecond")) {
Type ret = ScalarType.createDatetimeV2Type(6);
fn.setReturnType(ret);
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("from_millisecond")) {
Type ret = ScalarType.createDatetimeV2Type(3);
fn.setReturnType(ret);
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("from_second")) {
Type ret = ScalarType.createDatetimeV2Type(0);
fn.setReturnType(ret);
}
if (fnName.getFunction().equalsIgnoreCase("map")) {
if ((children.size() & 1) == 1) {
throw new AnalysisException("map can't be odd parameters, need even parameters: "
+ this.toSql());
}
}
if (fnName.getFunction().equalsIgnoreCase("named_struct")) {
if ((children.size() & 1) == 1) {
throw new AnalysisException("named_struct can't be odd parameters, need even parameters: "
+ this.toSql());
}
for (int i = 0; i < children.size(); i++) {
if ((i & 1) == 0) {
if (!(getChild(i) instanceof StringLiteral)) {
throw new AnalysisException(
"named_struct only allows constant string parameter in odd position: " + this.toSql());
}
}
}
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("struct_element")) {
if (children.size() < 2) {
throw new AnalysisException(fnName.getFunction() + " needs two parameters: " + this.toSql());
}
if (getChild(0).type instanceof StructType) {
StructType s = ((StructType) children.get(0).type);
if (getChild(1) instanceof StringLiteral) {
String fieldName = children.get(1).getStringValue();
if (s.getField(fieldName) == null) {
throw new AnalysisException(
"the specified field name " + fieldName + " was not found: " + this.toSql());
}
} else if (getChild(1) instanceof IntLiteral) {
int pos = (int) ((IntLiteral) children.get(1)).getValue();
if (pos < 1 || pos > s.getFields().size()) { // the index start from 1
throw new AnalysisException(
"the specified field index out of bound: " + this.toSql());
}
} else {
throw new AnalysisException(
"struct_element only allows constant int or string second parameter: " + this.toSql());
}
}
}
if (fn.getFunctionName().getFunction().equalsIgnoreCase("sha2")) {
if ((children.size() != 2) || (getChild(1).isConstant() == false)
|| !(getChild(1) instanceof IntLiteral)) {
throw new AnalysisException(
fnName.getFunction() + " needs two params, and the second is must be a integer constant: "
+ this.toSql());
}
final Integer constParam = (int) ((IntLiteral) getChild(1)).getValue();
if (!Lists.newArrayList(224, 256, 384, 512).contains(constParam)) {
throw new AnalysisException("sha2 functions only support digest length of 224/256/384/512");
}
}
if (isAggregateFunction()) {
final String functionName = fnName.getFunction();
// subexprs must not contain aggregates
if (Expr.containsAggregate(children)) {
throw new AnalysisException(
"aggregate function cannot contain aggregate parameters: " + this.toSql());
}
if (STDDEV_FUNCTION_SET.contains(functionName) && argTypes[0].isDateType()) {
throw new AnalysisException("Stddev/variance function do not support Date/Datetime type");
}
if (functionName.equalsIgnoreCase("multi_distinct_sum") && argTypes[0].isDateType()) {
throw new AnalysisException("Sum in multi distinct functions do not support Date/Datetime type");
}
} else {
if (fnParams.isStar()) {
throw new AnalysisException("Cannot pass '*' to scalar function.");
}
if (fnParams.isDistinct()) {
throw new AnalysisException("Cannot pass 'DISTINCT' to scalar function.");
}
}
Type[] args = fn.getArgs();
if (args.length > 0) {
// Implicitly cast all the children to match the function if necessary
for (int i = 0; i < argTypes.length - orderByElements.size(); ++i) {
// For varargs, we must compare with the last type in callArgs.argTypes.
int ix = Math.min(args.length - 1, i);
// map varargs special case map(key_type, value_type, ...)
if (i >= args.length && i >= 2 && args.length >= 2
&& fnName.getFunction().equalsIgnoreCase("map")) {
ix = i % 2 == 0 ? 0 : 1;
}
// array_zip varargs special case array_zip(array1, array2, ...)
// we only specialize array_zip with first array type, next type we same with custom type
if (i >= args.length && (fnName.getFunction().equalsIgnoreCase("array_zip"))) {
if (argTypes[i].isNull()) {
uncheckedCastChild(args[i - 1], i);
}
continue;
}
if (i == 0 && (fnName.getFunction().equalsIgnoreCase("char"))) {
continue;
}
if (fnName.getFunction().equalsIgnoreCase("count") && args[i].isComplexType()) {
continue;
}
if ((fnName.getFunction().equalsIgnoreCase("money_format") || fnName.getFunction()
.equalsIgnoreCase("histogram")
|| fnName.getFunction().equalsIgnoreCase("hist")
|| fnName.getFunction().equalsIgnoreCase("linear_histogram"))
&& children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3()) {
continue;
} else if ((fnName.getFunction().equalsIgnoreCase("array_min") || fnName.getFunction()
.equalsIgnoreCase("array_max") || fnName.getFunction().equalsIgnoreCase("element_at"))
&& ((
children.get(0).getType().isDecimalV3() && ((ArrayType) args[ix]).getItemType()
.isDecimalV3())
|| (children.get(0).getType().isDatetimeV2()
&& ((ArrayType) args[ix]).getItemType().isDatetimeV2())
|| (children.get(0).getType().isDecimalV2()
&& ((ArrayType) args[ix]).getItemType().isDecimalV2()))) {
continue;
} else if ((fnName.getFunction().equalsIgnoreCase("array")
|| fnName.getFunction().equalsIgnoreCase("array_distinct")
|| fnName.getFunction().equalsIgnoreCase("array_remove")
|| fnName.getFunction().equalsIgnoreCase("array_sort")
|| fnName.getFunction().equalsIgnoreCase("array_reverse_sort")
|| fnName.getFunction().equalsIgnoreCase("array_overlap")
|| fnName.getFunction().equalsIgnoreCase("array_union")
|| fnName.getFunction().equalsIgnoreCase("array_intersect")
|| fnName.getFunction().equalsIgnoreCase("array_compact")
|| fnName.getFunction().equalsIgnoreCase("array_slice")
|| fnName.getFunction().equalsIgnoreCase("array_popback")
|| fnName.getFunction().equalsIgnoreCase("array_popfront")
|| fnName.getFunction().equalsIgnoreCase("array_pushfront")
|| fnName.getFunction().equalsIgnoreCase("array_pushback")
|| fnName.getFunction().equalsIgnoreCase("array_cum_sum")
|| fnName.getFunction().equalsIgnoreCase("reverse")
|| fnName.getFunction().equalsIgnoreCase("%element_slice%")
|| fnName.getFunction().equalsIgnoreCase("array_concat")
|| fnName.getFunction().equalsIgnoreCase("array_shuffle")
|| fnName.getFunction().equalsIgnoreCase("shuffle")
|| fnName.getFunction().equalsIgnoreCase("array_except")
|| fnName.getFunction().equalsIgnoreCase("array_apply")
|| fnName.getFunction().equalsIgnoreCase("array_position")
|| fnName.getFunction().equalsIgnoreCase("array_contains")
|| fnName.getFunction().equalsIgnoreCase("width_bucket"))
&& (args[ix].isDecimalV3() || (children.get(0).getType().isArrayType()
&& (((ArrayType) children.get(0).getType()).getItemType().isDecimalV3())
&& (args[ix].isArrayType())
&& ((ArrayType) args[ix]).getItemType().isDecimalV3()))) {
continue;
} else if (!argTypes[i].matchesType(args[ix])
&& ROUND_FUNCTION_SET.contains(fnName.getFunction())
&& ConnectContext.get() != null
&& ConnectContext.get().getSessionVariable().roundPreciseDecimalV2Value
&& argTypes[i].isDecimalV2()
&& args[ix].isDecimalV3()) {
uncheckedCastChild(ScalarType.createDecimalV3Type(ScalarType.MAX_DECIMALV2_PRECISION,
((ScalarType) argTypes[i]).getScalarScale()), i);
} else if (!argTypes[i].matchesType(args[ix])
&& !(argTypes[i].isDecimalV3OrContainsDecimalV3()
&& args[ix].isDecimalV3OrContainsDecimalV3())) {
// Do not do this cast if types are both decimalv3 with different precision/scale.
uncheckedCastChild(args[ix], i);
} else if (fnName.getFunction().equalsIgnoreCase("if")
&& argTypes[i].isArrayType() && ((ArrayType) argTypes[i]).getItemType().isNull()) {
uncheckedCastChild(args[ix], i);
}
}
}
/**
* The return type of str_to_date depends on whether the time part is included
* in the format.
* If included, it is datetime, otherwise it is date.
* If the format parameter is not constant, the return type will be datetime.
* The above judgment has been completed in the FE query planning stage,
* so here we directly set the value type to the return type set in the query
* plan.
*
* For example:
* A table with one column k1 varchar, and has 2 lines:
* "%Y-%m-%d"
* "%Y-%m-%d %H:%i:%s"
* Query:
* SELECT str_to_date("2020-09-01", k1) from tbl;
* Result will be:
* 2020-09-01 00:00:00
* 2020-09-01 00:00:00
*
* Query:
* SELECT str_to_date("2020-09-01", "%Y-%m-%d");
* Return type is DATE
*
* Query:
* SELECT str_to_date("2020-09-01", "%Y-%m-%d %H:%i:%s");
* Return type is DATETIME
*/
if (fn.getFunctionName().getFunction().equalsIgnoreCase("str_to_date")) {
Expr child1Result = getChild(1).getResultValue(false);
if (child1Result instanceof StringLiteral) {
if (DateLiteral.hasTimePart(child1Result.getStringValue())) {
this.type = Type.DATETIMEV2_WITH_MAX_SCALAR;
} else {
this.type = Type.DATEV2;
}
} else {
this.type = Type.DATETIMEV2_WITH_MAX_SCALAR;
}
} else if (TIME_FUNCTIONS_WITH_PRECISION.contains(fnName.getFunction().toLowerCase())
&& fn.getReturnType().isDatetimeV2()) {
if (children.size() == 1 && children.get(0) instanceof IntLiteral) {
this.type = ScalarType.createDatetimeV2Type((int) ((IntLiteral) children.get(0)).getLongValue());
} else if (children.size() == 1) {
this.type = ScalarType.createDatetimeV2Type(6);
}
} else {
this.type = fn.getReturnType();
}
if (this.type.isDecimalV2()) {
this.type = Type.MAX_DECIMALV2_TYPE;
fn.setReturnType(Type.MAX_DECIMALV2_TYPE);
}
if (this.type.isDecimalV3() || (this.type.isArrayType()
&& ((ArrayType) this.type).getItemType().isDecimalV3())
|| (this.type.isDatetimeV2()
&& !TIME_FUNCTIONS_WITH_PRECISION.contains(fnName.getFunction().toLowerCase()))) {
// TODO(gabriel): If type exceeds max precision of DECIMALV3, we should change
// it to a double function
this.type = PRECISION_INFER_RULE.getOrDefault(fnName.getFunction(), DEFAULT_PRECISION_INFER_RULE)
.apply(children, this.type);
}
// cast(xx as char(N)/varchar(N)) will be handled as substr(cast(xx as char, varchar), 1, N),
// but type is varchar(*), we change it to varchar(N);
if (fn.getFunctionName().getFunction().equalsIgnoreCase("substr")
&& children.size() == 3
&& children.get(1) instanceof IntLiteral
&& children.get(2) instanceof IntLiteral) {
long len = ((IntLiteral) children.get(2)).getValue();
if (type.isWildcardChar()) {
this.type = ScalarType.createCharType(((int) (len)));
} else if (type.isWildcardVarchar()) {
this.type = ScalarType.createVarchar(((int) (len)));
}
}
// rewrite return type if is nested type function
analyzeNestedFunction();
for (OrderByElement o : orderByElements) {
if (!o.getExpr().isAnalyzed) {
o.getExpr().analyzeImpl(analyzer);
}
}
}