1#include <Poco/String.h>
2#include <Core/Names.h>
3#include <Interpreters/QueryNormalizer.h>
4#include <Interpreters/IdentifierSemantic.h>
5#include <Interpreters/Context.h>
6#include <Parsers/ASTFunction.h>
7#include <Parsers/ASTIdentifier.h>
8#include <Parsers/ASTSelectQuery.h>
9#include <Parsers/ASTQueryParameter.h>
10#include <Parsers/ASTTablesInSelectQuery.h>
11#include <Common/StringUtils/StringUtils.h>
12#include <Common/quoteString.h>
13
14namespace DB
15{
16
17namespace ErrorCodes
18{
19 extern const int TOO_DEEP_AST;
20 extern const int CYCLIC_ALIASES;
21 extern const int UNKNOWN_QUERY_PARAMETER;
22}
23
24
25class CheckASTDepth
26{
27public:
28 CheckASTDepth(QueryNormalizer::Data & data_)
29 : data(data_)
30 {
31 if (data.level > data.settings.max_ast_depth)
32 throw Exception("Normalized AST is too deep. Maximum: " + toString(data.settings.max_ast_depth), ErrorCodes::TOO_DEEP_AST);
33 ++data.level;
34 }
35
36 ~CheckASTDepth()
37 {
38 --data.level;
39 }
40
41private:
42 QueryNormalizer::Data & data;
43};
44
45
46class RestoreAliasOnExitScope
47{
48public:
49 RestoreAliasOnExitScope(String & alias_)
50 : alias(alias_)
51 , copy(alias_)
52 {}
53
54 ~RestoreAliasOnExitScope()
55 {
56 alias = copy;
57 }
58
59private:
60 String & alias;
61 const String copy;
62};
63
64
65void QueryNormalizer::visit(ASTIdentifier & node, ASTPtr & ast, Data & data)
66{
67 auto & current_asts = data.current_asts;
68 String & current_alias = data.current_alias;
69
70 if (!IdentifierSemantic::getColumnName(node))
71 return;
72
73 /// If it is an alias, but not a parent alias (for constructs like "SELECT column + 1 AS column").
74 auto it_alias = data.aliases.find(node.name);
75 if (it_alias != data.aliases.end() && current_alias != node.name)
76 {
77 if (!IdentifierSemantic::canBeAlias(node))
78 {
79 /// This means that column had qualified name, which was translated (so, canBeAlias() returns false).
80 /// But there is an alias with the same name. So, let's use original name for that column.
81 /// If alias wasn't set, use original column name as alias.
82 /// That helps to avoid result set with columns which have same names but different values.
83 if (node.alias.empty())
84 {
85 node.name.swap(node.alias);
86 node.restoreCompoundName();
87 node.name.swap(node.alias);
88 }
89
90 return;
91 }
92
93 auto & alias_node = it_alias->second;
94
95 /// Let's replace it with the corresponding tree node.
96 if (current_asts.count(alias_node.get()))
97 throw Exception("Cyclic aliases", ErrorCodes::CYCLIC_ALIASES);
98
99 String my_alias = ast->tryGetAlias();
100 if (!my_alias.empty() && my_alias != alias_node->getAliasOrColumnName())
101 {
102 /// Avoid infinite recursion here
103 auto opt_name = IdentifierSemantic::getColumnName(alias_node);
104 bool is_cycle = opt_name && *opt_name == node.name;
105
106 if (!is_cycle)
107 {
108 /// In a construct like "a AS b", where a is an alias, you must set alias b to the result of substituting alias a.
109 ast = alias_node->clone();
110 ast->setAlias(my_alias);
111 }
112 }
113 else
114 ast = alias_node;
115 }
116}
117
118void QueryNormalizer::visit(ASTTablesInSelectQueryElement & node, const ASTPtr &, Data & data)
119{
120 /// normalize JOIN ON section
121 if (node.table_join)
122 {
123 auto & join = node.table_join->as<ASTTableJoin &>();
124 if (join.on_expression)
125 visit(join.on_expression, data);
126 }
127}
128
129static bool needVisitChild(const ASTPtr & child)
130{
131 if (child->as<ASTSelectQuery>() || child->as<ASTTableExpression>())
132 return false;
133 return true;
134}
135
136/// special visitChildren() for ASTSelectQuery
137void QueryNormalizer::visit(ASTSelectQuery & select, const ASTPtr &, Data & data)
138{
139 for (auto & child : select.children)
140 if (needVisitChild(child))
141 visit(child, data);
142
143 /// If the WHERE clause or HAVING consists of a single alias, the reference must be replaced not only in children,
144 /// but also in where_expression and having_expression.
145 if (select.prewhere())
146 visit(select.refPrewhere(), data);
147 if (select.where())
148 visit(select.refWhere(), data);
149 if (select.having())
150 visit(select.refHaving(), data);
151}
152
153/// Don't go into subqueries.
154/// Don't go into select query. It processes children itself.
155/// Do not go to the left argument of lambda expressions, so as not to replace the formal parameters
156/// on aliases in expressions of the form 123 AS x, arrayMap(x -> 1, [2]).
157void QueryNormalizer::visitChildren(const ASTPtr & node, Data & data)
158{
159 if (const auto * func_node = node->as<ASTFunction>())
160 {
161 /// We skip the first argument. We also assume that the lambda function can not have parameters.
162 size_t first_pos = 0;
163 if (func_node->name == "lambda")
164 first_pos = 1;
165
166 auto & func_children = func_node->arguments->children;
167
168 for (size_t i = first_pos; i < func_children.size(); ++i)
169 {
170 auto & child = func_children[i];
171
172 if (needVisitChild(child))
173 visit(child, data);
174 }
175 }
176 else if (!node->as<ASTSelectQuery>())
177 {
178 for (auto & child : node->children)
179 if (needVisitChild(child))
180 visit(child, data);
181 }
182}
183
184void QueryNormalizer::visit(ASTPtr & ast, Data & data)
185{
186 CheckASTDepth scope1(data);
187 RestoreAliasOnExitScope scope2(data.current_alias);
188
189 auto & finished_asts = data.finished_asts;
190 auto & current_asts = data.current_asts;
191
192 if (finished_asts.count(ast))
193 {
194 ast = finished_asts[ast];
195 return;
196 }
197
198 ASTPtr initial_ast = ast;
199 current_asts.insert(initial_ast.get());
200
201 {
202 String my_alias = ast->tryGetAlias();
203 if (!my_alias.empty())
204 data.current_alias = my_alias;
205 }
206
207 if (auto * node_id = ast->as<ASTIdentifier>())
208 visit(*node_id, ast, data);
209 else if (auto * node_tables = ast->as<ASTTablesInSelectQueryElement>())
210 visit(*node_tables, ast, data);
211 else if (auto * node_select = ast->as<ASTSelectQuery>())
212 visit(*node_select, ast, data);
213 else if (auto * node_param = ast->as<ASTQueryParameter>())
214 throw Exception("Query parameter " + backQuote(node_param->name) + " was not set", ErrorCodes::UNKNOWN_QUERY_PARAMETER);
215
216 /// If we replace the root of the subtree, we will be called again for the new root, in case the alias is replaced by an alias.
217 if (ast.get() != initial_ast.get())
218 visit(ast, data);
219 else
220 visitChildren(ast, data);
221
222 current_asts.erase(initial_ast.get());
223 current_asts.erase(ast.get());
224 finished_asts[initial_ast] = ast;
225
226 /// @note can not place it in CheckASTDepth dtor cause of exception.
227 if (data.level == 1)
228 {
229 try
230 {
231 ast->checkSize(data.settings.max_expanded_ast_elements);
232 }
233 catch (Exception & e)
234 {
235 e.addMessage("(after expansion of aliases)");
236 throw;
237 }
238 }
239}
240
241}
242