2017-05-23 14:38:14 +00:00
|
|
|
#include <Interpreters/convertFieldToType.h>
|
|
|
|
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <IO/ReadBufferFromString.h>
|
|
|
|
#include <IO/ReadHelpers.h>
|
2016-02-13 06:37:19 +00:00
|
|
|
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <DataTypes/DataTypeArray.h>
|
2017-12-03 08:16:49 +00:00
|
|
|
#include <DataTypes/DataTypeTuple.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
2018-07-23 20:19:26 +00:00
|
|
|
#include <DataTypes/DataTypesDecimal.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <DataTypes/DataTypeString.h>
|
|
|
|
#include <DataTypes/DataTypeFixedString.h>
|
|
|
|
#include <DataTypes/DataTypeDate.h>
|
|
|
|
#include <DataTypes/DataTypeDateTime.h>
|
|
|
|
#include <DataTypes/DataTypeEnum.h>
|
|
|
|
#include <DataTypes/DataTypeNullable.h>
|
2016-02-13 06:37:19 +00:00
|
|
|
|
2017-05-24 20:25:36 +00:00
|
|
|
#include <Core/AccurateComparison.h>
|
2017-11-24 13:55:31 +00:00
|
|
|
#include <Common/FieldVisitors.h>
|
2017-07-13 20:58:19 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2018-02-28 04:30:27 +00:00
|
|
|
#include <Common/NaNUtils.h>
|
2017-07-04 10:42:53 +00:00
|
|
|
#include <DataTypes/DataTypeUUID.h>
|
2018-09-27 15:55:22 +00:00
|
|
|
#include <DataTypes/DataTypeLowCardinality.h>
|
2016-02-13 06:37:19 +00:00
|
|
|
|
2018-09-26 19:43:10 +00:00
|
|
|
#include <common/DateLUT.h>
|
|
|
|
|
2018-02-28 04:30:27 +00:00
|
|
|
|
2016-02-13 06:37:19 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int TYPE_MISMATCH;
|
2016-02-13 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-02 17:37:49 +00:00
|
|
|
/** 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.
|
2016-02-13 06:37:19 +00:00
|
|
|
*
|
2017-04-02 17:37:49 +00:00
|
|
|
* If falls into a range, then `from` is converted to the `Field` closest to the `To` type.
|
|
|
|
* If not, return Field(Null).
|
2016-02-13 06:37:19 +00:00
|
|
|
*/
|
|
|
|
|
2016-08-24 16:02:34 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
2016-02-13 06:37:19 +00:00
|
|
|
template <typename From, typename To>
|
|
|
|
static Field convertNumericTypeImpl(const Field & from)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
From value = from.get<From>();
|
2016-02-13 06:37:19 +00:00
|
|
|
|
2018-02-28 04:30:27 +00:00
|
|
|
/// 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>)
|
|
|
|
return value;
|
|
|
|
|
2017-05-23 14:38:14 +00:00
|
|
|
if (!accurate::equalsOp(value, To(value)))
|
2017-04-01 07:20:54 +00:00
|
|
|
return {};
|
2016-02-13 06:37:19 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
return Field(typename NearestFieldType<To>::Type(value));
|
2016-02-13 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename To>
|
|
|
|
static Field convertNumericType(const Field & from, const IDataType & type)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (from.getType() == Field::Types::UInt64)
|
|
|
|
return convertNumericTypeImpl<UInt64, To>(from);
|
|
|
|
if (from.getType() == Field::Types::Int64)
|
|
|
|
return convertNumericTypeImpl<Int64, To>(from);
|
|
|
|
if (from.getType() == Field::Types::Float64)
|
|
|
|
return convertNumericTypeImpl<Float64, To>(from);
|
|
|
|
|
|
|
|
throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: "
|
|
|
|
+ Field::Types::toString(from.getType()), ErrorCodes::TYPE_MISMATCH);
|
2016-02-13 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-23 20:19:26 +00:00
|
|
|
template <typename From, typename To>
|
2018-07-30 18:10:38 +00:00
|
|
|
static Field convertIntToDecimalType(const Field & from, const To & type)
|
2018-07-23 20:19:26 +00:00
|
|
|
{
|
2018-07-25 19:38:21 +00:00
|
|
|
using FieldType = typename To::FieldType;
|
2018-07-23 20:19:26 +00:00
|
|
|
|
|
|
|
From value = from.get<From>();
|
|
|
|
if (!type.canStoreWhole(value))
|
|
|
|
throw Exception("Number is too much to place in " + type.getName(), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
|
|
|
|
|
|
|
FieldType scaled_value = type.getScaleMultiplier() * value;
|
2018-08-24 16:35:00 +00:00
|
|
|
return Field(typename NearestFieldType<FieldType>::Type(scaled_value, type.getScale()));
|
2018-07-23 20:19:26 +00:00
|
|
|
}
|
|
|
|
|
2018-07-30 18:10:38 +00:00
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static Field convertStringToDecimalType(const Field & from, const DataTypeDecimal<T> & type)
|
|
|
|
{
|
|
|
|
using FieldType = typename DataTypeDecimal<T>::FieldType;
|
|
|
|
|
|
|
|
const String & str_value = from.get<String>();
|
|
|
|
T value = type.parseFromString(str_value);
|
2018-08-24 16:35:00 +00:00
|
|
|
return Field(typename NearestFieldType<FieldType>::Type(value, type.getScale()));
|
2018-07-30 18:10:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-23 20:19:26 +00:00
|
|
|
template <typename To>
|
2018-07-30 18:10:38 +00:00
|
|
|
static Field convertDecimalType(const Field & from, const To & type)
|
2018-07-23 20:19:26 +00:00
|
|
|
{
|
|
|
|
if (from.getType() == Field::Types::UInt64)
|
2018-07-30 18:10:38 +00:00
|
|
|
return convertIntToDecimalType<UInt64>(from, type);
|
2018-07-23 20:19:26 +00:00
|
|
|
if (from.getType() == Field::Types::Int64)
|
2018-07-30 18:10:38 +00:00
|
|
|
return convertIntToDecimalType<Int64>(from, type);
|
|
|
|
if (from.getType() == Field::Types::String)
|
|
|
|
return convertStringToDecimalType(from, type);
|
2018-07-23 20:19:26 +00:00
|
|
|
|
|
|
|
throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: "
|
|
|
|
+ Field::Types::toString(from.getType()), ErrorCodes::TYPE_MISMATCH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-25 13:29:15 +00:00
|
|
|
DayNum stringToDate(const String & s)
|
2017-05-23 14:38:14 +00:00
|
|
|
{
|
|
|
|
ReadBufferFromString in(s);
|
2018-05-25 13:29:15 +00:00
|
|
|
DayNum date{};
|
2017-05-23 14:38:14 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-26 19:43:10 +00:00
|
|
|
Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const IDataType * from_type_hint)
|
2016-02-13 06:37:19 +00:00
|
|
|
{
|
2018-09-26 19:43:10 +00:00
|
|
|
WhichDataType which_type(type);
|
|
|
|
WhichDataType which_from_type;
|
|
|
|
if (from_type_hint)
|
|
|
|
which_from_type = WhichDataType(*from_type_hint);
|
|
|
|
|
|
|
|
/// Conversion between Date and DateTime and vice versa.
|
|
|
|
if (which_type.isDate() && which_from_type.isDateTime())
|
|
|
|
{
|
|
|
|
return UInt64(static_cast<const DataTypeDateTime &>(*from_type_hint).getTimeZone().toDayNum(src.get<UInt64>()));
|
|
|
|
}
|
|
|
|
else if (which_type.isDateTime() && which_from_type.isDate())
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-26 19:43:10 +00:00
|
|
|
return UInt64(static_cast<const DataTypeDateTime &>(type).getTimeZone().fromDayNum(DayNum(src.get<UInt64>())));
|
|
|
|
}
|
|
|
|
else if (type.isValueRepresentedByNumber())
|
|
|
|
{
|
|
|
|
if (which_type.isUInt8()) return convertNumericType<UInt8>(src, type);
|
|
|
|
if (which_type.isUInt16()) return convertNumericType<UInt16>(src, type);
|
|
|
|
if (which_type.isUInt32()) return convertNumericType<UInt32>(src, type);
|
|
|
|
if (which_type.isUInt64()) return convertNumericType<UInt64>(src, type);
|
|
|
|
if (which_type.isInt8()) return convertNumericType<Int8>(src, type);
|
|
|
|
if (which_type.isInt16()) return convertNumericType<Int16>(src, type);
|
|
|
|
if (which_type.isInt32()) return convertNumericType<Int32>(src, type);
|
|
|
|
if (which_type.isInt64()) return convertNumericType<Int64>(src, type);
|
|
|
|
if (which_type.isFloat32()) return convertNumericType<Float32>(src, type);
|
|
|
|
if (which_type.isFloat64()) return convertNumericType<Float64>(src, type);
|
2018-08-21 04:31:35 +00:00
|
|
|
if (auto * ptype = typeid_cast<const DataTypeDecimal<Decimal32> *>(&type)) return convertDecimalType(src, *ptype);
|
|
|
|
if (auto * ptype = typeid_cast<const DataTypeDecimal<Decimal64> *>(&type)) return convertDecimalType(src, *ptype);
|
|
|
|
if (auto * ptype = typeid_cast<const DataTypeDecimal<Decimal128> *>(&type)) return convertDecimalType(src, *ptype);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-09-26 19:43:10 +00:00
|
|
|
if (!which_type.isDateOrDateTime() && !which_type.isUUID() && !which_type.isEnum())
|
|
|
|
throw Exception{"Logical error: unknown numeric type " + type.getName(), ErrorCodes::LOGICAL_ERROR};
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
/// Numeric values for Enums should not be used directly in IN section
|
2018-09-26 19:43:10 +00:00
|
|
|
if (src.getType() == Field::Types::UInt64 && !which_type.isEnum())
|
2017-04-01 07:20:54 +00:00
|
|
|
return src;
|
|
|
|
|
|
|
|
if (src.getType() == Field::Types::String)
|
|
|
|
{
|
2018-09-26 19:43:10 +00:00
|
|
|
if (which_type.isDate())
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
/// Convert 'YYYY-MM-DD' Strings to Date
|
|
|
|
return UInt64(stringToDate(src.get<const String &>()));
|
|
|
|
}
|
2018-09-26 19:43:10 +00:00
|
|
|
else if (which_type.isDateTime())
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
/// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime
|
|
|
|
return stringToDateTime(src.get<const String &>());
|
|
|
|
}
|
2018-09-26 19:43:10 +00:00
|
|
|
else if (which_type.isUUID())
|
2017-06-15 09:12:32 +00:00
|
|
|
{
|
2017-07-04 10:42:53 +00:00
|
|
|
return stringToUUID(src.get<const String &>());
|
2017-06-15 09:12:32 +00:00
|
|
|
}
|
2018-09-26 19:43:10 +00:00
|
|
|
else if (which_type.isEnum())
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
/// Convert String to Enum's value
|
|
|
|
return dynamic_cast<const IDataTypeEnum &>(type).castToValue(src);
|
|
|
|
}
|
|
|
|
}
|
2017-12-03 08:16:49 +00:00
|
|
|
}
|
2018-09-26 19:43:10 +00:00
|
|
|
else if (which_type.isStringOrFixedString())
|
2017-12-03 08:16:49 +00:00
|
|
|
{
|
|
|
|
if (src.getType() == Field::Types::String)
|
|
|
|
return src;
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
else if (const DataTypeArray * type_array = typeid_cast<const DataTypeArray *>(&type))
|
|
|
|
{
|
2017-12-03 08:16:49 +00:00
|
|
|
if (src.getType() == Field::Types::Array)
|
|
|
|
{
|
2017-12-09 07:32:32 +00:00
|
|
|
const DataTypePtr nested_type = removeNullable(type_array->getNestedType());
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-12-03 08:16:49 +00:00
|
|
|
const Array & src_arr = src.get<Array>();
|
|
|
|
size_t src_arr_size = src_arr.size();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-12-03 08:16:49 +00:00
|
|
|
Array res(src_arr_size);
|
|
|
|
for (size_t i = 0; i < src_arr_size; ++i)
|
2017-12-09 07:32:32 +00:00
|
|
|
res[i] = convertFieldToType(src_arr[i], *nested_type);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-12-03 08:16:49 +00:00
|
|
|
return res;
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-12-03 08:16:49 +00:00
|
|
|
else if (const DataTypeTuple * type_tuple = typeid_cast<const DataTypeTuple *>(&type))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-12-03 08:16:49 +00:00
|
|
|
if (src.getType() == Field::Types::Tuple)
|
|
|
|
{
|
|
|
|
const TupleBackend & src_tuple = src.get<Tuple>();
|
|
|
|
size_t src_tuple_size = src_tuple.size();
|
|
|
|
size_t dst_tuple_size = type_tuple->getElements().size();
|
|
|
|
|
|
|
|
if (dst_tuple_size != src_tuple_size)
|
|
|
|
throw Exception("Bad size of tuple in IN or VALUES section. Expected size: "
|
|
|
|
+ toString(dst_tuple_size) + ", actual size: " + toString(src_tuple_size), ErrorCodes::TYPE_MISMATCH);
|
|
|
|
|
|
|
|
TupleBackend res(dst_tuple_size);
|
|
|
|
for (size_t i = 0; i < dst_tuple_size; ++i)
|
|
|
|
res[i] = convertFieldToType(src_tuple[i], *type_tuple->getElements()[i]);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2017-12-03 08:16:49 +00:00
|
|
|
throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: "
|
|
|
|
+ Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH);
|
2016-02-13 06:37:19 +00:00
|
|
|
}
|
|
|
|
|
2016-08-24 16:02:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 22:05:48 +00:00
|
|
|
Field convertFieldToType(const Field & from_value, const IDataType & to_type, const IDataType * from_type_hint)
|
2016-08-24 16:02:34 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (from_value.isNull())
|
|
|
|
return from_value;
|
|
|
|
|
|
|
|
if (from_type_hint && from_type_hint->equals(to_type))
|
|
|
|
return from_value;
|
|
|
|
|
2018-09-27 15:55:22 +00:00
|
|
|
if (auto * low_cardinality_type = typeid_cast<const DataTypeLowCardinality *>(&to_type))
|
|
|
|
return convertFieldToType(from_value, *low_cardinality_type->getDictionaryType(), from_type_hint);
|
2018-07-20 15:41:01 +00:00
|
|
|
else if (auto * nullable_type = typeid_cast<const DataTypeNullable *>(&to_type))
|
2018-09-26 19:43:10 +00:00
|
|
|
return convertFieldToTypeImpl(from_value, *nullable_type->getNestedType(), from_type_hint);
|
2017-04-01 07:20:54 +00:00
|
|
|
else
|
2018-09-26 19:43:10 +00:00
|
|
|
return convertFieldToTypeImpl(from_value, to_type, from_type_hint);
|
2016-08-24 16:02:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-13 06:37:19 +00:00
|
|
|
}
|