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:
alexey-milovidov 2020-12-18 00:45:27 +03:00 committed by GitHub
commit 6fc3ca8b7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 973 additions and 293 deletions

View File

@ -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.

View File

@ -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(); }
};
}

View File

@ -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);

View File

@ -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>

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -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>();

View File

@ -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 {};
}
};
}

View File

@ -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));
}
}
}
};

View File

@ -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);

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -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

View 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');

View File

@ -1,6 +0,0 @@
-9223372036854775808
170141183460469231731687303715884105727
-9223372036854775808
170141183460469231731687303715884105727
0
0

View File

@ -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 }

View 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 }

View File

@ -0,0 +1,11 @@
1
0
1
1
2
2
1
1
1
0
0

View File

@ -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 }

View File

@ -0,0 +1,8 @@
5
5
5
5
5
5
1.000000000
12

View 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)');