#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int LOGICAL_ERROR; extern const int TYPE_MISMATCH; } /** Checking for a `Field from` of `From` type falls to a range of values of type `To`. * `From` and `To` - numeric types. They can be floating-point types. * `From` is one of UInt64, Int64, Float64, * whereas `To` can also be 8, 16, 32 bit. * * If falls into a range, then `from` is converted to the `Field` closest to the `To` type. * If not, return Field(Null). */ namespace { template static Field convertNumericTypeImpl(const Field & from) { From value = from.get(); if (!accurate::equalsOp(value, To(value))) return {}; return Field(typename NearestFieldType::Type(value)); } template static Field convertNumericType(const Field & from, const IDataType & type) { if (from.getType() == Field::Types::UInt64) return convertNumericTypeImpl(from); if (from.getType() == Field::Types::Int64) return convertNumericTypeImpl(from); if (from.getType() == Field::Types::Float64) return convertNumericTypeImpl(from); throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: " + Field::Types::toString(from.getType()), ErrorCodes::TYPE_MISMATCH); } DayNum_t stringToDate(const String & s) { ReadBufferFromString in(s); DayNum_t date{}; readDateText(date, in); if (!in.eof()) throw Exception("String is too long for Date: " + s); return date; } UInt64 stringToDateTime(const String & s) { ReadBufferFromString in(s); time_t date_time{}; readDateTimeText(date_time, in); if (!in.eof()) throw Exception("String is too long for DateTime: " + s); return UInt64(date_time); } UUID stringToUUID(const String & s) { ReadBufferFromString in(s); UUID uuid; readText(uuid, in); if (!in.eof()) throw Exception("String is too long for UUID: " + s); return uuid; } Field convertFieldToTypeImpl(const Field & src, const IDataType & type) { if (type.isNumeric()) { if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); if (typeid_cast(&type)) return convertNumericType(src, type); const bool is_date = typeid_cast(&type); bool is_datetime = false; bool is_enum = false; bool is_uuid = false; if (!is_date) if (!(is_datetime = typeid_cast(&type))) if (!(is_uuid = typeid_cast(&type))) if (!(is_enum = dynamic_cast(&type))) throw Exception{"Logical error: unknown numeric type " + type.getName(), ErrorCodes::LOGICAL_ERROR}; /// Numeric values for Enums should not be used directly in IN section if (src.getType() == Field::Types::UInt64 && !is_enum) return src; if (src.getType() == Field::Types::String) { if (is_date) { /// Convert 'YYYY-MM-DD' Strings to Date return UInt64(stringToDate(src.get())); } else if (is_datetime) { /// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime return stringToDateTime(src.get()); } else if (is_uuid) { return stringToUUID(src.get()); } else if (is_enum) { /// Convert String to Enum's value return dynamic_cast(type).castToValue(src); } } throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: " + Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH); } else if (const DataTypeArray * type_array = typeid_cast(&type)) { if (src.getType() != Field::Types::Array) throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: " + Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH); const IDataType & nested_type = *DataTypeTraits::removeNullable(type_array->getNestedType()); const Array & src_arr = src.get(); size_t src_arr_size = src_arr.size(); Array res(src_arr_size); for (size_t i = 0; i < src_arr_size; ++i) res[i] = convertFieldToType(src_arr[i], nested_type); return res; } else { if (src.getType() == Field::Types::UInt64 || src.getType() == Field::Types::Int64 || src.getType() == Field::Types::Float64 || src.getType() == Field::Types::Array || (src.getType() == Field::Types::String && !typeid_cast(&type) && !typeid_cast(&type))) throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: " + Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH); } return src; } } Field convertFieldToType(const Field & from_value, const IDataType & to_type, const IDataType * from_type_hint) { if (from_value.isNull()) return from_value; if (from_type_hint && from_type_hint->equals(to_type)) return from_value; if (to_type.isNullable()) { const DataTypeNullable & nullable_type = static_cast(to_type); const DataTypePtr & nested_type = nullable_type.getNestedType(); return convertFieldToTypeImpl(from_value, *nested_type); } else return convertFieldToTypeImpl(from_value, to_type); } }