mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 07:01:59 +00:00
Fixed parseDateTime64BestEffort implementation
Fixed argument resolution issues. Added tests and made sure -orNull and -orZero variants alwo work correctly.
This commit is contained in:
parent
ee665d6837
commit
403aae9126
@ -1110,6 +1110,8 @@ public:
|
||||
std::is_same_v<ToDataType, DataTypeDecimal<Decimal64>> ||
|
||||
std::is_same_v<ToDataType, DataTypeDecimal<Decimal128>>;
|
||||
|
||||
static constexpr bool to_datetime64 = std::is_same_v<ToDataType, DataTypeDateTime64>;
|
||||
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionConvertFromString>(); }
|
||||
static FunctionPtr create() { return std::make_shared<FunctionConvertFromString>(); }
|
||||
|
||||
@ -1126,67 +1128,17 @@ public:
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if ((arguments.size() != 1 && arguments.size() != 2) || (to_decimal && arguments.size() != 2))
|
||||
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) +
|
||||
", should be 1 or 2. Second argument only make sense for DateTime (time zone, optional) and Decimal (scale).",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
if (!isStringOrFixedString(arguments[0].type))
|
||||
{
|
||||
if (this->getName().find("OrZero") != std::string::npos ||
|
||||
this->getName().find("OrNull") != std::string::npos)
|
||||
throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName() +
|
||||
". Conversion functions with postfix 'OrZero' or 'OrNull' should take String argument",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
else
|
||||
throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
}
|
||||
|
||||
if (arguments.size() == 2)
|
||||
{
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDateTime>)
|
||||
{
|
||||
if (!isString(arguments[1].type))
|
||||
throw Exception("Illegal type " + arguments[1].type->getName() + " of 2nd argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
}
|
||||
else if constexpr (to_decimal)
|
||||
{
|
||||
if (!isInteger(arguments[1].type))
|
||||
throw Exception("Illegal type " + arguments[1].type->getName() + " of 2nd argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
if (!arguments[1].column)
|
||||
throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
||||
+ toString(arguments.size()) + ", should be 1. Second argument makes sense only for DateTime and Decimal.",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
DataTypePtr res;
|
||||
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDateTime>)
|
||||
res = std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 1, 0));
|
||||
else if constexpr (to_decimal)
|
||||
if constexpr (to_datetime64)
|
||||
{
|
||||
UInt64 scale = extractToDecimalScale(arguments[1]);
|
||||
validateFunctionArgumentTypes(*this, arguments,
|
||||
FunctionArgumentDescriptors{{"string", isStringOrFixedString, nullptr, "String or FixedString"}},
|
||||
// optional
|
||||
FunctionArgumentDescriptors{
|
||||
{"precision", isUInt8, isColumnConst, "const UInt8"},
|
||||
{"timezone", isStringOrFixedString, isColumnConst, "const String or FixedString"},
|
||||
});
|
||||
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal32>>)
|
||||
res = createDecimal<DataTypeDecimal>(9, scale);
|
||||
else if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal64>>)
|
||||
res = createDecimal<DataTypeDecimal>(18, scale);
|
||||
else if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal128>>)
|
||||
res = createDecimal<DataTypeDecimal>(38, scale);
|
||||
|
||||
if (!res)
|
||||
throw Exception("Someting wrong with toDecimalNNOrZero() or toDecimalNNOrNull()", ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
else if constexpr (std::is_same_v<ToDataType, DataTypeDateTime64>)
|
||||
{
|
||||
UInt64 scale = DataTypeDateTime64::default_scale;
|
||||
if (arguments.size() > 1)
|
||||
scale = extractToDecimalScale(arguments[1]);
|
||||
@ -1194,7 +1146,67 @@ public:
|
||||
res = std::make_shared<DataTypeDateTime64>(scale, timezone);
|
||||
}
|
||||
else
|
||||
res = std::make_shared<ToDataType>();
|
||||
{
|
||||
if ((arguments.size() != 1 && arguments.size() != 2) || (to_decimal && arguments.size() != 2))
|
||||
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) +
|
||||
", should be 1 or 2. Second argument only make sense for DateTime (time zone, optional) and Decimal (scale).",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
if (!isStringOrFixedString(arguments[0].type))
|
||||
{
|
||||
if (this->getName().find("OrZero") != std::string::npos ||
|
||||
this->getName().find("OrNull") != std::string::npos)
|
||||
throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName() +
|
||||
". Conversion functions with postfix 'OrZero' or 'OrNull' should take String argument",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
else
|
||||
throw Exception("Illegal type " + arguments[0].type->getName() + " of first argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
}
|
||||
|
||||
if (arguments.size() == 2)
|
||||
{
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDateTime>)
|
||||
{
|
||||
if (!isString(arguments[1].type))
|
||||
throw Exception("Illegal type " + arguments[1].type->getName() + " of 2nd argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
}
|
||||
else if constexpr (to_decimal)
|
||||
{
|
||||
if (!isInteger(arguments[1].type))
|
||||
throw Exception("Illegal type " + arguments[1].type->getName() + " of 2nd argument of function " + getName(),
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
if (!arguments[1].column)
|
||||
throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
||||
+ toString(arguments.size()) + ", should be 1. Second argument makes sense only for DateTime and Decimal.",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDateTime>)
|
||||
res = std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 1, 0));
|
||||
else if constexpr (to_decimal)
|
||||
{
|
||||
UInt64 scale = extractToDecimalScale(arguments[1]);
|
||||
|
||||
if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal32>>)
|
||||
res = createDecimal<DataTypeDecimal>(9, scale);
|
||||
else if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal64>>)
|
||||
res = createDecimal<DataTypeDecimal>(18, scale);
|
||||
else if constexpr (std::is_same_v<ToDataType, DataTypeDecimal<Decimal128>>)
|
||||
res = createDecimal<DataTypeDecimal>(38, scale);
|
||||
|
||||
if (!res)
|
||||
throw Exception("Someting wrong with toDecimalNNOrZero() or toDecimalNNOrNull()", ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
else
|
||||
res = std::make_shared<ToDataType>();
|
||||
}
|
||||
|
||||
if constexpr (exception_mode == ConvertFromStringExceptionMode::Null)
|
||||
res = std::make_shared<DataTypeNullable>(res);
|
||||
@ -1207,12 +1219,9 @@ public:
|
||||
const IDataType * from_type = block.getByPosition(arguments[0]).type.get();
|
||||
|
||||
bool ok = true;
|
||||
if constexpr (to_decimal || std::is_same_v<ToDataType, DataTypeDateTime64>)
|
||||
if constexpr (to_decimal || to_datetime64)
|
||||
{
|
||||
if (arguments.size() != 2)
|
||||
throw Exception{"Function " + getName() + " expects 2 arguments for Decimal.", ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION};
|
||||
|
||||
UInt32 scale = extractToDecimalScale(block.getByPosition(arguments[1]));
|
||||
const UInt32 scale = assert_cast<const ToDataType &>(*removeNullable(block.getByPosition(result).type)).getScale();
|
||||
|
||||
if (checkAndGetDataType<DataTypeString>(from_type))
|
||||
{
|
||||
@ -1241,7 +1250,6 @@ public:
|
||||
}
|
||||
else
|
||||
ok = false;
|
||||
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
@ -1252,7 +1260,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Conversion to fixed string is implemented only for strings.
|
||||
*/
|
||||
class FunctionToFixedString : public IFunction
|
||||
|
@ -562,8 +562,17 @@ ReturnType parseDateTime64BestEffortImpl(DateTime64 & res, UInt32 scale, ReadBuf
|
||||
{
|
||||
time_t whole;
|
||||
DateTimeSubsecondPart subsecond = {0, 0}; // needs to be explicitly initialized sine it could be missing from input string
|
||||
if (!parseDateTimeBestEffortImpl<bool>(whole, in, local_time_zone, utc_time_zone, &subsecond))
|
||||
return ReturnType(false);
|
||||
|
||||
if constexpr (std::is_same_v<ReturnType, bool>)
|
||||
{
|
||||
if (!parseDateTimeBestEffortImpl<bool>(whole, in, local_time_zone, utc_time_zone, &subsecond))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseDateTimeBestEffortImpl<ReturnType>(whole, in, local_time_zone, utc_time_zone, &subsecond);
|
||||
}
|
||||
|
||||
|
||||
DateTime64::NativeType fractional = subsecond.value;
|
||||
if (scale < subsecond.digits)
|
||||
|
@ -0,0 +1,15 @@
|
||||
orNull
|
||||
2020-05-14 03:37:03.253
|
||||
\N
|
||||
orZero
|
||||
2020-05-14 03:37:03.253
|
||||
0000-00-00 00:00:00.000
|
||||
non-const
|
||||
2020-05-14 03:37:03.253
|
||||
Timezones
|
||||
2020-05-14 03:37:03.253
|
||||
2020-05-14 06:37:03.253
|
||||
Formats
|
||||
2020-05-14 03:37:03.253
|
||||
2020-05-14 03:37:03.000
|
||||
2020-05-14 03:37:03.000
|
@ -0,0 +1,33 @@
|
||||
-- Error cases
|
||||
SELECT parseDateTime64BestEffort(); -- {serverError 42}
|
||||
SELECT parseDateTime64BestEffort(123); -- {serverError 43}
|
||||
SELECT parseDateTime64BestEffort('foo'); -- {serverError 41}
|
||||
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 'bar'); -- {serverError 43} -- invalid scale parameter
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 3, 4); -- {serverError 43} -- invalid timezone parameter
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 3, 'baz'); -- {serverError 1000} -- unknown timezone
|
||||
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', materialize(3), 4); -- {serverError 44} -- non-const precision
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 3, materialize('UTC')); -- {serverError 44} -- non-const timezone
|
||||
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184012345678910111213141516171819Z', 3, 'UTC'); -- {serverError 6}
|
||||
|
||||
SELECT 'orNull';
|
||||
SELECT parseDateTime64BestEffortOrNull('2020-05-14T03:37:03.253184Z', 3, 'UTC');
|
||||
SELECT parseDateTime64BestEffortOrNull('foo', 3, 'UTC');
|
||||
|
||||
SELECT 'orZero';
|
||||
SELECT parseDateTime64BestEffortOrZero('2020-05-14T03:37:03.253184Z', 3, 'UTC');
|
||||
SELECT parseDateTime64BestEffortOrZero('bar', 3, 'UTC');
|
||||
|
||||
SELECT 'non-const';
|
||||
SELECT parseDateTime64BestEffort(materialize('2020-05-14T03:37:03.253184Z'), 3, 'UTC');
|
||||
|
||||
SELECT 'Timezones';
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 3, 'UTC');
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184Z', 3, 'Europe/Minsk');
|
||||
|
||||
SELECT 'Formats';
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03.253184', 3, 'UTC');
|
||||
SELECT parseDateTime64BestEffort('2020-05-14T03:37:03', 3, 'UTC');
|
||||
SELECT parseDateTime64BestEffort('2020-05-14 03:37:03', 3, 'UTC');
|
Loading…
Reference in New Issue
Block a user