ClickHouse/dbms/DataTypes/DataTypesDecimal.h

192 lines
8.2 KiB
C++
Raw Normal View History

2018-07-20 19:05:07 +00:00
#pragma once
2019-05-15 12:28:44 +00:00
#include <common/arithmeticOverflow.h>
#include <Common/typeid_cast.h>
2018-08-01 19:50:19 +00:00
#include <DataTypes/IDataType.h>
#include <DataTypes/DataTypeDecimalBase.h>
2018-07-20 19:05:07 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int DECIMAL_OVERFLOW;
}
2018-07-20 19:05:07 +00:00
/// Implements Decimal(P, S), where P is precision, S is scale.
/// Maximum precisions for underlying types are:
/// Int32 9
/// Int64 18
/// 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. The allowed valuas are [0, precision]
2018-07-20 19:05:07 +00:00
template <typename T>
class DataTypeDecimal final : public DataTypeDecimalBase<T>
2018-07-20 19:05:07 +00:00
{
using Base = DataTypeDecimalBase<T>;
static_assert(IsDecimalNumber<T>);
2018-07-20 19:05:07 +00:00
public:
using typename Base::FieldType;
using typename Base::ColumnType;
using Base::Base;
2018-07-20 19:05:07 +00:00
static constexpr auto family_name = "Decimal";
2019-10-30 10:10:51 +00:00
const char * getFamilyName() const override { return family_name; }
std::string doGetName() const override;
TypeIndex getTypeId() const override { return TypeId<T>::value; }
bool canBePromoted() const override { return true; }
DataTypePtr promoteNumericType() const override;
2018-07-20 19:05:07 +00:00
void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override;
void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override;
2019-05-15 13:51:17 +00:00
void deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override;
2018-07-20 19:05:07 +00:00
void serializeProtobuf(const IColumn & column, size_t row_num, ProtobufWriter & protobuf, size_t & value_index) const override;
void deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const override;
2018-07-20 19:05:07 +00:00
bool equals(const IDataType & rhs) const override;
T parseFromString(const String & str) const;
void readText(T & x, ReadBuffer & istr, bool csv = false) const { readText(x, istr, this->precision, this->scale, csv); }
static void readText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_, bool csv = false);
static bool tryReadText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_);
2018-07-20 19:05:07 +00:00
};
2018-07-25 19:38:21 +00:00
template <typename T>
inline const DataTypeDecimal<T> * checkDecimal(const IDataType & data_type)
{
return typeid_cast<const DataTypeDecimal<T> *>(&data_type);
}
2018-07-20 19:05:07 +00:00
inline UInt32 getDecimalScale(const IDataType & data_type, UInt32 default_value = std::numeric_limits<UInt32>::max())
2018-08-21 18:25:38 +00:00
{
2018-08-21 18:55:36 +00:00
if (auto * decimal_type = checkDecimal<Decimal32>(data_type))
2018-08-21 18:25:38 +00:00
return decimal_type->getScale();
2018-08-21 18:55:36 +00:00
if (auto * decimal_type = checkDecimal<Decimal64>(data_type))
2018-08-21 18:25:38 +00:00
return decimal_type->getScale();
2018-08-21 18:55:36 +00:00
if (auto * decimal_type = checkDecimal<Decimal128>(data_type))
2018-08-21 18:25:38 +00:00
return decimal_type->getScale();
return default_value;
2018-08-21 18:25:38 +00:00
}
template <typename T>
inline UInt32 getDecimalScale(const DataTypeDecimal<T> & data_type)
{
return data_type.getScale();
}
2018-07-20 19:05:07 +00:00
2018-08-21 18:25:38 +00:00
template <typename FromDataType, typename ToDataType>
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
2018-08-21 18:25:38 +00:00
convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to)
{
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;
2018-08-21 18:25:38 +00:00
MaxNativeType converted_value;
if (scale_to > scale_from)
2018-08-21 18:25:38 +00:00
{
converted_value = MaxFieldType::getScaleMultiplier(scale_to - scale_from);
if (common::mulOverflow(static_cast<MaxNativeType>(value), converted_value, converted_value))
throw Exception(std::string(ToDataType::family_name) + " convert overflow",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
2018-08-21 18:25:38 +00:00
}
else
converted_value = value / MaxFieldType::getScaleMultiplier(scale_from - scale_to);
if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType))
2018-08-21 18:25:38 +00:00
{
if (converted_value < std::numeric_limits<typename ToFieldType::NativeType>::min() ||
converted_value > std::numeric_limits<typename ToFieldType::NativeType>::max())
throw Exception(std::string(ToDataType::family_name) + " convert overflow",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
2018-08-21 18:25:38 +00:00
}
return converted_value;
2018-08-21 18:25:38 +00:00
}
template <typename FromDataType, typename ToDataType>
2019-10-30 10:10:51 +00:00
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, typename ToDataType::FieldType>
convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
2018-08-21 18:25:38 +00:00
{
using FromFieldType = typename FromDataType::FieldType;
using ToFieldType = typename ToDataType::FieldType;
2018-08-21 18:25:38 +00:00
if constexpr (std::is_floating_point_v<ToFieldType>)
return static_cast<ToFieldType>(value) / FromFieldType::getScaleMultiplier(scale);
2018-08-21 18:25:38 +00:00
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(std::string(FromDataType::family_name) + " convert overflow",
2019-10-30 10:10:51 +00:00
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(std::string(FromDataType::family_name) + " convert overflow",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
}
}
return converted_value;
}
2018-08-21 18:25:38 +00:00
}
template <typename FromDataType, typename ToDataType>
2019-10-30 10:10:51 +00:00
inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
2018-08-21 18:25:38 +00:00
{
using FromFieldType = typename FromDataType::FieldType;
using ToFieldType = typename ToDataType::FieldType;
using ToNativeType = typename ToFieldType::NativeType;
2018-08-21 18:25:38 +00:00
if constexpr (std::is_floating_point_v<FromFieldType>)
2019-05-15 12:28:44 +00:00
{
2019-06-13 20:28:14 +00:00
if (!std::isfinite(value))
throw Exception(std::string(ToDataType::family_name) + " convert overflow. Cannot convert infinity or NaN to decimal",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
2019-06-13 20:28:14 +00:00
auto out = value * ToFieldType::getScaleMultiplier(scale);
2019-06-13 08:40:30 +00:00
if constexpr (std::is_same_v<ToNativeType, Int128>)
{
2019-06-17 14:49:21 +00:00
static constexpr __int128 min_int128 = __int128(0x8000000000000000ll) << 64;
static constexpr __int128 max_int128 = (__int128(0x7fffffffffffffffll) << 64) + 0xffffffffffffffffll;
2019-06-18 05:25:16 +00:00
if (out <= static_cast<ToNativeType>(min_int128) || out >= static_cast<ToNativeType>(max_int128))
throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
2019-06-13 08:40:30 +00:00
}
else
{
2019-06-18 05:25:16 +00:00
if (out <= std::numeric_limits<ToNativeType>::min() || out >= std::numeric_limits<ToNativeType>::max())
throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range",
2019-10-30 10:10:51 +00:00
ErrorCodes::DECIMAL_OVERFLOW);
2019-06-13 08:40:30 +00:00
}
2019-06-17 14:49:21 +00:00
return out;
2019-06-13 14:48:13 +00:00
}
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);
}
2018-08-21 18:25:38 +00:00
}
2018-07-20 19:05:07 +00:00
}