From af14418fc1db3a7d1e6ff513f8d703af95116e55 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 2 Aug 2020 02:24:54 +0300 Subject: [PATCH] Fix assert in IN with tuple literals and functions --- src/Interpreters/ActionsVisitor.cpp | 17 ++++++++++++++--- .../0_stateless/01421_assert_in_in.reference | 0 .../queries/0_stateless/01421_assert_in_in.sql | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/queries/0_stateless/01421_assert_in_in.reference create mode 100644 tests/queries/0_stateless/01421_assert_in_in.sql diff --git a/src/Interpreters/ActionsVisitor.cpp b/src/Interpreters/ActionsVisitor.cpp index 377f612d2da..41784d5e35c 100644 --- a/src/Interpreters/ActionsVisitor.cpp +++ b/src/Interpreters/ActionsVisitor.cpp @@ -161,6 +161,8 @@ static Block createBlockFromAST(const ASTPtr & node, const DataTypes & types, co { if (num_columns == 1) { + /// One column at the left of IN. + Field value = extractValueFromNode(elem, *types[0], context); 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() || elem->as()) { + /// Multiple columns at the left of IN. + /// The right hand side of in should be a set of tuples. + Field function_result; const Tuple * tuple = nullptr; + /// Tuple can be represented as a function in AST. auto * func = elem->as(); if (func && func->name != "tuple") { if (!tuple_type) tuple_type = std::make_shared(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); if (function_result.getType() != Field::Types::Tuple) 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 can be represented as a literal in AST. auto * literal = elem->as(); 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 " + 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()) tuple_values.resize(tuple_size); + /// Fill tuple values by evaluation of constant expressions. size_t i = 0; 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); - /// 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) break; diff --git a/tests/queries/0_stateless/01421_assert_in_in.reference b/tests/queries/0_stateless/01421_assert_in_in.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01421_assert_in_in.sql b/tests/queries/0_stateless/01421_assert_in_in.sql new file mode 100644 index 00000000000..73f712b4015 --- /dev/null +++ b/tests/queries/0_stateless/01421_assert_in_in.sql @@ -0,0 +1 @@ +SELECT (1, 2) IN ((1, (2, 3)), (1 + 1, 1)); -- { serverError 53 }