diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 30e01413e1be..2d45c435b034 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -578,6 +578,8 @@ static constexpr UInt64 operator""_Gb(unsigned long long value) \ M(Bool, allow_unrestricted_reads_from_keeper, false, "Allow unrestricted (without condition on path) reads from system.zookeeper table, can be handy, but is not safe for zookeeper", 0) \ \ + M(Bool, remove_redundant_select_asterisk_from, true, "Remove reduntant SELECT * FROM () in the query/subqueries", 0) \ + \ /** Experimental functions */ \ M(Bool, allow_experimental_funnel_functions, false, "Enable experimental functions for funnel analysis.", 0) \ M(Bool, allow_experimental_nlp_functions, false, "Enable experimental functions for natural language processing.", 0) \ diff --git a/src/Interpreters/UnionSelectOptimizerVisitor.cpp b/src/Interpreters/UnionSelectOptimizerVisitor.cpp new file mode 100644 index 000000000000..9c4b76dcf921 --- /dev/null +++ b/src/Interpreters/UnionSelectOptimizerVisitor.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include + +/* +option a) +┌─explain─────────────────────────────────────┐ +│ SelectWithUnionQuery (children 1) │ <- Replace this node +│ ExpressionList (children 1) │ +│ SelectQuery (children 2) │ +│ ExpressionList (children 1) │ +│ Asterisk │ +│ TablesInSelectQuery (children 1) │ +│ TablesInSelectQueryElement (children 1) │ +│ TableExpression (children 1) │ +│ Subquery (children 1) │ +│ SelectWithUnionQuery (children 1) │ <- with this one +│ (whatever) │ +└─────────────────────────────────────────────┘ + +option b) +┌─explain─────────────────────────────────────┐ +│ SelectWithUnionQuery (children 1) │ +│ ExpressionList (children 2) │ +│ SelectQuery (children 2) │ <- Replace this node +│ ExpressionList (children 1) │ +│ Asterisk │ +│ TablesInSelectQuery (children 1) │ +│ TablesInSelectQueryElement (children 1) │ +│ TableExpression (children 1) │ +│ Subquery (children 1) │ +│ SelectWithUnionQuery (children 1) │ +│ ExpressionList (children 1) │ +│ SelectQuery (children 1) │ <- with this one +│ ExpressionList (children 1) │ +│ Literal UInt64_1 (alias n) │ +│ SelectQuery (children 2) │ +│ ExpressionList (children 1) │ +│ Asterisk │ +│ TablesInSelectQuery (children 1) │ +│ TablesInSelectQueryElement (children 1) │ +│ TableExpression (children 1) │ +│ Subquery (children 1) │ +│ SelectWithUnionQuery (children 1) │ +│ ExpressionList (children 1) │ +│ SelectQuery (children 1) │ +│ ExpressionList (children 1) │ +│ Literal UInt64_2 (alias n) │ +└─────────────────────────────────────────────┘ + + +*/ +namespace DB +{ + +void UnionSelectOptimizerVisitor::visit(ASTPtr & ast) +{ + if (ast->as() || ast->as()) + while (visit_replace(ast)) + ; + + for (ASTPtr & child : ast->children) + visit(child); +} + +bool UnionSelectOptimizerVisitor::visit_replace(DB::ASTPtr & node) +{ + DB::ASTPtr query; + DB::ASTPtr next = node; + if (next->as()) + { + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + for (DB::ASTPtr & child : next->children) + { + if (child->as()) + { + next = child; + if (!move_next(next)) + return false; + continue; + } + if (child->as()) + { + next = child; + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + query = next; + continue; + } + else + return false; + } + } + else if (next->as()) + { + if (next->children.size() != 2) + return false; + + for (DB::ASTPtr & child : next->children) + { + if (child->as()) + { + next = child; + if (!move_next(next)) + return false; + continue; + } + if (child->as()) + { + next = child; + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + if (!move_next(next)) + return false; + + query = next; + continue; + } + else + return false; + } + } + + node.swap(query); + return true; +} + +} diff --git a/src/Interpreters/UnionSelectOptimizerVisitor.h b/src/Interpreters/UnionSelectOptimizerVisitor.h new file mode 100644 index 000000000000..f7c2e967612a --- /dev/null +++ b/src/Interpreters/UnionSelectOptimizerVisitor.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace DB +{ + +struct UnionSelectOptimizerVisitor +{ +public: + static void visit(ASTPtr & ast); + +private: + template + static bool move_next(DB::ASTPtr & item) + { + if (item->children.size() != 1 || !item->children.at(0)->as()) + { + return false; + } + item = item->children.at(0); + return true; + } + + + static bool visit_replace(ASTPtr & node); +}; +} diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index d1596c083187..0d5d564bd811 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -68,6 +68,7 @@ #include #include +#include namespace ProfileEvents @@ -567,6 +568,12 @@ static std::tuple executeQueryImpl( NormalizeSelectWithUnionQueryVisitor{data}.visit(ast); } + /// Remove redundant queries + if (settings.remove_redundant_select_asterisk_from) + { + UnionSelectOptimizerVisitor::visit(ast); + } + /// Check the limits. checkASTSizeLimits(*ast, settings);