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
9namespace DB
10{
11
12namespace 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]
27template <typename T>
28class DataTypeDecimal final : public DataTypeDecimalBase<T>
29{
30 using Base = DataTypeDecimalBase<T>;
31 static_assert(IsDecimalNumber<T>);
32
33public:
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
63private:
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
70template <typename T>
71inline const DataTypeDecimal<T> * checkDecimal(const IDataType & data_type)
72{
73 return typeid_cast<const DataTypeDecimal<T> *>(&data_type);
74}
75
76inline 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
87template <typename T>
88inline UInt32 getDecimalScale(const DataTypeDecimal<T> & data_type)
89{
90 return data_type.getScale();
91}
92
93template <typename FromDataType, typename ToDataType>
94inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
95convertDecimals(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
124template <typename FromDataType, typename ToDataType>
125inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, typename ToDataType::FieldType>
126convertFromDecimal(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
160template <typename FromDataType, typename ToDataType>
161inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
162convertToDecimal(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