private void analyzeBuiltinAggFunction()

in fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java [845:1211]


    private void analyzeBuiltinAggFunction(Analyzer analyzer) throws AnalysisException {
        if (fnParams.isStar() && !fnName.getFunction().equalsIgnoreCase(FunctionSet.COUNT)) {
            throw new AnalysisException(
                    "'*' can only be used in conjunction with COUNT: " + this.toSql());
        }

        if (fnName.getFunction().equalsIgnoreCase(FunctionSet.COUNT)) {
            // for multiple exprs count must be qualified with distinct
            if (children.size() > 1 && !fnParams.isDistinct()) {
                throw new AnalysisException(
                        "COUNT must have DISTINCT for multiple arguments: " + this.toSql());
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase("json_array")) {
            String res = parseJsonDataType(false);
            if (children.size() == originChildSize) {
                children.add(new StringLiteral(res));
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase("json_object")) {
            if ((children.size() & 1) == 1 && (originChildSize == children.size())) {
                throw new AnalysisException("json_object can't be odd parameters, need even parameters: "
                        + this.toSql());
            }
            String res = parseJsonDataType(true);
            if (children.size() == originChildSize) {
                children.add(new StringLiteral(res));
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase("json_insert")
                || fnName.getFunction().equalsIgnoreCase("json_replace")
                || fnName.getFunction().equalsIgnoreCase("json_set")) {
            if (((children.size() & 1) == 0 || children.size() < 3)
                    && (originChildSize == children.size())) {
                throw new AnalysisException(fnName.getFunction() + " need odd parameters, and >= 3 arguments: "
                    + this.toSql());
            }
            String res = parseJsonValueModifyDataType();
            if (children.size() == originChildSize) {
                children.add(new StringLiteral(res));
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase("group_concat")) {
            if (children.size() - orderByElements.size() > 2 || children.isEmpty()) {
                throw new AnalysisException(
                        "group_concat requires one or two parameters: " + this.toSql());
            }

            Expr arg0 = getChild(0);
            if (!arg0.type.isStringType() && !arg0.type.isNull()) {
                throw new AnalysisException(
                        "group_concat requires first parameter to be of type STRING: " + this.toSql());
            }

            if (children.size() - orderByElements.size() == 2) {
                Expr arg1 = getChild(1);
                if (!arg1.type.isStringType() && !arg1.type.isNull()) {
                    throw new AnalysisException(
                            "group_concat requires second parameter to be of type STRING: " + this.toSql());
                }
            }

            if (fnParams.isDistinct() && !orderByElements.isEmpty()) {
                throw new AnalysisException(
                        "group_concat don't support using distinct with order by together: " + this.toSql());
            }

            return;
        }
        if (fnName.getFunction().equalsIgnoreCase("field")) {
            if (children.size() < 2) {
                throw new AnalysisException(fnName.getFunction() + " function parameter size is less than 2.");
            } else {
                for (int i = 1; i < children.size(); ++i) {
                    if (!getChild(i).isConstant()) {
                        throw new AnalysisException(fnName.getFunction()
                                + " function except for the first argument, other parameter must be a constant.");
                    }
                }
            }
        }
        if (fnName.getFunction().equalsIgnoreCase("lag")
                || fnName.getFunction().equalsIgnoreCase("lead")) {
            if (!isAnalyticFnCall) {
                throw new AnalysisException(fnName.getFunction() + " only used in analytic function");
            } else {
                if (children.size() > 2) {
                    if (!getChild(1).isConstant() || !getChild(2).isConstant()) {
                        throw new AnalysisException(
                                "The default parameter (parameter 2 or parameter 3) of LEAD/LAG must be a constant: "
                                        + this.toSql());
                    }
                    uncheckedCastChild(Type.BIGINT, 1);
                    if (!getChild(2).type.matchesType(getChild(0).type) && !getChild(2).type.matchesType(Type.NULL)) {
                        uncheckedCastChild(getChild(0).type, 2);
                    }
                }
                return;
            }
        }

        if (fnName.getFunction().equalsIgnoreCase("dense_rank")
                || fnName.getFunction().equalsIgnoreCase("rank")
                || fnName.getFunction().equalsIgnoreCase("row_number")
                || fnName.getFunction().equalsIgnoreCase("first_value")
                || fnName.getFunction().equalsIgnoreCase("last_value")
                || fnName.getFunction().equalsIgnoreCase("first_value_rewrite")
                || fnName.getFunction().equalsIgnoreCase("ntile")) {
            if (!isAnalyticFnCall) {
                throw new AnalysisException(fnName.getFunction() + " only used in analytic function");
            }
        }

        // Function's arg can't be null for the following functions.
        Expr arg = getChild(0);
        if (arg == null) {
            return;
        }

        // SUM and AVG cannot be applied to non-numeric types
        if ((fnName.getFunction().equalsIgnoreCase("sum")
                || fnName.getFunction().equalsIgnoreCase("avg"))
                && ((!arg.type.isNumericType() && !arg.type.isNull() && !arg.type.isBoolean())
                        || arg.type.isOnlyMetricType())) {
            throw new AnalysisException(fnName.getFunction() + " requires a numeric parameter: " + this.toSql());
        }
        // DecimalV3 scale lower than DEFAULT_MIN_AVG_DECIMAL128_SCALE should do cast
        if (fnName.getFunction().equalsIgnoreCase("avg") && arg.type.isDecimalV3()
                && arg.type.getDecimalDigits() < ScalarType.DEFAULT_MIN_AVG_DECIMAL128_SCALE) {
            int precision = arg.type.getPrecision();
            int scale = arg.type.getDecimalDigits();
            precision = precision - scale + ScalarType.DEFAULT_MIN_AVG_DECIMAL128_SCALE;
            scale = ScalarType.DEFAULT_MIN_AVG_DECIMAL128_SCALE;
            if (SessionVariable.getEnableDecimal256()) {
                if (precision > ScalarType.MAX_DECIMAL256_PRECISION) {
                    precision = ScalarType.MAX_DECIMAL256_PRECISION;
                }
            } else {
                if (precision > ScalarType.MAX_DECIMAL128_PRECISION) {
                    precision = ScalarType.MAX_DECIMAL128_PRECISION;
                }
            }
            Type t = ScalarType.createDecimalType(arg.type.getPrimitiveType(), precision, scale);
            Expr e = getChild(0).castTo(t);
            setChild(0, e);
        }
        if (fnName.getFunction().equalsIgnoreCase("sum_distinct")
                && ((!arg.type.isNumericType() && !arg.type.isNull()) || arg.type.isOnlyMetricType())) {
            throw new AnalysisException(
                    "SUM_DISTINCT requires a numeric parameter: " + this.toSql());
        }

        if ((fnName.getFunction().equalsIgnoreCase("min")
                || fnName.getFunction().equalsIgnoreCase("max")
                || fnName.getFunction().equalsIgnoreCase("DISTINCT_PC")
                || fnName.getFunction().equalsIgnoreCase("DISTINCT_PCSA")
                || fnName.getFunction().equalsIgnoreCase("NDV"))
                && arg.type.isOnlyMetricType()) {
            throw new AnalysisException(Type.OnlyMetricTypeErrorMsg);
        }

        if ((fnName.getFunction().equalsIgnoreCase(FunctionSet.BITMAP_UNION_INT) && !arg.type.isInteger32Type())) {
            throw new AnalysisException("BITMAP_UNION_INT params only support TINYINT or SMALLINT or INT");
        }

        if (fnName.getFunction().equalsIgnoreCase(FunctionSet.INTERSECT_COUNT) || fnName.getFunction()
                .equalsIgnoreCase(FunctionSet.ORTHOGONAL_BITMAP_INTERSECT) || fnName.getFunction()
                .equalsIgnoreCase(FunctionSet.ORTHOGONAL_BITMAP_INTERSECT_COUNT) || fnName.getFunction()
                .equalsIgnoreCase(FunctionSet.ORTHOGONAL_BITMAP_EXPR_CALCULATE_COUNT) || fnName.getFunction()
                .equalsIgnoreCase(FunctionSet.ORTHOGONAL_BITMAP_EXPR_CALCULATE)) {
            if (children.size() <= 2) {
                throw new AnalysisException(fnName + "(bitmap_column, column_to_filter, filter_values) "
                        + "function requires at least three parameters");
            }

            Type inputType = getChild(0).getType();
            if (!inputType.isBitmapType()) {
                throw new AnalysisException(
                        fnName + "function first argument should be of BITMAP type, but was " + inputType);
            }

            for (int i = 2; i < children.size(); i++) {
                if (!getChild(i).isConstant()) {
                    throw new AnalysisException(fnName + " function filter_values arg must be constant");
                }
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase(FunctionSet.BITMAP_COUNT)
                || fnName.getFunction().equalsIgnoreCase(FunctionSet.BITMAP_UNION)
                || fnName.getFunction().equalsIgnoreCase(FunctionSet.BITMAP_UNION_COUNT)
                || fnName.getFunction().equalsIgnoreCase(FunctionSet.BITMAP_INTERSECT)) {
            if (children.size() != 1) {
                throw new AnalysisException(fnName + " function could only have one child");
            }
            Type inputType = getChild(0).getType();
            if (!inputType.isBitmapType()) {
                throw new AnalysisException(fnName
                        + " function's argument should be of BITMAP type, but was " + inputType);
            }
            return;
        }

        if (fnName.getFunction().equalsIgnoreCase(FunctionSet.QUANTILE_UNION)) {
            if (children.size() != 1) {
                throw new AnalysisException(fnName + " function could only have one child");
            }
            Type inputType = getChild(0).getType();
            if (!inputType.isQuantileStateType()) {
                throw new AnalysisException(fnName
                        + " function's argument should be of QUANTILE_STATE type, but was" + inputType);
            }
        }

        if (fnName.getFunction().equalsIgnoreCase(FunctionSet.TO_QUANTILE_STATE)) {
            if (children.size() != 2) {
                throw new AnalysisException(fnName + "function must have two children");
            }
            if (!getChild(1).isConstant()) {
                throw new AnalysisException(fnName + "function's second argument should be constant");
            }
        }

        if ((fnName.getFunction().equalsIgnoreCase("HLL_UNION_AGG")
                || fnName.getFunction().equalsIgnoreCase("HLL_CARDINALITY")
                || fnName.getFunction().equalsIgnoreCase("HLL_RAW_AGG")
                || fnName.getFunction().equalsIgnoreCase("HLL_UNION"))
                && !arg.type.isHllType()) {
            throw new AnalysisException(
                    "HLL_UNION, HLL_UNION_AGG, HLL_RAW_AGG and HLL_CARDINALITY's params must be hll column");
        }

        if (fnName.getFunction().equalsIgnoreCase("min")
                || fnName.getFunction().equalsIgnoreCase("max")) {
            fnParams.setIsDistinct(false);  // DISTINCT is meaningless here
        } else if (fnName.getFunction().equalsIgnoreCase("DISTINCT_PC")
                || fnName.getFunction().equalsIgnoreCase("DISTINCT_PCSA")
                || fnName.getFunction().equalsIgnoreCase("NDV")
                || fnName.getFunction().equalsIgnoreCase("HLL_UNION_AGG")) {
            fnParams.setIsDistinct(false);
        }

        if (fnName.getFunction().equalsIgnoreCase("percentile")) {
            if (children.size() != 2) {
                throw new AnalysisException("percentile(expr, DOUBLE) requires two parameters");
            }
            if (!getChild(1).isConstant()) {
                throw new AnalysisException("percentile requires second parameter must be a constant : "
                        + this.toSql());
            }
        }

        if (fnName.getFunction().equalsIgnoreCase("percentile_approx")) {
            if (children.size() != 2 && children.size() != 3) {
                throw new AnalysisException("percentile_approx(expr, DOUBLE [, B]) requires two or three parameters");
            }
            if (!getChild(1).isConstant()) {
                throw new AnalysisException("percentile_approx requires second parameter must be a constant : "
                        + this.toSql());
            }
            if (children.size() == 3) {
                if (!getChild(2).isConstant()) {
                    throw new AnalysisException("percentile_approx requires the third parameter must be a constant : "
                            + this.toSql());
                }
            }
        }

        if (fnName.getFunction().equalsIgnoreCase("topn")) {
            if (children.size() != 2 && children.size() != 3) {
                throw new AnalysisException("topn(expr, INT [, B]) requires two or three parameters");
            }
            if (!getChild(1).isConstant() || !getChild(1).getType().isIntegerType()) {
                throw new AnalysisException("topn requires second parameter must be a constant Integer Type: "
                        + this.toSql());
            }
            if (!getChild(1).getType().equals(ScalarType.INT)) {
                Expr e = getChild(1).castTo(ScalarType.INT);
                setChild(1, e);
            }
            if (children.size() == 3) {
                if (!getChild(2).isConstant() || !getChild(2).getType().isIntegerType()) {
                    throw new AnalysisException("topn requires the third parameter must be a constant Integer Type: "
                            + this.toSql());
                }
                if (!getChild(2).getType().equals(ScalarType.INT)) {
                    Expr e = getChild(2).castTo(ScalarType.INT);
                    setChild(2, e);
                }
            }
        }
        if ((fnName.getFunction().equalsIgnoreCase("aes_decrypt")
                || fnName.getFunction().equalsIgnoreCase("aes_encrypt")
                || fnName.getFunction().equalsIgnoreCase("sm4_decrypt")
                || fnName.getFunction().equalsIgnoreCase("sm4_encrypt"))
                && (children.size() == 2 || children.size() == 3)) {
            Set<String> aesModes = new HashSet<>(Arrays.asList(
                    "AES_128_ECB",
                    "AES_192_ECB",
                    "AES_256_ECB",
                    "AES_128_CBC",
                    "AES_192_CBC",
                    "AES_256_CBC",
                    "AES_128_CFB",
                    "AES_192_CFB",
                    "AES_256_CFB",
                    "AES_128_CFB1",
                    "AES_192_CFB1",
                    "AES_256_CFB1",
                    "AES_128_CFB8",
                    "AES_192_CFB8",
                    "AES_256_CFB8",
                    "AES_128_CFB128",
                    "AES_192_CFB128",
                    "AES_256_CFB128",
                    "AES_128_CTR",
                    "AES_192_CTR",
                    "AES_256_CTR",
                    "AES_128_OFB",
                    "AES_192_OFB",
                    "AES_256_OFB"
            ));
            Set<String> sm4Modes = new HashSet<>(Arrays.asList(
                    "SM4_128_ECB",
                    "SM4_128_CBC",
                    "SM4_128_CFB128",
                    "SM4_128_OFB",
                    "SM4_128_CTR"));

            String blockEncryptionMode = "";
            if (ConnectContext.get() != null) {
                blockEncryptionMode = ConnectContext.get().getSessionVariable().getBlockEncryptionMode();
                if (fnName.getFunction().equalsIgnoreCase("aes_decrypt")
                        || fnName.getFunction().equalsIgnoreCase("aes_encrypt")) {
                    if (StringUtils.isAllBlank(blockEncryptionMode)) {
                        blockEncryptionMode = "AES_128_ECB";
                    }
                    if (!aesModes.contains(blockEncryptionMode.toUpperCase())) {
                        throw new AnalysisException("session variable block_encryption_mode is invalid with aes");
                    }
                }
                if (fnName.getFunction().equalsIgnoreCase("sm4_decrypt")
                        || fnName.getFunction().equalsIgnoreCase("sm4_encrypt")) {
                    if (StringUtils.isAllBlank(blockEncryptionMode)) {
                        blockEncryptionMode = "SM4_128_ECB";
                    }
                    if (!sm4Modes.contains(blockEncryptionMode.toUpperCase())) {
                        throw new AnalysisException(
                                "session variable block_encryption_mode is invalid with sm4");
                    }
                }
            } else {
                throw new AnalysisException("cannot get session variable `block_encryption_mode`, "
                        + "please explicitly specify by using 4-args function");
            }
            children.add(new StringLiteral(blockEncryptionMode));
        }
    }