[Bug](decimalv3) Use correct decimal scale for function round (#17232)

Co-authored-by: maochongxin <maochongxin@gmail.com>
This commit is contained in:
Gabriel
2023-03-01 12:28:41 +08:00
committed by GitHub
parent cbdf1af2d5
commit 979cf42d7a
4 changed files with 134 additions and 82 deletions

View File

@ -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<T>::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<const NativeType*>(in.data()) + in.size();
NativeType* __restrict p_out = reinterpret_cast<NativeType*>(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;
}

View File

@ -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<String> 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<String> 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<String, java.util.function.BiFunction<ArrayList<Expr>, Type, Type>> PRECISION_INFER_RULE;
public static final java.util.function.BiFunction<ArrayList<Expr>, Type, Type> DEFAULT_PRECISION_INFER_RULE;
public static final ImmutableSet<String> 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<ArrayList<Expr>, Type, Type> sumRule = (children, returnType) -> {
@ -101,11 +105,10 @@ public class FunctionCallExpr extends Expr {
java.util.function.BiFunction<ArrayList<Expr>, 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<String> 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<String> 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<String> 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<Expr> 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;

View File

@ -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

View File

@ -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"