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