forked from ClickHouse/ClickHouse
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDataTypeDecimalBase.h
More file actions
198 lines (164 loc) · 7.22 KB
/
Copy pathDataTypeDecimalBase.h
File metadata and controls
198 lines (164 loc) · 7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#pragma once
#include <type_traits>
#include <Columns/ColumnDecimal.h>
#include <Core/TypeId.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/IDataType.h>
#include <Interpreters/Context_fwd.h>
#include <Common/SipHash.h>
namespace DB
{
namespace ErrorCodes
{
extern const int ARGUMENT_OUT_OF_BOUND;
}
bool decimalCheckComparisonOverflow(ContextPtr context);
bool decimalCheckArithmeticOverflow(ContextPtr context);
inline UInt32 leastDecimalPrecisionFor(TypeIndex int_type)
{
switch (int_type)
{
case TypeIndex::Int8: [[fallthrough]];
case TypeIndex::UInt8:
return 3;
case TypeIndex::Int16: [[fallthrough]];
case TypeIndex::UInt16:
return 5;
case TypeIndex::Int32: [[fallthrough]];
case TypeIndex::UInt32:
return 10;
case TypeIndex::Int64:
return 19;
case TypeIndex::UInt64:
return 20;
default:
break;
}
return 0;
}
/// Base class for decimals, like Decimal(P, S), where P is precision, S is scale.
/// Maximum precisions for underlying types are:
/// Int32 9
/// Int64 18
/// Int128 38
/// Int256 76
/// Operation between two decimals leads to Decimal(P, S), where
/// P is one of (9, 18, 38, 76); equals to the maximum precision for the biggest underlying type of operands.
/// S is maximum scale of operands. The allowed valuas are [0, precision]
template <is_decimal T>
class DataTypeDecimalBase : public IDataType
{
public:
using FieldType = T;
using ColumnType = ColumnDecimal<T>;
static constexpr auto type_id = TypeToTypeIndex<T>;
static constexpr bool is_parametric = true;
static constexpr size_t maxPrecision();
DataTypeDecimalBase(UInt32 precision_, UInt32 scale_)
: precision(precision_),
scale(scale_)
{
if (unlikely(precision < 1 || precision > maxPrecision()))
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND,
"Precision {} is out of bounds (precision range: [1, {}])",
std::to_string(precision), maxPrecision());
if (unlikely(scale > maxPrecision()))
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds (max scale: {})",
std::to_string(scale), maxPrecision());
}
TypeIndex getTypeId() const override { return TypeToTypeIndex<T>; }
Field getDefault() const final;
MutableColumnPtr createColumn() const final;
bool isParametric() const final { return true; }
bool haveSubtypes() const final { return false; }
bool shouldAlignRightInPrettyFormats() const final { return true; }
bool textCanContainOnlyValidUTF8() const final { return true; }
bool isComparable() const final { return true; }
bool isValueRepresentedByNumber() const final { return true; }
bool isValueUnambiguouslyRepresentedInContiguousMemoryRegion() const final { return true; }
bool haveMaximumSizeOfValue() const final { return true; }
size_t getSizeOfValueInMemory() const final { return sizeof(T); }
bool isSummable() const override { return true; }
bool canBeUsedInBooleanContext() const final { return false; }
bool canBeInsideNullable() const final { return true; }
/// Decimal specific
UInt32 getPrecision() const { return precision; }
UInt32 getScale() const { return scale; }
T getScaleMultiplier() const { return getScaleMultiplier(scale); }
T wholePart(T x) const;
T fractionalPart(T x) const;
T maxWholeValue() const { return getScaleMultiplier(precision - scale) - T(1); }
template <typename U>
bool canStoreWhole(U x) const
{
static_assert(is_signed_v<typename T::NativeType>);
T max = maxWholeValue();
if constexpr (std::is_floating_point_v<U>)
return static_cast<U>(-max.value) <= x && x <= static_cast<U>(max.value);
else if constexpr (is_signed_v<U>)
return -max.value <= x && x <= max.value;
else
return x <= static_cast<make_unsigned_t<typename T::NativeType>>(max.value);
}
/// @returns multiplier for U to become T with correct scale
template <typename U>
T scaleFactorFor(const DataTypeDecimalBase<U> & x, bool) const
{
if (getScale() < x.getScale())
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Decimal result's scale is less than argument's one");
UInt32 scale_delta = getScale() - x.getScale(); /// scale_delta >= 0
return getScaleMultiplier(scale_delta);
}
template <typename U>
T scaleFactorFor(const DataTypeNumber<U> & , bool is_multiply_or_divisor) const
{
if (is_multiply_or_divisor)
return T(1);
return getScaleMultiplier();
}
static T getScaleMultiplier(UInt32 scale);
void updateHashImpl(SipHash & hash) const override
{
hash.update(precision);
hash.update(scale);
}
protected:
const UInt32 precision;
const UInt32 scale;
};
template <typename T>
inline const DataTypeDecimalBase<T> * checkDecimalBase(const IDataType & data_type)
{
if (isColumnedAsDecimalT<T>(data_type))
return static_cast<const DataTypeDecimalBase<T> *>(&data_type);
return nullptr;
}
template <> constexpr size_t DataTypeDecimalBase<Decimal32>::maxPrecision() { return 9; };
template <> constexpr size_t DataTypeDecimalBase<Decimal64>::maxPrecision() { return 18; };
template <> constexpr size_t DataTypeDecimalBase<DateTime64>::maxPrecision() { return 18; };
template <> constexpr size_t DataTypeDecimalBase<Time64>::maxPrecision() { return 18; };
template <> constexpr size_t DataTypeDecimalBase<Decimal128>::maxPrecision() { return 38; };
template <> constexpr size_t DataTypeDecimalBase<Decimal256>::maxPrecision() { return 76; };
extern template class DataTypeDecimalBase<Decimal32>;
extern template class DataTypeDecimalBase<Decimal64>;
extern template class DataTypeDecimalBase<DateTime64>;
extern template class DataTypeDecimalBase<Time64>;
extern template class DataTypeDecimalBase<Decimal128>;
extern template class DataTypeDecimalBase<Decimal256>;
template <template <typename> typename DecimalType>
inline DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value)
{
if (precision_value < 1 || precision_value > DataTypeDecimalBase<Decimal256>::maxPrecision())
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Wrong precision: it must be between {} and {}, got {}",
1, DataTypeDecimalBase<Decimal256>::maxPrecision(), precision_value);
if (scale_value > precision_value)
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Negative scales and scales larger than precision are not supported");
if (precision_value <= DataTypeDecimalBase<Decimal32>::maxPrecision())
return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value);
if (precision_value <= DataTypeDecimalBase<Decimal64>::maxPrecision())
return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value);
if (precision_value <= DataTypeDecimalBase<Decimal128>::maxPrecision())
return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value);
return std::make_shared<DecimalType<Decimal256>>(precision_value, scale_value);
}
}