Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cast to decimal overflow bug (#3922) #4082

Merged
24 changes: 15 additions & 9 deletions dbms/src/Functions/FunctionsTiDBConversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -816,21 +816,27 @@ struct TiDBConvertToDecimal
using FromFieldType = typename FromDataType::FieldType;

template <typename T, typename U>
static U toTiDBDecimalInternal(T value, PrecType prec, ScaleType scale, const Context & context)
static U toTiDBDecimalInternal(T int_value, PrecType prec, ScaleType scale, const Context & context)
{
// int_value is the value that exposes to user. Such as cast(val to decimal), val is the int_value which used by user.
// And val * scale_mul is the scaled_value, which is stored in ColumnDecimal internally.
static_assert(std::is_integral_v<T>);
using UType = typename U::NativeType;
auto max_value = DecimalMaxValue::get(prec);
if (value > max_value || value < -max_value)
UType scale_mul = getScaleMultiplier<U>(scale);

Int256 scaled_value = static_cast<Int256>(int_value) * static_cast<Int256>(scale_mul);
Int256 scaled_max_value = DecimalMaxValue::get(prec);

if (scaled_value > scaled_max_value || scaled_value < -scaled_max_value)
{
context.getDAGContext()->handleOverflowError("cast to decimal", Errors::Types::Truncated);
if (value > 0)
return static_cast<UType>(max_value);
if (int_value > 0)
return static_cast<UType>(scaled_max_value);
else
return static_cast<UType>(-max_value);
return static_cast<UType>(-scaled_max_value);
}
UType scale_mul = getScaleMultiplier<U>(scale);
U result = static_cast<UType>(value) * scale_mul;
return result;

return static_cast<UType>(scaled_value);
}

template <typename U>
Expand Down
22 changes: 22 additions & 0 deletions dbms/src/Functions/tests/gtest_tidb_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,28 @@ try
executeFunction(func_name,
{createColumn<Nullable<UInt64>>({MAX_INT64, {}}),
createCastTypeConstColumn("Nullable(Decimal(65,0))")}));

ASSERT_THROW(executeFunction(func_name,
{createColumn<Nullable<Int32>>({9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}),
TiFlashException);

ASSERT_THROW(executeFunction(func_name,
{createColumn<Nullable<Int32>>({-9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}),
TiFlashException);

ASSERT_COLUMN_EQ(
createColumn<Nullable<Decimal32>>(
std::make_tuple(4, 1),
{DecimalField32(static_cast<Int32>(9990), 1)}),
executeFunction(func_name,
{createColumn<Nullable<Int32>>({999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}));

ASSERT_COLUMN_EQ(
createColumn<Nullable<Decimal32>>(
std::make_tuple(4, 1),
{DecimalField32(static_cast<Int32>(-9990), 1)}),
executeFunction(func_name,
{createColumn<Nullable<Int32>>({-999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}));
}
CATCH

Expand Down
18 changes: 18 additions & 0 deletions tests/fullstack-test/expr/cast_as_decimal.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
mysql> drop table if exists test.t1;
mysql> create table test.t1(c1 int);
mysql> insert into test.t1 values(9999), (-9999), (99), (-99);
mysql> alter table test.t1 set tiflash replica 1;
func> wait_table test t1
mysql> set @@tidb_isolation_read_engines='tiflash'; select cast(c1 as decimal(4, 1)) from test.t1 order by 1;
cast(c1 as decimal(4, 1))
-999.9
-99.0
99.0
999.9
mysql> set @@tidb_isolation_read_engines='tiflash'; select cast(c1 as decimal(2, 2)) from test.t1 order by 1;
cast(c1 as decimal(2, 2))
-0.99
-0.99
0.99
0.99
mysql> drop table if exists test.t1;
mysql> create table test.t1(c1 datetime(5));
mysql> insert into test.t1 values('2022-10-10 10:10:10.12345');
mysql> alter table test.t1 set tiflash replica 1;
Expand All @@ -21,3 +38,4 @@ mysql> set @@tidb_isolation_read_engines='tiflash'; set @@tidb_enforce_mpp = 1;
+------------------------------------+
| 20221010101010.123 |
+------------------------------------+