diff --git a/be/src/exprs/decimal_cast_expr.h b/be/src/exprs/decimal_cast_expr.h index bb120f27325..4ec08842cec 100644 --- a/be/src/exprs/decimal_cast_expr.h +++ b/be/src/exprs/decimal_cast_expr.h @@ -140,6 +140,15 @@ struct DecimalNonDecimalCast; static inline ColumnPtr decimal_from(const ColumnPtr& column, int precision, int scale) { + if (scale == 0) { + return _decimal_from(column, precision, scale); + } else { + return _decimal_from(column, precision, scale); + } + } + + template + static inline ColumnPtr _decimal_from(const ColumnPtr& column, int precision, int scale) { const auto num_rows = column->size(); auto result = DecimalColumnType::create(precision, scale, num_rows); const auto data = &ColumnHelper::cast_to_raw(column)->get_data().front(); @@ -163,9 +172,16 @@ struct DecimalNonDecimalCast>( (SignedBooleanType)(data[i]), scale_factor, &result_data[i]); } else if constexpr (lt_is_integer) { - overflow = - DecimalV3Cast::from_integer>( - data[i], scale_factor, &result_data[i]); + if constexpr (ZeroScale) { + // Fast path for integer-to-decimal conversion with scale 0. + overflow = + DecimalV3Cast::to_decimal_trivial>(data[i], &result_data[i]); + } else { + overflow = DecimalV3Cast::from_integer>(data[i], scale_factor, + &result_data[i]); + } } else if constexpr (lt_is_float) { overflow = DecimalV3Cast::from_float(data[i], scale_factor, &result_data[i]); @@ -218,6 +234,7 @@ struct DecimalNonDecimalCast) { ColumnBuilder builder(result, null_column, has_null); return builder.build(column->is_constant()); diff --git a/be/src/runtime/int128_arithmetics_x86_64.h b/be/src/runtime/int128_arithmetics_x86_64.h index d91d3dfc516..af8a599cf3f 100644 --- a/be/src/runtime/int128_arithmetics_x86_64.h +++ b/be/src/runtime/int128_arithmetics_x86_64.h @@ -210,6 +210,14 @@ static inline int64_t i32_x_i32_produce_i64(int32_t a, int32_t b) { } static inline int multi3(const int128_t& x, const int128_t& y, int128_t& res) { + // This algorithm mistakenly treats `INT128_MIN * 1` as an overflow, because `abs(INT128_MIN)` remains `INT128_MIN`, + // which causes `asm_add` to detect `SF=1`. Therefore, we add a special case here to bypass this issue. + if (UNLIKELY((x == std::numeric_limits::min() && y == 1) || + (y == std::numeric_limits::min() && x == 1))) { + res = std::numeric_limits::min(); + return 0; + } + // sgn(x) auto sx = x >> 127; // sgn(y) diff --git a/test/sql/test_decimal/R/test_decimal_cast b/test/sql/test_decimal/R/test_decimal_cast new file mode 100644 index 00000000000..4fb558b1f14 --- /dev/null +++ b/test/sql/test_decimal/R/test_decimal_cast @@ -0,0 +1,179 @@ +-- name: test_decimal_cast +CREATE TABLE t1 ( + k1 bigint NULL, + + c_tinyint tinyint null, + c_int int null, + c_bigint bigint null, + c_largeint largeint null +) ENGINE=OLAP +DUPLICATE KEY(`k1`) +DISTRIBUTED BY HASH(`k1`) BUCKETS 96 +PROPERTIES ( + "replication_num" = "1" +); +-- result: +-- !result +insert into t1 values + (1, 127, 2147483647, 9223372036854775807, 170141183460469231731687303715884105727), + (2, -128, -2147483648, -9223372036854775808, -170141183460469231731687303715884105728), + (3, null, null, null, null), + (4, 0, 0, 0, 0), + (5, 1, 1, 1, 1), + (6, -1, -1, -1, -1), + (7, 12, 214748364, 922337203685477580, 17014118346046923173168730371588410572), + (8, -12, -214748364, -922337203685477580, -17014118346046923173168730371588410572); +-- result: +-- !result +select + k1, + cast(c_tinyint as DECIMAL(9,0)), + cast(c_int as DECIMAL(9,0)), + cast(c_bigint as DECIMAL(9,0)), + cast(c_largeint as DECIMAL(9,0)) +from t1 +order by k1; +-- result: +1 127 2147483647 9223372036854775807 None +2 -128 -2147483648 -9223372036854775808 None +3 None None None None +4 0 0 0 0 +5 1 1 1 1 +6 -1 -1 -1 -1 +7 12 214748364 922337203685477580 None +8 -12 -214748364 -922337203685477580 None +-- !result +select + k1, + cast(c_tinyint as DECIMAL(9,1)), + cast(c_int as DECIMAL(9,1)), + cast(c_bigint as DECIMAL(9,1)), + cast(c_largeint as DECIMAL(9,1)) +from t1 +order by k1; +-- result: +1 127.0 2147483647.0 None None +2 -128.0 -2147483648.0 None None +3 None None None None +4 0.0 0.0 0.0 0.0 +5 1.0 1.0 1.0 1.0 +6 -1.0 -1.0 -1.0 -1.0 +7 12.0 214748364.0 922337203685477580.0 None +8 -12.0 -214748364.0 -922337203685477580.0 None +-- !result +select + k1, + cast(c_tinyint as DECIMAL(27,0)), + cast(c_int as DECIMAL(27,0)), + cast(c_bigint as DECIMAL(27,0)), + cast(c_largeint as DECIMAL(27,0)) +from t1 +order by k1; +-- result: +1 127 2147483647 9223372036854775807 170141183460469231731687303715884105727 +2 -128 -2147483648 -9223372036854775808 -170141183460469231731687303715884105728 +3 None None None None +4 0 0 0 0 +5 1 1 1 1 +6 -1 -1 -1 -1 +7 12 214748364 922337203685477580 17014118346046923173168730371588410572 +8 -12 -214748364 -922337203685477580 -17014118346046923173168730371588410572 +-- !result +select + k1, + cast(c_tinyint as DECIMAL(27,1)), + cast(c_int as DECIMAL(27,1)), + cast(c_bigint as DECIMAL(27,1)), + cast(c_largeint as DECIMAL(27,1)) +from t1 +order by k1; +-- result: +1 127.0 2147483647.0 9223372036854775807.0 None +2 -128.0 -2147483648.0 -9223372036854775808.0 None +3 None None None None +4 0.0 0.0 0.0 0.0 +5 1.0 1.0 1.0 1.0 +6 -1.0 -1.0 -1.0 -1.0 +7 12.0 214748364.0 922337203685477580.0 17014118346046923173168730371588410572.0 +8 -12.0 -214748364.0 -922337203685477580.0 -17014118346046923173168730371588410572.0 +-- !result +select + k1, + cast(c_tinyint as DECIMAL(38,0)), + cast(c_int as DECIMAL(38,0)), + cast(c_bigint as DECIMAL(38,0)), + cast(c_largeint as DECIMAL(38,0)) +from t1 +order by k1; +-- result: +1 127 2147483647 9223372036854775807 170141183460469231731687303715884105727 +2 -128 -2147483648 -9223372036854775808 -170141183460469231731687303715884105728 +3 None None None None +4 0 0 0 0 +5 1 1 1 1 +6 -1 -1 -1 -1 +7 12 214748364 922337203685477580 17014118346046923173168730371588410572 +8 -12 -214748364 -922337203685477580 -17014118346046923173168730371588410572 +-- !result +select + k1, + cast(c_tinyint as DECIMAL(38,1)), + cast(c_int as DECIMAL(38,1)), + cast(c_bigint as DECIMAL(38,1)), + cast(c_largeint as DECIMAL(38,1)) +from t1 +order by k1; +-- result: +1 127.0 2147483647.0 9223372036854775807.0 None +2 -128.0 -2147483648.0 -9223372036854775808.0 None +3 None None None None +4 0.0 0.0 0.0 0.0 +5 1.0 1.0 1.0 1.0 +6 -1.0 -1.0 -1.0 -1.0 +7 12.0 214748364.0 922337203685477580.0 17014118346046923173168730371588410572.0 +8 -12.0 -214748364.0 -922337203685477580.0 -17014118346046923173168730371588410572.0 +-- !result +select k1, c_tinyint * 0, c_int * 0, c_bigint * 0, c_largeint * 0 from t1 order by k1; +-- result: +1 0 0 0 0 +2 0 0 0 0 +3 None None None None +4 0 0 0 0 +5 0 0 0 0 +6 0 0 0 0 +7 0 0 0 0 +8 0 0 0 0 +-- !result +select k1, c_tinyint * 1, c_int * 1, c_bigint * 1, c_largeint * 1 from t1 order by k1; +-- result: +1 127 2147483647 9223372036854775807 170141183460469231731687303715884105727 +2 -128 -2147483648 -9223372036854775808 -170141183460469231731687303715884105728 +3 None None None None +4 0 0 0 0 +5 1 1 1 1 +6 -1 -1 -1 -1 +7 12 214748364 922337203685477580 17014118346046923173168730371588410572 +8 -12 -214748364 -922337203685477580 -17014118346046923173168730371588410572 +-- !result +select k1, cast(c_tinyint * 0 as decimal(38, 0)), cast(c_int * 0 as decimal(38, 0)), cast(c_bigint * 0 as decimal(38, 0)), cast(c_largeint * 0 as decimal(38, 0)) from t1 order by k1; +-- result: +1 0 0 0 0 +2 0 0 0 0 +3 None None None None +4 0 0 0 0 +5 0 0 0 0 +6 0 0 0 0 +7 0 0 0 0 +8 0 0 0 0 +-- !result +select k1, cast(c_tinyint * 1 as decimal(38, 0)), cast(c_int * 1 as decimal(38, 0)), cast(c_bigint * 1 as decimal(38, 0)), cast(c_largeint * 1 as decimal(38, 0)) from t1 order by k1; +-- result: +1 127 2147483647 9223372036854775807 170141183460469231731687303715884105727 +2 -128 -2147483648 -9223372036854775808 -170141183460469231731687303715884105728 +3 None None None None +4 0 0 0 0 +5 1 1 1 1 +6 -1 -1 -1 -1 +7 12 214748364 922337203685477580 17014118346046923173168730371588410572 +8 -12 -214748364 -922337203685477580 -17014118346046923173168730371588410572 +-- !result \ No newline at end of file diff --git a/test/sql/test_decimal/T/test_decimal_cast b/test/sql/test_decimal/T/test_decimal_cast new file mode 100644 index 00000000000..4cccd3ab17a --- /dev/null +++ b/test/sql/test_decimal/T/test_decimal_cast @@ -0,0 +1,85 @@ +-- name: test_decimal_cast + +CREATE TABLE t1 ( + k1 bigint NULL, + + c_tinyint tinyint null, + c_int int null, + c_bigint bigint null, + c_largeint largeint null +) ENGINE=OLAP +DUPLICATE KEY(`k1`) +DISTRIBUTED BY HASH(`k1`) BUCKETS 96 +PROPERTIES ( + "replication_num" = "1" +); + +insert into t1 values + (1, 127, 2147483647, 9223372036854775807, 170141183460469231731687303715884105727), + (2, -128, -2147483648, -9223372036854775808, -170141183460469231731687303715884105728), + (3, null, null, null, null), + (4, 0, 0, 0, 0), + (5, 1, 1, 1, 1), + (6, -1, -1, -1, -1), + (7, 12, 214748364, 922337203685477580, 17014118346046923173168730371588410572), + (8, -12, -214748364, -922337203685477580, -17014118346046923173168730371588410572); + +select + k1, + cast(c_tinyint as DECIMAL(9,0)), + cast(c_int as DECIMAL(9,0)), + cast(c_bigint as DECIMAL(9,0)), + cast(c_largeint as DECIMAL(9,0)) +from t1 +order by k1; + +select + k1, + cast(c_tinyint as DECIMAL(9,1)), + cast(c_int as DECIMAL(9,1)), + cast(c_bigint as DECIMAL(9,1)), + cast(c_largeint as DECIMAL(9,1)) +from t1 +order by k1; + +select + k1, + cast(c_tinyint as DECIMAL(27,0)), + cast(c_int as DECIMAL(27,0)), + cast(c_bigint as DECIMAL(27,0)), + cast(c_largeint as DECIMAL(27,0)) +from t1 +order by k1; + +select + k1, + cast(c_tinyint as DECIMAL(27,1)), + cast(c_int as DECIMAL(27,1)), + cast(c_bigint as DECIMAL(27,1)), + cast(c_largeint as DECIMAL(27,1)) +from t1 +order by k1; + +select + k1, + cast(c_tinyint as DECIMAL(38,0)), + cast(c_int as DECIMAL(38,0)), + cast(c_bigint as DECIMAL(38,0)), + cast(c_largeint as DECIMAL(38,0)) +from t1 +order by k1; + +select + k1, + cast(c_tinyint as DECIMAL(38,1)), + cast(c_int as DECIMAL(38,1)), + cast(c_bigint as DECIMAL(38,1)), + cast(c_largeint as DECIMAL(38,1)) +from t1 +order by k1; + +select k1, c_tinyint * 0, c_int * 0, c_bigint * 0, c_largeint * 0 from t1 order by k1; +select k1, c_tinyint * 1, c_int * 1, c_bigint * 1, c_largeint * 1 from t1 order by k1; + +select k1, cast(c_tinyint * 0 as decimal(38, 0)), cast(c_int * 0 as decimal(38, 0)), cast(c_bigint * 0 as decimal(38, 0)), cast(c_largeint * 0 as decimal(38, 0)) from t1 order by k1; +select k1, cast(c_tinyint * 1 as decimal(38, 0)), cast(c_int * 1 as decimal(38, 0)), cast(c_bigint * 1 as decimal(38, 0)), cast(c_largeint * 1 as decimal(38, 0)) from t1 order by k1;