diff --git a/docs/en/getting-started/example-datasets/tpch.md b/docs/en/getting-started/example-datasets/tpch.md index 1ec6c9503724..6a3cffdf0f4c 100644 --- a/docs/en/getting-started/example-datasets/tpch.md +++ b/docs/en/getting-started/example-datasets/tpch.md @@ -812,26 +812,6 @@ ORDER BY DROP VIEW revenue0; ``` -::::note -As of October 2024, the view definition does not work out-of-the box. Corresponding issue: https://github.com/ClickHouse/ClickHouse/issues/70139 - -This alternative view definition does work: - -```sql -CREATE VIEW revenue0 AS - SELECT - l_suppkey AS supplier_no, - sum(l_extendedprice * (1 - l_discount)) AS total_revenue - FROM - lineitem - WHERE - l_shipdate >= DATE '1996-01-01' - AND l_shipdate < DATE '1996-01-01' + INTERVAL '3' MONTH - GROUP BY - l_suppkey; -``` -:::: - **Q16** ```sql diff --git a/docs/en/sql-reference/statements/create/view.md b/docs/en/sql-reference/statements/create/view.md index a8be515765bc..e7984ead7fa4 100644 --- a/docs/en/sql-reference/statements/create/view.md +++ b/docs/en/sql-reference/statements/create/view.md @@ -13,7 +13,7 @@ Creates a new view. Views can be [normal](#normal-view), [materialized](#materia Syntax: ``` sql -CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] +CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [(alias1 [, alias2 ...])] [ON CLUSTER cluster_name] [DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }] AS SELECT ... [COMMENT 'comment'] diff --git a/docs/en/sql-reference/statements/select/index.md b/docs/en/sql-reference/statements/select/index.md index ed54f7bd776e..ee7410226d8a 100644 --- a/docs/en/sql-reference/statements/select/index.md +++ b/docs/en/sql-reference/statements/select/index.md @@ -11,12 +11,12 @@ sidebar_label: SELECT ## Syntax ``` sql -[WITH expr_list|(subquery)] +[WITH expr_list(subquery)] SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list [FROM [db.]table | (subquery) | table_function] [FINAL] [SAMPLE sample_coeff] [ARRAY JOIN ...] -[GLOBAL] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI] JOIN (subquery)|table (ON )|(USING ) +[GLOBAL] [ANY|ALL|ASOF] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI] JOIN (subquery)|table [(alias1 [, alias2 ...])] (ON )|(USING ) [PREWHERE expr] [WHERE expr] [GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS] diff --git a/src/Analyzer/QueryNode.cpp b/src/Analyzer/QueryNode.cpp index 8499c431b846..26665754a167 100644 --- a/src/Analyzer/QueryNode.cpp +++ b/src/Analyzer/QueryNode.cpp @@ -29,6 +29,7 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; + extern const int BAD_ARGUMENTS; } QueryNode::QueryNode(ContextMutablePtr context_, SettingsChanges settings_changes_) @@ -50,9 +51,20 @@ QueryNode::QueryNode(ContextMutablePtr context_) void QueryNode::resolveProjectionColumns(NamesAndTypes projection_columns_value) { - if (projection_columns_value.size() != getProjection().getNodes().size()) - throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected projection columns size to match projection nodes size"); + // Ensure the number of aliases matches the number of projection columns + if (!this->projection_aliases_to_override.empty()) + { + if (this->projection_aliases_to_override.size() != projection_columns_value.size()) + throw Exception(ErrorCodes::BAD_ARGUMENTS, + "Number of aliases does not match number of projection columns. " + "Expected {}, got {}", + projection_columns_value.size(), + this->projection_aliases_to_override.size()); + + for (size_t i = 0; i < projection_columns_value.size(); ++i) + projection_columns_value[i].name = this->projection_aliases_to_override[i]; + } projection_columns = std::move(projection_columns_value); } @@ -297,6 +309,12 @@ void QueryNode::updateTreeHashImpl(HashState & state, CompareOptions) const state.update(projection_column_type_name); } + for (const auto & projection_alias : projection_aliases_to_override) + { + state.update(projection_alias.size()); + state.update(projection_alias); + } + state.update(is_recursive_with); state.update(is_distinct); state.update(is_limit_with_ties); @@ -338,6 +356,7 @@ QueryTreeNodePtr QueryNode::cloneImpl() const result_query_node->cte_name = cte_name; result_query_node->projection_columns = projection_columns; result_query_node->settings_changes = settings_changes; + result_query_node->projection_aliases_to_override = projection_aliases_to_override; return result_query_node; } diff --git a/src/Analyzer/QueryNode.h b/src/Analyzer/QueryNode.h index 2333fc56218d..f4b5754be49c 100644 --- a/src/Analyzer/QueryNode.h +++ b/src/Analyzer/QueryNode.h @@ -630,6 +630,11 @@ class QueryNode final : public IQueryTreeNode void dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, size_t indent) const override; + void setProjectionAliasesToOverride(Names pr_aliases) + { + projection_aliases_to_override = std::move(pr_aliases); + } + protected: bool isEqualImpl(const IQueryTreeNode & rhs, CompareOptions) const override; @@ -654,6 +659,7 @@ class QueryNode final : public IQueryTreeNode std::string cte_name; NamesAndTypes projection_columns; + Names projection_aliases_to_override; ContextMutablePtr context; SettingsChanges settings_changes; diff --git a/src/Analyzer/QueryTreeBuilder.cpp b/src/Analyzer/QueryTreeBuilder.cpp index 4714661690c0..dfbddacd25d9 100644 --- a/src/Analyzer/QueryTreeBuilder.cpp +++ b/src/Analyzer/QueryTreeBuilder.cpp @@ -93,11 +93,13 @@ class QueryTreeBuilder QueryTreeNodePtr buildSelectOrUnionExpression(const ASTPtr & select_or_union_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const; QueryTreeNodePtr buildSelectWithUnionExpression(const ASTPtr & select_with_union_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const; QueryTreeNodePtr buildSelectIntersectExceptQuery(const ASTPtr & select_intersect_except_query, @@ -108,6 +110,7 @@ class QueryTreeBuilder QueryTreeNodePtr buildSelectExpression(const ASTPtr & select_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const; QueryTreeNodePtr buildSortList(const ASTPtr & order_by_expression_list, const ContextPtr & context) const; @@ -122,7 +125,7 @@ class QueryTreeBuilder QueryTreeNodePtr buildWindow(const ASTPtr & window_definition, const ContextPtr & context) const; - QueryTreeNodePtr buildJoinTree(const ASTPtr & tables_in_select_query, const ContextPtr & context) const; + QueryTreeNodePtr buildJoinTree(const ASTSelectQuery & select_query, const ContextPtr & context) const; ColumnTransformersNodes buildColumnTransformers(const ASTPtr & matcher_expression, const ContextPtr & context) const; @@ -138,7 +141,7 @@ QueryTreeBuilder::QueryTreeBuilder(ASTPtr query_, ContextPtr context_) if (query->as() || query->as() || query->as()) - query_tree_node = buildSelectOrUnionExpression(query, false /*is_subquery*/, {} /*cte_name*/, context_); + query_tree_node = buildSelectOrUnionExpression(query, false /*is_subquery*/, {} /*cte_name*/, nullptr /*aliases*/, context_); else if (query->as()) query_tree_node = buildExpressionList(query, context_); else @@ -148,16 +151,17 @@ QueryTreeBuilder::QueryTreeBuilder(ASTPtr query_, ContextPtr context_) QueryTreeNodePtr QueryTreeBuilder::buildSelectOrUnionExpression(const ASTPtr & select_or_union_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const { QueryTreeNodePtr query_node; if (select_or_union_query->as()) - query_node = buildSelectWithUnionExpression(select_or_union_query, is_subquery /*is_subquery*/, cte_name /*cte_name*/, context); + query_node = buildSelectWithUnionExpression(select_or_union_query, is_subquery /*is_subquery*/, cte_name /*cte_name*/, nullptr /*aliases*/, context); else if (select_or_union_query->as()) query_node = buildSelectIntersectExceptQuery(select_or_union_query, is_subquery /*is_subquery*/, cte_name /*cte_name*/, context); else if (select_or_union_query->as()) - query_node = buildSelectExpression(select_or_union_query, is_subquery /*is_subquery*/, cte_name /*cte_name*/, context); + query_node = buildSelectExpression(select_or_union_query, is_subquery /*is_subquery*/, cte_name /*cte_name*/, aliases, context); else throw Exception(ErrorCodes::UNSUPPORTED_METHOD, "SELECT or UNION query {} is not supported", select_or_union_query->formatForErrorMessage()); @@ -168,13 +172,14 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectOrUnionExpression(const ASTPtr & s QueryTreeNodePtr QueryTreeBuilder::buildSelectWithUnionExpression(const ASTPtr & select_with_union_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const { auto & select_with_union_query_typed = select_with_union_query->as(); auto & select_lists = select_with_union_query_typed.list_of_selects->as(); if (select_lists.children.size() == 1) - return buildSelectOrUnionExpression(select_lists.children[0], is_subquery, cte_name, context); + return buildSelectOrUnionExpression(select_lists.children[0], is_subquery, cte_name, aliases, context); auto union_node = std::make_shared(Context::createCopy(context), select_with_union_query_typed.union_mode); union_node->setIsSubquery(is_subquery); @@ -187,7 +192,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectWithUnionExpression(const ASTPtr & for (size_t i = 0; i < select_lists_children_size; ++i) { auto & select_list_node = select_lists.children[i]; - QueryTreeNodePtr query_node = buildSelectOrUnionExpression(select_list_node, false /*is_subquery*/, {} /*cte_name*/, context); + QueryTreeNodePtr query_node = buildSelectOrUnionExpression(select_list_node, false /*is_subquery*/, {} /*cte_name*/, aliases, context); union_node->getQueries().getNodes().push_back(std::move(query_node)); } @@ -203,7 +208,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectIntersectExceptQuery(const ASTPtr auto select_lists = select_intersect_except_query_typed.getListOfSelects(); if (select_lists.size() == 1) - return buildSelectExpression(select_lists[0], is_subquery, cte_name, context); + return buildSelectExpression(select_lists[0], is_subquery, cte_name, nullptr /*aliases*/, context); SelectUnionMode union_mode; if (select_intersect_except_query_typed.final_operator == ASTSelectIntersectExceptQuery::Operator::INTERSECT_ALL) @@ -228,7 +233,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectIntersectExceptQuery(const ASTPtr for (size_t i = 0; i < select_lists_size; ++i) { auto & select_list_node = select_lists[i]; - QueryTreeNodePtr query_node = buildSelectOrUnionExpression(select_list_node, false /*is_subquery*/, {} /*cte_name*/, context); + QueryTreeNodePtr query_node = buildSelectOrUnionExpression(select_list_node, false /*is_subquery*/, {} /*cte_name*/, nullptr /*aliases*/, context); union_node->getQueries().getNodes().push_back(std::move(query_node)); } @@ -238,6 +243,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectIntersectExceptQuery(const ASTPtr QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_query, bool is_subquery, const std::string & cte_name, + const ASTPtr & aliases, const ContextPtr & context) const { const auto & select_query_typed = select_query->as(); @@ -308,7 +314,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q auto current_context = current_query_tree->getContext(); - current_query_tree->getJoinTree() = buildJoinTree(select_query_typed.tables(), current_context); + current_query_tree->getJoinTree() = buildJoinTree(select_query_typed, current_context); auto select_with_list = select_query_typed.with(); if (select_with_list) @@ -332,6 +338,23 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q if (select_expression_list) current_query_tree->getProjectionNode() = buildExpressionList(select_expression_list, current_context); + // Apply the override aliases to the projection nodes + if (aliases) + { + // Collect the aliases into a vector of strings + Names collected_aliases; + auto & override_aliases_children = aliases->as().children; + collected_aliases.reserve(override_aliases_children.size()); + + for (const auto & child : override_aliases_children) + { + const auto & alias_ast = child->as(); + collected_aliases.push_back(alias_ast.name()); + } + + current_query_tree->setProjectionAliasesToOverride(collected_aliases); + } + auto prewhere_expression = select_query_typed.prewhere(); if (prewhere_expression) current_query_tree->getPrewhere() = buildExpression(prewhere_expression, current_context); @@ -691,19 +714,19 @@ QueryTreeNodePtr QueryTreeBuilder::buildExpression(const ASTPtr & expression, co else if (const auto * subquery = expression->as()) { auto subquery_query = subquery->children[0]; - auto query_node = buildSelectWithUnionExpression(subquery_query, true /*is_subquery*/, {} /*cte_name*/, context); + auto query_node = buildSelectWithUnionExpression(subquery_query, true /*is_subquery*/, {} /*cte_name*/, nullptr /*aliases*/, context); result = std::move(query_node); } else if (const auto * /*select_with_union_query*/ _ = expression->as()) { - auto query_node = buildSelectWithUnionExpression(expression, false /*is_subquery*/, {} /*cte_name*/, context); + auto query_node = buildSelectWithUnionExpression(expression, false /*is_subquery*/, {} /*cte_name*/, nullptr /*aliases*/, context); result = std::move(query_node); } else if (const auto * with_element = expression->as()) { auto with_element_subquery = with_element->subquery->as().children.at(0); - auto query_node = buildSelectWithUnionExpression(with_element_subquery, true /*is_subquery*/, with_element->name /*cte_name*/, context); + auto query_node = buildSelectWithUnionExpression(with_element_subquery, true /*is_subquery*/, with_element->name /*cte_name*/, with_element->aliases /*aliases*/, context); result = std::move(query_node); } @@ -802,8 +825,9 @@ QueryTreeNodePtr QueryTreeBuilder::buildWindow(const ASTPtr & window_definition, return window_node; } -QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(const ASTPtr & tables_in_select_query, const ContextPtr & context) const +QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(const ASTSelectQuery & select_query, const ContextPtr & context) const { + const auto & tables_in_select_query = select_query.tables(); if (!tables_in_select_query) { /** If no table is specified in SELECT query we substitute system.one table. @@ -868,7 +892,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(const ASTPtr & tables_in_select auto & subquery_expression = table_expression.subquery->as(); const auto & select_with_union_query = subquery_expression.children[0]; - auto node = buildSelectWithUnionExpression(select_with_union_query, true /*is_subquery*/, {} /*cte_name*/, context); + auto node = buildSelectWithUnionExpression(select_with_union_query, true /*is_subquery*/, {} /*cte_name*/, select_query.aliases(), context); node->setAlias(subquery_expression.tryGetAlias()); node->setOriginalAST(select_with_union_query); @@ -898,7 +922,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(const ASTPtr & tables_in_select table_function_expression.formatForErrorMessage()); if (argument->as() || argument->as() || argument->as()) - node->getArguments().getNodes().push_back(buildSelectOrUnionExpression(argument, false /*is_subquery*/, {} /*cte_name*/, context)); + node->getArguments().getNodes().push_back(buildSelectOrUnionExpression(argument, false /*is_subquery*/, {} /*cte_name*/, nullptr /*aliases*/, context)); else if (const auto * ast_set = argument->as()) node->setSettingsChanges(ast_set->changes); else diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index c2527dbd2a61..3291bd3c8978 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -940,6 +940,45 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::getTableProperti if (create.isParameterizedView()) return properties; + if (create.aliases_list) + { + auto & aliases_children = create.aliases_list->children; + const auto * select_with_union_query = create.select->as(); + + if (!select_with_union_query) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected ASTSelectWithUnionQuery"); + + const auto & selects = select_with_union_query->list_of_selects->children; + + for (const auto & select : selects) + { + const auto * select_query = select->as(); + + if (!select_query) + throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected ASTSelectQuery inside ASTSelectWithUnionQuery"); + + auto select_expression_list = select_query->select(); + + if (!select_expression_list) + throw Exception(ErrorCodes::LOGICAL_ERROR, "No select expressions in SELECT query"); + + auto & select_expressions = select_expression_list->children; + + if (select_expressions.size() != aliases_children.size()) + { + throw Exception(ErrorCodes::BAD_ARGUMENTS, + "Number of aliases does not match number of expressions in SELECT list"); + } + + for (size_t i = 0; i < select_expressions.size(); ++i) + { + auto & expr = select_expressions[i]; + const auto & alias_ast = aliases_children[i]->as(); + expr->setAlias(alias_ast.name()); + } + } + } + Block as_select_sample; if (getContext()->getSettingsRef()[Setting::allow_experimental_analyzer]) diff --git a/src/Parsers/ASTCreateQuery.cpp b/src/Parsers/ASTCreateQuery.cpp index 786436213c89..6894f21b1a78 100644 --- a/src/Parsers/ASTCreateQuery.cpp +++ b/src/Parsers/ASTCreateQuery.cpp @@ -241,6 +241,8 @@ ASTPtr ASTCreateQuery::clone() const if (columns_list) res->set(res->columns_list, columns_list->clone()); + if (aliases_list) + res->set(res->aliases_list, aliases_list->clone()); if (storage) res->set(res->storage, storage->clone()); if (select) @@ -480,6 +482,16 @@ void ASTCreateQuery::formatQueryImpl(WriteBuffer & ostr, const FormatSettings & ostr << (settings.one_line ? ")" : "\n)"); } + frame.expression_list_always_start_on_new_line = true; + + if (is_ordinary_view && aliases_list && !as_table_function) + { + ostr << (settings.one_line ? " (" : "\n("); + FormatStateStacked frame_nested = frame; + aliases_list->format(ostr, settings, state, frame_nested); + ostr << (settings.one_line ? ")" : "\n)"); + } + if (dictionary_attributes_list) { ostr << (settings.one_line ? " (" : "\n("); diff --git a/src/Parsers/ASTCreateQuery.h b/src/Parsers/ASTCreateQuery.h index 26304bc89a58..6bc0c9300b4c 100644 --- a/src/Parsers/ASTCreateQuery.h +++ b/src/Parsers/ASTCreateQuery.h @@ -107,6 +107,7 @@ class ASTCreateQuery : public ASTQueryWithTableAndOutput, public ASTQueryWithOnC bool has_uuid{false}; // CREATE TABLE x UUID '...' ASTColumns * columns_list = nullptr; + ASTExpressionList * aliases_list = nullptr; /// Aliases such as "(a, b)" in "CREATE VIEW my_view (a, b) AS SELECT 1, 2" ASTStorage * storage = nullptr; ASTPtr watermark_function; @@ -186,6 +187,7 @@ class ASTCreateQuery : public ASTQueryWithTableAndOutput, public ASTQueryWithOnC void forEachPointerToChild(std::function f) override { f(reinterpret_cast(&columns_list)); + f(reinterpret_cast(&aliases_list)); f(reinterpret_cast(&storage)); f(reinterpret_cast(&targets)); f(reinterpret_cast(&as_table_function)); diff --git a/src/Parsers/ASTSelectQuery.cpp b/src/Parsers/ASTSelectQuery.cpp index 8b18a883b485..d0601bf8bd0c 100644 --- a/src/Parsers/ASTSelectQuery.cpp +++ b/src/Parsers/ASTSelectQuery.cpp @@ -88,6 +88,18 @@ void ASTSelectQuery::formatImpl(WriteBuffer & ostr, const FormatSettings & s, Fo tables()->format(ostr, s, state, frame); } + if (aliases()) + { + const bool prep_whitespace = frame.expression_list_prepend_whitespace; + frame.expression_list_prepend_whitespace = false; + + ostr << (s.hilite ? hilite_none : "") << indent_str << " ("; + aliases()->format(ostr, s, state, frame); + ostr << (s.hilite ? hilite_none : "") << indent_str << ")"; + + frame.expression_list_prepend_whitespace = prep_whitespace; + } + if (prewhere()) { ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "PREWHERE " << (s.hilite ? hilite_none : ""); diff --git a/src/Parsers/ASTSelectQuery.h b/src/Parsers/ASTSelectQuery.h index a3964e50b7ac..1d66d75f175d 100644 --- a/src/Parsers/ASTSelectQuery.h +++ b/src/Parsers/ASTSelectQuery.h @@ -20,6 +20,8 @@ class ASTSelectQuery : public IAST WITH, SELECT, TABLES, + ALIASES, + CTE_ALIASES, PREWHERE, WHERE, GROUP_BY, @@ -46,6 +48,10 @@ class ASTSelectQuery : public IAST return "SELECT"; case Expression::TABLES: return "TABLES"; + case Expression::ALIASES: + return "ALIASES"; + case Expression::CTE_ALIASES: + return "CTE_ALIASES"; case Expression::PREWHERE: return "PREWHERE"; case Expression::WHERE: @@ -96,6 +102,8 @@ class ASTSelectQuery : public IAST ASTPtr & refSelect() { return getExpression(Expression::SELECT); } ASTPtr & refTables() { return getExpression(Expression::TABLES); } + ASTPtr & refAliases() { return getExpression(Expression::ALIASES); } + ASTPtr & refCteAliases() { return getExpression(Expression::CTE_ALIASES); } ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); } ASTPtr & refWhere() { return getExpression(Expression::WHERE); } ASTPtr & refHaving() { return getExpression(Expression::HAVING); } @@ -104,6 +112,8 @@ class ASTSelectQuery : public IAST ASTPtr with() const { return getExpression(Expression::WITH); } ASTPtr select() const { return getExpression(Expression::SELECT); } ASTPtr tables() const { return getExpression(Expression::TABLES); } + ASTPtr aliases() const { return getExpression(Expression::ALIASES); } + ASTPtr cteAliases() const { return getExpression(Expression::CTE_ALIASES); } ASTPtr prewhere() const { return getExpression(Expression::PREWHERE); } ASTPtr where() const { return getExpression(Expression::WHERE); } ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); } @@ -151,7 +161,6 @@ class ASTSelectQuery : public IAST QueryKind getQueryKind() const override { return QueryKind::Select; } bool hasQueryParameters() const; -protected: void formatImpl(WriteBuffer & ostr, const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override; private: diff --git a/src/Parsers/ASTWithElement.cpp b/src/Parsers/ASTWithElement.cpp index 450453eb0e22..f8f4fcfe9a65 100644 --- a/src/Parsers/ASTWithElement.cpp +++ b/src/Parsers/ASTWithElement.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,6 +11,8 @@ ASTPtr ASTWithElement::clone() const const auto res = std::make_shared(*this); res->children.clear(); res->subquery = subquery->clone(); + if (aliases) + res->aliases = aliases->clone(); res->children.emplace_back(res->subquery); return res; } @@ -21,6 +24,17 @@ void ASTWithElement::formatImpl(WriteBuffer & ostr, const FormatSettings & setti ostr << (settings.hilite ? hilite_alias : ""); settings.writeIdentifier(ostr, name, /*ambiguous=*/false); ostr << (settings.hilite ? hilite_none : ""); + if (aliases) + { + const bool prep_whitespace = frame.expression_list_prepend_whitespace; + frame.expression_list_prepend_whitespace = false; + + ostr << " ("; + aliases->format(ostr, settings, state, frame); + ostr << ")"; + + frame.expression_list_prepend_whitespace = prep_whitespace; + } ostr << (settings.hilite ? hilite_keyword : "") << " AS" << (settings.hilite ? hilite_none : ""); ostr << settings.nl_or_ws << indent_str; dynamic_cast(*subquery).formatImplWithoutAlias(ostr, settings, state, frame); diff --git a/src/Parsers/ASTWithElement.h b/src/Parsers/ASTWithElement.h index a51d1897d94d..9020503c7214 100644 --- a/src/Parsers/ASTWithElement.h +++ b/src/Parsers/ASTWithElement.h @@ -12,13 +12,13 @@ class ASTWithElement : public IAST public: String name; ASTPtr subquery; + ASTPtr aliases; /** Get the text that identifies this element. */ String getID(char) const override { return "WithElement"; } ASTPtr clone() const override; -protected: void formatImpl(WriteBuffer & ostr, const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override; }; diff --git a/src/Parsers/ExpressionListParsers.cpp b/src/Parsers/ExpressionListParsers.cpp index c82403f3b29e..da9a93beb2a1 100644 --- a/src/Parsers/ExpressionListParsers.cpp +++ b/src/Parsers/ExpressionListParsers.cpp @@ -337,6 +337,12 @@ bool ParserOrderByExpressionList::parseImpl(Pos & pos, ASTPtr & node, Expected & .parse(pos, node, expected); } +bool ParserAliasesExpressionList::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) +{ + return ParserList(std::make_unique(), std::make_unique(TokenType::Comma), false) + .parse(pos, node, expected); +} + bool ParserGroupingSetsExpressionListElements::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { auto command_list = std::make_shared(); diff --git a/src/Parsers/ExpressionListParsers.h b/src/Parsers/ExpressionListParsers.h index 8e2304f3e475..30fa5a5cc0c7 100644 --- a/src/Parsers/ExpressionListParsers.h +++ b/src/Parsers/ExpressionListParsers.h @@ -268,6 +268,13 @@ class ParserOrderByExpressionList : public IParserBase bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; }; +class ParserAliasesExpressionList : public IParserBase +{ +protected: + const char * getName() const override { return "list of aliases expressions"; } + bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; +}; + class ParserGroupingSetsExpressionList : public IParserBase { protected: diff --git a/src/Parsers/ParserCreateQuery.cpp b/src/Parsers/ParserCreateQuery.cpp index 2d3fdaf3c6b5..06d2869c1dc2 100644 --- a/src/Parsers/ParserCreateQuery.cpp +++ b/src/Parsers/ParserCreateQuery.cpp @@ -1520,6 +1520,7 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ParserStorage storage_p{ParserStorage::TABLE_ENGINE}; ParserIdentifier name_p; ParserTablePropertiesDeclarationList table_properties_p; + ParserAliasesExpressionList expr_list_aliases; ParserSelectWithUnionQuery select_p; ParserNameList names_p; ParserSQLSecurity sql_security_p; @@ -1528,6 +1529,7 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ASTPtr to_table; ASTPtr to_inner_uuid; ASTPtr columns_list; + ASTPtr aliases_list; ASTPtr storage; ASTPtr as_database; ASTPtr as_table; @@ -1605,13 +1607,31 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec } /// Optional - a list of columns can be specified. It must fully comply with SELECT. - if (s_lparen.ignore(pos, expected)) + if (s_lparen.ignore(pos, expected)) // Parsing cases like CREATE VIEW (a Int64, b Int64) (a, b) SELECT ... { + bool has_aliases = false; if (!table_properties_p.parse(pos, columns_list, expected)) - return false; + { + if (!expr_list_aliases.parse(pos, aliases_list, expected)) + return false; + else + has_aliases = true; + } + else + { + if (!s_rparen.ignore(pos, expected)) + return false; + if (s_lparen.ignore(pos, expected)) + { + has_aliases = true; + if (!expr_list_aliases.parse(pos, aliases_list, expected)) + return false; + } + } - if (!s_rparen.ignore(pos, expected)) - return false; + if (has_aliases) + if (!s_rparen.ignore(pos, expected)) + return false; } if (is_materialized_view) @@ -1687,6 +1707,7 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query->children.push_back(query->table); query->set(query->columns_list, columns_list); + query->set(query->aliases_list, aliases_list); if (refresh_strategy) query->set(query->refresh_strategy, refresh_strategy); diff --git a/src/Parsers/ParserSelectQuery.cpp b/src/Parsers/ParserSelectQuery.cpp index 067a3c5b67bf..2ad204356d27 100644 --- a/src/Parsers/ParserSelectQuery.cpp +++ b/src/Parsers/ParserSelectQuery.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -72,6 +73,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ParserNotEmptyExpressionList exp_list(false); ParserNotEmptyExpressionList exp_list_for_with_clause(false); ParserNotEmptyExpressionList exp_list_for_select_clause(/*allow_alias_without_as_keyword*/ true, /*allow_trailing_commas*/ true); + ParserAliasesExpressionList exp_list_for_aliases; ParserExpressionWithOptionalAlias exp_elem(false); ParserOrderByExpressionList order_list; ParserGroupingSetsExpressionList grouping_sets_list; @@ -83,6 +85,8 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ASTPtr with_expression_list; ASTPtr select_expression_list; ASTPtr tables; + ASTPtr expression_list_for_aliases; + ASTPtr expression_list_for_cte_aliases; ASTPtr prewhere_expression; ASTPtr where_expression; ASTPtr group_expression_list; @@ -111,6 +115,13 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) return false; if (with_expression_list->children.empty()) return false; + + for (const auto & child : with_expression_list->children) /// For cases: WITH _ (a, b) AS ... <- (a, b) are aliases + { + if (auto * with_element = child->as()) + if (with_element->aliases) + expression_list_for_cte_aliases = with_element->aliases; + } } } @@ -189,6 +200,15 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) return false; } + if (tables && open_bracket.ignore(pos, expected)) + { + if (!exp_list_for_aliases.parse(pos, expression_list_for_aliases, expected)) + return false; + + if (!close_bracket.ignore(pos, expected)) + return false; + } + /// PREWHERE expr if (s_prewhere.ignore(pos, expected)) { @@ -500,6 +520,8 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) select_query->setExpression(ASTSelectQuery::Expression::WITH, std::move(with_expression_list)); select_query->setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expression_list)); select_query->setExpression(ASTSelectQuery::Expression::TABLES, std::move(tables)); + select_query->setExpression(ASTSelectQuery::Expression::ALIASES, std::move(expression_list_for_aliases)); + select_query->setExpression(ASTSelectQuery::Expression::CTE_ALIASES, std::move(expression_list_for_cte_aliases)); select_query->setExpression(ASTSelectQuery::Expression::PREWHERE, std::move(prewhere_expression)); select_query->setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_expression)); select_query->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list)); diff --git a/src/Parsers/ParserWithElement.cpp b/src/Parsers/ParserWithElement.cpp index 5b31c9296858..41b9c2c0c738 100644 --- a/src/Parsers/ParserWithElement.cpp +++ b/src/Parsers/ParserWithElement.cpp @@ -15,15 +15,45 @@ bool ParserWithElement::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ParserIdentifier s_ident; ParserKeyword s_as(Keyword::AS); ParserSubquery s_subquery; + ParserAliasesExpressionList exp_list_for_aliases; + ParserToken open_bracket(TokenType::OpeningRoundBracket); + ParserToken close_bracket(TokenType::ClosingRoundBracket); auto old_pos = pos; - if (ASTPtr name, subquery; - s_ident.parse(pos, name, expected) && s_as.ignore(pos, expected) && s_subquery.parse(pos, subquery, expected)) + auto with_element = std::make_shared(); + + // Trying to parse structure: identifier [(alias1, alias2, ...)] AS (subquery) + if (ASTPtr name_or_expr; + (s_ident.parse(pos, name_or_expr, expected) || ParserExpressionWithOptionalAlias(false).parse(pos, name_or_expr, expected)) && + ( + [&]() -> bool { + auto saved_pos = pos; + if (open_bracket.ignore(pos, expected)) + { + if (ASTPtr expression_list_for_aliases; exp_list_for_aliases.parse(pos, expression_list_for_aliases, expected)) + { + with_element->aliases = expression_list_for_aliases; + if (!close_bracket.ignore(pos, expected)) + return false; + return true; + } + else + { + pos = saved_pos; + return false; + } + } + return true; + }() + ) && + s_as.ignore(pos, expected) && + s_subquery.parse(pos, with_element->subquery, expected)) { - auto with_element = std::make_shared(); - tryGetIdentifierNameInto(name, with_element->name); - with_element->subquery = subquery; + if (name_or_expr) + tryGetIdentifierNameInto(name_or_expr, with_element->name); + with_element->children.push_back(with_element->subquery); + node = with_element; } else diff --git a/tests/queries/0_stateless/03280_aliases_for_selects_and_views.reference b/tests/queries/0_stateless/03280_aliases_for_selects_and_views.reference new file mode 100644 index 000000000000..53f647a551cd --- /dev/null +++ b/tests/queries/0_stateless/03280_aliases_for_selects_and_views.reference @@ -0,0 +1,28 @@ +0 +2 +0 +1 +1 +2 +1 +2 +0 +QUERY id: 0 + PROJECTION COLUMNS + b UInt8 + PROJECTION + LIST id: 1, nodes: 1 + COLUMN id: 2, column_name: b, result_type: UInt8, source_id: 3 + JOIN TREE + QUERY id: 3, alias: __table1, is_subquery: 1, is_cte: 1, cte_name: t + PROJECTION COLUMNS + b UInt8 + PROJECTION + LIST id: 4, nodes: 1 + CONSTANT id: 5, constant_value: UInt64_2, constant_value_type: UInt8 + JOIN TREE + TABLE id: 6, alias: __table2, table_name: system.one + +SELECT __table1.b AS b +FROM +t AS __table1 diff --git a/tests/queries/0_stateless/03280_aliases_for_selects_and_views.sql b/tests/queries/0_stateless/03280_aliases_for_selects_and_views.sql new file mode 100644 index 000000000000..bccc89e7836b --- /dev/null +++ b/tests/queries/0_stateless/03280_aliases_for_selects_and_views.sql @@ -0,0 +1,75 @@ +EXPLAIN AST CREATE VIEW test_view_1_03280 (a, b] AS SELECT 1, 2; -- { clientError SYNTAX_ERROR } + +EXPLAIN AST CREATE VIEW test_view_1_03280 ((a, b)) AS SELECT 1, 2; -- { clientError SYNTAX_ERROR } + +SET enable_analyzer = 1; + +SELECT b FROM +( + SELECT number, number*2 + FROM numbers(2) +) AS x (a, b); + +SELECT a FROM +( + SELECT number, number*2 + FROM numbers(2) +) AS x (a, b); + +SELECT a FROM +( + SELECT number, number*2 + FROM numbers(2) +) AS x (a); -- { serverError BAD_ARGUMENTS } + +SELECT c FROM +( + SELECT number, number*2 + FROM numbers(2) +) as x (a, b); -- { serverError UNKNOWN_IDENTIFIER } + +DROP VIEW IF EXISTS test_view_03280; + +CREATE VIEW test_view_03280 (a,b) AS SELECT 1, 2; + +SELECT a FROM test_view_03280; + +SELECT b FROM test_view_03280; + +SELECT c FROM test_view_03280; -- { serverError UNKNOWN_IDENTIFIER } + +DROP VIEW IF EXISTS test_view_03280; + +CREATE VIEW test_view_1_03280 (a) AS SELECT 1, 2; -- { serverError BAD_ARGUMENTS } + +WITH t (a, b) AS ( + SELECT 1, 2 +) +SELECT a +FROM t; + +WITH t (a, b) AS ( + SELECT 1, 2 +) +SELECT b +FROM t; + +WITH t (a) AS ( + SELECT * FROM numbers(1) +) +SELECT a +FROM t; + +explain query tree dump_ast = 1 WITH t (a, b) AS (SELECT 1, 2) SELECT b FROM t; + +WITH t (a) AS ( + SELECT 1, 2 +) +SELECT b +FROM t; -- { serverError BAD_ARGUMENTS } + +WITH t (a, b) AS ( + SELECT 1, 2 +) +SELECT c +FROM t; -- { serverError UNKNOWN_IDENTIFIER }