diff --git a/dbms/src/Parsers/ExpressionElementParsers.cpp b/dbms/src/Parsers/ExpressionElementParsers.cpp index b3bfeae8bb7..e38689467ea 100644 --- a/dbms/src/Parsers/ExpressionElementParsers.cpp +++ b/dbms/src/Parsers/ExpressionElementParsers.cpp @@ -1130,6 +1130,9 @@ const char * ParserAlias::restricted_keywords[] = "FORMAT", "UNION", "INTO", + "NOT", + "BETWEEN", + "LIKE", nullptr }; diff --git a/dbms/src/Parsers/ExpressionListParsers.cpp b/dbms/src/Parsers/ExpressionListParsers.cpp index 467ca1af71f..00f72d6a369 100644 --- a/dbms/src/Parsers/ExpressionListParsers.cpp +++ b/dbms/src/Parsers/ExpressionListParsers.cpp @@ -228,9 +228,10 @@ bool ParserVariableArityOperatorList::parseImpl(Pos & pos, ASTPtr & node, Expect bool ParserBetweenExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { - /// For the expression (subject BETWEEN left AND right) + /// For the expression (subject [NOT] BETWEEN left AND right) /// create an AST the same as for (subject> = left AND subject <= right). + ParserKeyword s_not("NOT"); ParserKeyword s_between("BETWEEN"); ParserKeyword s_and("AND"); @@ -241,8 +242,16 @@ bool ParserBetweenExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & exp if (!elem_parser.parse(pos, subject, expected)) return false; + bool negative = s_not.ignore(pos, expected); + if (!s_between.ignore(pos, expected)) + { + if (negative) + --pos; + + /// No operator was parsed, just return element. node = subject; + } else { if (!elem_parser.parse(pos, left, expected)) @@ -254,40 +263,50 @@ bool ParserBetweenExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & exp if (!elem_parser.parse(pos, right, expected)) return false; - /// AND function - auto f_and = std::make_shared(); - auto args_and = std::make_shared(); + auto f_combined_expression = std::make_shared(); + auto args_combined_expression = std::make_shared(); - /// >= - auto f_ge = std::make_shared(); - auto args_ge = std::make_shared(); + /// [NOT] BETWEEN left AND right + auto f_left_expr = std::make_shared(); + auto args_left_expr = std::make_shared(); - /// <= - auto f_le = std::make_shared(); - auto args_le = std::make_shared(); + auto f_right_expr = std::make_shared(); + auto args_right_expr = std::make_shared(); - args_ge->children.emplace_back(subject); - args_ge->children.emplace_back(left); + args_left_expr->children.emplace_back(subject); + args_left_expr->children.emplace_back(left); - args_le->children.emplace_back(subject); - args_le->children.emplace_back(right); + args_right_expr->children.emplace_back(subject); + args_right_expr->children.emplace_back(right); - f_ge->name = "greaterOrEquals"; - f_ge->arguments = args_ge; - f_ge->children.emplace_back(f_ge->arguments); + if (negative) + { + /// NOT BETWEEN + f_left_expr->name = "less"; + f_right_expr->name = "greater"; + f_combined_expression->name = "or"; + } + else + { + /// BETWEEN + f_left_expr->name = "greaterOrEquals"; + f_right_expr->name = "lessOrEquals"; + f_combined_expression->name = "and"; + } - f_le->name = "lessOrEquals"; - f_le->arguments = args_le; - f_le->children.emplace_back(f_le->arguments); + f_left_expr->arguments = args_left_expr; + f_left_expr->children.emplace_back(f_left_expr->arguments); - args_and->children.emplace_back(f_ge); - args_and->children.emplace_back(f_le); + f_right_expr->arguments = args_right_expr; + f_right_expr->children.emplace_back(f_right_expr->arguments); - f_and->name = "and"; - f_and->arguments = args_and; - f_and->children.emplace_back(f_and->arguments); + args_combined_expression->children.emplace_back(f_left_expr); + args_combined_expression->children.emplace_back(f_right_expr); - node = f_and; + f_combined_expression->arguments = args_combined_expression; + f_combined_expression->children.emplace_back(f_combined_expression->arguments); + + node = f_combined_expression; } return true; diff --git a/dbms/tests/queries/0_stateless/00834_not_between.reference b/dbms/tests/queries/0_stateless/00834_not_between.reference new file mode 100644 index 00000000000..5a758d0e67c --- /dev/null +++ b/dbms/tests/queries/0_stateless/00834_not_between.reference @@ -0,0 +1,18 @@ +1 +0 +1 +5 +6 +7 +8 +9 +0 0 +0 0 +0 0 +0 0 +1 1 +1 1 +1 1 +0 0 +0 0 +0 0 diff --git a/dbms/tests/queries/0_stateless/00834_not_between.sql b/dbms/tests/queries/0_stateless/00834_not_between.sql new file mode 100644 index 00000000000..45709c7a1b7 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00834_not_between.sql @@ -0,0 +1,4 @@ +SELECT 2 NOT BETWEEN 2 + 1 AND 4 - 1; +SELECT number FROM ( SELECT number FROM system.numbers LIMIT 10 ) WHERE number NOT BETWEEN 2 AND 4; + +SELECT number BETWEEN 4 AND 6, NOT number NOT BETWEEN 4 AND 6 AND 1 FROM numbers(10);