mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge pull request #16724 from kitaisreal/added-function-accurate-cast-or-null
Added function accurateCastOrNull, allow different types inside IN subquery
This commit is contained in:
commit
6fc3ca8b7b
@ -430,6 +430,63 @@ SELECT toTypeName(CAST(x, 'Nullable(UInt16)')) FROM t_null
|
||||
|
||||
- [cast_keep_nullable](../../operations/settings/settings.md#cast_keep_nullable) setting
|
||||
|
||||
## accurateCast(x, T) {#type_conversion_function-accurate-cast}
|
||||
|
||||
Converts ‘x’ to the ‘t’ data type. The differente from cast(x, T) is that accurateCast
|
||||
does not allow overflow of numeric types during cast if type value x does not fit
|
||||
bounds of type T.
|
||||
|
||||
Example
|
||||
``` sql
|
||||
SELECT cast(-1, 'UInt8') as uint8;
|
||||
```
|
||||
|
||||
|
||||
``` text
|
||||
┌─uint8─┐
|
||||
│ 255 │
|
||||
└───────┘
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT accurateCast(-1, 'UInt8') as uint8;
|
||||
```
|
||||
|
||||
``` text
|
||||
Code: 70. DB::Exception: Received from localhost:9000. DB::Exception: Value in column Int8 cannot be safely converted into type UInt8: While processing accurateCast(-1, 'UInt8') AS uint8.
|
||||
|
||||
```
|
||||
|
||||
## accurateCastOrNull(x, T) {#type_conversion_function-accurate-cast_or_null}
|
||||
|
||||
Converts ‘x’ to the ‘t’ data type. Always returns nullable type and returns NULL
|
||||
if the casted value is not representable in the target type.
|
||||
|
||||
Example:
|
||||
|
||||
``` sql
|
||||
SELECT
|
||||
accurateCastOrNull(-1, 'UInt8') as uint8,
|
||||
accurateCastOrNull(128, 'Int8') as int8,
|
||||
accurateCastOrNull('Test', 'FixedString(2)') as fixed_string
|
||||
```
|
||||
|
||||
``` text
|
||||
┌─uint8─┬─int8─┬─fixed_string─┐
|
||||
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │
|
||||
└───────┴──────┴──────────────┘┘
|
||||
```
|
||||
|
||||
``` sql
|
||||
SELECT toTypeName(accurateCastOrNull(5, 'UInt8'))
|
||||
```
|
||||
|
||||
``` text
|
||||
┌─toTypeName(accurateCastOrNull(5, 'UInt8'))─┐
|
||||
│ Nullable(UInt8) │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## toInterval(Year\|Quarter\|Month\|Week\|Day\|Hour\|Minute\|Second) {#function-tointerval}
|
||||
|
||||
Converts a Number type argument to an [Interval](../../sql-reference/data-types/special-data-types/interval.md) data type.
|
||||
|
@ -28,23 +28,28 @@ struct UInt128
|
||||
UInt64 low;
|
||||
UInt64 high;
|
||||
|
||||
/// TODO: Make this constexpr. Currently it is used in unions
|
||||
/// and union cannot contain member with non trivial constructor
|
||||
/// constructor must be non user provided but compiler cannot constexpr constructor
|
||||
/// if members low and high are not initialized, if we default member initialize them
|
||||
/// constructor becomes non trivial.
|
||||
UInt128() = default;
|
||||
explicit UInt128(const UInt64 low_, const UInt64 high_) : low(low_), high(high_) {}
|
||||
explicit constexpr UInt128(const UInt64 low_, const UInt64 high_) : low(low_), high(high_) { }
|
||||
|
||||
/// We need Int128 to UInt128 conversion or AccurateComparison will call greaterOp<Int128, UInt64> instead of greaterOp<Int128, UInt128>
|
||||
explicit UInt128(const Int128 rhs) : low(rhs), high(rhs >> 64) {}
|
||||
explicit UInt128(const Int64 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const Int32 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const Int16 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const Int8 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const UInt8 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const UInt16 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const UInt32 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const UInt64 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const Float32 rhs) : low(rhs), high() {}
|
||||
explicit UInt128(const Float64 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Int128 rhs) : low(rhs), high(rhs >> 64) {}
|
||||
explicit constexpr UInt128(const Int64 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Int32 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Int16 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Int8 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const UInt8 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const UInt16 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const UInt32 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const UInt64 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Float32 rhs) : low(rhs), high() {}
|
||||
explicit constexpr UInt128(const Float64 rhs) : low(rhs), high() {}
|
||||
|
||||
auto tuple() const { return std::tie(high, low); }
|
||||
constexpr auto tuple() const { return std::tie(high, low); }
|
||||
|
||||
String toHexString() const
|
||||
{
|
||||
@ -53,31 +58,31 @@ struct UInt128
|
||||
return res;
|
||||
}
|
||||
|
||||
bool inline operator== (const UInt128 rhs) const { return tuple() == rhs.tuple(); }
|
||||
bool inline operator!= (const UInt128 rhs) const { return tuple() != rhs.tuple(); }
|
||||
bool inline operator< (const UInt128 rhs) const { return tuple() < rhs.tuple(); }
|
||||
bool inline operator<= (const UInt128 rhs) const { return tuple() <= rhs.tuple(); }
|
||||
bool inline operator> (const UInt128 rhs) const { return tuple() > rhs.tuple(); }
|
||||
bool inline operator>= (const UInt128 rhs) const { return tuple() >= rhs.tuple(); }
|
||||
constexpr bool operator== (const UInt128 rhs) const { return tuple() == rhs.tuple(); }
|
||||
constexpr bool operator!= (const UInt128 rhs) const { return tuple() != rhs.tuple(); }
|
||||
constexpr bool operator< (const UInt128 rhs) const { return tuple() < rhs.tuple(); }
|
||||
constexpr bool operator<= (const UInt128 rhs) const { return tuple() <= rhs.tuple(); }
|
||||
constexpr bool operator> (const UInt128 rhs) const { return tuple() > rhs.tuple(); }
|
||||
constexpr bool operator>= (const UInt128 rhs) const { return tuple() >= rhs.tuple(); }
|
||||
|
||||
bool inline operator == (const Int128 rhs) const { return *this == UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
bool inline operator != (const Int128 rhs) const { return *this != UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
bool inline operator >= (const Int128 rhs) const { return *this >= UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
bool inline operator > (const Int128 rhs) const { return *this > UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
bool inline operator <= (const Int128 rhs) const { return *this <= UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
bool inline operator < (const Int128 rhs) const { return *this < UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
constexpr bool operator == (const Int128 rhs) const { return *this == UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
constexpr bool operator != (const Int128 rhs) const { return *this != UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
constexpr bool operator >= (const Int128 rhs) const { return *this >= UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
constexpr bool operator > (const Int128 rhs) const { return *this > UInt128(rhs, rhs >> 64) || rhs < 0; }
|
||||
constexpr bool operator <= (const Int128 rhs) const { return *this <= UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
constexpr bool operator < (const Int128 rhs) const { return *this < UInt128(rhs, rhs >> 64) && rhs >= 0; }
|
||||
|
||||
bool inline operator > (const Int256 rhs) const { return (rhs < 0) || ((Int256(high) << 64) + low) > rhs; }
|
||||
bool inline operator > (const UInt256 rhs) const { return ((UInt256(high) << 64) + low) > rhs; }
|
||||
bool inline operator < (const Int256 rhs) const { return (rhs >= 0) && ((Int256(high) << 64) + low) < rhs; }
|
||||
bool inline operator < (const UInt256 rhs) const { return ((UInt256(high) << 64) + low) < rhs; }
|
||||
constexpr bool operator > (const Int256 rhs) const { return (rhs < 0) || ((Int256(high) << 64) + low) > rhs; }
|
||||
constexpr bool operator > (const UInt256 rhs) const { return ((UInt256(high) << 64) + low) > rhs; }
|
||||
constexpr bool operator < (const Int256 rhs) const { return (rhs >= 0) && ((Int256(high) << 64) + low) < rhs; }
|
||||
constexpr bool operator < (const UInt256 rhs) const { return ((UInt256(high) << 64) + low) < rhs; }
|
||||
|
||||
template <typename T> bool inline operator== (const T rhs) const { return *this == UInt128(rhs); }
|
||||
template <typename T> bool inline operator!= (const T rhs) const { return *this != UInt128(rhs); }
|
||||
template <typename T> bool inline operator>= (const T rhs) const { return *this >= UInt128(rhs); }
|
||||
template <typename T> bool inline operator> (const T rhs) const { return *this > UInt128(rhs); }
|
||||
template <typename T> bool inline operator<= (const T rhs) const { return *this <= UInt128(rhs); }
|
||||
template <typename T> bool inline operator< (const T rhs) const { return *this < UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator== (const T rhs) const { return *this == UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator!= (const T rhs) const { return *this != UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator>= (const T rhs) const { return *this >= UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator> (const T rhs) const { return *this > UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator<= (const T rhs) const { return *this <= UInt128(rhs); }
|
||||
template <typename T> constexpr bool operator< (const T rhs) const { return *this < UInt128(rhs); }
|
||||
|
||||
template <typename T> explicit operator T() const
|
||||
{
|
||||
@ -91,15 +96,15 @@ struct UInt128
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
UInt128 & operator= (const UInt64 rhs) { low = rhs; high = 0; return *this; }
|
||||
constexpr UInt128 & operator= (const UInt64 rhs) { low = rhs; high = 0; return *this; }
|
||||
};
|
||||
|
||||
template <typename T> bool inline operator == (T a, const UInt128 b) { return b.operator==(a); }
|
||||
template <typename T> bool inline operator != (T a, const UInt128 b) { return b.operator!=(a); }
|
||||
template <typename T> bool inline operator >= (T a, const UInt128 b) { return b <= a; }
|
||||
template <typename T> bool inline operator > (T a, const UInt128 b) { return b < a; }
|
||||
template <typename T> bool inline operator <= (T a, const UInt128 b) { return b >= a; }
|
||||
template <typename T> bool inline operator < (T a, const UInt128 b) { return b > a; }
|
||||
template <typename T> constexpr bool operator == (T a, const UInt128 b) { return b.operator==(a); }
|
||||
template <typename T> constexpr bool operator != (T a, const UInt128 b) { return b.operator!=(a); }
|
||||
template <typename T> constexpr bool operator >= (T a, const UInt128 b) { return b <= a; }
|
||||
template <typename T> constexpr bool operator > (T a, const UInt128 b) { return b < a; }
|
||||
template <typename T> constexpr bool operator <= (T a, const UInt128 b) { return b >= a; }
|
||||
template <typename T> constexpr bool operator < (T a, const UInt128 b) { return b > a; }
|
||||
|
||||
template <> inline constexpr bool IsNumber<UInt128> = true;
|
||||
template <> struct TypeName<UInt128> { static constexpr const char * get() { return "UInt128"; } };
|
||||
@ -246,4 +251,42 @@ template <> struct hash<DB::UInt128>
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class numeric_limits<DB::UInt128>
|
||||
{
|
||||
public:
|
||||
static constexpr bool is_specialized = true;
|
||||
static constexpr bool is_signed = ::is_signed<DB::UInt128>::value;
|
||||
static constexpr bool is_integer = ::is_integer<DB::UInt128>::value;
|
||||
static constexpr bool is_exact = true;
|
||||
static constexpr bool has_infinity = false;
|
||||
static constexpr bool has_quiet_NaN = false;
|
||||
static constexpr bool has_signaling_NaN = false;
|
||||
static constexpr std::float_denorm_style has_denorm = std::denorm_absent;
|
||||
static constexpr bool has_denorm_loss = false;
|
||||
static constexpr std::float_round_style round_style = std::round_toward_zero;
|
||||
static constexpr bool is_iec559 = false;
|
||||
static constexpr bool is_bounded = true;
|
||||
static constexpr bool is_modulo = true;
|
||||
static constexpr int digits = std::numeric_limits<UInt64>::digits * 2;
|
||||
static constexpr int digits10 = digits * 0.30103 /*std::log10(2)*/;
|
||||
static constexpr int max_digits10 = 0;
|
||||
static constexpr int radix = 2;
|
||||
static constexpr int min_exponent = 0;
|
||||
static constexpr int min_exponent10 = 0;
|
||||
static constexpr int max_exponent = 0;
|
||||
static constexpr int max_exponent10 = 0;
|
||||
static constexpr bool traps = true;
|
||||
static constexpr bool tinyness_before = false;
|
||||
|
||||
static constexpr DB::UInt128 min() noexcept { return DB::UInt128(0, 0); }
|
||||
|
||||
static constexpr DB::UInt128 max() noexcept
|
||||
{
|
||||
return DB::UInt128(std::numeric_limits<UInt64>::max(), std::numeric_limits<UInt64>::max());
|
||||
}
|
||||
|
||||
static constexpr DB::UInt128 lowest() noexcept { return min(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -515,11 +515,32 @@ inline bool NO_SANITIZE_UNDEFINED convertNumeric(From value, To & result)
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Note that NaNs doesn't compare equal to anything, but they are still in range of any Float type.
|
||||
if (isNaN(value) && std::is_floating_point_v<To>)
|
||||
if constexpr (std::is_floating_point_v<From> && std::is_floating_point_v<To>)
|
||||
{
|
||||
result = value;
|
||||
return true;
|
||||
/// Note that NaNs doesn't compare equal to anything, but they are still in range of any Float type.
|
||||
if (isNaN(value))
|
||||
{
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value == std::numeric_limits<From>::infinity())
|
||||
{
|
||||
result = std::numeric_limits<To>::infinity();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value == -std::numeric_limits<From>::infinity())
|
||||
{
|
||||
result = -std::numeric_limits<To>::infinity();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (accurate::greaterOp(value, std::numeric_limits<To>::max())
|
||||
|| accurate::greaterOp(std::numeric_limits<To>::lowest(), value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = static_cast<To>(value);
|
||||
|
@ -206,23 +206,32 @@ inline typename DecimalType::NativeType getFractionalPart(const DecimalType & de
|
||||
}
|
||||
|
||||
/// Decimal to integer/float conversion
|
||||
template <typename To, typename DecimalType>
|
||||
To convertTo(const DecimalType & decimal, size_t scale)
|
||||
template <typename To, typename DecimalType, typename ReturnType>
|
||||
ReturnType convertToImpl(const DecimalType & decimal, size_t scale, To & result)
|
||||
{
|
||||
using NativeT = typename DecimalType::NativeType;
|
||||
static constexpr bool throw_exception = std::is_same_v<ReturnType, void>;
|
||||
|
||||
if constexpr (std::is_floating_point_v<To>)
|
||||
{
|
||||
return static_cast<To>(decimal.value) / static_cast<To>(scaleMultiplier<NativeT>(scale));
|
||||
result = static_cast<To>(decimal.value) / static_cast<To>(scaleMultiplier<NativeT>(scale));
|
||||
}
|
||||
else if constexpr (is_integer_v<To> && (sizeof(To) >= sizeof(NativeT)))
|
||||
{
|
||||
NativeT whole = getWholePart(decimal, scale);
|
||||
|
||||
if constexpr (is_unsigned_v<To>)
|
||||
{
|
||||
if (whole < 0)
|
||||
throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
return static_cast<To>(whole);
|
||||
{
|
||||
if constexpr (throw_exception)
|
||||
throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
else
|
||||
return ReturnType(true);
|
||||
}
|
||||
}
|
||||
|
||||
result = static_cast<To>(whole);
|
||||
}
|
||||
else if constexpr (is_integer_v<To>)
|
||||
{
|
||||
@ -235,9 +244,34 @@ To convertTo(const DecimalType & decimal, size_t scale)
|
||||
static const constexpr CastTo max_to = std::numeric_limits<ToNativeT>::max();
|
||||
|
||||
if (whole < min_to || whole > max_to)
|
||||
throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
return static_cast<CastTo>(whole);
|
||||
{
|
||||
if constexpr (throw_exception)
|
||||
throw Exception("Convert overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||
else
|
||||
return ReturnType(true);
|
||||
}
|
||||
|
||||
result = static_cast<CastTo>(whole);
|
||||
}
|
||||
|
||||
return ReturnType(true);
|
||||
}
|
||||
|
||||
|
||||
template <typename To, typename DecimalType>
|
||||
To convertTo(const DecimalType & decimal, size_t scale)
|
||||
{
|
||||
To result;
|
||||
|
||||
convertToImpl<To, DecimalType, void>(decimal, scale, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename To, typename DecimalType>
|
||||
bool tryConvertTo(const DecimalType & decimal, size_t scale, To & result)
|
||||
{
|
||||
return convertToImpl<To, DecimalType, bool>(decimal, scale, result);
|
||||
}
|
||||
|
||||
template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
|
||||
|
@ -16,10 +16,8 @@ namespace ErrorCodes
|
||||
extern const int UNKNOWN_ELEMENT_IN_CONFIG;
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_SETTINGS_TRAITS(SettingsTraits, LIST_OF_SETTINGS)
|
||||
|
||||
|
||||
/** Set the settings from the profile (in the server configuration, many settings can be listed in one profile).
|
||||
* The profile can also be set using the `set` functions, like the `profile` setting.
|
||||
*/
|
||||
|
@ -96,22 +96,29 @@ inline UInt32 getDecimalScale(const DataTypeDecimal<T> & data_type)
|
||||
return data_type.getScale();
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to)
|
||||
template <typename FromDataType, typename ToDataType, typename ReturnType = void>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, 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))
|
||||
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<MaxNativeType>(scale_from - scale_to);
|
||||
@ -120,35 +127,87 @@ convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_fro
|
||||
{
|
||||
if (converted_value < std::numeric_limits<typename ToFieldType::NativeType>::min() ||
|
||||
converted_value > std::numeric_limits<typename ToFieldType::NativeType>::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<typename ToFieldType::NativeType>(converted_value);
|
||||
result = static_cast<typename ToFieldType::NativeType>(converted_value);
|
||||
|
||||
return ReturnType(true);
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, 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>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>, 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>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, 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>
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, typename ToDataType::FieldType>
|
||||
convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
||||
{
|
||||
using ToFieldType = typename ToDataType::FieldType;
|
||||
typename ToDataType::FieldType result;
|
||||
|
||||
return DecimalUtils::convertTo<ToFieldType>(value, scale);
|
||||
convertFromDecimalImpl<FromDataType, ToDataType, void>(value, scale, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, typename ToDataType::FieldType>
|
||||
convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
||||
inline std::enable_if_t<IsDataTypeDecimal<FromDataType> && IsNumber<typename ToDataType::FieldType>, 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>
|
||||
inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, 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))
|
||||
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<FromFieldType>(DecimalUtils::scaleMultiplier<ToNativeType>(scale));
|
||||
if constexpr (std::is_same_v<ToNativeType, Int128>)
|
||||
@ -157,29 +216,60 @@ convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale)
|
||||
static constexpr Int128 max_int128 = maxInt128();
|
||||
|
||||
if (out <= static_cast<ToNativeType>(min_int128) || out >= static_cast<ToNativeType>(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<FromFieldType>(std::numeric_limits<ToNativeType>::min()) ||
|
||||
out >= static_cast<FromFieldType>(std::numeric_limits<ToNativeType>::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<ToNativeType>(out);
|
||||
|
||||
result = static_cast<ToNativeType>(out);
|
||||
|
||||
return ReturnType(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (is_big_int_v<FromFieldType>)
|
||||
return convertDecimals<DataTypeDecimal<Decimal256>, ToDataType>(static_cast<Int256>(value), 0, scale);
|
||||
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal256>, ToDataType, ReturnType>(static_cast<Int256>(value), 0, scale, result));
|
||||
else if constexpr (std::is_same_v<FromFieldType, UInt64>)
|
||||
return convertDecimals<DataTypeDecimal<Decimal128>, ToDataType>(value, 0, scale);
|
||||
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal128>, ToDataType, ReturnType>(value, 0, scale, result));
|
||||
else
|
||||
return convertDecimals<DataTypeDecimal<Decimal64>, ToDataType>(value, 0, scale);
|
||||
return ReturnType(convertDecimalsImpl<DataTypeDecimal<Decimal64>, ToDataType, ReturnType>(value, 0, scale, result));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FromDataType, typename ToDataType>
|
||||
inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, 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>
|
||||
inline std::enable_if_t<IsNumber<typename FromDataType::FieldType> && IsDataTypeDecimal<ToDataType>, 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)
|
||||
{
|
||||
|
@ -1,16 +1,10 @@
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionsConversion.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
FunctionOverloadResolverImplPtr CastOverloadResolver::create(const Context & context)
|
||||
{
|
||||
return createImpl(context.getSettingsRef().cast_keep_nullable);
|
||||
}
|
||||
|
||||
void registerFunctionFixedString(FunctionFactory & factory);
|
||||
|
||||
void registerFunctionsConversion(FunctionFactory & factory)
|
||||
@ -44,7 +38,10 @@ void registerFunctionsConversion(FunctionFactory & factory)
|
||||
registerFunctionFixedString(factory);
|
||||
|
||||
factory.registerFunction<FunctionToUnixTimestamp>();
|
||||
factory.registerFunction<CastOverloadResolver>(FunctionFactory::CaseInsensitive);
|
||||
|
||||
factory.registerFunction<CastOverloadResolver<CastType::nonAccurate>>(FunctionFactory::CaseInsensitive);
|
||||
factory.registerFunction<CastOverloadResolver<CastType::accurate>>();
|
||||
factory.registerFunction<CastOverloadResolver<CastType::accurateOrNull>>();
|
||||
|
||||
factory.registerFunction<FunctionToUInt8OrZero>();
|
||||
factory.registerFunction<FunctionToUInt16OrZero>();
|
||||
|
@ -38,13 +38,15 @@
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <Common/assert_cast.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <Core/AccurateComparison.h>
|
||||
#include <Functions/IFunctionAdaptors.h>
|
||||
#include <Functions/FunctionsMiscellaneous.h>
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
#include <Functions/DateTimeTransforms.h>
|
||||
#include <Functions/toFixedString.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
#include <Columns/ColumnLowCardinality.h>
|
||||
#include <Functions/toFixedString.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -96,6 +98,15 @@ inline UInt32 extractToDecimalScale(const ColumnWithTypeAndName & named_column)
|
||||
/// Function toUnixTimestamp has exactly the same implementation as toDateTime of String type.
|
||||
struct NameToUnixTimestamp { static constexpr auto name = "toUnixTimestamp"; };
|
||||
|
||||
struct AccurateConvertStrategyAdditions
|
||||
{
|
||||
UInt32 scale { 0 };
|
||||
};
|
||||
|
||||
struct AccurateOrNullConvertStrategyAdditions
|
||||
{
|
||||
UInt32 scale { 0 };
|
||||
};
|
||||
|
||||
/** Conversion of number types to each other, enums to numbers, dates and datetimes to numbers and back: done by straight assignment.
|
||||
* (Date is represented internally as number of days from some day; DateTime - as unix timestamp)
|
||||
@ -108,7 +119,7 @@ struct ConvertImpl
|
||||
|
||||
template <typename Additions = void *>
|
||||
static ColumnPtr NO_SANITIZE_UNDEFINED execute(
|
||||
const ColumnsWithTypeAndName & arguments, const DataTypePtr & /*result_type*/, size_t /*input_rows_count*/,
|
||||
const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type [[maybe_unused]], size_t /*input_rows_count*/,
|
||||
Additions additions [[maybe_unused]] = Additions())
|
||||
{
|
||||
const ColumnWithTypeAndName & named_from = arguments[0];
|
||||
@ -138,7 +149,17 @@ struct ConvertImpl
|
||||
typename ColVecTo::MutablePtr col_to = nullptr;
|
||||
if constexpr (IsDataTypeDecimal<ToDataType>)
|
||||
{
|
||||
UInt32 scale = additions;
|
||||
UInt32 scale;
|
||||
if constexpr (std::is_same_v<Additions, AccurateConvertStrategyAdditions>
|
||||
|| std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
{
|
||||
scale = additions.scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = additions;
|
||||
}
|
||||
|
||||
col_to = ColVecTo::create(0, scale);
|
||||
}
|
||||
else
|
||||
@ -149,36 +170,106 @@ struct ConvertImpl
|
||||
size_t size = vec_from.size();
|
||||
vec_to.resize(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
ColumnUInt8::MutablePtr col_null_map_to;
|
||||
ColumnUInt8::Container * vec_null_map_to [[maybe_unused]] = nullptr;
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
{
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> || IsDataTypeDecimal<ToDataType>)
|
||||
{
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertDecimals<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), vec_to.getScale());
|
||||
else if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeNumber<ToDataType>)
|
||||
vec_to[i] = convertFromDecimal<FromDataType, ToDataType>(vec_from[i], vec_from.getScale());
|
||||
else if constexpr (IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertToDecimal<FromDataType, ToDataType>(vec_from[i], vec_to.getScale());
|
||||
else
|
||||
throw Exception("Unsupported data type in conversion function", ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
}
|
||||
else if constexpr (is_big_int_v<FromFieldType> || is_big_int_v<ToFieldType>)
|
||||
{
|
||||
if constexpr (std::is_same_v<FromFieldType, UInt128> || std::is_same_v<ToFieldType, UInt128>)
|
||||
throw Exception("Unexpected UInt128 to big int conversion", ErrorCodes::NOT_IMPLEMENTED);
|
||||
/// If From Data is Nan or Inf, throw exception
|
||||
else if (!isFinite(vec_from[i]))
|
||||
throw Exception("Unexpected inf or nan to big int conversion", ErrorCodes::NOT_IMPLEMENTED);
|
||||
else
|
||||
vec_to[i] = bigint_cast<ToFieldType>(vec_from[i]);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ToFieldType, UInt128> && sizeof(FromFieldType) <= sizeof(UInt64))
|
||||
vec_to[i] = static_cast<ToFieldType>(static_cast<UInt64>(vec_from[i]));
|
||||
else
|
||||
vec_to[i] = static_cast<ToFieldType>(vec_from[i]);
|
||||
col_null_map_to = ColumnUInt8::create(size, false);
|
||||
vec_null_map_to = &col_null_map_to->getData();
|
||||
}
|
||||
|
||||
return col_to;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if constexpr ((is_big_int_v<FromFieldType> || is_big_int_v<ToFieldType>) &&
|
||||
(std::is_same_v<FromFieldType, UInt128> || std::is_same_v<ToFieldType, UInt128>))
|
||||
{
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
(*vec_null_map_to)[i] = true;
|
||||
else
|
||||
throw Exception("Unexpected UInt128 to big int conversion", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> || IsDataTypeDecimal<ToDataType>)
|
||||
{
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
{
|
||||
ToFieldType result;
|
||||
bool convert_result = false;
|
||||
|
||||
if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
convert_result = tryConvertDecimals<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), vec_to.getScale(), result);
|
||||
else if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeNumber<ToDataType>)
|
||||
convert_result = tryConvertFromDecimal<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), result);
|
||||
else if constexpr (IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
convert_result = tryConvertToDecimal<FromDataType, ToDataType>(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<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertDecimals<FromDataType, ToDataType>(vec_from[i], vec_from.getScale(), vec_to.getScale());
|
||||
else if constexpr (IsDataTypeDecimal<FromDataType> && IsDataTypeNumber<ToDataType>)
|
||||
vec_to[i] = convertFromDecimal<FromDataType, ToDataType>(vec_from[i], vec_from.getScale());
|
||||
else if constexpr (IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>)
|
||||
vec_to[i] = convertToDecimal<FromDataType, ToDataType>(vec_from[i], vec_to.getScale());
|
||||
else
|
||||
throw Exception("Unsupported data type in conversion function", ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// If From Data is Nan or Inf and we convert to integer type, throw exception
|
||||
if constexpr (std::is_floating_point_v<FromFieldType> && !std::is_floating_point_v<ToFieldType>)
|
||||
{
|
||||
if (!isFinite(vec_from[i]))
|
||||
{
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
{
|
||||
(*vec_null_map_to)[i] = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
throw Exception("Unexpected inf or nan to integer conversion", ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>
|
||||
|| std::is_same_v<Additions, AccurateConvertStrategyAdditions>)
|
||||
{
|
||||
bool convert_result = accurate::convertNumeric(vec_from[i], vec_to[i]);
|
||||
|
||||
if (!convert_result)
|
||||
{
|
||||
if (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
{
|
||||
(*vec_null_map_to)[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(
|
||||
"Value in column " + named_from.column->getName() + " cannot be safely converted into type "
|
||||
+ result_type->getName(),
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vec_to[i] = static_cast<ToFieldType>(vec_from[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<Additions, AccurateOrNullConvertStrategyAdditions>)
|
||||
return ColumnNullable::create(std::move(col_to), std::move(col_null_map_to));
|
||||
else
|
||||
return col_to;
|
||||
}
|
||||
else
|
||||
throw Exception("Illegal column " + named_from.column->getName() + " of first argument of function " + Name::name,
|
||||
@ -945,7 +1036,9 @@ struct ConvertImpl<DataTypeString, DataTypeUInt32, NameToUnixTimestamp>
|
||||
template <typename T, typename Name>
|
||||
struct ConvertImpl<std::enable_if_t<!T::is_parametric, T>, T, Name>
|
||||
{
|
||||
static ColumnPtr execute(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/)
|
||||
template <typename Additions = void *>
|
||||
static ColumnPtr execute(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/,
|
||||
Additions additions [[maybe_unused]] = Additions())
|
||||
{
|
||||
return arguments[0].column;
|
||||
}
|
||||
@ -1931,9 +2024,15 @@ private:
|
||||
std::optional<Diagnostic> diagnostic;
|
||||
};
|
||||
|
||||
|
||||
struct NameCast { static constexpr auto name = "CAST"; };
|
||||
|
||||
enum class CastType
|
||||
{
|
||||
nonAccurate,
|
||||
accurate,
|
||||
accurateOrNull
|
||||
};
|
||||
|
||||
class FunctionCast final : public IFunctionBaseImpl
|
||||
{
|
||||
public:
|
||||
@ -1942,9 +2041,11 @@ public:
|
||||
using Diagnostic = ExecutableFunctionCast::Diagnostic;
|
||||
|
||||
FunctionCast(const char * name_, MonotonicityForRange && monotonicity_for_range_
|
||||
, const DataTypes & argument_types_, const DataTypePtr & return_type_, std::optional<Diagnostic> diagnostic_)
|
||||
: name(name_), monotonicity_for_range(monotonicity_for_range_)
|
||||
, const DataTypes & argument_types_, const DataTypePtr & return_type_
|
||||
, std::optional<Diagnostic> diagnostic_, CastType cast_type_)
|
||||
: name(name_), monotonicity_for_range(std::move(monotonicity_for_range_))
|
||||
, argument_types(argument_types_), return_type(return_type_), diagnostic(std::move(diagnostic_))
|
||||
, cast_type(cast_type_)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1991,70 +2092,118 @@ private:
|
||||
DataTypePtr return_type;
|
||||
|
||||
std::optional<Diagnostic> diagnostic;
|
||||
CastType cast_type;
|
||||
|
||||
template <typename DataType>
|
||||
WrapperType createWrapper(const DataTypePtr & from_type, const DataType * const, bool requested_result_is_nullable) const
|
||||
WrapperType createFunctionAdaptor(FunctionPtr function, const DataTypePtr & from_type) const
|
||||
{
|
||||
FunctionPtr function;
|
||||
auto function_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(function))
|
||||
.build({ColumnWithTypeAndName{nullptr, from_type, ""}});
|
||||
|
||||
return [function_adaptor]
|
||||
(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
return function_adaptor->execute(arguments, result_type, input_rows_count);
|
||||
};
|
||||
}
|
||||
|
||||
static WrapperType createToNullableColumnWrapper()
|
||||
{
|
||||
return [] (ColumnsWithTypeAndName &, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
ColumnPtr res = result_type->createColumn();
|
||||
ColumnUInt8::Ptr col_null_map_to = ColumnUInt8::create(input_rows_count, true);
|
||||
return ColumnNullable::create(res->cloneResized(input_rows_count), std::move(col_null_map_to));
|
||||
};
|
||||
}
|
||||
|
||||
template <typename ToDataType>
|
||||
WrapperType createWrapper(const DataTypePtr & from_type, const ToDataType * const to_type, bool requested_result_is_nullable) const
|
||||
{
|
||||
TypeIndex from_type_index = from_type->getTypeId();
|
||||
WhichDataType which(from_type_index);
|
||||
bool can_apply_accurate_cast = (cast_type == CastType::accurate || cast_type == CastType::accurateOrNull)
|
||||
&& (which.isInt() || which.isUInt() || which.isFloat());
|
||||
|
||||
if (requested_result_is_nullable && checkAndGetDataType<DataTypeString>(from_type.get()))
|
||||
{
|
||||
/// In case when converting to Nullable type, we apply different parsing rule,
|
||||
/// that will not throw an exception but return NULL in case of malformed input.
|
||||
function = FunctionConvertFromString<DataType, NameCast, ConvertFromStringExceptionMode::Null>::create();
|
||||
FunctionPtr function = FunctionConvertFromString<ToDataType, NameCast, ConvertFromStringExceptionMode::Null>::create();
|
||||
return createFunctionAdaptor(function, from_type);
|
||||
}
|
||||
else
|
||||
function = FunctionTo<DataType>::Type::create();
|
||||
|
||||
auto function_adaptor =
|
||||
FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(function))
|
||||
.build({ColumnWithTypeAndName{nullptr, from_type, ""}});
|
||||
|
||||
return [function_adaptor] (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
else if (!can_apply_accurate_cast)
|
||||
{
|
||||
return function_adaptor->execute(arguments, result_type, input_rows_count);
|
||||
FunctionPtr function = FunctionTo<ToDataType>::Type::create();
|
||||
return createFunctionAdaptor(function, from_type);
|
||||
}
|
||||
|
||||
auto wrapper_cast_type = cast_type;
|
||||
|
||||
return [wrapper_cast_type, from_type_index, to_type]
|
||||
(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *column_nullable, size_t input_rows_count)
|
||||
{
|
||||
ColumnPtr result_column;
|
||||
auto res = callOnIndexAndDataType<ToDataType>(from_type_index, [&](const auto & types) -> bool {
|
||||
using Types = std::decay_t<decltype(types)>;
|
||||
using LeftDataType = typename Types::LeftType;
|
||||
using RightDataType = typename Types::RightType;
|
||||
|
||||
if constexpr (IsDataTypeNumber<LeftDataType> && IsDataTypeNumber<RightDataType>)
|
||||
{
|
||||
if (wrapper_cast_type == CastType::accurate)
|
||||
{
|
||||
result_column = ConvertImpl<LeftDataType, RightDataType, NameCast>::execute(
|
||||
arguments, result_type, input_rows_count, AccurateConvertStrategyAdditions());
|
||||
}
|
||||
else
|
||||
{
|
||||
result_column = ConvertImpl<LeftDataType, RightDataType, NameCast>::execute(
|
||||
arguments, result_type, input_rows_count, AccurateOrNullConvertStrategyAdditions());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/// Additionally check if callOnIndexAndDataType wasn't called at all.
|
||||
if (!res)
|
||||
{
|
||||
if (wrapper_cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
auto nullable_column_wrapper = FunctionCast::createToNullableColumnWrapper();
|
||||
return nullable_column_wrapper(arguments, result_type, column_nullable, input_rows_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception{"Conversion from " + std::string(getTypeName(from_type_index)) + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
}
|
||||
|
||||
return result_column;
|
||||
};
|
||||
}
|
||||
|
||||
static WrapperType createStringWrapper(const DataTypePtr & from_type)
|
||||
WrapperType createStringWrapper(const DataTypePtr & from_type) const
|
||||
{
|
||||
FunctionPtr function = FunctionToString::create();
|
||||
|
||||
auto function_adaptor =
|
||||
FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(function))
|
||||
.build({ColumnWithTypeAndName{nullptr, from_type, ""}});
|
||||
|
||||
return [function_adaptor] (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
return function_adaptor->execute(arguments, result_type, input_rows_count);
|
||||
};
|
||||
return createFunctionAdaptor(function, from_type);
|
||||
}
|
||||
|
||||
static WrapperType createFixedStringWrapper(const DataTypePtr & from_type, const size_t N)
|
||||
WrapperType createFixedStringWrapper(const DataTypePtr & from_type, const size_t N) const
|
||||
{
|
||||
if (!isStringOrFixedString(from_type))
|
||||
throw Exception{"CAST AS FixedString is only implemented for types String and FixedString", ErrorCodes::NOT_IMPLEMENTED};
|
||||
|
||||
return [N] (ColumnsWithTypeAndName & arguments, const DataTypePtr &, const ColumnNullable *, size_t /*input_rows_count*/)
|
||||
bool exception_mode_null = cast_type == CastType::accurateOrNull;
|
||||
return [exception_mode_null, N] (ColumnsWithTypeAndName & arguments, const DataTypePtr &, const ColumnNullable *, size_t /*input_rows_count*/)
|
||||
{
|
||||
return FunctionToFixedString::executeForN(arguments, N);
|
||||
};
|
||||
}
|
||||
|
||||
static WrapperType createUUIDWrapper(const DataTypePtr & from_type, const DataTypeUUID * const, bool requested_result_is_nullable)
|
||||
{
|
||||
if (requested_result_is_nullable)
|
||||
throw Exception{"CAST AS Nullable(UUID) is not implemented", ErrorCodes::NOT_IMPLEMENTED};
|
||||
|
||||
FunctionPtr function = FunctionTo<DataTypeUUID>::Type::create();
|
||||
|
||||
auto function_adaptor =
|
||||
FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(function))
|
||||
.build({ColumnWithTypeAndName{nullptr, from_type, ""}});
|
||||
|
||||
return [function_adaptor] (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
return function_adaptor->execute(arguments, result_type, input_rows_count);
|
||||
if (exception_mode_null)
|
||||
return FunctionToFixedString::executeForN<ConvertToFixedStringExceptionMode::Null>(arguments, N);
|
||||
else
|
||||
return FunctionToFixedString::executeForN<ConvertToFixedStringExceptionMode::Throw>(arguments, N);
|
||||
};
|
||||
}
|
||||
|
||||
@ -2066,41 +2215,73 @@ private:
|
||||
UInt32 scale = to_type->getScale();
|
||||
|
||||
WhichDataType which(type_index);
|
||||
bool ok = which.isNativeInt() ||
|
||||
which.isNativeUInt() ||
|
||||
which.isDecimal() ||
|
||||
which.isFloat() ||
|
||||
which.isDateOrDateTime() ||
|
||||
which.isStringOrFixedString();
|
||||
bool ok = which.isNativeInt() || which.isNativeUInt() || which.isDecimal() || which.isFloat() || which.isDateOrDateTime()
|
||||
|| which.isStringOrFixedString();
|
||||
if (!ok)
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
{
|
||||
if (cast_type == CastType::accurateOrNull)
|
||||
return createToNullableColumnWrapper();
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
|
||||
return [type_index, scale, to_type] (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
auto wrapper_cast_type = cast_type;
|
||||
|
||||
return [wrapper_cast_type, type_index, scale, to_type]
|
||||
(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *column_nullable, size_t input_rows_count)
|
||||
{
|
||||
ColumnPtr result_column;
|
||||
auto res = callOnIndexAndDataType<ToDataType>(type_index, [&](const auto & types) -> bool
|
||||
{
|
||||
auto res = callOnIndexAndDataType<ToDataType>(type_index, [&](const auto & types) -> bool {
|
||||
using Types = std::decay_t<decltype(types)>;
|
||||
using LeftDataType = typename Types::LeftType;
|
||||
using RightDataType = typename Types::RightType;
|
||||
|
||||
if constexpr (IsDataTypeDecimalOrNumber<LeftDataType> && IsDataTypeDecimalOrNumber<RightDataType>)
|
||||
{
|
||||
if (wrapper_cast_type == CastType::accurate)
|
||||
{
|
||||
AccurateConvertStrategyAdditions additions;
|
||||
additions.scale = scale;
|
||||
result_column = ConvertImpl<LeftDataType, RightDataType, NameCast>::execute(
|
||||
arguments, result_type, input_rows_count, additions);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (wrapper_cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
AccurateOrNullConvertStrategyAdditions additions;
|
||||
additions.scale = scale;
|
||||
result_column = ConvertImpl<LeftDataType, RightDataType, NameCast>::execute(
|
||||
arguments, result_type, input_rows_count, additions);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result_column = ConvertImpl<LeftDataType, RightDataType, NameCast>::execute(arguments, result_type, input_rows_count, scale);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
/// Additionally check if callOnIndexAndDataType wasn't called at all.
|
||||
if (!res)
|
||||
{
|
||||
throw Exception{"Conversion from " + std::string(getTypeName(type_index)) + " to " + to_type->getName() +
|
||||
" is not supported", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
if (wrapper_cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
auto nullable_column_wrapper = FunctionCast::createToNullableColumnWrapper();
|
||||
return nullable_column_wrapper(arguments, result_type, column_nullable, input_rows_count);
|
||||
}
|
||||
else
|
||||
throw Exception{"Conversion from " + std::string(getTypeName(type_index)) + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
|
||||
return result_column;
|
||||
};
|
||||
}
|
||||
|
||||
static WrapperType createAggregateFunctionWrapper(const DataTypePtr & from_type_untyped, const DataTypeAggregateFunction * to_type)
|
||||
WrapperType createAggregateFunctionWrapper(const DataTypePtr & from_type_untyped, const DataTypeAggregateFunction * to_type) const
|
||||
{
|
||||
/// Conversion from String through parsing.
|
||||
if (checkAndGetDataType<DataTypeString>(from_type_untyped.get()))
|
||||
@ -2111,8 +2292,13 @@ private:
|
||||
};
|
||||
}
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type_untyped->getName() + " to " + to_type->getName() +
|
||||
" is not supported", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
{
|
||||
if (cast_type == CastType::accurateOrNull)
|
||||
return createToNullableColumnWrapper();
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type_untyped->getName() + " to " + to_type->getName() +
|
||||
" is not supported", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
}
|
||||
|
||||
WrapperType createArrayWrapper(const DataTypePtr & from_type_untyped, const DataTypeArray * to_type) const
|
||||
@ -2361,17 +2547,16 @@ private:
|
||||
else if (isNativeNumber(from_type) || isEnum(from_type))
|
||||
{
|
||||
auto function = Function::create();
|
||||
auto func_or_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(function))
|
||||
.build(ColumnsWithTypeAndName{{nullptr, from_type, "" }});
|
||||
|
||||
return [func_or_adaptor] (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
return func_or_adaptor->execute(arguments, result_type, input_rows_count);
|
||||
};
|
||||
return createFunctionAdaptor(function, from_type);
|
||||
}
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() +
|
||||
" is not supported", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
{
|
||||
if (cast_type == CastType::accurateOrNull)
|
||||
return createToNullableColumnWrapper();
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename EnumTypeFrom, typename EnumTypeTo>
|
||||
@ -2472,7 +2657,16 @@ private:
|
||||
if (from_type->onlyNull())
|
||||
{
|
||||
if (!to_nested->isNullable())
|
||||
throw Exception{"Cannot convert NULL to a non-nullable type", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
{
|
||||
if (cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
return createToNullableColumnWrapper();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception{"Cannot convert NULL to a non-nullable type", ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
}
|
||||
|
||||
return [](ColumnsWithTypeAndName &, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count)
|
||||
{
|
||||
@ -2662,7 +2856,8 @@ private:
|
||||
std::is_same_v<ToDataType, DataTypeFloat32> ||
|
||||
std::is_same_v<ToDataType, DataTypeFloat64> ||
|
||||
std::is_same_v<ToDataType, DataTypeDate> ||
|
||||
std::is_same_v<ToDataType, DataTypeDateTime>)
|
||||
std::is_same_v<ToDataType, DataTypeDateTime> ||
|
||||
std::is_same_v<ToDataType, DataTypeUUID>)
|
||||
{
|
||||
ret = createWrapper(from_type, checkAndGetDataType<ToDataType>(to_type.get()), requested_result_is_nullable);
|
||||
return true;
|
||||
@ -2684,14 +2879,6 @@ private:
|
||||
ret = createDecimalWrapper(from_type, checkAndGetDataType<ToDataType>(to_type.get()));
|
||||
return true;
|
||||
}
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeUUID>)
|
||||
{
|
||||
if (isStringOrFixedString(from_type))
|
||||
{
|
||||
ret = createUUIDWrapper(from_type, checkAndGetDataType<ToDataType>(to_type.get()), requested_result_is_nullable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@ -2719,20 +2906,91 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
if (cast_type == CastType::accurateOrNull)
|
||||
return createToNullableColumnWrapper();
|
||||
else
|
||||
throw Exception{"Conversion from " + from_type->getName() + " to " + to_type->getName() + " is not supported",
|
||||
ErrorCodes::CANNOT_CONVERT_TYPE};
|
||||
}
|
||||
};
|
||||
|
||||
class MonotonicityHelper
|
||||
{
|
||||
public:
|
||||
using MonotonicityForRange = FunctionCast::MonotonicityForRange;
|
||||
|
||||
template <typename DataType>
|
||||
static auto monotonicityForType(const DataType * const)
|
||||
{
|
||||
return FunctionTo<DataType>::Type::Monotonic::get;
|
||||
}
|
||||
|
||||
static MonotonicityForRange getMonotonicityInformation(const DataTypePtr & from_type, const IDataType * to_type)
|
||||
{
|
||||
if (const auto type = checkAndGetDataType<DataTypeUInt8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeUInt16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeUInt32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeUInt64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeUInt256>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt128>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeInt256>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeFloat32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeFloat64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeDate>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeDateTime>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeString>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (isEnum(from_type))
|
||||
{
|
||||
if (const auto type = checkAndGetDataType<DataTypeEnum8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto type = checkAndGetDataType<DataTypeEnum16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
}
|
||||
/// other types like Null, FixedString, Array and Tuple have no monotonicity defined
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
template<CastType cast_type>
|
||||
class CastOverloadResolver : public IFunctionOverloadResolverImpl
|
||||
{
|
||||
public:
|
||||
using MonotonicityForRange = FunctionCast::MonotonicityForRange;
|
||||
using Diagnostic = FunctionCast::Diagnostic;
|
||||
|
||||
static constexpr auto name = "CAST";
|
||||
static constexpr auto accurate_cast_name = "accurateCast";
|
||||
static constexpr auto accurate_cast_or_null_name = "accurateCastOrNull";
|
||||
static constexpr auto cast_name = "CAST";
|
||||
|
||||
static constexpr auto name = cast_type == CastType::accurate
|
||||
? accurate_cast_name
|
||||
: (cast_type == CastType::accurateOrNull ? accurate_cast_or_null_name : cast_name);
|
||||
|
||||
static FunctionOverloadResolverImplPtr create(const Context & context)
|
||||
{
|
||||
return createImpl(context.getSettingsRef().cast_keep_nullable);
|
||||
}
|
||||
|
||||
static FunctionOverloadResolverImplPtr create(const Context & context);
|
||||
static FunctionOverloadResolverImplPtr createImpl(bool keep_nullable, std::optional<Diagnostic> diagnostic = {})
|
||||
{
|
||||
return std::make_unique<CastOverloadResolver>(keep_nullable, std::move(diagnostic));
|
||||
@ -2758,8 +3016,8 @@ protected:
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
data_types[i] = arguments[i].type;
|
||||
|
||||
auto monotonicity = getMonotonicityInformation(arguments.front().type, return_type.get());
|
||||
return std::make_unique<FunctionCast>(name, std::move(monotonicity), data_types, return_type, diagnostic);
|
||||
auto monotonicity = MonotonicityHelper::getMonotonicityInformation(arguments.front().type, return_type.get());
|
||||
return std::make_unique<FunctionCast>(name, std::move(monotonicity), data_types, return_type, diagnostic, cast_type);
|
||||
}
|
||||
|
||||
DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const override
|
||||
@ -2777,9 +3035,17 @@ protected:
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
DataTypePtr type = DataTypeFactory::instance().get(type_col->getValue<String>());
|
||||
if (keep_nullable && arguments.front().type->isNullable())
|
||||
|
||||
if constexpr (cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
return makeNullable(type);
|
||||
return type;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (keep_nullable && arguments.front().type->isNullable())
|
||||
return makeNullable(type);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForNulls() const override { return false; }
|
||||
@ -2788,57 +3054,6 @@ protected:
|
||||
private:
|
||||
bool keep_nullable;
|
||||
std::optional<Diagnostic> diagnostic;
|
||||
|
||||
template <typename DataType>
|
||||
static auto monotonicityForType(const DataType * const)
|
||||
{
|
||||
return FunctionTo<DataType>::Type::Monotonic::get;
|
||||
}
|
||||
|
||||
static MonotonicityForRange getMonotonicityInformation(const DataTypePtr & from_type, const IDataType * to_type)
|
||||
{
|
||||
if (const auto * type = checkAndGetDataType<DataTypeUInt8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeUInt16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeUInt32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeUInt64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeUInt256>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt128>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeInt256>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeFloat32>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeFloat64>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeDate>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeDateTime>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeString>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (isEnum(from_type))
|
||||
{
|
||||
if (const auto * type = checkAndGetDataType<DataTypeEnum8>(to_type))
|
||||
return monotonicityForType(type);
|
||||
if (const auto * type = checkAndGetDataType<DataTypeEnum16>(to_type))
|
||||
return monotonicityForType(type);
|
||||
}
|
||||
/// other types like Null, FixedString, Array and Tuple have no monotonicity defined
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnFixedString.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <Columns/ColumnNullable.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
|
||||
@ -18,6 +20,11 @@ namespace ErrorCodes
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
enum class ConvertToFixedStringExceptionMode
|
||||
{
|
||||
Throw,
|
||||
Null
|
||||
};
|
||||
|
||||
/** Conversion to fixed string is implemented only for strings.
|
||||
*/
|
||||
@ -55,13 +62,22 @@ public:
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
|
||||
{
|
||||
const auto n = arguments[1].column->getUInt(0);
|
||||
return executeForN(arguments, n);
|
||||
return executeForN<ConvertToFixedStringExceptionMode::Throw>(arguments, n);
|
||||
}
|
||||
|
||||
template<ConvertToFixedStringExceptionMode exception_mode>
|
||||
static ColumnPtr executeForN(const ColumnsWithTypeAndName & arguments, const size_t n)
|
||||
{
|
||||
const auto & column = arguments[0].column;
|
||||
|
||||
ColumnUInt8::MutablePtr col_null_map_to;
|
||||
ColumnUInt8::Container * vec_null_map_to [[maybe_unused]] = nullptr;
|
||||
if constexpr (exception_mode == ConvertToFixedStringExceptionMode::Null)
|
||||
{
|
||||
col_null_map_to = ColumnUInt8::create(column->size(), false);
|
||||
vec_null_map_to = &col_null_map_to->getData();
|
||||
}
|
||||
|
||||
if (const auto * column_string = checkAndGetColumn<ColumnString>(column.get()))
|
||||
{
|
||||
auto column_fixed = ColumnFixedString::create(n);
|
||||
@ -77,18 +93,42 @@ public:
|
||||
const size_t off = i ? in_offsets[i - 1] : 0;
|
||||
const size_t len = in_offsets[i] - off - 1;
|
||||
if (len > n)
|
||||
throw Exception("String too long for type FixedString(" + toString(n) + ")",
|
||||
ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
{
|
||||
if constexpr (exception_mode == ConvertToFixedStringExceptionMode::Throw)
|
||||
{
|
||||
throw Exception("String too long for type FixedString(" + toString(n) + ")",
|
||||
ErrorCodes::TOO_LARGE_STRING_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*vec_null_map_to)[i] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
memcpy(&out_chars[i * n], &in_chars[off], len);
|
||||
}
|
||||
|
||||
return column_fixed;
|
||||
if constexpr (exception_mode == ConvertToFixedStringExceptionMode::Null)
|
||||
return ColumnNullable::create(std::move(column_fixed), std::move(col_null_map_to));
|
||||
else
|
||||
return column_fixed;
|
||||
}
|
||||
else if (const auto * column_fixed_string = checkAndGetColumn<ColumnFixedString>(column.get()))
|
||||
{
|
||||
const auto src_n = column_fixed_string->getN();
|
||||
if (src_n > n)
|
||||
throw Exception{"String too long for type FixedString(" + toString(n) + ")", ErrorCodes::TOO_LARGE_STRING_SIZE};
|
||||
{
|
||||
if constexpr (exception_mode == ConvertToFixedStringExceptionMode::Throw)
|
||||
{
|
||||
throw Exception{"String too long for type FixedString(" + toString(n) + ")", ErrorCodes::TOO_LARGE_STRING_SIZE};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto column_fixed = ColumnFixedString::create(n);
|
||||
std::fill(vec_null_map_to->begin(), vec_null_map_to->end(), true);
|
||||
return ColumnNullable::create(column_fixed->cloneResized(column->size()), std::move(col_null_map_to));
|
||||
}
|
||||
}
|
||||
|
||||
auto column_fixed = ColumnFixedString::create(n);
|
||||
|
||||
@ -103,7 +143,16 @@ public:
|
||||
return column_fixed;
|
||||
}
|
||||
else
|
||||
throw Exception("Unexpected column: " + column->getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
{
|
||||
if constexpr (exception_mode == ConvertToFixedStringExceptionMode::Throw)
|
||||
throw Exception("Unexpected column: " + column->getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
else
|
||||
{
|
||||
auto column_fixed = ColumnFixedString::create(n);
|
||||
std::fill(vec_null_map_to->begin(), vec_null_map_to->end(), true);
|
||||
return ColumnNullable::create(column_fixed->cloneResized(column->size()), std::move(col_null_map_to));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -662,10 +662,10 @@ ActionsDAGPtr ActionsDAG::makeConvertingActions(
|
||||
auto * right_arg = const_cast<Node *>(&actions_dag->addColumn(std::move(column), true));
|
||||
auto * left_arg = src_node;
|
||||
|
||||
CastOverloadResolver::Diagnostic diagnostic = {src_node->result_name, res_elem.name};
|
||||
FunctionCast::Diagnostic diagnostic = {src_node->result_name, res_elem.name};
|
||||
FunctionOverloadResolverPtr func_builder_cast =
|
||||
std::make_shared<FunctionOverloadResolverAdaptor>(
|
||||
CastOverloadResolver::createImpl(false, std::move(diagnostic)));
|
||||
CastOverloadResolver<CastType::nonAccurate>::createImpl(false, std::move(diagnostic)));
|
||||
|
||||
Inputs children = { left_arg, right_arg };
|
||||
src_node = &actions_dag->addFunction(func_builder_cast, std::move(children), {}, true);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Interpreters/NullableUtils.h>
|
||||
#include <Interpreters/sortBlock.h>
|
||||
#include <Interpreters/castColumn.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
#include <Storages/MergeTree/KeyCondition.h>
|
||||
@ -30,7 +31,6 @@
|
||||
#include <ext/range.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -251,11 +251,26 @@ ColumnPtr Set::execute(const Block & block, bool negative) const
|
||||
|
||||
/// The constant columns to the left of IN are not supported directly. For this, they first materialize.
|
||||
Columns materialized_columns;
|
||||
materialized_columns.reserve(num_key_columns);
|
||||
|
||||
for (size_t i = 0; i < num_key_columns; ++i)
|
||||
{
|
||||
checkTypesEqual(i, block.safeGetByPosition(i).type);
|
||||
materialized_columns.emplace_back(block.safeGetByPosition(i).column->convertToFullColumnIfConst());
|
||||
ColumnPtr result;
|
||||
|
||||
const auto & column_before_cast = block.safeGetByPosition(i);
|
||||
ColumnWithTypeAndName column_to_cast
|
||||
= {column_before_cast.column->convertToFullColumnIfConst(), column_before_cast.type, column_before_cast.name};
|
||||
|
||||
if (!transform_null_in && data_types[i]->canBeInsideNullable())
|
||||
{
|
||||
result = castColumnAccurateOrNull(column_to_cast, data_types[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = castColumnAccurate(column_to_cast, data_types[i]);
|
||||
}
|
||||
|
||||
materialized_columns.emplace_back() = result;
|
||||
key_columns.emplace_back() = materialized_columns.back().get();
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,12 @@
|
||||
#include <Core/Field.h>
|
||||
#include <Interpreters/castColumn.h>
|
||||
#include <Interpreters/ExpressionActions.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Functions/IFunctionAdaptors.h>
|
||||
#include <Functions/FunctionsConversion.h>
|
||||
|
||||
#include <Functions/FunctionsConversion.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ColumnPtr castColumn(const ColumnWithTypeAndName & arg, const DataTypePtr & type)
|
||||
template <CastType cast_type = CastType::nonAccurate>
|
||||
static ColumnPtr castColumn(const ColumnWithTypeAndName & arg, const DataTypePtr & type)
|
||||
{
|
||||
if (arg.type->equals(*type))
|
||||
return arg.column;
|
||||
@ -25,10 +22,33 @@ ColumnPtr castColumn(const ColumnWithTypeAndName & arg, const DataTypePtr & type
|
||||
};
|
||||
|
||||
FunctionOverloadResolverPtr func_builder_cast =
|
||||
std::make_shared<FunctionOverloadResolverAdaptor>(CastOverloadResolver::createImpl(false));
|
||||
std::make_shared<FunctionOverloadResolverAdaptor>(CastOverloadResolver<cast_type>::createImpl(false));
|
||||
|
||||
auto func_cast = func_builder_cast->build(arguments);
|
||||
return func_cast->execute(arguments, type, arg.column->size());
|
||||
|
||||
if constexpr (cast_type == CastType::accurateOrNull)
|
||||
{
|
||||
return func_cast->execute(arguments, makeNullable(type), arg.column->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return func_cast->execute(arguments, type, arg.column->size());
|
||||
}
|
||||
}
|
||||
|
||||
ColumnPtr castColumn(const ColumnWithTypeAndName & arg, const DataTypePtr & type)
|
||||
{
|
||||
return castColumn<CastType::nonAccurate>(arg, type);
|
||||
}
|
||||
|
||||
ColumnPtr castColumnAccurate(const ColumnWithTypeAndName & arg, const DataTypePtr & type)
|
||||
{
|
||||
return castColumn<CastType::accurate>(arg, type);
|
||||
}
|
||||
|
||||
ColumnPtr castColumnAccurateOrNull(const ColumnWithTypeAndName & arg, const DataTypePtr & type)
|
||||
{
|
||||
return castColumn<CastType::accurateOrNull>(arg, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
#include <Core/ColumnWithTypeAndName.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ColumnPtr castColumn(const ColumnWithTypeAndName & arg, const DataTypePtr & type);
|
||||
ColumnPtr castColumnAccurate(const ColumnWithTypeAndName & arg, const DataTypePtr & type);
|
||||
ColumnPtr castColumnAccurateOrNull(const ColumnWithTypeAndName & arg, const DataTypePtr & type);
|
||||
|
||||
}
|
||||
|
@ -1135,7 +1135,7 @@ bool KeyCondition::tryParseAtomFromAST(const ASTPtr & node, const Context & cont
|
||||
ColumnsWithTypeAndName arguments{
|
||||
{nullptr, key_expr_type, ""}, {DataTypeString().createColumnConst(1, common_type->getName()), common_type, ""}};
|
||||
FunctionOverloadResolverPtr func_builder_cast
|
||||
= std::make_shared<FunctionOverloadResolverAdaptor>(CastOverloadResolver::createImpl(false));
|
||||
= std::make_shared<FunctionOverloadResolverAdaptor>(CastOverloadResolver<CastType::nonAccurate>::createImpl(false));
|
||||
auto func_cast = func_builder_cast->build(arguments);
|
||||
|
||||
/// If we know the given range only contains one value, then we treat all functions as positive monotonic.
|
||||
|
@ -4,6 +4,7 @@
|
||||
1 Nullable(Int32)
|
||||
2 Nullable(Float32)
|
||||
2 Nullable(UInt8)
|
||||
00000000-0000-0002-0000-000000000000 Nullable(UUID)
|
||||
3 Nullable(Int32)
|
||||
\N Nullable(Int32)
|
||||
42 Nullable(Int32)
|
||||
|
@ -10,7 +10,7 @@ SELECT CAST(toNullable(toInt8(1)) AS Int32) as x, toTypeName(x);
|
||||
|
||||
SELECT CAST(toNullable(toFloat32(2)), 'Float32') as x, toTypeName(x);
|
||||
SELECT CAST(toNullable(toFloat32(2)), 'UInt8') as x, toTypeName(x);
|
||||
SELECT CAST(toNullable(toFloat32(2)), 'UUID') as x, toTypeName(x); -- { serverError 70 }
|
||||
SELECT CAST(toNullable(toFloat32(2)), 'UUID') as x, toTypeName(x);
|
||||
|
||||
SELECT CAST(if(1 = 1, toNullable(toInt8(3)), NULL) AS Int32) as x, toTypeName(x);
|
||||
SELECT CAST(if(1 = 0, toNullable(toInt8(3)), NULL) AS Int32) as x, toTypeName(x);
|
||||
|
@ -0,0 +1,30 @@
|
||||
\N
|
||||
5
|
||||
\N
|
||||
\N
|
||||
5
|
||||
\N
|
||||
\N
|
||||
5
|
||||
\N
|
||||
\N
|
||||
5
|
||||
\N
|
||||
5
|
||||
\N
|
||||
5
|
||||
\N
|
||||
\N
|
||||
1.000000000
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
||||
\N
|
35
tests/queries/0_stateless/01556_accurate_cast_or_null.sql
Normal file
35
tests/queries/0_stateless/01556_accurate_cast_or_null.sql
Normal file
@ -0,0 +1,35 @@
|
||||
SELECT accurateCastOrNull(-1, 'UInt8');
|
||||
SELECT accurateCastOrNull(5, 'UInt8');
|
||||
SELECT accurateCastOrNull(257, 'UInt8');
|
||||
SELECT accurateCastOrNull(-1, 'UInt16');
|
||||
SELECT accurateCastOrNull(5, 'UInt16');
|
||||
SELECT accurateCastOrNull(65536, 'UInt16');
|
||||
SELECT accurateCastOrNull(-1, 'UInt32');
|
||||
SELECT accurateCastOrNull(5, 'UInt32');
|
||||
SELECT accurateCastOrNull(4294967296, 'UInt32');
|
||||
SELECT accurateCastOrNull(-1, 'UInt64');
|
||||
SELECT accurateCastOrNull(5, 'UInt64');
|
||||
SELECT accurateCastOrNull(-1, 'UInt256');
|
||||
SELECT accurateCastOrNull(5, 'UInt256');
|
||||
|
||||
SELECT accurateCastOrNull(-129, 'Int8');
|
||||
SELECT accurateCastOrNull(5, 'Int8');
|
||||
SELECT accurateCastOrNull(128, 'Int8');
|
||||
|
||||
SELECT accurateCastOrNull(10, 'Decimal32(9)');
|
||||
SELECT accurateCastOrNull(1, 'Decimal32(9)');
|
||||
SELECT accurateCastOrNull(-10, 'Decimal32(9)');
|
||||
|
||||
SELECT accurateCastOrNull('123', 'FixedString(2)');
|
||||
|
||||
SELECT accurateCastOrNull(inf, 'Int64');
|
||||
SELECT accurateCastOrNull(inf, 'Int128');
|
||||
SELECT accurateCastOrNull(inf, 'Int256');
|
||||
SELECT accurateCastOrNull(nan, 'Int64');
|
||||
SELECT accurateCastOrNull(nan, 'Int128');
|
||||
SELECT accurateCastOrNull(nan, 'Int256');
|
||||
|
||||
SELECT accurateCastOrNull(inf, 'UInt64');
|
||||
SELECT accurateCastOrNull(inf, 'UInt256');
|
||||
SELECT accurateCastOrNull(nan, 'UInt64');
|
||||
SELECT accurateCastOrNull(nan, 'UInt256');
|
@ -1,6 +0,0 @@
|
||||
-9223372036854775808
|
||||
170141183460469231731687303715884105727
|
||||
-9223372036854775808
|
||||
170141183460469231731687303715884105727
|
||||
0
|
||||
0
|
@ -1,10 +0,0 @@
|
||||
SELECT toInt64(inf);
|
||||
SELECT toInt128(inf);
|
||||
SELECT toInt256(inf); -- { serverError 48 }
|
||||
SELECT toInt64(nan);
|
||||
SELECT toInt128(nan);
|
||||
SELECT toInt256(nan); -- { serverError 48 }
|
||||
SELECT toUInt64(inf);
|
||||
SELECT toUInt256(inf); -- { serverError 48 }
|
||||
SELECT toUInt64(nan);
|
||||
SELECT toUInt256(nan); -- { serverError 48 }
|
10
tests/queries/0_stateless/01581_to_int_inf_nan.sql
Normal file
10
tests/queries/0_stateless/01581_to_int_inf_nan.sql
Normal file
@ -0,0 +1,10 @@
|
||||
SELECT toInt64(inf); -- { serverError 70 }
|
||||
SELECT toInt128(inf); -- { serverError 70 }
|
||||
SELECT toInt256(inf); -- { serverError 70 }
|
||||
SELECT toInt64(nan); -- { serverError 70 }
|
||||
SELECT toInt128(nan); -- { serverError 70 }
|
||||
SELECT toInt256(nan); -- { serverError 70 }
|
||||
SELECT toUInt64(inf); -- { serverError 70 }
|
||||
SELECT toUInt256(inf); -- { serverError 70 }
|
||||
SELECT toUInt64(nan); -- { serverError 70 }
|
||||
SELECT toUInt256(nan); -- { serverError 70 }
|
@ -0,0 +1,11 @@
|
||||
1
|
||||
0
|
||||
1
|
||||
1
|
||||
2
|
||||
2
|
||||
1
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
@ -0,0 +1,35 @@
|
||||
SELECT 1 IN (SELECT 1);
|
||||
SELECT -1 IN (SELECT 1);
|
||||
|
||||
DROP TABLE IF EXISTS select_in_test;
|
||||
|
||||
CREATE TABLE select_in_test(value UInt8) ENGINE=TinyLog;
|
||||
INSERT INTO select_in_test VALUES (1), (2), (3);
|
||||
|
||||
SELECT value FROM select_in_test WHERE value IN (-1);
|
||||
SELECT value FROM select_in_test WHERE value IN (SELECT -1);
|
||||
|
||||
SELECT value FROM select_in_test WHERE value IN (1);
|
||||
SELECT value FROM select_in_test WHERE value IN (SELECT 1);
|
||||
|
||||
DROP TABLE select_in_test;
|
||||
|
||||
CREATE TABLE select_in_test(value Int8) ENGINE=TinyLog;
|
||||
INSERT INTO select_in_test VALUES (-1), (2), (3);
|
||||
|
||||
SELECT value FROM select_in_test WHERE value IN (1);
|
||||
SELECT value FROM select_in_test WHERE value IN (SELECT 1);
|
||||
|
||||
SELECT value FROM select_in_test WHERE value IN (2);
|
||||
SELECT value FROM select_in_test WHERE value IN (SELECT 2);
|
||||
|
||||
DROP TABLE select_in_test;
|
||||
|
||||
SELECT 1 IN (1);
|
||||
SELECT '1' IN (SELECT 1);
|
||||
|
||||
SELECT 1 IN (SELECT 1) SETTINGS transform_null_in = 1;
|
||||
SELECT 1 IN (SELECT 'a') SETTINGS transform_null_in = 1;
|
||||
SELECT 'a' IN (SELECT 1) SETTINGS transform_null_in = 1; -- { serverError 6 }
|
||||
SELECT 1 IN (SELECT -1) SETTINGS transform_null_in = 1;
|
||||
SELECT -1 IN (SELECT 1) SETTINGS transform_null_in = 1; -- { serverError 70 }
|
8
tests/queries/0_stateless/01601_accurate_cast.reference
Normal file
8
tests/queries/0_stateless/01601_accurate_cast.reference
Normal file
@ -0,0 +1,8 @@
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
1.000000000
|
||||
12
|
24
tests/queries/0_stateless/01601_accurate_cast.sql
Normal file
24
tests/queries/0_stateless/01601_accurate_cast.sql
Normal file
@ -0,0 +1,24 @@
|
||||
SELECT accurateCast(-1, 'UInt8'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'UInt8');
|
||||
SELECT accurateCast(257, 'UInt8'); -- { serverError 70 }
|
||||
SELECT accurateCast(-1, 'UInt16'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'UInt16');
|
||||
SELECT accurateCast(65536, 'UInt16'); -- { serverError 70 }
|
||||
SELECT accurateCast(-1, 'UInt32'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'UInt32');
|
||||
SELECT accurateCast(4294967296, 'UInt32'); -- { serverError 70 }
|
||||
SELECT accurateCast(-1, 'UInt64'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'UInt64');
|
||||
SELECT accurateCast(-1, 'UInt256'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'UInt256');
|
||||
|
||||
SELECT accurateCast(-129, 'Int8'); -- { serverError 70 }
|
||||
SELECT accurateCast(5, 'Int8');
|
||||
SELECT accurateCast(128, 'Int8'); -- { serverError 70 }
|
||||
|
||||
SELECT accurateCast(10, 'Decimal32(9)'); -- { serverError 407 }
|
||||
SELECT accurateCast(1, 'Decimal32(9)');
|
||||
SELECT accurateCast(-10, 'Decimal32(9)'); -- { serverError 407 }
|
||||
|
||||
SELECT accurateCast('123', 'FixedString(2)'); -- { serverError 131 }
|
||||
SELECT accurateCast('12', 'FixedString(2)');
|
Loading…
Reference in New Issue
Block a user