From 4ab00524564fa94c3f3f991a21e38539ae294396 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 6 Aug 2015 00:38:31 +0300 Subject: [PATCH] dbms: more compact formatting of queries with aliases: development [#METR-17606]. --- dbms/include/DB/Parsers/ASTAlterQuery.h | 84 ++++++++++ dbms/include/DB/Parsers/ASTAsterisk.h | 6 + dbms/include/DB/Parsers/ASTCheckQuery.h | 22 +++ .../include/DB/Parsers/ASTColumnDeclaration.h | 20 +++ dbms/include/DB/Parsers/ASTCreateQuery.h | 67 ++++++++ dbms/include/DB/Parsers/ASTDropQuery.h | 19 +++ dbms/include/DB/Parsers/ASTExpressionList.h | 34 ++++ dbms/include/DB/Parsers/ASTFunction.h | 3 + dbms/include/DB/Parsers/ASTIdentifier.h | 23 +++ dbms/include/DB/Parsers/ASTInsertQuery.h | 37 +++++ dbms/include/DB/Parsers/ASTJoin.h | 31 ++++ dbms/include/DB/Parsers/ASTLiteral.h | 16 ++ dbms/include/DB/Parsers/ASTNameTypePair.h | 9 ++ dbms/include/DB/Parsers/ASTOptimizeQuery.h | 7 + dbms/include/DB/Parsers/ASTOrderByElement.h | 12 ++ .../DB/Parsers/ASTQueryWithTableAndOutput.h | 22 +-- dbms/include/DB/Parsers/ASTRenameQuery.h | 16 ++ dbms/include/DB/Parsers/ASTSelectQuery.h | 3 + dbms/include/DB/Parsers/IAST.h | 64 ++++++++ dbms/include/DB/Parsers/formatAST.h | 2 +- dbms/src/Parsers/ASTSelectQuery.cpp | 148 ++++++++++++++++++ dbms/src/Parsers/formatAST.cpp | 27 +++- 22 files changed, 657 insertions(+), 15 deletions(-) diff --git a/dbms/include/DB/Parsers/ASTAlterQuery.h b/dbms/include/DB/Parsers/ASTAlterQuery.h index 1d22d25e186..9bd8356762c 100644 --- a/dbms/include/DB/Parsers/ASTAlterQuery.h +++ b/dbms/include/DB/Parsers/ASTAlterQuery.h @@ -1,6 +1,8 @@ #pragma once #include +#include + namespace DB { @@ -98,5 +100,87 @@ public: } return res; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + frame.need_parens = false; + + std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' '); + + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ALTER TABLE " << (settings.hilite ? hilite_none : ""); + + if (!table.empty()) + { + if (!database.empty()) + { + settings.ostr << indent_str << database; + settings.ostr << "."; + } + settings.ostr << indent_str << table; + } + settings.ostr << nl_or_ws; + + for (size_t i = 0; i < parameters.size(); ++i) + { + const ASTAlterQuery::Parameters & p = parameters[i]; + + if (p.type == ASTAlterQuery::ADD_COLUMN) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ADD COLUMN " << (settings.hilite ? hilite_none : ""); + p.col_decl->formatImpl(settings, state, frame); + + /// AFTER + if (p.column) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << " AFTER " << (settings.hilite ? hilite_none : ""); + p.column->formatImpl(settings, state, frame); + } + } + else if (p.type == ASTAlterQuery::DROP_COLUMN) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "DROP COLUMN " << (settings.hilite ? hilite_none : ""); + p.column->formatImpl(settings, state, frame); + } + else if (p.type == ASTAlterQuery::MODIFY_COLUMN) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "MODIFY COLUMN " << (settings.hilite ? hilite_none : ""); + p.col_decl->formatImpl(settings, state, frame); + } + else if (p.type == ASTAlterQuery::DROP_PARTITION) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << (p.detach ? "DETACH" : "DROP") << " PARTITION " + << (settings.hilite ? hilite_none : ""); + p.partition->formatImpl(settings, state, frame); + } + else if (p.type == ASTAlterQuery::ATTACH_PARTITION) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "ATTACH " << (p.unreplicated ? "UNREPLICATED " : "") + << (p.part ? "PART " : "PARTITION ") << (settings.hilite ? hilite_none : ""); + p.partition->formatImpl(settings, state, frame); + } + else if (p.type == ASTAlterQuery::FETCH_PARTITION) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "FETCH " << (p.unreplicated ? "UNREPLICATED " : "") + << "PARTITION " << (settings.hilite ? hilite_none : ""); + p.partition->formatImpl(settings, state, frame); + settings.ostr << (settings.hilite ? hilite_keyword : "") + << " FROM " << (settings.hilite ? hilite_none : "") << mysqlxx::quote << p.from; + } + else if (p.type == ASTAlterQuery::FREEZE_PARTITION) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "FREEZE PARTITION " << (settings.hilite ? hilite_none : ""); + p.partition->formatImpl(settings, state, frame); + } + else + throw Exception("Unexpected type of ALTER", ErrorCodes::UNEXPECTED_AST_STRUCTURE); + + std::string comma = (i < (parameters.size() -1) ) ? "," : ""; + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << comma << (settings.hilite ? hilite_none : ""); + + settings.ostr << settings.nl_or_ws; + } + } }; + } diff --git a/dbms/include/DB/Parsers/ASTAsterisk.h b/dbms/include/DB/Parsers/ASTAsterisk.h index aa90d676c71..f0741539267 100644 --- a/dbms/include/DB/Parsers/ASTAsterisk.h +++ b/dbms/include/DB/Parsers/ASTAsterisk.h @@ -16,6 +16,12 @@ public: String getID() const override { return "Asterisk"; } ASTPtr clone() const override { return new ASTAsterisk(*this); } String getColumnName() const override { return "*"; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + settings.ostr << "*"; + } }; } diff --git a/dbms/include/DB/Parsers/ASTCheckQuery.h b/dbms/include/DB/Parsers/ASTCheckQuery.h index 901ad7ef567..74b64aeaef8 100644 --- a/dbms/include/DB/Parsers/ASTCheckQuery.h +++ b/dbms/include/DB/Parsers/ASTCheckQuery.h @@ -19,6 +19,28 @@ struct ASTCheckQuery : public IAST std::string database; std::string table; + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + std::string nl_or_nothing = settings.one_line ? "" : "\n"; + + std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' '); + std::string nl_or_ws = settings.one_line ? " " : "\n"; + + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "CHECK TABLE " << (settings.hilite ? hilite_none : ""); + + if (!table.empty()) + { + if (!database.empty()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << database << (settings.hilite ? hilite_none : ""); + settings.ostr << "."; + } + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << table << (settings.hilite ? hilite_none : ""); + } + settings.ostr << nl_or_ws; + } }; } diff --git a/dbms/include/DB/Parsers/ASTColumnDeclaration.h b/dbms/include/DB/Parsers/ASTColumnDeclaration.h index 9862c5a81b6..722bc6d8283 100644 --- a/dbms/include/DB/Parsers/ASTColumnDeclaration.h +++ b/dbms/include/DB/Parsers/ASTColumnDeclaration.h @@ -40,6 +40,26 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + frame.need_parens = false; + std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' '); + + settings.ostr << settings.nl_or_ws << indent_str << backQuoteIfNeed(name); + if (type) + { + settings.ostr << ' '; + type->formatImpl(settings, state, frame); + } + + if (default_expression) + { + settings.ostr << ' ' << (settings.hilite ? hilite_keyword : "") << default_specifier << (settings.hilite ? hilite_none : "") << ' '; + default_expression->formatImpl(settings, state, frame); + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTCreateQuery.h b/dbms/include/DB/Parsers/ASTCreateQuery.h index 33d69a8e8e3..57cfc1a35b2 100644 --- a/dbms/include/DB/Parsers/ASTCreateQuery.h +++ b/dbms/include/DB/Parsers/ASTCreateQuery.h @@ -48,6 +48,73 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + frame.need_parens = false; + + if (!database.empty() && table.empty()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << (attach ? "ATTACH DATABASE " : "CREATE DATABASE ") << (if_not_exists ? "IF NOT EXISTS " : "") << (settings.hilite ? hilite_none : "") + << backQuoteIfNeed(database); + return; + } + + { + std::string what = "TABLE"; + if (is_view) + what = "VIEW"; + if (is_materialized_view) + what = "MATERIALIZED VIEW"; + + settings.ostr + << (settings.hilite ? hilite_keyword : "") + << (attach ? "ATTACH " : "CREATE ") + << (is_temporary ? "TEMPORARY " : "") + << what + << " " << (if_not_exists ? "IF NOT EXISTS " : "") + << (settings.hilite ? hilite_none : "") + << (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table); + } + + if (!as_table.empty()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " AS " << (settings.hilite ? hilite_none : "") + << (!as_database.empty() ? backQuoteIfNeed(as_database) + "." : "") << backQuoteIfNeed(as_table); + } + + if (columns) + { + settings.ostr << (settings.one_line ? " (" : "\n("); + ++frame.indent; + columns->formatImpl(settings, state, frame); + settings.ostr << (settings.one_line ? ")" : "\n)"); + } + + if (storage && !is_materialized_view && !is_view) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " ENGINE" << (settings.hilite ? hilite_none : "") << " = "; + storage->formatImpl(settings, state, frame); + } + + if (inner_storage) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " ENGINE" << (settings.hilite ? hilite_none : "") << " = "; + inner_storage->formatImpl(settings, state, frame); + } + + if (is_populate) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " POPULATE" << (settings.hilite ? hilite_none : ""); + } + + if (select) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " AS" << settings.nl_or_ws << (settings.hilite ? hilite_none : ""); + select->formatImpl(settings, state, frame); + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTDropQuery.h b/dbms/include/DB/Parsers/ASTDropQuery.h index c0ac24017d0..897f9afd4c6 100644 --- a/dbms/include/DB/Parsers/ASTDropQuery.h +++ b/dbms/include/DB/Parsers/ASTDropQuery.h @@ -24,6 +24,25 @@ public: String getID() const override { return (detach ? "DetachQuery_" : "DropQuery_") + database + "_" + table; }; ASTPtr clone() const override { return new ASTDropQuery(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + if (table.empty() && !database.empty()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") + << (detach ? "DETACH DATABASE " : "DROP DATABASE ") + << (if_exists ? "IF EXISTS " : "") + << (settings.hilite ? hilite_none : "") + << backQuoteIfNeed(database); + return; + } + + settings.ostr << (settings.hilite ? hilite_keyword : "") + << (detach ? "DETACH TABLE " : "DROP TABLE ") + << (if_exists ? "IF EXISTS " : "") << (settings.hilite ? hilite_none : "") + << (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table); + } }; } diff --git a/dbms/include/DB/Parsers/ASTExpressionList.h b/dbms/include/DB/Parsers/ASTExpressionList.h index 1ec814a8d1b..1a2fdb19cb7 100644 --- a/dbms/include/DB/Parsers/ASTExpressionList.h +++ b/dbms/include/DB/Parsers/ASTExpressionList.h @@ -31,6 +31,40 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + for (ASTs::const_iterator it = children.begin(); it != children.end(); ++it) + { + if (it != children.begin()) + settings.ostr << ", "; + + (*it)->formatImpl(settings, state, frame); + } + } + + + friend class ASTSelectQuery; + + /** Вывести список выражений в секциях запроса SELECT - по одному выражению на строку. + */ + void formatImplMultiline(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const + { + std::string indent_str = "\n" + std::string(4 * (frame.indent + 1), ' '); + + ++frame.indent; + for (ASTs::const_iterator it = children.begin(); it != children.end(); ++it) + { + if (it != children.begin()) + settings.ostr << ", "; + + if (children.size() > 1) + settings.ostr << indent_str; + + (*it)->formatImpl(settings, state, frame); + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTFunction.h b/dbms/include/DB/Parsers/ASTFunction.h index 70380b67e7e..3801a32e638 100644 --- a/dbms/include/DB/Parsers/ASTFunction.h +++ b/dbms/include/DB/Parsers/ASTFunction.h @@ -82,6 +82,9 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override; }; diff --git a/dbms/include/DB/Parsers/ASTIdentifier.h b/dbms/include/DB/Parsers/ASTIdentifier.h index 58ae38ca434..550973f298f 100644 --- a/dbms/include/DB/Parsers/ASTIdentifier.h +++ b/dbms/include/DB/Parsers/ASTIdentifier.h @@ -2,6 +2,7 @@ #include #include +#include namespace DB @@ -41,6 +42,28 @@ public: { set.insert(name); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + if (frame.need_parens && !alias.empty()) + settings.ostr << '('; + + settings.ostr << (settings.hilite ? hilite_identifier : ""); + + WriteBufferFromOStream wb(settings.ostr, 32); + writeProbablyBackQuotedString(name, wb); + wb.next(); + + settings.ostr << (settings.hilite ? hilite_none : ""); + + if (!alias.empty()) + { + writeAlias(alias, settings.ostr, settings.hilite); + if (frame.need_parens) + settings.ostr << ')'; + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTInsertQuery.h b/dbms/include/DB/Parsers/ASTInsertQuery.h index 5e6988bcfc6..c7a1879b55d 100644 --- a/dbms/include/DB/Parsers/ASTInsertQuery.h +++ b/dbms/include/DB/Parsers/ASTInsertQuery.h @@ -42,6 +42,43 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + frame.need_parens = false; + + settings.ostr << (settings.hilite ? hilite_keyword : "") << "INSERT INTO " << (settings.hilite ? hilite_none : "") + << (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table); + + if (!insert_id.empty()) + settings.ostr << (settings.hilite ? hilite_keyword : "") << " ID = " << (settings.hilite ? hilite_none : "") + << mysqlxx::quote << insert_id; + + if (columns) + { + settings.ostr << " ("; + columns->formatImpl(settings, state, frame); + settings.ostr << ")"; + } + + if (select) + { + settings.ostr << " "; + select->formatImpl(settings, state, frame); + } + else + { + if (!format.empty()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " FORMAT " << (settings.hilite ? hilite_none : "") << format; + } + else + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " VALUES" << (settings.hilite ? hilite_none : ""); + } + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTJoin.h b/dbms/include/DB/Parsers/ASTJoin.h index 96f161c5c60..029d4f49350 100644 --- a/dbms/include/DB/Parsers/ASTJoin.h +++ b/dbms/include/DB/Parsers/ASTJoin.h @@ -83,6 +83,37 @@ public: return ptr; } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + frame.need_parens = false; + + settings.ostr << (settings.hilite ? hilite_keyword : ""); + + if (locality == ASTJoin::Global) + settings.ostr << "GLOBAL "; + + if (kind != ASTJoin::Cross) + settings.ostr << (strictness == ASTJoin::Any ? "ANY " : "ALL "); + + settings.ostr << (kind == ASTJoin::Inner ? "INNER " + : (kind == ASTJoin::Left ? "LEFT " + : (kind == ASTJoin::Right ? "RIGHT " + : (kind == ASTJoin::Cross ? "CROSS " + : "FULL OUTER ")))); + + settings.ostr << "JOIN " + << (settings.hilite ? hilite_none : ""); + + table->formatImpl(settings, state, frame); + + if (kind != ASTJoin::Cross) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " USING " << (settings.hilite ? hilite_none : ""); + using_expr_list->formatImpl(settings, state, frame); + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTLiteral.h b/dbms/include/DB/Parsers/ASTLiteral.h index 2a610255be8..c5f1ec91ed4 100644 --- a/dbms/include/DB/Parsers/ASTLiteral.h +++ b/dbms/include/DB/Parsers/ASTLiteral.h @@ -26,6 +26,22 @@ public: String getID() const override { return "Literal_" + apply_visitor(FieldVisitorDump(), value); } ASTPtr clone() const override { return new ASTLiteral(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + if (frame.need_parens && !alias.empty()) + settings.ostr <<'('; + + settings.ostr <formatImpl(settings, state, frame); + } }; } diff --git a/dbms/include/DB/Parsers/ASTOptimizeQuery.h b/dbms/include/DB/Parsers/ASTOptimizeQuery.h index 906b3d1edb5..2fb6921d2ed 100644 --- a/dbms/include/DB/Parsers/ASTOptimizeQuery.h +++ b/dbms/include/DB/Parsers/ASTOptimizeQuery.h @@ -22,6 +22,13 @@ public: String getID() const override { return "OptimizeQuery_" + database + "_" + table; }; ASTPtr clone() const override { return new ASTOptimizeQuery(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << "OPTIMIZE TABLE " << (settings.hilite ? hilite_none : "") + << (!database.empty() ? backQuoteIfNeed(database) + "." : "") << backQuoteIfNeed(table); + } }; } diff --git a/dbms/include/DB/Parsers/ASTOrderByElement.h b/dbms/include/DB/Parsers/ASTOrderByElement.h index f341265d93b..d7103a48e64 100644 --- a/dbms/include/DB/Parsers/ASTOrderByElement.h +++ b/dbms/include/DB/Parsers/ASTOrderByElement.h @@ -29,6 +29,18 @@ public: String getID() const override { return "OrderByElement"; } ASTPtr clone() const override { return new ASTOrderByElement(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + children.front()->formatImpl(settings, state, frame); + settings.ostr << (settings.hilite ? hilite_keyword : "") << (direction == -1 ? " DESC" : " ASC") << (settings.hilite ? hilite_none : ""); + if (!collator.isNull()) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " COLLATE " << (settings.hilite ? hilite_none : "") + << "'" << collator->getLocale() << "'"; + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTQueryWithTableAndOutput.h b/dbms/include/DB/Parsers/ASTQueryWithTableAndOutput.h index 32ebb1e528d..015cb010ead 100644 --- a/dbms/include/DB/Parsers/ASTQueryWithTableAndOutput.h +++ b/dbms/include/DB/Parsers/ASTQueryWithTableAndOutput.h @@ -8,17 +8,17 @@ namespace DB { - /** Запрос с указанием названия таблицы и, возможно, БД и секцией FORMAT. - */ - class ASTQueryWithTableAndOutput : public ASTQueryWithOutput - { - public: - String database; - String table; - - ASTQueryWithTableAndOutput() = default; - ASTQueryWithTableAndOutput(const StringRange range_) : ASTQueryWithOutput(range_) {} - }; +/** Запрос с указанием названия таблицы и, возможно, БД и секцией FORMAT. + */ +class ASTQueryWithTableAndOutput : public ASTQueryWithOutput +{ +public: + String database; + String table; + + ASTQueryWithTableAndOutput() = default; + ASTQueryWithTableAndOutput(const StringRange range_) : ASTQueryWithOutput(range_) {} +}; /// Объявляет класс-наследник ASTQueryWithTableAndOutput с реализованными методами getID и clone. diff --git a/dbms/include/DB/Parsers/ASTRenameQuery.h b/dbms/include/DB/Parsers/ASTRenameQuery.h index 4eb6624e4c3..ffb59c3f0f8 100644 --- a/dbms/include/DB/Parsers/ASTRenameQuery.h +++ b/dbms/include/DB/Parsers/ASTRenameQuery.h @@ -34,6 +34,22 @@ public: String getID() const override { return "Rename"; }; ASTPtr clone() const override { return new ASTRenameQuery(*this); } + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << "RENAME TABLE " << (settings.hilite ? hilite_none : ""); + + for (ASTRenameQuery::Elements::const_iterator it = elements.begin(); it != elements.end(); ++it) + { + if (it != elements.begin()) + settings.ostr << ", "; + + settings.ostr << (!it->from.database.empty() ? backQuoteIfNeed(it->from.database) + "." : "") << backQuoteIfNeed(it->from.table) + << (settings.hilite ? hilite_keyword : "") << " TO " << (settings.hilite ? hilite_none : "") + << (!it->to.database.empty() ? backQuoteIfNeed(it->to.database) + "." : "") << backQuoteIfNeed(it->to.table); + } + } }; } diff --git a/dbms/include/DB/Parsers/ASTSelectQuery.h b/dbms/include/DB/Parsers/ASTSelectQuery.h index b941046c534..6839f6616c9 100644 --- a/dbms/include/DB/Parsers/ASTSelectQuery.h +++ b/dbms/include/DB/Parsers/ASTSelectQuery.h @@ -68,6 +68,9 @@ public: ASTPtr prev_union_all; /// Следующий запрос SELECT в цепочке UNION ALL, если такой есть ASTPtr next_union_all; + +protected: + void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override; }; } diff --git a/dbms/include/DB/Parsers/IAST.h b/dbms/include/DB/Parsers/IAST.h index 803cc9eb31f..cb08ef5b408 100644 --- a/dbms/include/DB/Parsers/IAST.h +++ b/dbms/include/DB/Parsers/IAST.h @@ -133,6 +133,65 @@ public: (*it)->collectIdentifierNames(set); } + + /// Преобразовать в строку. + + /// Настройки формата. + struct FormatSettings + { + std::ostream & ostr; + bool hilite; + bool one_line; + + char nl_or_ws; + + FormatSettings(std::ostream & ostr_, bool hilite_, bool one_line_) + : ostr(ostr_), hilite(hilite_), one_line(one_line_) + { + nl_or_ws = one_line ? ' ' : '\n'; + } + }; + + /// Состояние. Например, множество узлов DAG, которых мы уже обошли. + struct FormatState + { + /// TODO + }; + + /// Состояние, которое копируется при форматировании каждого узла. Например, уровень вложенности. + struct FormatStateStacked + { + bool indent = 0; + bool need_parens = false; + }; + + void format(const FormatSettings & settings) const + { + FormatState state; + formatImpl(settings, state, FormatStateStacked()); + } + +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; + + + virtual void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const + { + throw Exception("Unknown element in AST: " + getID() + + ((range.first && (range.second > range.first)) + ? " '" + std::string(range.first, range.second - range.first) + "'" + : ""), + ErrorCodes::UNKNOWN_ELEMENT_IN_AST); + } + + void writeAlias(const String & name, std::ostream & s, bool hilite); + private: size_t checkDepthImpl(size_t max_depth, size_t level) const { @@ -152,4 +211,9 @@ private: typedef SharedPtr ASTPtr; typedef std::vector ASTs; + +/// Квотировать идентификатор обратными кавычками, если это требуется. +String backQuoteIfNeed(const String & x); + + } diff --git a/dbms/include/DB/Parsers/formatAST.h b/dbms/include/DB/Parsers/formatAST.h index 811d946f044..3fdfded2aa3 100644 --- a/dbms/include/DB/Parsers/formatAST.h +++ b/dbms/include/DB/Parsers/formatAST.h @@ -12,7 +12,7 @@ namespace DB /** Берёт синтаксическое дерево и превращает его обратно в текст. * В случае запроса INSERT, данные будут отсутствовать. */ -void formatAST(const IAST & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false); +void formatAST(const IAST & ast, std::ostream & s, size_t indent = 0, bool hilite = true, bool one_line = false, bool need_parens = false); String formatColumnsForCreateQuery(NamesAndTypesList & columns); diff --git a/dbms/src/Parsers/ASTSelectQuery.cpp b/dbms/src/Parsers/ASTSelectQuery.cpp index ff6f53d5a4e..5da7767dda5 100644 --- a/dbms/src/Parsers/ASTSelectQuery.cpp +++ b/dbms/src/Parsers/ASTSelectQuery.cpp @@ -1,5 +1,7 @@ +#include #include + namespace DB { @@ -217,5 +219,151 @@ const IAST * ASTSelectQuery::getFormat() const return query->format.get(); } + +void ASTSelectQuery::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override +{ + frame.need_parens = false; + std::string indent_str = settings.one_line ? "" : std::string(4 * frame.indent, ' '); + + settings.ostr << (settings.hilite ? hilite_keyword : "") << indent_str << "SELECT " << (distinct ? "DISTINCT " : "") << (settings.hilite ? hilite_none : ""); + + settings.one_line + ? select_expression_list->formatImpl(settings, state, frame) + : typeid_cast(*select_expression_list).formatImplMultiline(settings, state, frame); + + if (table) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "FROM " << (settings.hilite ? hilite_none : ""); + if (database) + { + database->formatImpl(settings, state, frame); + settings.ostr << "."; + } + + if (typeid_cast(&*table)) + { + if (settings.one_line) + settings.ostr << " ("; + else + settings.ostr << "\n" << indent_str << "(\n"; + + table->formatImpl(settings, state, frame); + + if (settings.one_line) + settings.ostr << ")"; + else + settings.ostr << "\n" << indent_str << ")"; + } + else + table->formatImpl(settings, state, frame); + } + + if (final) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "FINAL" << (settings.hilite ? hilite_none : ""); + } + + if (sample_size) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "SAMPLE " << (settings.hilite ? hilite_none : ""); + sample_size->formatImpl(settings, state, frame); + } + + if (array_join_expression_list) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str + << (array_join_is_left ? "LEFT " : "") << "ARRAY JOIN " << (settings.hilite ? hilite_none : ""); + + settings.one_line + ? array_join_expression_list->formatImpl(settings, state, frame) + : typeid_cast(*array_join_expression_list).formatImplMultiline(settings, state, frame); + } + + if (join) + { + settings.ostr << " "; + join->formatImpl(settings, state, frame); + } + + if (prewhere_expression) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "PREWHERE " << (settings.hilite ? hilite_none : ""); + prewhere_expression->formatImpl(settings, state, frame); + } + + if (where_expression) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "WHERE " << (settings.hilite ? hilite_none : ""); + where_expression, s, indent, hilite, settings.one_line); + } + + if (group_expression_list) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "GROUP BY " << (settings.hilite ? hilite_none : ""); + settings.one_line + ? group_expression_list->formatImpl(settings, state, frame) + : typeid_cast(*group_expression_list).formatImplMultiline(settings, state, frame); + } + + if (group_by_with_totals) + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << (settings.one_line ? "" : " ") << "WITH TOTALS" << (settings.hilite ? hilite_none : ""); + + if (having_expression) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "HAVING " << (settings.hilite ? hilite_none : ""); + having_expression->formatImpl(settings, state, frame); + } + + if (order_expression_list) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "ORDER BY " << (settings.hilite ? hilite_none : ""); + settings.one_line + ? order_expression_list->formatImpl(settings, state, frame) + : typeid_cast(*order_expression_list).formatImplMultiline(settings, state, frame); + } + + if (limit_length) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "LIMIT " << (settings.hilite ? hilite_none : ""); + if (limit_offset) + { + limit_offset->formatImpl(settings, state, frame); + settings.ostr << ", "; + } + limit_length->formatImpl(settings, state, frame); + } + + if (settings) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "SETTINGS " << (settings.hilite ? hilite_none : ""); + + const ASTSetQuery & ast_set = typeid_cast(*settings); + for (ASTSetQuery::Changes::const_iterator it = ast_set.changes.begin(); it != ast_set.changes.end(); ++it) + { + if (it != ast_set.changes.begin()) + settings.ostr << ", "; + + settings.ostr << it->name << " = " << apply_visitor(FieldVisitorToString(), it->value); + } + } + + if (format) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "FORMAT " << (settings.hilite ? hilite_none : ""); + format->formatImpl(settings, state, frame); + } + + if (next_union_all) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << settings.nl_or_ws << settings.ostr << indent_str << "UNION ALL " << settings.nl_or_ws << settings.ostr << (settings.hilite ? hilite_none : ""); + + // NOTE Мы можем безопасно применить static_cast вместо typeid_cast, потому что знаем, что в цепочке UNION ALL + // имеются только деревья типа SELECT. + const ASTSelectQuery & next_ast = static_cast(*next_union_all); + + next_ast->formatImpl(settings, state, frame); + } +} + }; diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 5d0854827ef..4e0d38ea6b8 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -63,7 +63,7 @@ String backQuoteIfNeed(const String & x) } -static String hightlight(const String & keyword, const String & color_sequence, const bool hilite) +static String highlight(const String & keyword, const String & color_sequence, const bool hilite) { return hilite ? color_sequence + keyword + hilite_none : keyword; } @@ -81,6 +81,21 @@ static void writeAlias(const String & name, std::ostream & s, bool hilite, bool } +struct FormatState +{ + std::ostream & s; + bool hilite; + bool one_line; + + void formatImpl(const IAST & ast, size_t indent, bool need_parens); + + +}; + + + + + void formatAST(const ASTExpressionList & ast, std::ostream & s, size_t indent, bool hilite, bool one_line, bool need_parens) { for (ASTs::const_iterator it = ast.children.begin(); it != ast.children.end(); ++it) @@ -729,7 +744,7 @@ void formatAST(const ASTColumnDeclaration & ast, std::ostream & s, size_t indent if (ast.default_expression) { - s << ' ' << hightlight(ast.default_specifier, hilite_keyword, hilite) << ' '; + s << ' ' << highlight(ast.default_specifier, hilite_keyword, hilite) << ' '; formatAST(*ast.default_expression, s, indent, hilite, one_line); } } @@ -908,10 +923,16 @@ void formatAST(const ASTMultiQuery & ast, std::ostream & s, size_t indent, bool void formatAST(const IAST & ast, std::ostream & s, size_t indent, bool hilite, bool one_line, bool need_parens) { + FormatState state = { .s = s, .hilite = hilite, .one_line = one_line }; + state.formatImpl(ast, indent, need_parens); +} + +void FormatState::formatImpl(const IAST & ast, size_t indent, bool need_parens) +{ #define DISPATCH(NAME) \ else if (const AST ## NAME * concrete = typeid_cast(&ast)) \ - formatAST(*concrete, s, indent, hilite, one_line, need_parens); + state.formatImpl(*concrete, indent, need_parens); if (false) {} DISPATCH(SelectQuery)