mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
fix decimal to int, int to decimal convertion [issue-3177]
This commit is contained in:
parent
002331be1b
commit
96ceca6c05
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <common/likely.h>
|
||||
#include <common/arithmeticOverflow.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Columns/ColumnDecimal.h>
|
||||
#include <DataTypes/IDataType.h>
|
||||
@ -12,9 +12,9 @@ namespace DB
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
extern const int CANNOT_CONVERT_TYPE;
|
||||
extern const int DECIMAL_OVERFLOW;
|
||||
}
|
||||
|
||||
class Context;
|
||||
@ -39,9 +39,7 @@ DataTypePtr createDecimal(UInt64 precision, UInt64 scale);
|
||||
/// Int128 38
|
||||
/// Operation between two decimals leads to Decimal(P, S), where
|
||||
/// P is one of (9, 18, 38); equals to the maximum precision for the biggest underlying type of operands.
|
||||
/// S is maximum scale of operands.
|
||||
///
|
||||
/// NOTE: It's possible to set scale as a template parameter then most of functions become static.
|
||||
/// S is maximum scale of operands. The allowed valuas are [0, precision]
|
||||
template <typename T>
|
||||
class DataTypeDecimal final : public DataTypeWithSimpleSerialization
|
||||
{
|
||||
@ -156,7 +154,7 @@ public:
|
||||
|
||||
private:
|
||||
const UInt32 precision;
|
||||
const UInt32 scale; /// TODO: should we support scales out of [0, precision]?
|
||||
const UInt32 scale;
|
||||
};
|
||||
|
||||
|
||||
@ -221,49 +219,86 @@ template <> constexpr bool IsDataTypeDecimal<DataTypeDecimal<Decimal32>> = true;
|
||||
template <> constexpr bool IsDataTypeDecimal<DataTypeDecimal<Decimal64>> = true;
|
||||
template <> constexpr bool IsDataTypeDecimal<DataTypeDecimal<Decimal128>> = true;
|
||||
|
||||
template <typename DataType> constexpr bool IsDataTypeDecimalOrNumber = IsDataTypeDecimal<DataType> || IsDataTypeNumber<DataType>;
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to)
|
||||
{
|
||||
ToDataType type_to(ToDataType::maxPrecision(), scale_to);
|
||||
FromDataType type_from(FromDataType::maxPrecision(), scale_from);
|
||||
using FromFieldType = typename FromDataType::FieldType;
|
||||
using ToFieldType = typename ToDataType::FieldType;
|
||||
using MaxFieldType = std::conditional_t<(sizeof(FromFieldType) > sizeof(ToFieldType)), FromFieldType, ToFieldType>;
|
||||
using MaxNativeType = typename MaxFieldType::NativeType;
|
||||
|
||||
if (scale_from > scale_to)
|
||||
MaxNativeType converted_value;
|
||||
if (scale_to > scale_from)
|
||||
{
|
||||
typename FromDataType::FieldType factor = type_from.scaleFactorFor(type_to, false);
|
||||
return value / factor;
|
||||
converted_value = DataTypeDecimal<MaxFieldType>::getScaleMultiplier(scale_to - scale_from);
|
||||
if (common::mulOverflow(static_cast<MaxNativeType>(value), converted_value, converted_value))
|
||||
throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
}
|
||||
else
|
||||
converted_value = value / DataTypeDecimal<MaxFieldType>::getScaleMultiplier(scale_from - scale_to);
|
||||
|
||||
if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType))
|
||||
{
|
||||
typename ToDataType::FieldType factor = type_to.scaleFactorFor(type_from, false);
|
||||
return value * factor;
|
||||
if (converted_value < std::numeric_limits<typename ToFieldType::NativeType>::min() ||
|
||||
converted_value > std::numeric_limits<typename ToFieldType::NativeType>::max())
|
||||
throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
}
|
||||
|
||||
return converted_value;
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeNumber<ToDataType>, typename ToDataType::FieldType>
|
||||
convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
||||
{
|
||||
using FromFieldType = typename FromDataType::FieldType;
|
||||
using ToFieldType = typename ToDataType::FieldType;
|
||||
|
||||
if constexpr (std::is_floating_point_v<ToFieldType>)
|
||||
return static_cast<ToFieldType>(value) / FromDataType::getScaleMultiplier(scale);
|
||||
else
|
||||
{
|
||||
FromFieldType converted_value = convertDecimals<FromDataType, FromDataType>(value, scale, 0);
|
||||
|
||||
if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType) || !std::numeric_limits<ToFieldType>::is_signed)
|
||||
{
|
||||
if constexpr (std::numeric_limits<ToFieldType>::is_signed)
|
||||
{
|
||||
if (converted_value < std::numeric_limits<ToFieldType>::min() ||
|
||||
converted_value > std::numeric_limits<ToFieldType>::max())
|
||||
throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
using CastIntType = std::conditional_t<std::is_same_v<ToFieldType, UInt64>, Int128, Int64>;
|
||||
|
||||
if (converted_value < 0 ||
|
||||
converted_value > static_cast<CastIntType>(std::numeric_limits<ToFieldType>::max()))
|
||||
throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
}
|
||||
}
|
||||
return converted_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && !IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale [[maybe_unused]])
|
||||
inline std::enable_if_t<IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
||||
{
|
||||
if (scale > FromDataType::maxPrecision())
|
||||
throw Exception("Wrong decimal scale", ErrorCodes::LOGICAL_ERROR);
|
||||
using FromFieldType = typename FromDataType::FieldType;
|
||||
|
||||
if constexpr (!std::is_same_v<ToDataType, DataTypeNumber<typename ToDataType::FieldType>>)
|
||||
throw Exception("Illegal convertion from decimal", ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
else
|
||||
return static_cast<typename ToDataType::FieldType>(value) / FromDataType::getScaleMultiplier(scale);
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<!IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale [[maybe_unused]])
|
||||
{
|
||||
if (scale > ToDataType::maxPrecision())
|
||||
throw Exception("Wrong decimal scale", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
if constexpr (!std::is_same_v<FromDataType, DataTypeNumber<typename FromDataType::FieldType>>)
|
||||
throw Exception("Illegal convertion to decimal", ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
else
|
||||
if constexpr (std::is_floating_point_v<FromFieldType>)
|
||||
return value * ToDataType::getScaleMultiplier(scale);
|
||||
else
|
||||
{
|
||||
if constexpr (std::is_same_v<FromFieldType, UInt64>)
|
||||
if (value > static_cast<UInt64>(std::numeric_limits<Int64>::max()))
|
||||
return convertDecimals<DataTypeDecimal<Decimal128>, ToDataType>(value, 0, scale);
|
||||
return convertDecimals<DataTypeDecimal<Decimal64>, ToDataType>(value, 0, scale);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,6 +107,13 @@ struct ConvertImpl
|
||||
using ColVecFrom = std::conditional_t<IsDecimalNumber<FromFieldType>, ColumnDecimal<FromFieldType>, ColumnVector<FromFieldType>>;
|
||||
using ColVecTo = std::conditional_t<IsDecimalNumber<ToFieldType>, ColumnDecimal<ToFieldType>, ColumnVector<ToFieldType>>;
|
||||
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> || IsDataTypeDecimal<ToDataType>)
|
||||
{
|
||||
if constexpr (!IsDataTypeDecimalOrNumber<FromDataType> || !IsDataTypeDecimalOrNumber<ToDataType>)
|
||||
throw Exception("Illegal column " + named_from.column->getName() + " of first argument of function " + Name::name,
|
||||
ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
|
||||
if (const ColVecFrom * col_from = checkAndGetColumn<ColVecFrom>(named_from.column.get()))
|
||||
{
|
||||
typename ColVecTo::MutablePtr col_to = nullptr;
|
||||
@ -125,12 +132,15 @@ struct ConvertImpl
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertDecimals<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), vec_to.getScale());
|
||||
else if constexpr (IsDataTypeDecimal<FromDataType>)
|
||||
vec_to[i] = convertFromDecimal<FromDataType, ToDataType>(vec_from[i], vec_from.getScale());
|
||||
else if constexpr (IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertToDecimal<FromDataType, ToDataType>(vec_from[i], vec_to.getScale());
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> || IsDataTypeDecimal<ToDataType>)
|
||||
{
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertDecimals<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), vec_to.getScale());
|
||||
else if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeNumber<ToDataType>)
|
||||
vec_to[i] = convertFromDecimal<FromDataType, ToDataType>(vec_from[i], vec_from.getScale());
|
||||
else if constexpr (IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertToDecimal<FromDataType, ToDataType>(vec_from[i], vec_to.getScale());
|
||||
}
|
||||
else
|
||||
vec_to[i] = static_cast<ToFieldType>(vec_from[i]);
|
||||
}
|
||||
|
@ -129,3 +129,39 @@
|
||||
12345678901234567890123456789012345678
|
||||
0.123456789 0.123456789123456789
|
||||
0.12345678901234567890123456789012345678
|
||||
1234567890.0000000000000000000000000000 1234567890.00000000000000000000000000000 1234567890.00000000000000000000000000000
|
||||
1234567890.00000000 1234567890.000000000 1234567890.000000000
|
||||
12345678.0 12345678.00 12345678.00
|
||||
9223372036854775807.000000 9223372036854775807 -9223372036854775807
|
||||
9223372036854775800 9223372036854775800 -9223372036854775800
|
||||
92233720368547758.00 92233720368547758 -92233720368547758
|
||||
2147483647.0000000000 2147483647 -2147483647
|
||||
2147483647.00 2147483647 -2147483647
|
||||
92233720368547757.99 92233720368547757 -92233720368547757
|
||||
2147483640.99 2147483640 -2147483640
|
||||
-0.90000000 0
|
||||
-0.90000000 0
|
||||
-0.90000000 0
|
||||
-0.8000 0
|
||||
-0.8000 0
|
||||
-0.8000 0
|
||||
-0.70 0
|
||||
-0.70 0
|
||||
-0.70 0
|
||||
-0.600000 0
|
||||
-0.600000 0
|
||||
-0.600000 0
|
||||
18446744073709551615 18446744073709551615
|
||||
18446744073709551615.00000000 18446744073709551615
|
||||
4294967295 4294967295
|
||||
4294967295.0000000000 4294967295
|
||||
4294967295 4294967295
|
||||
4294967295.0000 4294967295
|
||||
65535 65535
|
||||
65535.0000000000 65535
|
||||
65535 65535
|
||||
65535.0000 65535
|
||||
2147483647 2147483647
|
||||
-2147483647 -2147483647
|
||||
2147483647 2147483647
|
||||
9223372036854775807 9223372036854775807
|
||||
|
@ -150,4 +150,94 @@ SELECT CAST('0.123456789', 'Decimal(9,8)'); -- { serverError 69 }
|
||||
SELECT CAST('0.123456789123456789', 'Decimal(18,17)'); -- { serverError 69 }
|
||||
SELECT CAST('0.12345678901234567890123456789012345678', 'Decimal(38,37)'); -- { serverError 69 }
|
||||
|
||||
SELECT toDecimal128('1234567890', 28) AS x, toDecimal128(x, 29), toDecimal128(toDecimal128('1234567890', 28), 29);
|
||||
SELECT toDecimal128(toDecimal128('1234567890', 28), 30); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal64('1234567890', 8) AS x, toDecimal64(x, 9), toDecimal64(toDecimal64('1234567890', 8), 9);
|
||||
SELECT toDecimal64(toDecimal64('1234567890', 8), 10); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal32('12345678', 1) AS x, toDecimal32(x, 2), toDecimal32(toDecimal32('12345678', 1), 2);
|
||||
SELECT toDecimal32(toDecimal32('12345678', 1), 3); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal64(toDecimal64('92233720368547758.1', 1), 2); -- { serverError 407 }
|
||||
SELECT toDecimal64(toDecimal64('-92233720368547758.1', 1), 2); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal128('9223372036854775807', 6) AS x, toInt64(x), toInt64(-x);
|
||||
SELECT toDecimal128('9223372036854775809', 6) AS x, toInt64(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('9223372036854775809', 6) AS x, toInt64(-x); -- { serverError 407 }
|
||||
SELECT toDecimal64('922337203685477580', 0) * 10 AS x, toInt64(x), toInt64(-x);
|
||||
SELECT toDecimal64(toDecimal64('92233720368547758.0', 1), 2) AS x, toInt64(x), toInt64(-x);
|
||||
|
||||
SELECT toDecimal128('2147483647', 10) AS x, toInt32(x), toInt32(-x);
|
||||
SELECT toDecimal128('2147483649', 10) AS x, toInt32(x), toInt32(-x); -- { serverError 407 }
|
||||
SELECT toDecimal64('2147483647', 2) AS x, toInt32(x), toInt32(-x);
|
||||
SELECT toDecimal64('2147483649', 2) AS x, toInt32(x), toInt32(-x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal128('92233720368547757.99', 2) AS x, toInt64(x), toInt64(-x);
|
||||
SELECT toDecimal64('2147483640.99', 2) AS x, toInt32(x), toInt32(-x);
|
||||
|
||||
SELECT toDecimal128('-0.9', 8) AS x, toUInt64(x);
|
||||
SELECT toDecimal64('-0.9', 8) AS x, toUInt64(x);
|
||||
SELECT toDecimal32('-0.9', 8) AS x, toUInt64(x);
|
||||
|
||||
SELECT toDecimal128('-0.8', 4) AS x, toUInt32(x);
|
||||
SELECT toDecimal64('-0.8', 4) AS x, toUInt32(x);
|
||||
SELECT toDecimal32('-0.8', 4) AS x, toUInt32(x);
|
||||
|
||||
SELECT toDecimal128('-0.7', 2) AS x, toUInt16(x);
|
||||
SELECT toDecimal64('-0.7', 2) AS x, toUInt16(x);
|
||||
SELECT toDecimal32('-0.7', 2) AS x, toUInt16(x);
|
||||
|
||||
SELECT toDecimal128('-0.6', 6) AS x, toUInt8(x);
|
||||
SELECT toDecimal64('-0.6', 6) AS x, toUInt8(x);
|
||||
SELECT toDecimal32('-0.6', 6) AS x, toUInt8(x);
|
||||
|
||||
SELECT toDecimal128('-1', 7) AS x, toUInt64(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('-1', 7) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('-1', 7) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('-1', 7) AS x, toUInt8(x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal64('-1', 5) AS x, toUInt64(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('-1', 5) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('-1', 5) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('-1', 5) AS x, toUInt8(x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal32('-1', 3) AS x, toUInt64(x); -- { serverError 407 }
|
||||
SELECT toDecimal32('-1', 3) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal32('-1', 3) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal32('-1', 3) AS x, toUInt8(x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal128('18446744073709551615', 0) AS x, toUInt64(x);
|
||||
SELECT toDecimal128('18446744073709551616', 0) AS x, toUInt64(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('18446744073709551615', 8) AS x, toUInt64(x);
|
||||
SELECT toDecimal128('18446744073709551616', 8) AS x, toUInt64(x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal128('4294967295', 0) AS x, toUInt32(x);
|
||||
SELECT toDecimal128('4294967296', 0) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('4294967295', 10) AS x, toUInt32(x);
|
||||
SELECT toDecimal128('4294967296', 10) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('4294967295', 0) AS x, toUInt32(x);
|
||||
SELECT toDecimal64('4294967296', 0) AS x, toUInt32(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('4294967295', 4) AS x, toUInt32(x);
|
||||
SELECT toDecimal64('4294967296', 4) AS x, toUInt32(x); -- { serverError 407 }
|
||||
|
||||
SELECT toDecimal128('65535', 0) AS x, toUInt16(x);
|
||||
SELECT toDecimal128('65536', 0) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal128('65535', 10) AS x, toUInt16(x);
|
||||
SELECT toDecimal128('65536', 10) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('65535', 0) AS x, toUInt16(x);
|
||||
SELECT toDecimal64('65536', 0) AS x, toUInt16(x); -- { serverError 407 }
|
||||
SELECT toDecimal64('65535', 4) AS x, toUInt16(x);
|
||||
SELECT toDecimal64('65536', 4) AS x, toUInt16(x); -- { serverError 407 }
|
||||
|
||||
SELECT toInt64('2147483647') AS x, toDecimal32(x, 0);
|
||||
SELECT toInt64('-2147483647') AS x, toDecimal32(x, 0);
|
||||
SELECT toUInt64('2147483647') AS x, toDecimal32(x, 0);
|
||||
SELECT toInt64('2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 }
|
||||
SELECT toInt64('-2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 }
|
||||
SELECT toUInt64('2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 }
|
||||
|
||||
SELECT toUInt64('9223372036854775807') AS x, toDecimal64(x, 0);
|
||||
SELECT toUInt64('9223372036854775809') AS x, toDecimal64(x, 0); -- { serverError 407 }
|
||||
|
||||
DROP TABLE IF EXISTS test.decimal;
|
||||
|
Loading…
Reference in New Issue
Block a user