diff --git a/be/src/vec/functions/round.h b/be/src/vec/functions/round.h index 679af9b4b2..456d6264a2 100644 --- a/be/src/vec/functions/round.h +++ b/be/src/vec/functions/round.h @@ -66,22 +66,22 @@ struct IntegerRoundingComputation { static size_t prepare(size_t scale) { return scale; } /// Integer overflow is Ok. - static ALWAYS_INLINE T compute_impl(T x, T scale) { + static ALWAYS_INLINE T compute_impl(T x, T scale, T target_scale) { switch (rounding_mode) { case RoundingMode::Trunc: { - return x / scale * scale; + return target_scale > 1 ? x / scale * target_scale : x / scale; } case RoundingMode::Floor: { if (x < 0) { x -= scale - 1; } - return x / scale * scale; + return target_scale > 1 ? x / scale * target_scale : x / scale; } case RoundingMode::Ceil: { if (x >= 0) { x += scale - 1; } - return x / scale * scale; + return target_scale > 1 ? x / scale * target_scale : x / scale; } case RoundingMode::Round: { if (x < 0) { @@ -89,48 +89,49 @@ struct IntegerRoundingComputation { } switch (tie_breaking_mode) { case TieBreakingMode::Auto: { - x = (x + scale / 2) / scale * scale; + x = (x + scale / 2) / scale; break; } case TieBreakingMode::Bankers: { T quotient = (x + scale / 2) / scale; if (quotient * scale == x + scale / 2) { // round half to even - x = ((quotient + (x < 0)) & ~1) * scale; + x = (quotient + (x < 0)) & ~1; } else { // round the others as usual - x = quotient * scale; + x = quotient; } break; } } - return x; + return target_scale > 1 ? x * target_scale : x; } } __builtin_unreachable(); } - static ALWAYS_INLINE T compute(T x, T scale) { + static ALWAYS_INLINE T compute(T x, T scale, size_t target_scale) { switch (scale_mode) { case ScaleMode::Zero: case ScaleMode::Positive: return x; case ScaleMode::Negative: - return compute_impl(x, scale); + return compute_impl(x, scale, target_scale); } __builtin_unreachable(); } - static ALWAYS_INLINE void compute(const T* __restrict in, size_t scale, T* __restrict out) { + static ALWAYS_INLINE void compute(const T* __restrict in, size_t scale, T* __restrict out, + size_t target_scale) { if constexpr (sizeof(T) <= sizeof(scale) && scale_mode == ScaleMode::Negative) { if (scale > size_t(std::numeric_limits::max())) { *out = 0; return; } } - *out = compute(*in, scale); + *out = compute(*in, scale, target_scale); } }; @@ -144,8 +145,8 @@ private: public: static NO_INLINE void apply(const Container& in, UInt32 in_scale, Container& out, - Int16 scale_arg) { - scale_arg = in_scale - scale_arg; + Int16 out_scale) { + Int16 scale_arg = in_scale - out_scale; if (scale_arg > 0) { size_t scale = int_exp10(scale_arg); @@ -153,10 +154,19 @@ public: const NativeType* end_in = reinterpret_cast(in.data()) + in.size(); NativeType* __restrict p_out = reinterpret_cast(out.data()); - while (p_in < end_in) { - Op::compute(p_in, scale, p_out); - ++p_in; - ++p_out; + if (out_scale < 0) { + size_t target_scale = int_exp10(-out_scale); + while (p_in < end_in) { + Op::compute(p_in, scale, p_out, target_scale); + ++p_in; + ++p_out; + } + } else { + while (p_in < end_in) { + Op::compute(p_in, scale, p_out, 1); + ++p_in; + ++p_out; + } } } else { memcpy(out.data(), in.data(), in.size() * sizeof(T)); @@ -353,7 +363,7 @@ public: T* __restrict p_out = out.data(); while (p_in < end_in) { - Op::compute(p_in, scale, p_out); + Op::compute(p_in, scale, p_out, 1); ++p_in; ++p_out; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java index 1459cb9ddc..1d90d181bc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java @@ -67,12 +67,16 @@ import java.util.Set; // TODO: for aggregations, we need to unify the code paths for builtins and UDAs. public class FunctionCallExpr extends Expr { - public static final ImmutableSet STDDEV_FUNCTION_SET = - new ImmutableSortedSet.Builder(String.CASE_INSENSITIVE_ORDER) - .add("stddev").add("stddev_val").add("stddev_samp").add("stddev_pop") - .add("variance").add("variance_pop").add("variance_pop").add("var_samp").add("var_pop").build(); + public static final ImmutableSet STDDEV_FUNCTION_SET = new ImmutableSortedSet.Builder( + String.CASE_INSENSITIVE_ORDER) + .add("stddev").add("stddev_val").add("stddev_samp").add("stddev_pop") + .add("variance").add("variance_pop").add("variance_pop").add("var_samp").add("var_pop").build(); public static final Map, Type, Type>> PRECISION_INFER_RULE; public static final java.util.function.BiFunction, Type, Type> DEFAULT_PRECISION_INFER_RULE; + public static final ImmutableSet ROUND_FUNCTION_SET = new ImmutableSortedSet.Builder( + String.CASE_INSENSITIVE_ORDER) + .add("round").add("round_bankers").add("ceil").add("floor") + .add("truncate").add("dround").add("dceil").add("dfloor").build(); static { java.util.function.BiFunction, Type, Type> sumRule = (children, returnType) -> { @@ -101,11 +105,10 @@ public class FunctionCallExpr extends Expr { java.util.function.BiFunction, Type, Type> roundRule = (children, returnType) -> { Preconditions.checkArgument(children != null && children.size() > 0); if (children.size() == 1 && children.get(0).getType().isDecimalV3()) { - return ScalarType.createDecimalV3Type(children.get(0).getType().getPrecision(), - ((ScalarType) children.get(0).getType()).decimalScale()); + return ScalarType.createDecimalV3Type(children.get(0).getType().getPrecision(), 0); } else if (children.size() == 2) { Preconditions.checkArgument(children.get(1) instanceof IntLiteral - || (children.get(1) instanceof CastExpr + || (children.get(1) instanceof CastExpr && children.get(1).getChild(0) instanceof IntLiteral), "2nd argument of function round/floor/ceil/truncate must be literal"); if (children.get(1) instanceof CastExpr && children.get(1).getChild(0) instanceof IntLiteral) { @@ -114,8 +117,9 @@ public class FunctionCallExpr extends Expr { } else { children.get(1).setType(Type.INT); } + int scaleArg = (int) (((IntLiteral) children.get(1)).getValue()); return ScalarType.createDecimalV3Type(children.get(0).getType().getPrecision(), - ((ScalarType) children.get(0).getType()).decimalScale()); + Math.max(scaleArg, 0)); } else { return returnType; } @@ -155,9 +159,10 @@ public class FunctionCallExpr extends Expr { Math.max(((ScalarType) children.get(1).getType()).decimalScale(), ((ScalarType) children.get(2).getType()).decimalScale())); } else if (children.get(1).getType().isDatetimeV2() && children.get(2).getType().isDatetimeV2()) { - return ((ScalarType) children.get(1).getType()).decimalScale() - > ((ScalarType) children.get(2).getType()).decimalScale() - ? children.get(1).getType() : children.get(2).getType(); + return ((ScalarType) children.get(1).getType()) + .decimalScale() > ((ScalarType) children.get(2).getType()).decimalScale() + ? children.get(1).getType() + : children.get(2).getType(); } else { return returnType; } @@ -178,9 +183,9 @@ public class FunctionCallExpr extends Expr { PRECISION_INFER_RULE.put("truncate", roundRule); } - public static final ImmutableSet TIME_FUNCTIONS_WITH_PRECISION = - new ImmutableSortedSet.Builder(String.CASE_INSENSITIVE_ORDER) - .add("now").add("current_timestamp").add("localtime").add("localtimestamp").build(); + public static final ImmutableSet TIME_FUNCTIONS_WITH_PRECISION = new ImmutableSortedSet.Builder( + String.CASE_INSENSITIVE_ORDER) + .add("now").add("current_timestamp").add("localtime").add("localtimestamp").build(); public static final int STDDEV_DECIMAL_SCALE = 9; private static final String ELEMENT_EXTRACT_FN_NAME = "%element_extract%"; @@ -199,7 +204,8 @@ public class FunctionCallExpr extends Expr { // check table function private boolean isTableFnCall = false; - // Indicates whether this is a merge aggregation function that should use the merge + // Indicates whether this is a merge aggregation function that should use the + // merge // instead of the update symbol. This flag also affects the behavior of // resetAnalysisState() which is used during expr substitution. private boolean isMergeAggFn; @@ -211,7 +217,8 @@ public class FunctionCallExpr extends Expr { private boolean isRewrote = false; - // TODO: this field will be removed when we support analyze aggregate function in the nereids framework. + // TODO: this field will be removed when we support analyze aggregate function + // in the nereids framework. private boolean shouldFinalizeForNereids = true; // this field is set by nereids, so we would not get arg types by the children. @@ -466,7 +473,7 @@ public class FunctionCallExpr extends Expr { } int len = children.size(); List result = Lists.newArrayList(); - //XXX_diff are used by nereids only + // XXX_diff are used by nereids only if (fnName.getFunction().equalsIgnoreCase("years_diff") || fnName.getFunction().equalsIgnoreCase("months_diff") || fnName.getFunction().equalsIgnoreCase("days_diff") @@ -477,7 +484,7 @@ public class FunctionCallExpr extends Expr { sb.append(children.get(0).toSql()).append(")"); return sb.toString(); } - //used by nereids END + // used by nereids END if (fnName.getFunction().equalsIgnoreCase("json_array") || fnName.getFunction().equalsIgnoreCase("json_object")) { @@ -1115,7 +1122,7 @@ public class FunctionCallExpr extends Expr { if (!VectorizedUtil.isVectorized()) { type = getChild(0).type.getMaxResolutionType(); } - fn = getBuiltinFunction(fnName.getFunction(), new Type[] {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(); @@ -1128,7 +1135,7 @@ public class FunctionCallExpr extends Expr { } } - fn = getBuiltinFunction(fnName.getFunction(), new Type[] {compatibleType}, + 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) { @@ -1174,12 +1181,12 @@ public class FunctionCallExpr extends Expr { 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"); + + fnName + " function must be boolean"); } childTypes[i] = children.get(i).type; } fn = getBuiltinFunction(fnName.getFunction(), childTypes, - Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); + 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) { @@ -1205,12 +1212,12 @@ public class FunctionCallExpr extends Expr { 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"); + + fnName + " function must be boolean"); } childTypes[i] = children.get(i).type; } fn = getBuiltinFunction(fnName.getFunction(), childTypes, - Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); + Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); } else if (fnName.getFunction().equalsIgnoreCase("if")) { Type[] childTypes = collectChildReturnTypes(); Type assignmentCompatibleType = ScalarType.getAssignmentCompatibleType(childTypes[1], childTypes[2], true); @@ -1230,7 +1237,6 @@ public class FunctionCallExpr extends Expr { fn.setReturnType(assignmentCompatibleType); } - } 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 @@ -1242,7 +1248,7 @@ public class FunctionCallExpr extends Expr { Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); } else if (STDDEV_FUNCTION_SET.contains(fnName.getFunction().toLowerCase()) && children.size() == 1 && collectChildReturnTypes()[0].isDecimalV3()) { - fn = getBuiltinFunction(fnName.getFunction(), new Type[] {Type.DOUBLE}, + fn = getBuiltinFunction(fnName.getFunction(), new Type[] { Type.DOUBLE }, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); } else { // now first find table function in table function sets @@ -1254,6 +1260,10 @@ public class FunctionCallExpr extends Expr { 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(); @@ -1279,8 +1289,8 @@ public class FunctionCallExpr extends Expr { // TODO(gaoxin): ExternalDatabase not implement udf yet. DatabaseIf db = Env.getCurrentEnv().getInternalCatalog().getDbNullable(dbName); if (db != null && (db instanceof Database)) { - Function searchDesc = - new Function(fnName, Arrays.asList(collectChildReturnTypes()), Type.INVALID, false); + Function searchDesc = new Function(fnName, Arrays.asList(collectChildReturnTypes()), + Type.INVALID, false); fn = ((Database) db).getFunction(searchDesc, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); } @@ -1366,22 +1376,23 @@ public class FunctionCallExpr extends Expr { continue; } else if (fnName.getFunction().equalsIgnoreCase("array") && (children.get(0).getType().isDecimalV3() && args[ix].isDecimalV3() - || children.get(0).getType().isDatetimeV2() && args[ix].isDatetimeV2())) { + || children.get(0).getType().isDatetimeV2() && args[ix].isDatetimeV2())) { 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()))) { + || (children.get(0).getType().isDatetimeV2() + && ((ArrayType) args[ix]).getItemType().isDatetimeV2()) + || (children.get(0).getType().isDecimalV2() + && ((ArrayType) args[ix]).getItemType().isDecimalV2()))) { continue; - } else if (!argTypes[i].matchesType(args[ix]) && !( - argTypes[i].isDateOrDateTime() && args[ix].isDateOrDateTime()) + } else if (!argTypes[i].matchesType(args[ix]) + && !(argTypes[i].isDateOrDateTime() && args[ix].isDateOrDateTime()) && (!fn.getReturnType().isDecimalV3() - || (argTypes[i].isValid() && !argTypes[i].isDecimalV3() && args[ix].isDecimalV3()))) { + || (argTypes[i].isValid() && !argTypes[i].isDecimalV3() + && args[ix].isDecimalV3()))) { uncheckedCastChild(args[ix], i); } } @@ -1389,28 +1400,30 @@ public class FunctionCallExpr extends Expr { } /** - * The return type of str_to_date depends on whether the time part is included in the format. + * 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. + * 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" + * "%Y-%m-%d" + * "%Y-%m-%d %H:%i:%s" * Query: - * SELECT str_to_date("2020-09-01", k1) from tbl; + * 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 + * 2020-09-01 00:00:00 + * 2020-09-01 00:00:00 * * Query: - * SELECT str_to_date("2020-09-01", "%Y-%m-%d"); + * 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"); + * SELECT str_to_date("2020-09-01", "%Y-%m-%d %H:%i:%s"); * Return type is DATETIME */ if (fn.getFunctionName().getFunction().equals("str_to_date")) { @@ -1442,7 +1455,8 @@ public class FunctionCallExpr extends Expr { if (this.type.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 + // 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); } @@ -1503,7 +1517,7 @@ public class FunctionCallExpr extends Expr { private static int parseNumber(String s) { - String[] n = s.split(""); //array of strings + String[] n = s.split(""); // array of strings int num = 0; for (String value : n) { // validating numbers @@ -1589,7 +1603,8 @@ public class FunctionCallExpr extends Expr { + "] args number is not equal to it's definition"); List oriParamsExprs = oriExpr.fnParams.exprs(); - // replace origin function params exprs' with input params expr depending on parameter name + // replace origin function params exprs' with input params expr depending on + // parameter name for (int i = 0; i < oriParamsExprs.size(); i++) { Expr expr = replaceParams(parameters, inputParamsExprs, oriParamsExprs.get(i)); oriParamsExprs.set(i, expr); @@ -1605,7 +1620,8 @@ public class FunctionCallExpr extends Expr { } /** - * replace origin function expr and it's children with input params exprs depending on parameter name + * replace origin function expr and it's children with input params exprs + * depending on parameter name * * @param parameters * @param inputParamsExprs @@ -1626,7 +1642,8 @@ public class FunctionCallExpr extends Expr { return inputParamsExprs.get(index); } } - // Initialize literalExpr without type information, because literalExpr does not save type information + // Initialize literalExpr without type information, because literalExpr does not + // save type information // when it is persisted, so after fe restart, read the image, // it will be missing type and report an error during analyze. if (oriExpr instanceof LiteralExpr && oriExpr.getType().equals(Type.INVALID)) { @@ -1684,7 +1701,8 @@ public class FunctionCallExpr extends Expr { @Override protected boolean isConstantImpl() { - // TODO: we can't correctly determine const-ness before analyzing 'fn_'. We should + // TODO: we can't correctly determine const-ness before analyzing 'fn_'. We + // should // rework logic so that we do not call this function on unanalyzed exprs. // Aggregate functions are never constant. if (fn instanceof AggregateFunction || fn == null) { @@ -1761,7 +1779,7 @@ public class FunctionCallExpr extends Expr { if (fnName.getFunction().equalsIgnoreCase("sum")) { // Prevent the cast type in vector exec engine Type childType = argTypes.get(0).getMaxResolutionType(); - fn = getBuiltinFunction(fnName.getFunction(), new Type[] {childType}, + fn = getBuiltinFunction(fnName.getFunction(), new Type[] { childType }, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); type = fn.getReturnType(); } else if (fnName.getFunction().equalsIgnoreCase("count")) { @@ -1778,7 +1796,7 @@ public class FunctionCallExpr extends Expr { || fnName.getFunction().equalsIgnoreCase("avg") || fnName.getFunction().equalsIgnoreCase("weekOfYear")) { Type childType = argTypes.get(0); - fn = getBuiltinFunction(fnName.getFunction(), new Type[] {childType}, + fn = getBuiltinFunction(fnName.getFunction(), new Type[] { childType }, Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF); type = fn.getReturnType(); } else { @@ -1798,7 +1816,8 @@ public class FunctionCallExpr extends Expr { } /** - * NOTICE: This function only used for Nereids, should not call it if u don't know what it is mean. + * NOTICE: This function only used for Nereids, should not call it if u don't + * know what it is mean. */ public void setMergeForNereids(boolean isMergeAggFn) { this.isMergeAggFn = isMergeAggFn; diff --git a/regression-test/data/query_p0/sql_functions/math_functions/test_round.out b/regression-test/data/query_p0/sql_functions/math_functions/test_round.out index a847c8b2a9..cafb8dc973 100644 --- a/regression-test/data/query_p0/sql_functions/math_functions/test_round.out +++ b/regression-test/data/query_p0/sql_functions/math_functions/test_round.out @@ -17,31 +17,46 @@ 3.0 1989.0 1002.0 24453.3 78945.0 3654.0 -- !select -- -16.000 16.00000 16.00000 +16 16 16 -- !select -- -16.000 16.00000 16.00000 +16 16 16 -- !select -- -17.000 17.00000 17.00000 +17 17 17 -- !select -- -16.000 16.00000 16.00000 +16 16 16 -- !select -- -16.030 16.03000 16.03000 +16.03 16.03 16.03 -- !select -- -16.020 16.02000 16.02000 +16.02 16.02 16.02 -- !select -- -16.030 16.03000 16.03000 +16.03 16.03 16.03 -- !select -- -16.020 16.02000 16.02000 +16.02 16.02 16.02 -- !select -- -16.020 16.02000 16.02000 +16.02 16.02 16.02 + +-- !select -- +20 20 20 + +-- !select -- +10 10 10 + +-- !select -- +20 20 20 + +-- !select -- +10 10 10 + +-- !select -- +20 20 20 -- !nereids_round_arg1 -- 10.0 diff --git a/regression-test/suites/query_p0/sql_functions/math_functions/test_round.groovy b/regression-test/suites/query_p0/sql_functions/math_functions/test_round.groovy index b30f35f78a..78be9967d7 100644 --- a/regression-test/suites/query_p0/sql_functions/math_functions/test_round.groovy +++ b/regression-test/suites/query_p0/sql_functions/math_functions/test_round.groovy @@ -38,11 +38,19 @@ suite("test_round") { qt_select """ SELECT floor(col1), floor(col2), floor(col3) FROM `${tableName}`; """ qt_select """ SELECT ceil(col1), ceil(col2), ceil(col3) FROM `${tableName}`; """ qt_select """ SELECT round_bankers(col1), round_bankers(col2), round_bankers(col3) FROM `${tableName}`; """ + qt_select """ SELECT round(col1, 2), round(col2, 2), round(col3, 2) FROM `${tableName}`; """ qt_select """ SELECT floor(col1, 2), floor(col2, 2), floor(col3, 2) FROM `${tableName}`; """ qt_select """ SELECT ceil(col1, 2), ceil(col2, 2), ceil(col3, 2) FROM `${tableName}`; """ qt_select """ SELECT truncate(col1, 2), truncate(col2, 2), truncate(col3, 2) FROM `${tableName}`; """ qt_select """ SELECT round_bankers(col1, 2), round_bankers(col2, 2), round_bankers(col3, 2) FROM `${tableName}`; """ + + qt_select """ SELECT round(col1, -1), round(col2, -1), round(col3, -1) FROM `${tableName}`; """ + qt_select """ SELECT floor(col1, -1), floor(col2, -1), floor(col3, -1) FROM `${tableName}`; """ + qt_select """ SELECT ceil(col1, -1), ceil(col2, -1), ceil(col3, -1) FROM `${tableName}`; """ + qt_select """ SELECT truncate(col1, -1), truncate(col2, -1), truncate(col3, -1) FROM `${tableName}`; """ + qt_select """ SELECT round_bankers(col1, -1), round_bankers(col2, -1), round_bankers(col3, -1) FROM `${tableName}`; """ + sql """ DROP TABLE IF EXISTS `${tableName}` """ sql "SET enable_nereids_planner=true"