From 87f58864d35c5103c6ede868bf2ffc2bb4fad897 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Mon, 23 Dec 2019 17:54:06 +0300 Subject: [PATCH] Fixed type check in toDateTime64 --- dbms/src/DataTypes/IDataType.h | 1 + dbms/src/Functions/FunctionHelpers.cpp | 53 +++++++++++++++++------- dbms/src/Functions/FunctionHelpers.h | 16 +++++-- dbms/src/Functions/FunctionsConversion.h | 17 ++++---- 4 files changed, 60 insertions(+), 27 deletions(-) diff --git a/dbms/src/DataTypes/IDataType.h b/dbms/src/DataTypes/IDataType.h index 73e9c1d78b4..92d0c1057c5 100644 --- a/dbms/src/DataTypes/IDataType.h +++ b/dbms/src/DataTypes/IDataType.h @@ -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(); } diff --git a/dbms/src/Functions/FunctionHelpers.cpp b/dbms/src/Functions/FunctionHelpers.cpp index 9531ad2c32e..4285ff93b07 100644 --- a/dbms/src/Functions/FunctionHelpers.cpp +++ b/dbms/src/Functions/FunctionHelpers.cpp @@ -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; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (a.argument_name) + result += "'" + std::string(a.argument_name) + "' : "; result += a.expected_type_description; + } else if constexpr (std::is_same_v) 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); } diff --git a/dbms/src/Functions/FunctionHelpers.h b/dbms/src/Functions/FunctionHelpers.h index 5f0488228f3..fd8aa6fc20c 100644 --- a/dbms/src/Functions/FunctionHelpers.h +++ b/dbms/src/Functions/FunctionHelpers.h @@ -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; +using FunctionArgumentDescriptors = std::vector; /** Validate that function arguments match specification. * @@ -117,7 +123,9 @@ using FunctionArgumentTypeValidators = std::vector, const ColumnArray::Offset *> diff --git a/dbms/src/Functions/FunctionsConversion.h b/dbms/src/Functions/FunctionsConversion.h index f6d6f615f0c..5065fc44182 100644 --- a/dbms/src/Functions/FunctionsConversion.h +++ b/dbms/src/Functions/FunctionsConversion.h @@ -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 && 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 || std::is_same_v) { - 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]);