#pragma once #include #include #include #include namespace DB { namespace ErrorCodes { extern const int DECIMAL_OVERFLOW; } /// Implements Decimal(P, S), where P is precision, S is scale. /// Maximum precisions for underlying types are: /// Int32 9 /// Int64 18 /// Int128 38 /// Operation between two decimals leads to Decimal(P, S), where /// P is one of (9, 18, 38); 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 class DataTypeDecimal final : public DataTypeDecimalBase { using Base = DataTypeDecimalBase; static_assert(IsDecimalNumber); public: using typename Base::FieldType; using typename Base::ColumnType; using Base::Base; static constexpr auto family_name = "Decimal"; const char * getFamilyName() const override { return family_name; } std::string doGetName() const override; TypeIndex getTypeId() const override { return TypeId::value; } bool canBePromoted() const override { return true; } DataTypePtr promoteNumericType() const override; void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override; void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; void deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; void serializeProtobuf(const IColumn & column, size_t row_num, ProtobufWriter & protobuf, size_t & value_index) const override; void deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const override; bool equals(const IDataType & rhs) const override; T parseFromString(const String & str) const; void readText(T & x, ReadBuffer & istr, bool csv = false) const { readText(x, istr, this->precision, this->scale, csv); } static void readText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_, bool csv = false); static bool tryReadText(T & x, ReadBuffer & istr, UInt32 precision_, UInt32 scale_); }; template inline const DataTypeDecimal * checkDecimal(const IDataType & data_type) { return typeid_cast *>(&data_type); } inline UInt32 getDecimalScale(const IDataType & data_type, UInt32 default_value = std::numeric_limits::max()) { if (auto * decimal_type = checkDecimal(data_type)) return decimal_type->getScale(); if (auto * decimal_type = checkDecimal(data_type)) return decimal_type->getScale(); if (auto * decimal_type = checkDecimal(data_type)) return decimal_type->getScale(); return default_value; } template inline UInt32 getDecimalScale(const DataTypeDecimal & data_type) { return data_type.getScale(); } template inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; using MaxFieldType = std::conditional_t<(sizeof(FromFieldType) > sizeof(ToFieldType)), FromFieldType, ToFieldType>; using MaxNativeType = typename MaxFieldType::NativeType; MaxNativeType converted_value; if (scale_to > scale_from) { converted_value = MaxFieldType::getScaleMultiplier(scale_to - scale_from); if (common::mulOverflow(static_cast(value), converted_value, converted_value)) throw Exception(std::string(ToDataType::family_name) + " convert overflow", ErrorCodes::DECIMAL_OVERFLOW); } else converted_value = value / MaxFieldType::getScaleMultiplier(scale_from - scale_to); if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType)) { if (converted_value < std::numeric_limits::min() || converted_value > std::numeric_limits::max()) throw Exception(std::string(ToDataType::family_name) + " convert overflow", ErrorCodes::DECIMAL_OVERFLOW); } return converted_value; } template inline std::enable_if_t && IsNumber, typename ToDataType::FieldType> convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; if constexpr (std::is_floating_point_v) return static_cast(value) / FromFieldType::getScaleMultiplier(scale); else { FromFieldType converted_value = convertDecimals(value, scale, 0); if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType) || !std::numeric_limits::is_signed) { if constexpr (std::numeric_limits::is_signed) { if (converted_value < std::numeric_limits::min() || converted_value > std::numeric_limits::max()) throw Exception(std::string(FromDataType::family_name) + " convert overflow", ErrorCodes::DECIMAL_OVERFLOW); } else { using CastIntType = std::conditional_t, Int128, Int64>; if (converted_value < 0 || converted_value > static_cast(std::numeric_limits::max())) throw Exception(std::string(FromDataType::family_name) + " convert overflow", ErrorCodes::DECIMAL_OVERFLOW); } } return converted_value; } } template inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; using ToNativeType = typename ToFieldType::NativeType; if constexpr (std::is_floating_point_v) { if (!std::isfinite(value)) throw Exception(std::string(ToDataType::family_name) + " convert overflow. Cannot convert infinity or NaN to decimal", ErrorCodes::DECIMAL_OVERFLOW); auto out = value * ToFieldType::getScaleMultiplier(scale); if constexpr (std::is_same_v) { static constexpr __int128 min_int128 = __int128(0x8000000000000000ll) << 64; static constexpr __int128 max_int128 = (__int128(0x7fffffffffffffffll) << 64) + 0xffffffffffffffffll; if (out <= static_cast(min_int128) || out >= static_cast(max_int128)) throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range", ErrorCodes::DECIMAL_OVERFLOW); } else { if (out <= std::numeric_limits::min() || out >= std::numeric_limits::max()) throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range", ErrorCodes::DECIMAL_OVERFLOW); } return out; } else { if constexpr (std::is_same_v) if (value > static_cast(std::numeric_limits::max())) return convertDecimals, ToDataType>(value, 0, scale); return convertDecimals, ToDataType>(value, 0, scale); } } }