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 | |