1#pragma once
2#include <cmath>
3
4#include <common/likely.h>
5#include <Columns/ColumnDecimal.h>
6#include <Core/DecimalFunctions.h>
7#include <DataTypes/IDataType.h>
8#include <DataTypes/DataTypesNumber.h>
9#include <DataTypes/DataTypeWithSimpleSerialization.h>
10
11#include <type_traits>
12
13
14namespace DB
15{
16
17namespace ErrorCodes
18{
19 extern const int ARGUMENT_OUT_OF_BOUND;
20 extern const int CANNOT_CONVERT_TYPE;
21 extern const int DECIMAL_OVERFLOW;
22}
23
24class Context;
25bool decimalCheckComparisonOverflow(const Context & context);
26bool decimalCheckArithmeticOverflow(const Context & context);
27
28inline UInt32 leastDecimalPrecisionFor(TypeIndex int_type)
29{
30 switch (int_type)
31 {
32 case TypeIndex::Int8: [[fallthrough]];
33 case TypeIndex::UInt8:
34 return 3;
35 case TypeIndex::Int16: [[fallthrough]];
36 case TypeIndex::UInt16:
37 return 5;
38 case TypeIndex::Int32: [[fallthrough]];
39 case TypeIndex::UInt32:
40 return 10;
41 case TypeIndex::Int64:
42 return 19;
43 case TypeIndex::UInt64:
44 return 20;
45 default:
46 break;
47 }
48 return 0;
49}
50
51/// Base class for decimals, like Decimal(P, S), where P is precision, S is scale.
52/// Maximum precisions for underlying types are:
53/// Int32 9
54/// Int64 18
55/// Int128 38
56/// Operation between two decimals leads to Decimal(P, S), where
57/// P is one of (9, 18, 38); equals to the maximum precision for the biggest underlying type of operands.
58/// S is maximum scale of operands. The allowed valuas are [0, precision]
59template <typename T>
60class DataTypeDecimalBase : public DataTypeWithSimpleSerialization
61{
62 static_assert(IsDecimalNumber<T>);
63
64public:
65 using FieldType = T;
66 using ColumnType = ColumnDecimal<T>;
67
68 static constexpr bool is_parametric = true;
69
70 static constexpr size_t maxPrecision() { return DecimalUtils::maxPrecision<T>(); }
71
72 DataTypeDecimalBase(UInt32 precision_, UInt32 scale_)
73 : precision(precision_),
74 scale(scale_)
75 {
76 if (unlikely(precision < 1 || precision > maxPrecision()))
77 throw Exception("Precision " + std::to_string(precision) + " is out of bounds", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
78 if (unlikely(scale < 0 || static_cast<UInt32>(scale) > maxPrecision()))
79 throw Exception("Scale " + std::to_string(scale) + " is out of bounds", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
80 }
81
82 TypeIndex getTypeId() const override { return TypeId<T>::value; }
83
84 Field getDefault() const override;
85 MutableColumnPtr createColumn() const override;
86
87 bool isParametric() const override { return true; }
88 bool haveSubtypes() const override { return false; }
89 bool shouldAlignRightInPrettyFormats() const override { return true; }
90 bool textCanContainOnlyValidUTF8() const override { return true; }
91 bool isComparable() const override { return true; }
92 bool isValueRepresentedByNumber() const override { return true; }
93 bool isValueUnambiguouslyRepresentedInContiguousMemoryRegion() const override { return true; }
94 bool haveMaximumSizeOfValue() const override { return true; }
95 size_t getSizeOfValueInMemory() const override { return sizeof(T); }
96
97 bool isSummable() const override { return true; }
98 bool canBeUsedInBooleanContext() const override { return true; }
99 bool canBeInsideNullable() const override { return true; }
100
101 void serializeBinary(const Field & field, WriteBuffer & ostr) const override;
102 void serializeBinary(const IColumn & column, size_t row_num, WriteBuffer & ostr) const override;
103 void serializeBinaryBulk(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const override;
104
105 void deserializeBinary(Field & field, ReadBuffer & istr) const override;
106 void deserializeBinary(IColumn & column, ReadBuffer & istr) const override;
107 void deserializeBinaryBulk(IColumn & column, ReadBuffer & istr, size_t limit, double avg_value_size_hint) const override;
108
109 /// Decimal specific
110
111 UInt32 getPrecision() const { return precision; }
112 UInt32 getScale() const { return scale; }
113 T getScaleMultiplier() const { return getScaleMultiplier(scale); }
114
115 T wholePart(T x) const
116 {
117 return DecimalUtils::getWholePart(x, scale);
118 }
119
120 T fractionalPart(T x) const
121 {
122 return DecimalUtils::getFractionalPart(x, scale);
123 }
124
125 T maxWholeValue() const { return getScaleMultiplier(maxPrecision() - scale) - T(1); }
126
127 bool canStoreWhole(T x) const
128 {
129 T max = maxWholeValue();
130 if (x > max || x < -max)
131 return false;
132 return true;
133 }
134
135 /// @returns multiplier for U to become T with correct scale
136 template <typename U>
137 T scaleFactorFor(const DataTypeDecimalBase<U> & x, bool) const
138 {
139 if (getScale() < x.getScale())
140 throw Exception("Decimal result's scale is less than argument's one", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
141 UInt32 scale_delta = getScale() - x.getScale(); /// scale_delta >= 0
142 return getScaleMultiplier(scale_delta);
143 }
144
145 template <typename U>
146 T scaleFactorFor(const DataTypeNumber<U> & , bool is_multiply_or_divisor) const
147 {
148 if (is_multiply_or_divisor)
149 return 1;
150 return getScaleMultiplier();
151 }
152
153 static T getScaleMultiplier(UInt32 scale);
154
155protected:
156 const UInt32 precision;
157 const UInt32 scale;
158};
159
160
161template <typename T, typename U, template <typename> typename DecimalType>
162typename std::enable_if_t<(sizeof(T) >= sizeof(U)), DecimalType<T>>
163decimalResultType(const DecimalType<T> & tx, const DecimalType<U> & ty, bool is_multiply, bool is_divide)
164{
165 UInt32 scale = (tx.getScale() > ty.getScale() ? tx.getScale() : ty.getScale());
166 if (is_multiply)
167 scale = tx.getScale() + ty.getScale();
168 else if (is_divide)
169 scale = tx.getScale();
170 return DecimalType<T>(DecimalUtils::maxPrecision<T>(), scale);
171}
172
173template <typename T, typename U, template <typename> typename DecimalType>
174typename std::enable_if_t<(sizeof(T) < sizeof(U)), const DecimalType<U>>
175decimalResultType(const DecimalType<T> & tx, const DecimalType<U> & ty, bool is_multiply, bool is_divide)
176{
177 UInt32 scale = (tx.getScale() > ty.getScale() ? tx.getScale() : ty.getScale());
178 if (is_multiply)
179 scale = tx.getScale() * ty.getScale();
180 else if (is_divide)
181 scale = tx.getScale();
182 return DecimalType<U>(DecimalUtils::maxPrecision<U>(), scale);
183}
184
185template <typename T, typename U, template <typename> typename DecimalType>
186const DecimalType<T> decimalResultType(const DecimalType<T> & tx, const DataTypeNumber<U> &, bool, bool)
187{
188 return DecimalType<T>(DecimalUtils::maxPrecision<T>(), tx.getScale());
189}
190
191template <typename T, typename U, template <typename> typename DecimalType>
192const DecimalType<U> decimalResultType(const DataTypeNumber<T> &, const DecimalType<U> & ty, bool, bool)
193{
194 return DecimalType<U>(DecimalUtils::maxPrecision<U>(), ty.getScale());
195}
196
197template <template <typename> typename DecimalType>
198DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value, const String & type_name = "Decimal", bool only_scale = false)
199{
200 if (precision_value < DecimalUtils::minPrecision() || precision_value > DecimalUtils::maxPrecision<Decimal128>())
201 throw Exception("Wrong precision", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
202
203 if (static_cast<UInt64>(scale_value) > precision_value)
204 throw Exception("Negative scales and scales larger than precision are not supported", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
205
206 if (precision_value <= DecimalUtils::maxPrecision<Decimal32>())
207 return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value, type_name, only_scale);
208 else if (precision_value <= DecimalUtils::maxPrecision<Decimal64>())
209 return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value, type_name, only_scale);
210 return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value, type_name, only_scale);
211}
212
213}
214