public void analyzeImpl()

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);
            }
        }
    }