diff --git a/src/Interpreters/QueryNormalizer.cpp b/src/Interpreters/QueryNormalizer.cpp index 7fef203aea6..aae714198b5 100644 --- a/src/Interpreters/QueryNormalizer.cpp +++ b/src/Interpreters/QueryNormalizer.cpp @@ -80,6 +80,9 @@ void QueryNormalizer::visit(ASTIdentifier & node, ASTPtr & ast, Data & data) /// If it is an alias, but not a parent alias (for constructs like "SELECT column + 1 AS column"). auto it_alias = data.aliases.find(node.name()); + if (!data.allow_self_aliases && current_alias == node.name()) + throw Exception(ErrorCodes::CYCLIC_ALIASES, "Self referencing of {} to {}. Cyclic alias", backQuote(current_alias), backQuote(node.name())); + if (it_alias != data.aliases.end() && current_alias != node.name()) { if (!IdentifierSemantic::canBeAlias(node)) diff --git a/src/Interpreters/QueryNormalizer.h b/src/Interpreters/QueryNormalizer.h index 5cb12c255c2..7fc0f4bdf82 100644 --- a/src/Interpreters/QueryNormalizer.h +++ b/src/Interpreters/QueryNormalizer.h @@ -48,18 +48,22 @@ public: MapOfASTs finished_asts; /// already processed vertices (and by what they replaced) SetOfASTs current_asts; /// vertices in the current call stack of this method std::string current_alias; /// the alias referencing to the ancestor of ast (the deepest ancestor with aliases) - bool ignore_alias; /// normalize query without any aliases + const bool ignore_alias; /// normalize query without any aliases - Data(const Aliases & aliases_, const NameSet & source_columns_set_, bool ignore_alias_, ExtractedSettings && settings_) + /// It's Ok to have "c + 1 AS c" in queries, but not in table definition + const bool allow_self_aliases; /// for constructs like "SELECT column + 1 AS column" + + Data(const Aliases & aliases_, const NameSet & source_columns_set_, bool ignore_alias_, ExtractedSettings && settings_, bool allow_self_aliases_) : aliases(aliases_) , source_columns_set(source_columns_set_) , settings(settings_) , level(0) , ignore_alias(ignore_alias_) + , allow_self_aliases(allow_self_aliases_) {} }; - QueryNormalizer(Data & data) + explicit QueryNormalizer(Data & data) : visitor_data(data) {} diff --git a/src/Interpreters/TreeRewriter.cpp b/src/Interpreters/TreeRewriter.cpp index 5b4a869d44b..f8c3ecaee34 100644 --- a/src/Interpreters/TreeRewriter.cpp +++ b/src/Interpreters/TreeRewriter.cpp @@ -913,7 +913,7 @@ TreeRewriterResultPtr TreeRewriter::analyzeSelect( all_source_columns_set.insert(name); } - normalize(query, result.aliases, all_source_columns_set, select_options.ignore_alias, settings); + normalize(query, result.aliases, all_source_columns_set, select_options.ignore_alias, settings, /* allow_self_aliases = */ true); /// Remove unneeded columns according to 'required_result_columns'. /// Leave all selected columns in case of DISTINCT; columns that contain arrayJoin function inside. @@ -968,7 +968,7 @@ TreeRewriterResultPtr TreeRewriter::analyze( TreeRewriterResult result(source_columns, storage, metadata_snapshot, false); - normalize(query, result.aliases, result.source_columns_set, false, settings); + normalize(query, result.aliases, result.source_columns_set, false, settings, /* allow_self_aliases = */ false); /// Executing scalar subqueries. Column defaults could be a scalar subquery. executeScalarSubqueries(query, getContext(), 0, result.scalars, false); @@ -994,7 +994,7 @@ TreeRewriterResultPtr TreeRewriter::analyze( } void TreeRewriter::normalize( - ASTPtr & query, Aliases & aliases, const NameSet & source_columns_set, bool ignore_alias, const Settings & settings) + ASTPtr & query, Aliases & aliases, const NameSet & source_columns_set, bool ignore_alias, const Settings & settings, bool allow_self_aliases) { CustomizeCountDistinctVisitor::Data data_count_distinct{settings.count_distinct_implementation}; CustomizeCountDistinctVisitor(data_count_distinct).visit(query); @@ -1054,7 +1054,7 @@ void TreeRewriter::normalize( FunctionNameNormalizer().visit(query.get()); /// Common subexpression elimination. Rewrite rules. - QueryNormalizer::Data normalizer_data(aliases, source_columns_set, ignore_alias, settings); + QueryNormalizer::Data normalizer_data(aliases, source_columns_set, ignore_alias, settings, allow_self_aliases); QueryNormalizer(normalizer_data).visit(query); } diff --git a/src/Interpreters/TreeRewriter.h b/src/Interpreters/TreeRewriter.h index 1f535325666..bb84ca532f0 100644 --- a/src/Interpreters/TreeRewriter.h +++ b/src/Interpreters/TreeRewriter.h @@ -115,7 +115,7 @@ public: std::shared_ptr table_join = {}) const; private: - static void normalize(ASTPtr & query, Aliases & aliases, const NameSet & source_columns_set, bool ignore_alias, const Settings & settings); + static void normalize(ASTPtr & query, Aliases & aliases, const NameSet & source_columns_set, bool ignore_alias, const Settings & settings, bool allow_self_aliases); }; } diff --git a/src/Interpreters/tests/gtest_cycle_aliases.cpp b/src/Interpreters/tests/gtest_cycle_aliases.cpp index df40d96a66e..2bdeac90f8f 100644 --- a/src/Interpreters/tests/gtest_cycle_aliases.cpp +++ b/src/Interpreters/tests/gtest_cycle_aliases.cpp @@ -9,6 +9,21 @@ using namespace DB; + +TEST(QueryNormalizer, SimpleLoopAlias) +{ + String query = "a as a"; + ParserExpressionList parser(false); + ASTPtr ast = parseQuery(parser, query, 0, 0); + + Aliases aliases; + aliases["a"] = parseQuery(parser, "a as a", 0, 0)->children[0]; + + Settings settings; + QueryNormalizer::Data normalizer_data(aliases, {}, false, settings, false); + EXPECT_THROW(QueryNormalizer(normalizer_data).visit(ast), Exception); +} + TEST(QueryNormalizer, SimpleCycleAlias) { String query = "a as b, b as a"; @@ -20,6 +35,6 @@ TEST(QueryNormalizer, SimpleCycleAlias) aliases["b"] = parseQuery(parser, "a as b", 0, 0)->children[0]; Settings settings; - QueryNormalizer::Data normalizer_data(aliases, {}, false, settings); + QueryNormalizer::Data normalizer_data(aliases, {}, false, settings, true); EXPECT_THROW(QueryNormalizer(normalizer_data).visit(ast), Exception); } diff --git a/tests/queries/0_stateless/01902_self_aliases_in_columns.reference b/tests/queries/0_stateless/01902_self_aliases_in_columns.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01902_self_aliases_in_columns.sql b/tests/queries/0_stateless/01902_self_aliases_in_columns.sql new file mode 100644 index 00000000000..b03d7c15f62 --- /dev/null +++ b/tests/queries/0_stateless/01902_self_aliases_in_columns.sql @@ -0,0 +1,14 @@ +CREATE TABLE a +( + `number` UInt64, + `x` MATERIALIZED x +) +ENGINE = MergeTree +ORDER BY number; --{ serverError 174} + +CREATE TABLE foo +( + i Int32, + j ALIAS j + 1 +) +ENGINE = MergeTree() ORDER BY i; --{ serverError 174}