| 1 | #pragma once |
| 2 | |
| 3 | #include <common/arithmeticOverflow.h> |
| 4 | #include <Common/typeid_cast.h> |
| 5 | #include <DataTypes/IDataType.h> |
| 6 | #include <DataTypes/DataTypeDecimalBase.h> |
| 7 | |
| 8 | |
| 9 | namespace DB |
| 10 | { |
| 11 | |
| 12 | namespace ErrorCodes |
| 13 | { |
| 14 | extern const int ARGUMENT_OUT_OF_BOUND; |
| 15 | extern const int CANNOT_CONVERT_TYPE; |
| 16 | extern const int DECIMAL_OVERFLOW; |
| 17 | } |
| 18 | |
| 19 | /// Implements Decimal(P, S), where P is precision, S is scale. |
| 20 | /// Maximum precisions for underlying types are: |
| 21 | /// Int32 9 |
| 22 | /// Int64 18 |
| 23 | /// Int128 38 |
| 24 | /// Operation between two decimals leads to Decimal(P, S), where |
| 25 | /// P is one of (9, 18, 38); equals to the maximum precision for the biggest underlying type of operands. |
| 26 | /// S is maximum scale of operands. The allowed valuas are [0, precision] |
| 27 | template <typename T> |
| 28 | class DataTypeDecimal final : public DataTypeDecimalBase<T> |
| 29 | { |
| 30 | using Base = DataTypeDecimalBase<T>; |
| 31 | static_assert(IsDecimalNumber<T>); |
| 32 | |
| 33 | public: |
| 34 | using typename Base::FieldType; |
| 35 | using typename Base::ColumnType; |
| 36 | using Base::Base; |
| 37 | |
| 38 | DataTypeDecimal(UInt32 precision_, UInt32 scale_, const String & type_name_ = "Decimal" , bool only_scale_ = false); |
| 39 | |
| 40 | static constexpr auto family_name = "Decimal" ; |
| 41 | |
| 42 | const char * getFamilyName() const override { return family_name; } |
| 43 | std::string doGetName() const override; |
| 44 | TypeIndex getTypeId() const override { return TypeId<T>::value; } |
| 45 | bool canBePromoted() const override { return true; } |
| 46 | DataTypePtr promoteNumericType() const override; |
| 47 | |
| 48 | void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override; |
| 49 | void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; |
| 50 | void deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; |
| 51 | |
| 52 | void serializeProtobuf(const IColumn & column, size_t row_num, ProtobufWriter & protobuf, size_t & value_index) const override; |
| 53 | void deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const override; |
| 54 | |
| 55 | bool equals(const IDataType & rhs) const override; |
| 56 | |
| 57 | T parseFromString(const String & str) const; |
| 58 | void readText(T & x, ReadBuffer & istr, bool csv = false) const { readText(x, istr, this->precision, this->scale, csv); } |
| 59 | |
| 60 | static void readText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_, bool csv = false); |
| 61 | static bool tryReadText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_); |
| 62 | |
| 63 | private: |
| 64 | /// The name of data type how the user specified it. A single data type may be referenced by various synonims. |
| 65 | const String type_name; |
| 66 | /// If the user specified it only with scale parameter but without precision. |
| 67 | bool only_scale = false; |
| 68 | }; |
| 69 | |
| 70 | template <typename T> |
| 71 | inline const DataTypeDecimal<T> * checkDecimal(const IDataType & data_type) |
| 72 | { |
| 73 | return typeid_cast<const DataTypeDecimal<T> *>(&data_type); |
| 74 | } |
| 75 | |
| 76 | inline UInt32 getDecimalScale(const IDataType & data_type, UInt32 default_value = std::numeric_limits<UInt32>::max()) |
| 77 | { |
| 78 | if (auto * decimal_type = checkDecimal<Decimal32>(data_type)) |
| 79 | return decimal_type->getScale(); |
| 80 | if (auto * decimal_type = checkDecimal<Decimal64>(data_type)) |
| 81 | return decimal_type->getScale(); |
| 82 | if (auto * decimal_type = checkDecimal<Decimal128>(data_type)) |
| 83 | return decimal_type->getScale(); |
| 84 | return default_value; |
| 85 | } |
| 86 | |
| 87 | template <typename T> |
| 88 | inline UInt32 getDecimalScale(const DataTypeDecimal<T> & data_type) |
| 89 | { |
| 90 | return data_type.getScale(); |
| 91 | } |
| 92 | |
| 93 | template <typename FromDataType, typename ToDataType> |
| 94 | inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType> |
| 95 | convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to) |
| 96 | { |
| 97 | using FromFieldType = typename FromDataType::FieldType; |
| 98 | using ToFieldType = typename ToDataType::FieldType; |
| 99 | using MaxFieldType = std::conditional_t<(sizeof(FromFieldType) > sizeof(ToFieldType)), FromFieldType, ToFieldType>; |
| 100 | using MaxNativeType = typename MaxFieldType::NativeType; |
| 101 | |
| 102 | MaxNativeType converted_value; |
| 103 | if (scale_to > scale_from) |
| 104 | { |
| 105 | converted_value = MaxFieldType::getScaleMultiplier(scale_to - scale_from); |
| 106 | if (common::mulOverflow(static_cast<MaxNativeType>(value), converted_value, converted_value)) |
| 107 | throw Exception(std::string(ToDataType::family_name) + " convert overflow" , |
| 108 | ErrorCodes::DECIMAL_OVERFLOW); |
| 109 | } |
| 110 | else |
| 111 | converted_value = value / MaxFieldType::getScaleMultiplier(scale_from - scale_to); |
| 112 | |
| 113 | if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType)) |
| 114 | { |
| 115 | if (converted_value < std::numeric_limits<typename ToFieldType::NativeType>::min() || |
| 116 | converted_value > std::numeric_limits<typename ToFieldType::NativeType>::max()) |
| 117 | throw Exception(std::string(ToDataType::family_name) + " convert overflow" , |
| 118 | ErrorCodes::DECIMAL_OVERFLOW); |
| 119 | } |
| 120 | |
| 121 | return converted_value; |
| 122 | } |
| 123 | |
| 124 | template <typename FromDataType, typename ToDataType> |
| 125 | inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, typename ToDataType::FieldType> |
| 126 | convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale) |
| 127 | { |
| 128 | using FromFieldType = typename FromDataType::FieldType; |
| 129 | using ToFieldType = typename ToDataType::FieldType; |
| 130 | |
| 131 | if constexpr (std::is_floating_point_v<ToFieldType>) |
| 132 | return static_cast<ToFieldType>(value) / FromFieldType::getScaleMultiplier(scale); |
| 133 | else |
| 134 | { |
| 135 | FromFieldType converted_value = convertDecimals<FromDataType, FromDataType>(value, scale, 0); |
| 136 | |
| 137 | if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType) || !std::numeric_limits<ToFieldType>::is_signed) |
| 138 | { |
| 139 | if constexpr (std::numeric_limits<ToFieldType>::is_signed) |
| 140 | { |
| 141 | if (converted_value < std::numeric_limits<ToFieldType>::min() || |
| 142 | converted_value > std::numeric_limits<ToFieldType>::max()) |
| 143 | throw Exception(std::string(FromDataType::family_name) + " convert overflow" , |
| 144 | ErrorCodes::DECIMAL_OVERFLOW); |
| 145 | } |
| 146 | else |
| 147 | { |
| 148 | using CastIntType = std::conditional_t<std::is_same_v<ToFieldType, UInt64>, Int128, Int64>; |
| 149 | |
| 150 | if (converted_value < 0 || |
| 151 | converted_value > static_cast<CastIntType>(std::numeric_limits<ToFieldType>::max())) |
| 152 | throw Exception(std::string(FromDataType::family_name) + " convert overflow" , |
| 153 | ErrorCodes::DECIMAL_OVERFLOW); |
| 154 | } |
| 155 | } |
| 156 | return converted_value; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | template <typename FromDataType, typename ToDataType> |
| 161 | inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType> |
| 162 | convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) |
| 163 | { |
| 164 | using FromFieldType = typename FromDataType::FieldType; |
| 165 | using ToFieldType = typename ToDataType::FieldType; |
| 166 | using ToNativeType = typename ToFieldType::NativeType; |
| 167 | |
| 168 | if constexpr (std::is_floating_point_v<FromFieldType>) |
| 169 | { |
| 170 | if (!std::isfinite(value)) |
| 171 | throw Exception(std::string(ToDataType::family_name) + " convert overflow. Cannot convert infinity or NaN to decimal" , |
| 172 | ErrorCodes::DECIMAL_OVERFLOW); |
| 173 | |
| 174 | auto out = value * ToFieldType::getScaleMultiplier(scale); |
| 175 | if constexpr (std::is_same_v<ToNativeType, Int128>) |
| 176 | { |
| 177 | static constexpr __int128 min_int128 = __int128(0x8000000000000000ll) << 64; |
| 178 | static constexpr __int128 max_int128 = (__int128(0x7fffffffffffffffll) << 64) + 0xffffffffffffffffll; |
| 179 | if (out <= static_cast<ToNativeType>(min_int128) || out >= static_cast<ToNativeType>(max_int128)) |
| 180 | throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range" , |
| 181 | ErrorCodes::DECIMAL_OVERFLOW); |
| 182 | } |
| 183 | else |
| 184 | { |
| 185 | if (out <= std::numeric_limits<ToNativeType>::min() || out >= std::numeric_limits<ToNativeType>::max()) |
| 186 | throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range" , |
| 187 | ErrorCodes::DECIMAL_OVERFLOW); |
| 188 | } |
| 189 | return out; |
| 190 | } |
| 191 | else |
| 192 | { |
| 193 | if constexpr (std::is_same_v<FromFieldType, UInt64>) |
| 194 | if (value > static_cast<UInt64>(std::numeric_limits<Int64>::max())) |
| 195 | return convertDecimals<DataTypeDecimal<Decimal128>, ToDataType>(value, 0, scale); |
| 196 | |
| 197 | return convertDecimals<DataTypeDecimal<Decimal64>, ToDataType>(value, 0, scale); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | } |
| 202 | |