diff --git a/src/Core/DecimalFunctions.h b/src/Core/DecimalFunctions.h index b82cfd88e98..21467a6e5d2 100644 --- a/src/Core/DecimalFunctions.h +++ b/src/Core/DecimalFunctions.h @@ -206,23 +206,32 @@ inline typename DecimalType::NativeType getFractionalPart(const DecimalType & de } /// Decimal to integer/float conversion -template -To convertTo(const DecimalType & decimal, size_t scale) +template +ReturnType convertToImpl(const DecimalType & decimal, size_t scale, To &result) { using NativeT = typename DecimalType::NativeType; + static constexpr bool throw_exception = std::is_same_v; if constexpr (std::is_floating_point_v) { - return static_cast(decimal.value) / static_cast(scaleMultiplier(scale)); + result = static_cast(decimal.value) / static_cast(scaleMultiplier(scale)); } else if constexpr (is_integer_v && (sizeof(To) >= sizeof(NativeT))) { NativeT whole = getWholePart(decimal, scale); if constexpr (is_unsigned_v) + { if (whole < 0) - throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); - return static_cast(whole); + { + if constexpr (throw_exception) + throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(true); + } + } + + result = static_cast(whole); } else if constexpr (is_integer_v) { @@ -235,9 +244,34 @@ To convertTo(const DecimalType & decimal, size_t scale) static const constexpr CastTo max_to = std::numeric_limits::max(); if (whole < min_to || whole > max_to) - throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); - return static_cast(whole); + { + if constexpr (throw_exception) + throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(true); + } + + result = static_cast(whole); } + + return ReturnType(true); +} + + +template +To convertTo(const DecimalType & decimal, size_t scale) +{ + To result; + + convertToImpl(decimal, scale, result); + + return result; +} + +template +bool tryConvertTo(const DecimalType & decimal, size_t scale, To& result) +{ + return convertToImpl(decimal, scale, result); } template typename DecimalType> diff --git a/src/DataTypes/DataTypesDecimal.h b/src/DataTypes/DataTypesDecimal.h index 079812c2e74..3f7b4e2ac63 100644 --- a/src/DataTypes/DataTypesDecimal.h +++ b/src/DataTypes/DataTypesDecimal.h @@ -96,22 +96,29 @@ 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) +template +inline std::enable_if_t && IsDataTypeDecimal, 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; + MaxNativeType converted_value; if (scale_to > scale_from) { converted_value = DecimalUtils::scaleMultiplier(scale_to - scale_from); if (common::mulOverflow(static_cast(value.value), converted_value, converted_value)) - throw Exception(std::string(ToDataType::family_name) + " convert overflow", - ErrorCodes::DECIMAL_OVERFLOW); + { + if constexpr (throw_exception) + throw Exception(std::string(ToDataType::family_name) + " convert overflow", + ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(false); + } } else converted_value = value.value / DecimalUtils::scaleMultiplier(scale_from - scale_to); @@ -120,35 +127,87 @@ convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_fro { 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); + { + if constexpr (throw_exception) + throw Exception(std::string(ToDataType::family_name) + " convert overflow", + ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(false); + } } - return static_cast(converted_value); + result = static_cast(converted_value); + + return ReturnType(true); +} + +template +inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> +convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to) +{ + using ToFieldType = typename ToDataType::FieldType; + ToFieldType result; + + convertDecimalsImpl(value, scale_from, scale_to, result); + + return result; +} + +template +inline std::enable_if_t && IsDataTypeDecimal, bool> +tryConvertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to, typename ToDataType::FieldType& result) +{ + return convertDecimalsImpl(value, scale_from, scale_to, result); +} + +template +inline std::enable_if_t && IsNumber, 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(value, scale, result); } template inline std::enable_if_t && IsNumber, typename ToDataType::FieldType> convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale) { - using ToFieldType = typename ToDataType::FieldType; + typename ToDataType::FieldType result; - return DecimalUtils::convertTo(value, scale); + convertFromDecimalImpl(value, scale, result); + + return result; } template -inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> -convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) +inline std::enable_if_t && IsNumber, bool> +tryConvertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result) +{ + return convertFromDecimalImpl(value, scale, result); +} + +template +inline std::enable_if_t && IsDataTypeDecimal, 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; + 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); + { + if constexpr (throw_exception) + throw Exception(std::string(ToDataType::family_name) + " convert overflow. Cannot convert infinity or NaN to decimal", + ErrorCodes::DECIMAL_OVERFLOW); + else + return false; + } auto out = value * static_cast(DecimalUtils::scaleMultiplier(scale)); if constexpr (std::is_same_v) @@ -157,29 +216,60 @@ convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) static constexpr Int128 max_int128 = maxInt128(); 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); + { + if constexpr (throw_exception) + throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range", + ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(false); + } } else { if (out <= static_cast(std::numeric_limits::min()) || out >= static_cast(std::numeric_limits::max())) - throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range", - ErrorCodes::DECIMAL_OVERFLOW); + { + if constexpr (throw_exception) + throw Exception(std::string(ToDataType::family_name) + " convert overflow. Float is out of Decimal range", + ErrorCodes::DECIMAL_OVERFLOW); + else + return ReturnType(false); + } } - return static_cast(out); + + result = static_cast(out); + + return ReturnType(true); } else { if constexpr (is_big_int_v) - return convertDecimals, ToDataType>(static_cast(value), 0, scale); + return ReturnType(convertDecimalsImpl, ToDataType, ReturnType>(static_cast(value), 0, scale, result)); else if constexpr (std::is_same_v) - return convertDecimals, ToDataType>(value, 0, scale); + return ReturnType(convertDecimalsImpl, ToDataType, ReturnType>(value, 0, scale, result)); else - return convertDecimals, ToDataType>(value, 0, scale); + return ReturnType(convertDecimalsImpl, ToDataType, ReturnType>(value, 0, scale, result)); } } +template +inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> +convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) +{ + typename ToDataType::FieldType result; + + convertToDecimalImpl(value, scale, result); + + return result; +} + +template +inline std::enable_if_t && IsDataTypeDecimal, bool> +tryConvertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale, typename ToDataType::FieldType& result) +{ + return convertToDecimalImpl(value, scale, result); +} + template inline DataTypePtr createDecimalMaxPrecision(UInt64 scale) { diff --git a/src/Functions/FunctionsConversion.h b/src/Functions/FunctionsConversion.h index 82a3772bc8c..51f77acd24c 100644 --- a/src/Functions/FunctionsConversion.h +++ b/src/Functions/FunctionsConversion.h @@ -191,7 +191,24 @@ struct ConvertImpl { if constexpr (IsDataTypeDecimal || IsDataTypeDecimal) { - try + if constexpr (std::is_same_v) + { + ToFieldType result; + bool convert_result = false; + + if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) + tryConvertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale(), result); + else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) + tryConvertFromDecimal(vec_from[i], vec_from.getScale(), result); + else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) + tryConvertToDecimal(vec_from[i], vec_to.getScale(), result); + + if (convert_result) + vec_to[i] = result; + else + (*vec_null_map_to)[i] = true; + } + else { if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) vec_to[i] = convertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale()); @@ -200,21 +217,7 @@ struct ConvertImpl else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) vec_to[i] = convertToDecimal(vec_from[i], vec_to.getScale()); else - { throw Exception("Unsupported data type in conversion function", ErrorCodes::CANNOT_CONVERT_TYPE); - } - } - catch (const Exception & exception) - { - if constexpr (std::is_same_v) - { - if (exception.code() == ErrorCodes::CANNOT_CONVERT_TYPE || exception.code() == ErrorCodes::DECIMAL_OVERFLOW) - (*vec_null_map_to)[i] = true; - else - throw exception; - } - else - throw exception; } } else