Fixed type check in toDateTime64

This commit is contained in:
Vasily Nemkov 2019-12-23 17:54:06 +03:00
parent 94318c50a1
commit 87f58864d3
4 changed files with 60 additions and 27 deletions

View File

@ -534,6 +534,7 @@ struct WhichDataType
inline bool isDate(const DataTypePtr & data_type) { return WhichDataType(data_type).isDate(); }
inline bool isDateOrDateTime(const DataTypePtr & data_type) { return WhichDataType(data_type).isDateOrDateTime(); }
inline bool isDateTime(const DataTypePtr & data_type) { return WhichDataType(data_type).isDateTime(); }
inline bool isDateTime64(const DataTypePtr & data_type) { return WhichDataType(data_type).isDateTime64(); }
inline bool isEnum(const DataTypePtr & data_type) { return WhichDataType(data_type).isEnum(); }
inline bool isDecimal(const DataTypePtr & data_type) { return WhichDataType(data_type).isDecimal(); }

View File

@ -19,6 +19,7 @@ namespace ErrorCodes
extern const int ILLEGAL_COLUMN;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int SIZES_OF_ARRAYS_DOESNT_MATCH;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}
const ColumnConst * checkAndGetColumnConstStringOrFixedString(const IColumn * column)
@ -124,9 +125,9 @@ namespace
void validateArgumentsImpl(const IFunction & func,
const ColumnsWithTypeAndName & arguments,
size_t argument_offset,
const FunctionArgumentTypeValidators & validators)
const FunctionArgumentDescriptors & decriptors)
{
for (size_t i = 0; i < validators.size(); ++i)
for (size_t i = 0; i < decriptors.size(); ++i)
{
const auto argument_index = i + argument_offset;
if (argument_index >= arguments.size())
@ -135,24 +136,36 @@ void validateArgumentsImpl(const IFunction & func,
}
const auto & arg = arguments[i + argument_offset];
const auto validator = validators[i];
if (!validator.validator_func(*arg.type))
throw Exception("Illegal type " + arg.type->getName() +
" of " + std::to_string(i) +
" argument of function " + func.getName() +
" expected " + validator.expected_type_description,
const auto validator = decriptors[i];
if (!validator.isValid(arg.type, arg.column))
throw Exception("Illegal type of argument #" + std::to_string(i)
+ (validator.argument_name ? " '" + std::string(validator.argument_name) + "'": std::string{})
+ " of function " + func.getName()
+ ", expected " + validator.expected_type_description
+ (arg.type ? ", got " + arg.type->getName() : std::string{}),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
}
}
bool FunctionArgumentDescriptor::isValid(const DataTypePtr & data_type, const ColumnPtr & column) const
{
if (type_validator_func && !(data_type && type_validator_func(*data_type)))
return false;
if (column_validator_func && !(column && column_validator_func(*column)))
return false;
return true;
}
void validateFunctionArgumentTypes(const IFunction & func,
const ColumnsWithTypeAndName & arguments,
const FunctionArgumentTypeValidators & mandatory_args,
const FunctionArgumentTypeValidators & optional_args)
const FunctionArgumentDescriptors & mandatory_args,
const FunctionArgumentDescriptors & optional_args)
{
if (arguments.size() < mandatory_args.size())
if (arguments.size() < mandatory_args.size() || arguments.size() > mandatory_args.size() + optional_args.size())
{
auto joinArgumentTypes = [](const auto & args, const String sep = ", ") -> String
{
@ -160,8 +173,12 @@ void validateFunctionArgumentTypes(const IFunction & func,
for (const auto & a : args)
{
using A = std::decay_t<decltype(a)>;
if constexpr (std::is_same_v<A, FunctionArgumentTypeValidator>)
if constexpr (std::is_same_v<A, FunctionArgumentDescriptor>)
{
if (a.argument_name)
result += "'" + std::string(a.argument_name) + "' : ";
result += a.expected_type_description;
}
else if constexpr (std::is_same_v<A, ColumnWithTypeAndName>)
result += a.type->getName();
@ -174,10 +191,14 @@ void validateFunctionArgumentTypes(const IFunction & func,
return result;
};
throw Exception("Incorrect number of arguments of function " + func.getName()
+ " provided " + std::to_string(arguments.size()) + " (" + joinArgumentTypes(arguments) + ")"
+ " expected " + std::to_string(mandatory_args.size()) + (optional_args.size() ? " or " + std::to_string(mandatory_args.size() + optional_args.size()) : "")
+ " (" + joinArgumentTypes(mandatory_args) + (optional_args.size() ? ", [" + joinArgumentTypes(mandatory_args) + "]" : "") + ")",
throw Exception("Incorrect number of arguments for function " + func.getName()
+ " provided " + std::to_string(arguments.size())
+ (arguments.size() ? " (" + joinArgumentTypes(arguments) + ")" : String{} )
+ ", expected " + std::to_string(mandatory_args.size())
+ (optional_args.size() ? " to " + std::to_string(mandatory_args.size() + optional_args.size()) : "")
+ " (" + joinArgumentTypes(mandatory_args)
+ (optional_args.size() ? ", [" + joinArgumentTypes(optional_args) + "]" : "")
+ ")",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
}

View File

@ -91,13 +91,19 @@ void validateArgumentType(const IFunction & func, const DataTypes & arguments,
const char * expected_type_description);
// Simple validator that is used in conjunction with validateFunctionArgumentTypes() to check if function arguments are as expected.
struct FunctionArgumentTypeValidator
struct FunctionArgumentDescriptor
{
bool (* validator_func)(const IDataType &);
const char * argument_name;
bool (* type_validator_func)(const IDataType &);
bool (* column_validator_func)(const IColumn &);
const char * expected_type_description;
bool isValid(const DataTypePtr & data_type, const ColumnPtr & column) const;
};
using FunctionArgumentTypeValidators = std::vector<FunctionArgumentTypeValidator>;
using FunctionArgumentDescriptors = std::vector<FunctionArgumentDescriptor>;
/** Validate that function arguments match specification.
*
@ -117,7 +123,9 @@ using FunctionArgumentTypeValidators = std::vector<FunctionArgumentTypeValidator
*
* If any mandatory arg is missing, throw an exception, with explicit description of expected arguments.
*/
void validateFunctionArgumentTypes(const IFunction & func, const ColumnsWithTypeAndName & arguments, const FunctionArgumentTypeValidators & mandatory_args, const FunctionArgumentTypeValidators & optional_args = {});
void validateFunctionArgumentTypes(const IFunction & func, const ColumnsWithTypeAndName & arguments,
const FunctionArgumentDescriptors & mandatory_args,
const FunctionArgumentDescriptors & optional_args = {});
/// Checks if a list of array columns have equal offsets. Return a pair of nested columns and offsets if true, otherwise throw.
std::pair<std::vector<const IColumn *>, const ColumnArray::Offset *>

View File

@ -898,16 +898,19 @@ public:
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
{
FunctionArgumentTypeValidators mandatory_args = {{[](const auto &) {return true;}, "ANY TYPE"}};
FunctionArgumentTypeValidators optional_args;
FunctionArgumentDescriptors mandatory_args = {{"Value", nullptr, nullptr, "ANY TYPE"}};
FunctionArgumentDescriptors optional_args;
if constexpr (to_decimal || to_datetime64)
{
mandatory_args.push_back(FunctionArgumentTypeValidator{&isNativeInteger, "Integer"}); // scale
mandatory_args.push_back({"scale", &isNativeInteger, &isColumnConst, "const Integer"});
}
else
// toString(DateTime or DateTime64, [timezone: String])
if ((std::is_same_v<Name, NameToString> && arguments.size() > 0 && (isDateTime64(arguments[0].type) || isDateTime(arguments[0].type)))
// toDateTime(value, [timezone: String]) or toDateTime64(value, scale : Integer, [timezone: string])
|| std::is_same_v<ToDataType, DataTypeDateTime> || std::is_same_v<ToDataType, DataTypeDateTime64>)
{
optional_args.push_back(FunctionArgumentTypeValidator{&isString, "String"}); // timezone
optional_args.push_back({"timezone", &isString, &isColumnConst, "const String"});
}
validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args);
@ -918,8 +921,8 @@ public:
}
else if constexpr (to_decimal)
{
if (!arguments[1].column)
throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN);
// if (!arguments[1].column)
// throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN);
UInt64 scale = extractToDecimalScale(arguments[1]);