diff --git a/dbms/include/DB/Parsers/ASTJoin.h b/dbms/include/DB/Parsers/ASTJoin.h index 34eaeecd5e5..bd6b5b42f04 100644 --- a/dbms/include/DB/Parsers/ASTJoin.h +++ b/dbms/include/DB/Parsers/ASTJoin.h @@ -42,6 +42,7 @@ public: ASTPtr table; /// "Правая" таблица для соединения - подзапрос или имя таблицы. ASTPtr using_expr_list; /// По каким столбцам выполнять соединение. + ASTPtr on_expr; /// Условие соединения. Поддерживается либо USING либо ON, но не одновременно. ASTJoin() = default; ASTJoin(const StringRange range_) : IAST(range_) {} @@ -108,11 +109,16 @@ protected: table->formatImpl(settings, state, frame); - if (kind != ASTJoin::Cross) + if (using_expr_list) { settings.ostr << (settings.hilite ? hilite_keyword : "") << " USING " << (settings.hilite ? hilite_none : ""); using_expr_list->formatImpl(settings, state, frame); } + else if (on_expr) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " ON " << (settings.hilite ? hilite_none : ""); + on_expr->formatImpl(settings, state, frame); + } } }; diff --git a/dbms/src/Parsers/ParserJoin.cpp b/dbms/src/Parsers/ParserJoin.cpp index 2871619eb82..bf5829e1f68 100644 --- a/dbms/src/Parsers/ParserJoin.cpp +++ b/dbms/src/Parsers/ParserJoin.cpp @@ -28,8 +28,10 @@ bool ParserJoin::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_parsed_p ParserString s_outer("OUTER", true, true); ParserString s_join("JOIN", true, true); ParserString s_using("USING", true, true); + ParserString s_on("ON", true, true); ParserNotEmptyExpressionList exp_list(false); + ParserLogicalOrExpression exp_elem; ParserWithOptionalAlias subquery(ParserPtr(new ParserSubquery), true); ParserIdentifier identifier; @@ -93,15 +95,41 @@ bool ParserJoin::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_parsed_p if (join->kind != ASTJoin::Cross) { - if (!s_using.ignore(pos, end, max_parsed_pos, expected)) + if (s_using.ignore(pos, end, max_parsed_pos, expected)) + { + ws.ignore(pos, end); + + /// Выражение для USING можно указать как в скобках, так и без них. + bool in_parens = ParserString("(").ignore(pos, end); + if (in_parens) + ws.ignore(pos, end); + + if (!exp_list.parse(pos, end, join->using_expr_list, max_parsed_pos, expected)) + return false; + + ws.ignore(pos, end); + + if (in_parens) + { + ws.ignore(pos, end); + if (!ParserString(")").ignore(pos, end)) + return false; + } + } + else if (s_on.ignore(pos, end, max_parsed_pos, expected)) + { + ws.ignore(pos, end); + + if (!exp_elem.parse(pos, end, join->on_expr, max_parsed_pos, expected)) + return false; + + ws.ignore(pos, end); + } + else + { + expected = "USING or ON"; return false; - - ws.ignore(pos, end); - - if (!exp_list.parse(pos, end, join->using_expr_list, max_parsed_pos, expected)) - return false; - - ws.ignore(pos, end); + } } join->children.push_back(join->table); diff --git a/dbms/src/Parsers/ParserSelectQuery.cpp b/dbms/src/Parsers/ParserSelectQuery.cpp index bb477c4162d..57349298d08 100644 --- a/dbms/src/Parsers/ParserSelectQuery.cpp +++ b/dbms/src/Parsers/ParserSelectQuery.cpp @@ -113,6 +113,9 @@ bool ParserSelectQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max_p if (s_dot.ignore(pos, end, max_parsed_pos, expected)) { select_query->database = select_query->table; + + ws.ignore(pos, end); + if (!ident.parse(pos, end, select_query->table, max_parsed_pos, expected)) return false; diff --git a/dbms/tests/queries/0_stateless/00269_database_table_whitespace.reference b/dbms/tests/queries/0_stateless/00269_database_table_whitespace.reference new file mode 100644 index 00000000000..aa47d0d46d4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00269_database_table_whitespace.reference @@ -0,0 +1,2 @@ +0 +0 diff --git a/dbms/tests/queries/0_stateless/00269_database_table_whitespace.sql b/dbms/tests/queries/0_stateless/00269_database_table_whitespace.sql new file mode 100644 index 00000000000..8e69d2713bc --- /dev/null +++ b/dbms/tests/queries/0_stateless/00269_database_table_whitespace.sql @@ -0,0 +1,2 @@ +SELECT * FROM system . one; +SELECT * FROM system /* Hello */. `one`;