mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-04 21:42:39 +00:00
270 lines
11 KiB
C++
270 lines
11 KiB
C++
#pragma once
|
|
|
|
#include <base/arithmeticOverflow.h>
|
|
#include <base/extended_types.h>
|
|
#include <Common/typeid_cast.h>
|
|
#include <DataTypes/IDataType.h>
|
|
#include <DataTypes/DataTypeDecimalBase.h>
|
|
#include <DataTypes/DataTypeDateTime64.h>
|
|
|
|
|
|
namespace DB
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
{
|
|
extern const int DECIMAL_OVERFLOW;
|
|
extern const int LOGICAL_ERROR;
|
|
}
|
|
|
|
/// Implements 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 DataTypeDecimal final : public DataTypeDecimalBase<T>
|
|
{
|
|
using Base = DataTypeDecimalBase<T>;
|
|
|
|
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 TypeToTypeIndex<T>; }
|
|
bool canBePromoted() const override { return true; }
|
|
DataTypePtr promoteNumericType() const override;
|
|
|
|
bool equals(const IDataType & rhs) const override;
|
|
T parseFromString(const String & str) const;
|
|
SerializationPtr doGetDefaultSerialization() const override;
|
|
};
|
|
|
|
using DataTypeDecimal32 = DataTypeDecimal<Decimal32>;
|
|
using DataTypeDecimal64 = DataTypeDecimal<Decimal64>;
|
|
using DataTypeDecimal128 = DataTypeDecimal<Decimal128>;
|
|
using DataTypeDecimal256 = DataTypeDecimal<Decimal256>;
|
|
|
|
template <typename T>
|
|
inline const DataTypeDecimal<T> * checkDecimal(const IDataType & data_type)
|
|
{
|
|
return typeid_cast<const DataTypeDecimal<T> *>(&data_type);
|
|
}
|
|
|
|
inline UInt32 getDecimalScale(const IDataType & data_type)
|
|
{
|
|
if (const auto * decimal_type = checkDecimal<Decimal32>(data_type))
|
|
return decimal_type->getScale();
|
|
if (const auto * decimal_type = checkDecimal<Decimal64>(data_type))
|
|
return decimal_type->getScale();
|
|
if (const auto * decimal_type = checkDecimal<Decimal128>(data_type))
|
|
return decimal_type->getScale();
|
|
if (const auto * decimal_type = checkDecimal<Decimal256>(data_type))
|
|
return decimal_type->getScale();
|
|
if (const auto * date_time_type = typeid_cast<const DataTypeDateTime64 *>(&data_type))
|
|
return date_time_type->getScale();
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot get decimal scale from type {}", data_type.getName());
|
|
}
|
|
|
|
inline UInt32 getDecimalPrecision(const IDataType & data_type)
|
|
{
|
|
if (const auto * decimal_type = checkDecimal<Decimal32>(data_type))
|
|
return decimal_type->getPrecision();
|
|
if (const auto * decimal_type = checkDecimal<Decimal64>(data_type))
|
|
return decimal_type->getPrecision();
|
|
if (const auto * decimal_type = checkDecimal<Decimal128>(data_type))
|
|
return decimal_type->getPrecision();
|
|
if (const auto * decimal_type = checkDecimal<Decimal256>(data_type))
|
|
return decimal_type->getPrecision();
|
|
if (const auto * date_time_type = typeid_cast<const DataTypeDateTime64 *>(&data_type))
|
|
return date_time_type->getPrecision();
|
|
|
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot get decimal precision from type {}", data_type.getName());
|
|
}
|
|
|
|
template <typename T>
|
|
inline UInt32 getDecimalScale(const DataTypeDecimal<T> & data_type)
|
|
{
|
|
return data_type.getScale();
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType, typename ReturnType = void>
|
|
requires (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
|
inline ReturnType convertDecimalsImpl(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to, typename ToDataType::FieldType & result)
|
|
{
|
|
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;
|
|
|
|
static constexpr bool throw_exception = std::is_same_v<ReturnType, void>;
|
|
|
|
MaxNativeType converted_value;
|
|
if (scale_to > scale_from)
|
|
{
|
|
converted_value = DecimalUtils::scaleMultiplier<MaxNativeType>(scale_to - scale_from);
|
|
if (common::mulOverflow(static_cast<MaxNativeType>(value.value), converted_value, converted_value))
|
|
{
|
|
if constexpr (throw_exception)
|
|
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow while multiplying {} by scale {}",
|
|
std::string(ToDataType::family_name), toString(value.value), toString(converted_value));
|
|
else
|
|
return ReturnType(false);
|
|
}
|
|
}
|
|
else if (scale_to == scale_from)
|
|
{
|
|
converted_value = value.value;
|
|
}
|
|
else
|
|
{
|
|
converted_value = value.value / DecimalUtils::scaleMultiplier<MaxNativeType>(scale_from - scale_to);
|
|
}
|
|
|
|
if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType))
|
|
{
|
|
if (converted_value < std::numeric_limits<typename ToFieldType::NativeType>::min() ||
|
|
converted_value > std::numeric_limits<typename ToFieldType::NativeType>::max())
|
|
{
|
|
if constexpr (throw_exception)
|
|
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow: {} is not in range ({}, {})",
|
|
std::string(ToDataType::family_name), toString(converted_value),
|
|
toString(std::numeric_limits<typename ToFieldType::NativeType>::min()),
|
|
toString(std::numeric_limits<typename ToFieldType::NativeType>::max()));
|
|
else
|
|
return ReturnType(false);
|
|
}
|
|
}
|
|
|
|
result = static_cast<typename ToFieldType::NativeType>(converted_value);
|
|
|
|
return ReturnType(true);
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
|
inline typename ToDataType::FieldType convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to)
|
|
{
|
|
using ToFieldType = typename ToDataType::FieldType;
|
|
ToFieldType result;
|
|
|
|
convertDecimalsImpl<FromDataType, ToDataType, void>(value, scale_from, scale_to, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
|
inline bool tryConvertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to, typename ToDataType::FieldType & result)
|
|
{
|
|
return convertDecimalsImpl<FromDataType, ToDataType, bool>(value, scale_from, scale_to, result);
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType, typename ReturnType>
|
|
requires (IsDataTypeDecimal<FromDataType> && is_arithmetic_v<typename ToDataType::FieldType>)
|
|
inline ReturnType convertFromDecimalImpl(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result)
|
|
{
|
|
using FromFieldType = typename FromDataType::FieldType;
|
|
using ToFieldType = typename ToDataType::FieldType;
|
|
|
|
return DecimalUtils::convertToImpl<ToFieldType, FromFieldType, ReturnType>(value, scale, result);
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (IsDataTypeDecimal<FromDataType> && is_arithmetic_v<typename ToDataType::FieldType>)
|
|
inline typename ToDataType::FieldType convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
|
{
|
|
typename ToDataType::FieldType result;
|
|
|
|
convertFromDecimalImpl<FromDataType, ToDataType, void>(value, scale, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (IsDataTypeDecimal<FromDataType> && is_arithmetic_v<typename ToDataType::FieldType>)
|
|
inline bool tryConvertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result)
|
|
{
|
|
return convertFromDecimalImpl<FromDataType, ToDataType, bool>(value, scale, result);
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType, typename ReturnType>
|
|
requires (is_arithmetic_v<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>)
|
|
inline ReturnType convertToDecimalImpl(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result)
|
|
{
|
|
using FromFieldType = typename FromDataType::FieldType;
|
|
using ToFieldType = typename ToDataType::FieldType;
|
|
using ToNativeType = typename ToFieldType::NativeType;
|
|
|
|
static constexpr bool throw_exception = std::is_same_v<ReturnType, void>;
|
|
|
|
if constexpr (std::is_floating_point_v<FromFieldType>)
|
|
{
|
|
if (!std::isfinite(value))
|
|
{
|
|
if constexpr (throw_exception)
|
|
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow. Cannot convert infinity or NaN to decimal", ToDataType::family_name);
|
|
else
|
|
return ReturnType(false);
|
|
}
|
|
|
|
auto out = value * static_cast<FromFieldType>(DecimalUtils::scaleMultiplier<ToNativeType>(scale));
|
|
|
|
if (out <= static_cast<FromFieldType>(std::numeric_limits<ToNativeType>::min()) ||
|
|
out >= static_cast<FromFieldType>(std::numeric_limits<ToNativeType>::max()))
|
|
{
|
|
if constexpr (throw_exception)
|
|
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "{} convert overflow. Float is out of Decimal range", ToDataType::family_name);
|
|
else
|
|
return ReturnType(false);
|
|
}
|
|
|
|
result = static_cast<ToNativeType>(out);
|
|
return ReturnType(true);
|
|
}
|
|
else
|
|
{
|
|
if constexpr (is_big_int_v<FromFieldType>)
|
|
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal256>, ToDataType, ReturnType>(static_cast<Int256>(value), 0, scale, result));
|
|
else if constexpr (std::is_same_v<FromFieldType, UInt64>)
|
|
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal128>, ToDataType, ReturnType>(static_cast<Int128>(value), 0, scale, result));
|
|
else
|
|
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal64>, ToDataType, ReturnType>(static_cast<Int64>(value), 0, scale, result));
|
|
}
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (is_arithmetic_v<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>)
|
|
inline typename ToDataType::FieldType convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
|
{
|
|
typename ToDataType::FieldType result;
|
|
convertToDecimalImpl<FromDataType, ToDataType, void>(value, scale, result);
|
|
return result;
|
|
}
|
|
|
|
template <typename FromDataType, typename ToDataType>
|
|
requires (is_arithmetic_v<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>)
|
|
inline bool tryConvertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result)
|
|
{
|
|
return convertToDecimalImpl<FromDataType, ToDataType, bool>(value, scale, result);
|
|
}
|
|
|
|
template <typename T>
|
|
inline DataTypePtr createDecimalMaxPrecision(UInt64 scale)
|
|
{
|
|
return std::make_shared<DataTypeDecimal<T>>(DecimalUtils::max_precision<T>, scale);
|
|
}
|
|
|
|
}
|