diff --git a/src/Parsers/ExpressionElementParsers.cpp b/src/Parsers/ExpressionElementParsers.cpp index 1d861c6d78a..b26e73287d0 100644 --- a/src/Parsers/ExpressionElementParsers.cpp +++ b/src/Parsers/ExpressionElementParsers.cpp @@ -789,6 +789,7 @@ bool ParserDateAddExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & exp ++pos; IntervalKind interval_kind; + ASTPtr interval_func_node; if (parseIntervalKind(pos, expected, interval_kind)) { /// function(unit, offset, timestamp) @@ -805,6 +806,13 @@ bool ParserDateAddExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & exp if (!ParserExpression().parse(pos, timestamp_node, expected)) return false; + auto interval_expr_list_args = std::make_shared(); + interval_expr_list_args->children = {offset_node}; + + interval_func_node = std::make_shared(); + interval_func_node->as().name = interval_kind.toNameOfFunctionToIntervalDataType(); + interval_func_node->as().arguments = std::move(interval_expr_list_args); + interval_func_node->as().children.push_back(interval_func_node->as().arguments); } else { @@ -816,27 +824,13 @@ bool ParserDateAddExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & exp return false; ++pos; - if (!ParserKeyword("INTERVAL").ignore(pos, expected)) - return false; - - if (!ParserExpression().parse(pos, offset_node, expected)) - return false; - - if (!parseIntervalKind(pos, expected, interval_kind)) + if (!ParserIntervalOperatorExpression{}.parse(pos, interval_func_node, expected)) return false; } if (pos->type != TokenType::ClosingRoundBracket) return false; ++pos; - auto interval_expr_list_args = std::make_shared(); - interval_expr_list_args->children = {offset_node}; - - auto interval_func_node = std::make_shared(); - interval_func_node->name = interval_kind.toNameOfFunctionToIntervalDataType(); - interval_func_node->arguments = std::move(interval_expr_list_args); - interval_func_node->children.push_back(interval_func_node->arguments); - auto expr_list_args = std::make_shared(); expr_list_args->children = {timestamp_node, interval_func_node}; diff --git a/src/Parsers/ExpressionListParsers.cpp b/src/Parsers/ExpressionListParsers.cpp index 4f4b97eff2d..d6678bb9a78 100644 --- a/src/Parsers/ExpressionListParsers.cpp +++ b/src/Parsers/ExpressionListParsers.cpp @@ -645,12 +645,45 @@ bool ParserTimestampOperatorExpression::parseImpl(Pos & pos, ASTPtr & node, Expe return true; } -bool ParserIntervalOperatorExpression::stringToIntervalKind(const String & literal, ASTPtr & number, IntervalKind & interval_kind) +bool ParserIntervalOperatorExpression::parseArgumentAndIntervalKind( + Pos & pos, ASTPtr & expr, IntervalKind & interval_kind, Expected & expected) { - Tokens tokens(literal.data(), literal.data() + literal.size()); - Pos pos(tokens, 0); - Expected expected; - return (ParserNumber().parse(pos, number, expected) && parseIntervalKind(pos, expected, interval_kind)); + auto begin = pos; + auto init_expected = expected; + ASTPtr string_literal; + //// A String literal followed INTERVAL keyword, + /// the literal can be a part of an expression or + /// include Number and INTERVAL TYPE at the same time + if (ParserStringLiteral{}.parse(pos, string_literal, expected)) + { + String literal; + if (string_literal->as().value.tryGet(literal)) + { + Tokens tokens(literal.data(), literal.data() + literal.size()); + Pos token_pos(tokens, 0); + Expected token_expected; + + if (!ParserNumber{}.parse(token_pos, expr, token_expected)) + return false; + else + { + /// case: INTERVAL '1' HOUR + /// back to begin + if (!token_pos.isValid()) + { + pos = begin; + expected = init_expected; + } + else + /// case: INTERVAL '1 HOUR' + return parseIntervalKind(token_pos, token_expected, interval_kind); + } + } + } + // case: INTERVAL expr HOUR + if (!ParserExpressionWithOptionalAlias(false).parse(pos, expr, expected)) + return false; + return parseIntervalKind(pos, expected, interval_kind); } bool ParserIntervalOperatorExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) @@ -661,44 +694,9 @@ bool ParserIntervalOperatorExpression::parseImpl(Pos & pos, ASTPtr & node, Expec if (!ParserKeyword("INTERVAL").ignore(pos, expected)) return next_parser.parse(pos, node, expected); - ASTPtr string_literal; - if (ParserStringLiteral().parse(pos, string_literal, expected)) - { - String literal; - if (string_literal->as().value.tryGet(literal)) - { - IntervalKind interval_kind; - ASTPtr number; - - /// parse function arguments and interval kind from string literal - if (!stringToIntervalKind(literal, number, interval_kind)) - return false; - - auto function = std::make_shared(); - - auto exp_list = std::make_shared(); - - function->name = interval_kind.toNameOfFunctionToIntervalDataType(); - function->arguments = exp_list; - function->children.push_back(exp_list); - - exp_list->children.push_back(number); - - node = function; - return true; - } - } - ASTPtr expr; - /// Any expression can be inside, because operator surrounds it. - if (!ParserExpressionWithOptionalAlias(false).parse(pos, expr, expected)) - { - pos = begin; - return next_parser.parse(pos, node, expected); - } - IntervalKind interval_kind; - if (!parseIntervalKind(pos, expected, interval_kind)) + if (!parseArgumentAndIntervalKind(pos, expr, interval_kind, expected)) { pos = begin; return next_parser.parse(pos, node, expected); diff --git a/src/Parsers/ExpressionListParsers.h b/src/Parsers/ExpressionListParsers.h index 72961f700fd..40efd0e02d2 100644 --- a/src/Parsers/ExpressionListParsers.h +++ b/src/Parsers/ExpressionListParsers.h @@ -233,7 +233,7 @@ protected: const char * getName() const override { return "INTERVAL operator expression"; } bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; - bool stringToIntervalKind(const String & literal, ASTPtr & number, IntervalKind & interval_kind); + bool parseArgumentAndIntervalKind(Pos & pos, ASTPtr & expr, IntervalKind & interval_kind, Expected & expected); }; class ParserAdditiveExpression : public IParserBase diff --git a/tests/queries/0_stateless/01523_interval_operator_support_string_literal.reference b/tests/queries/0_stateless/01523_interval_operator_support_string_literal.reference index 5ee4e7592f6..0451ef3afd5 100644 --- a/tests/queries/0_stateless/01523_interval_operator_support_string_literal.reference +++ b/tests/queries/0_stateless/01523_interval_operator_support_string_literal.reference @@ -4,3 +4,22 @@ 2 2 2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2009-02-14 00:31:30 +2009-02-14 00:31:30 +2009-02-14 00:31:30 +2009-02-15 23:31:30 +2009-02-15 23:31:30 +2009-02-15 23:31:30 diff --git a/tests/queries/0_stateless/01523_interval_operator_support_string_literal.sql b/tests/queries/0_stateless/01523_interval_operator_support_string_literal.sql index ce418e13e9f..2af2ba4996e 100644 --- a/tests/queries/0_stateless/01523_interval_operator_support_string_literal.sql +++ b/tests/queries/0_stateless/01523_interval_operator_support_string_literal.sql @@ -1,6 +1,25 @@ +SELECT INTERVAL 2 year; +SELECT INTERVAL '2' year; +SELECT INTERVAL '2 year'; +SELECT INTERVAL 2 month; +SELECT INTERVAL '2' month; +SELECT INTERVAL '2 month'; +SELECT INTERVAL 2 week; +SELECT INTERVAL '2' week; +SELECT INTERVAL '2 week'; SELECT INTERVAL 2 day; +SELECT INTERVAL '2' day; SELECT INTERVAL '2 day'; SELECT INTERVAL 2 hour; +SELECT INTERVAL '2' hour; SELECT INTERVAL '2 hour'; SELECT INTERVAL 2 minute; +SELECT INTERVAL '2' minute; SELECT INTERVAL '2 minute'; +SELECT INTERVAL '2' AS n minute; +SELECT DATE_ADD(hour, '1', toDateTime(1234567890, 'UTC')); +SELECT DATE_ADD(hour, 1, toDateTime(1234567890, 'UTC')); +SELECT DATE_ADD(hour, (SELECT 1), toDateTime(1234567890, 'UTC')); +SELECT DATE_ADD(toDateTime(1234567890, 'UTC'), INTERVAL 2 day); +SELECT DATE_ADD(toDateTime(1234567890, 'UTC'), INTERVAL '2 day'); +SELECT DATE_ADD(toDateTime(1234567890, 'UTC'), INTERVAL '2' day);