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 isDate(const DataTypePtr & data_type) { return WhichDataType(data_type).isDate(); }
inline bool isDateOrDateTime(const DataTypePtr & data_type) { return WhichDataType(data_type).isDateOrDateTime(); } 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 isDateTime64(const DataTypePtr & data_type) { return WhichDataType(data_type).isDateTime64(); }
inline bool isEnum(const DataTypePtr & data_type) { return WhichDataType(data_type).isEnum(); } inline bool isEnum(const DataTypePtr & data_type) { return WhichDataType(data_type).isEnum(); }
inline bool isDecimal(const DataTypePtr & data_type) { return WhichDataType(data_type).isDecimal(); } 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 ILLEGAL_COLUMN;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; extern const int SIZES_OF_ARRAYS_DOESNT_MATCH;
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
} }
const ColumnConst * checkAndGetColumnConstStringOrFixedString(const IColumn * column) const ColumnConst * checkAndGetColumnConstStringOrFixedString(const IColumn * column)
@ -124,9 +125,9 @@ namespace
void validateArgumentsImpl(const IFunction & func, void validateArgumentsImpl(const IFunction & func,
const ColumnsWithTypeAndName & arguments, const ColumnsWithTypeAndName & arguments,
size_t argument_offset, 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; const auto argument_index = i + argument_offset;
if (argument_index >= arguments.size()) if (argument_index >= arguments.size())
@ -135,24 +136,36 @@ void validateArgumentsImpl(const IFunction & func,
} }
const auto & arg = arguments[i + argument_offset]; const auto & arg = arguments[i + argument_offset];
const auto validator = validators[i]; const auto validator = decriptors[i];
if (!validator.validator_func(*arg.type)) if (!validator.isValid(arg.type, arg.column))
throw Exception("Illegal type " + arg.type->getName() + throw Exception("Illegal type of argument #" + std::to_string(i)
" of " + std::to_string(i) + + (validator.argument_name ? " '" + std::string(validator.argument_name) + "'": std::string{})
" argument of function " + func.getName() + + " of function " + func.getName()
" expected " + validator.expected_type_description, + ", expected " + validator.expected_type_description
+ (arg.type ? ", got " + arg.type->getName() : std::string{}),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); 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, void validateFunctionArgumentTypes(const IFunction & func,
const ColumnsWithTypeAndName & arguments, const ColumnsWithTypeAndName & arguments,
const FunctionArgumentTypeValidators & mandatory_args, const FunctionArgumentDescriptors & mandatory_args,
const FunctionArgumentTypeValidators & optional_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 auto joinArgumentTypes = [](const auto & args, const String sep = ", ") -> String
{ {
@ -160,8 +173,12 @@ void validateFunctionArgumentTypes(const IFunction & func,
for (const auto & a : args) for (const auto & a : args)
{ {
using A = std::decay_t<decltype(a)>; 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; result += a.expected_type_description;
}
else if constexpr (std::is_same_v<A, ColumnWithTypeAndName>) else if constexpr (std::is_same_v<A, ColumnWithTypeAndName>)
result += a.type->getName(); result += a.type->getName();
@ -174,10 +191,14 @@ void validateFunctionArgumentTypes(const IFunction & func,
return result; return result;
}; };
throw Exception("Incorrect number of arguments of function " + func.getName() throw Exception("Incorrect number of arguments for function " + func.getName()
+ " provided " + std::to_string(arguments.size()) + " (" + joinArgumentTypes(arguments) + ")" + " provided " + std::to_string(arguments.size())
+ " expected " + std::to_string(mandatory_args.size()) + (optional_args.size() ? " or " + std::to_string(mandatory_args.size() + optional_args.size()) : "") + (arguments.size() ? " (" + joinArgumentTypes(arguments) + ")" : String{} )
+ " (" + joinArgumentTypes(mandatory_args) + (optional_args.size() ? ", [" + joinArgumentTypes(mandatory_args) + "]" : "") + ")", + ", 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); 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); const char * expected_type_description);
// Simple validator that is used in conjunction with validateFunctionArgumentTypes() to check if function arguments are as expected. // 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; 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. /** 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. * 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. /// 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 *> std::pair<std::vector<const IColumn *>, const ColumnArray::Offset *>

View File

@ -898,16 +898,19 @@ public:
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
{ {
FunctionArgumentTypeValidators mandatory_args = {{[](const auto &) {return true;}, "ANY TYPE"}}; FunctionArgumentDescriptors mandatory_args = {{"Value", nullptr, nullptr, "ANY TYPE"}};
FunctionArgumentTypeValidators optional_args; FunctionArgumentDescriptors optional_args;
if constexpr (to_decimal || to_datetime64) 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); validateFunctionArgumentTypes(*this, arguments, mandatory_args, optional_args);
@ -918,8 +921,8 @@ public:
} }
else if constexpr (to_decimal) else if constexpr (to_decimal)
{ {
if (!arguments[1].column) // if (!arguments[1].column)
throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN); // throw Exception("Second argument for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN);
UInt64 scale = extractToDecimalScale(arguments[1]); UInt64 scale = extractToDecimalScale(arguments[1]);