Fix assert in IN with tuple literals and functions

This commit is contained in:
Alexey Milovidov 2020-08-02 02:24:54 +03:00
parent d17284e7bc
commit af14418fc1
3 changed files with 15 additions and 3 deletions

View File

@ -161,6 +161,8 @@ static Block createBlockFromAST(const ASTPtr & node, const DataTypes & types, co
{ {
if (num_columns == 1) if (num_columns == 1)
{ {
/// One column at the left of IN.
Field value = extractValueFromNode(elem, *types[0], context); Field value = extractValueFromNode(elem, *types[0], context);
if (!value.isNull() || context.getSettingsRef().transform_null_in) if (!value.isNull() || context.getSettingsRef().transform_null_in)
@ -168,15 +170,20 @@ static Block createBlockFromAST(const ASTPtr & node, const DataTypes & types, co
} }
else if (elem->as<ASTFunction>() || elem->as<ASTLiteral>()) else if (elem->as<ASTFunction>() || elem->as<ASTLiteral>())
{ {
/// Multiple columns at the left of IN.
/// The right hand side of in should be a set of tuples.
Field function_result; Field function_result;
const Tuple * tuple = nullptr; const Tuple * tuple = nullptr;
/// Tuple can be represented as a function in AST.
auto * func = elem->as<ASTFunction>(); auto * func = elem->as<ASTFunction>();
if (func && func->name != "tuple") if (func && func->name != "tuple")
{ {
if (!tuple_type) if (!tuple_type)
tuple_type = std::make_shared<DataTypeTuple>(types); tuple_type = std::make_shared<DataTypeTuple>(types);
/// If the function is not a tuple, treat it as a constant expression that returns tuple and extract it.
function_result = extractValueFromNode(elem, *tuple_type, context); function_result = extractValueFromNode(elem, *tuple_type, context);
if (function_result.getType() != Field::Types::Tuple) if (function_result.getType() != Field::Types::Tuple)
throw Exception("Invalid type of set. Expected tuple, got " + String(function_result.getTypeName()), throw Exception("Invalid type of set. Expected tuple, got " + String(function_result.getTypeName()),
@ -185,10 +192,12 @@ static Block createBlockFromAST(const ASTPtr & node, const DataTypes & types, co
tuple = &function_result.get<Tuple>(); tuple = &function_result.get<Tuple>();
} }
/// Tuple can be represented as a literal in AST.
auto * literal = elem->as<ASTLiteral>(); auto * literal = elem->as<ASTLiteral>();
if (literal) if (literal)
{ {
if (literal->value.getType() != Field::Types::Tuple) /// The literal must be tuple.
if (literal->value.getType() != Field::Types::Tuple)
throw Exception("Invalid type in set. Expected tuple, got " throw Exception("Invalid type in set. Expected tuple, got "
+ String(literal->value.getTypeName()), ErrorCodes::INCORRECT_ELEMENT_OF_SET); + String(literal->value.getTypeName()), ErrorCodes::INCORRECT_ELEMENT_OF_SET);
@ -203,13 +212,15 @@ static Block createBlockFromAST(const ASTPtr & node, const DataTypes & types, co
if (tuple_values.empty()) if (tuple_values.empty())
tuple_values.resize(tuple_size); tuple_values.resize(tuple_size);
/// Fill tuple values by evaluation of constant expressions.
size_t i = 0; size_t i = 0;
for (; i < tuple_size; ++i) for (; i < tuple_size; ++i)
{ {
Field value = tuple ? (*tuple)[i] Field value = tuple ? convertFieldToType((*tuple)[i], *types[i])
: extractValueFromNode(func->arguments->children[i], *types[i], context); : extractValueFromNode(func->arguments->children[i], *types[i], context);
/// If at least one of the elements of the tuple has an impossible (outside the range of the type) value, then the entire tuple too. /// If at least one of the elements of the tuple has an impossible (outside the range of the type) value,
/// then the entire tuple too.
if (value.isNull() && !context.getSettings().transform_null_in) if (value.isNull() && !context.getSettings().transform_null_in)
break; break;

View File

@ -0,0 +1 @@
SELECT (1, 2) IN ((1, (2, 3)), (1 + 1, 1)); -- { serverError 53 }