diff --git a/dbms/include/DB/Parsers/ASTWithAlias.h b/dbms/include/DB/Parsers/ASTWithAlias.h index 29478d6a381..0ade6e26ce9 100644 --- a/dbms/include/DB/Parsers/ASTWithAlias.h +++ b/dbms/include/DB/Parsers/ASTWithAlias.h @@ -21,32 +21,8 @@ public: String tryGetAlias() const override { return alias; } void setAlias(const String & to) override { alias = to; } - void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override final - { - if (!alias.empty()) - { - /// Если мы уже ранее вывели этот узел в другом месте запроса, то теперь достаточно вывести лишь алиас. - if (!state.printed_asts_with_alias.insert(this).second) - { - WriteBufferFromOStream wb(settings.ostr, 32); - writeProbablyBackQuotedString(alias, wb); - return; - } - } - - /// Если есть алиас, то требуются скобки вокруг всего выражения, включая алиас. Потому что запись вида 0 AS x + 0 синтаксически некорректна. - if (frame.need_parens && !alias.empty()) - settings.ostr <<'('; - - formatImplWithAlias(settings, state, frame); - - if (!alias.empty()) - { - writeAlias(alias, settings.ostr, settings.hilite); - if (frame.need_parens) - settings.ostr <<')'; - } - } + /// Вызывает formatImplWithAlias, а также выводит алиас. Если надо - заключает всё выражение в скобки. + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override final; virtual void formatImplWithAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const = 0; }; diff --git a/dbms/include/DB/Parsers/IAST.h b/dbms/include/DB/Parsers/IAST.h index fb1b2aee2af..2f3bc41db60 100644 --- a/dbms/include/DB/Parsers/IAST.h +++ b/dbms/include/DB/Parsers/IAST.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -152,10 +152,13 @@ public: } }; - /// Состояние. Например, множество узлов DAG, которых мы уже обошли. + /// Состояние. Например, может запоминаться множество узлов, которых мы уже обошли. struct FormatState { - std::unordered_set printed_asts_with_alias; + /** Запрос SELECT, в котором найден алиас; идентификатор узла с таким алиасом. + * Нужно, чтобы когда узел встретился повторно, выводить только алиас. + */ + std::set> printed_asts_with_alias; }; /// Состояние, которое копируется при форматировании каждого узла. Например, уровень вложенности. @@ -163,6 +166,7 @@ public: { bool indent = 0; bool need_parens = false; + const IAST * current_select = nullptr; }; void format(const FormatSettings & settings) const @@ -171,16 +175,6 @@ public: formatImpl(settings, state, FormatStateStacked()); } - - /// Для подсветки синтаксиса. - static const char * hilite_keyword; - static const char * hilite_identifier; - static const char * hilite_function; - static const char * hilite_operator; - static const char * hilite_alias; - static const char * hilite_none; - - virtual void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const { throw Exception("Unknown element in AST: " + getID() @@ -192,6 +186,15 @@ public: void writeAlias(const String & name, std::ostream & s, bool hilite) const; +protected: + /// Для подсветки синтаксиса. + static const char * hilite_keyword; + static const char * hilite_identifier; + static const char * hilite_function; + static const char * hilite_operator; + static const char * hilite_alias; + static const char * hilite_none; + private: size_t checkDepthImpl(size_t max_depth, size_t level) const { diff --git a/dbms/src/Parsers/ASTSelectQuery.cpp b/dbms/src/Parsers/ASTSelectQuery.cpp index 76064357b2f..35c4214e75c 100644 --- a/dbms/src/Parsers/ASTSelectQuery.cpp +++ b/dbms/src/Parsers/ASTSelectQuery.cpp @@ -222,6 +222,7 @@ const IAST * ASTSelectQuery::getFormat() const void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const { + frame.current_select = this; frame.need_parens = false; std::string indent_str = s.one_line ? "" : std::string(4 * frame.indent, ' '); diff --git a/dbms/src/Parsers/ASTWithAlias.cpp b/dbms/src/Parsers/ASTWithAlias.cpp new file mode 100644 index 00000000000..e1319fcafea --- /dev/null +++ b/dbms/src/Parsers/ASTWithAlias.cpp @@ -0,0 +1,33 @@ +#include + +namespace DB +{ + +void ASTWithAlias::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const +{ + if (!alias.empty()) + { + /// Если мы уже ранее вывели этот узел в другом месте запроса, то теперь достаточно вывести лишь алиас. + if (!state.printed_asts_with_alias.emplace(frame.current_select, getID()).second) + { + WriteBufferFromOStream wb(settings.ostr, 32); + writeProbablyBackQuotedString(alias, wb); + return; + } + } + + /// Если есть алиас, то требуются скобки вокруг всего выражения, включая алиас. Потому что запись вида 0 AS x + 0 синтаксически некорректна. + if (frame.need_parens && !alias.empty()) + settings.ostr <<'('; + + formatImplWithAlias(settings, state, frame); + + if (!alias.empty()) + { + writeAlias(alias, settings.ostr, settings.hilite); + if (frame.need_parens) + settings.ostr <<')'; + } +} + +} diff --git a/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.reference b/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.reference new file mode 100644 index 00000000000..dd143e07d02 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.reference @@ -0,0 +1 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.sql b/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.sql new file mode 100644 index 00000000000..4628b6ea26a --- /dev/null +++ b/dbms/tests/queries/0_stateless/00211_query_formatting_aliases.sql @@ -0,0 +1,6 @@ +SELECT toUInt64(1) IN (1234567890, 2345678901, 3456789012, 4567890123, 5678901234, 6789012345, 7890123456, 8901234567, 9012345678, 123456789) AS x, + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x +FROM remote('127.0.0.1', system, one) SETTINGS max_query_size = 10000;