1 | #include <Poco/String.h> |
2 | |
3 | #include <Interpreters/TranslateQualifiedNamesVisitor.h> |
4 | #include <Interpreters/IdentifierSemantic.h> |
5 | #include <Interpreters/AsteriskSemantic.h> |
6 | |
7 | #include <Common/typeid_cast.h> |
8 | #include <Core/Names.h> |
9 | |
10 | #include <Parsers/ASTIdentifier.h> |
11 | #include <Parsers/ASTAsterisk.h> |
12 | #include <Parsers/ASTQualifiedAsterisk.h> |
13 | #include <Parsers/ASTSelectQuery.h> |
14 | #include <Parsers/ASTSelectWithUnionQuery.h> |
15 | #include <Parsers/ASTTablesInSelectQuery.h> |
16 | #include <Parsers/ASTExpressionList.h> |
17 | #include <Parsers/ASTLiteral.h> |
18 | #include <Parsers/ASTFunction.h> |
19 | #include <Parsers/ASTColumnsMatcher.h> |
20 | |
21 | |
22 | namespace DB |
23 | { |
24 | |
25 | namespace ErrorCodes |
26 | { |
27 | extern const int UNKNOWN_IDENTIFIER; |
28 | extern const int UNKNOWN_ELEMENT_IN_AST; |
29 | extern const int LOGICAL_ERROR; |
30 | } |
31 | |
32 | bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const ASTIdentifier & identifier) const |
33 | { |
34 | const auto & table = tables[table_pos].table; |
35 | auto nested1 = IdentifierSemantic::extractNestedName(identifier, table.table); |
36 | auto nested2 = IdentifierSemantic::extractNestedName(identifier, table.alias); |
37 | |
38 | String short_name = identifier.shortName(); |
39 | const Names & column_names = tables[table_pos].columns; |
40 | for (auto & known_name : column_names) |
41 | { |
42 | if (short_name == known_name) |
43 | return false; |
44 | if (nested1 && *nested1 == known_name) |
45 | return false; |
46 | if (nested2 && *nested2 == known_name) |
47 | return false; |
48 | } |
49 | |
50 | const Names & hidden_names = tables[table_pos].hidden_columns; |
51 | for (auto & known_name : hidden_names) |
52 | { |
53 | if (short_name == known_name) |
54 | return false; |
55 | if (nested1 && *nested1 == known_name) |
56 | return false; |
57 | if (nested2 && *nested2 == known_name) |
58 | return false; |
59 | } |
60 | |
61 | return !column_names.empty(); |
62 | } |
63 | |
64 | bool TranslateQualifiedNamesMatcher::needChildVisit(ASTPtr & node, const ASTPtr & child) |
65 | { |
66 | /// Do not go to FROM, JOIN, subqueries. |
67 | if (child->as<ASTTableExpression>() || child->as<ASTSelectWithUnionQuery>()) |
68 | return false; |
69 | |
70 | /// Processed nodes. Do not go into children. |
71 | if (node->as<ASTQualifiedAsterisk>() || node->as<ASTTableJoin>()) |
72 | return false; |
73 | |
74 | /// ASTSelectQuery + others |
75 | return true; |
76 | } |
77 | |
78 | void TranslateQualifiedNamesMatcher::visit(ASTPtr & ast, Data & data) |
79 | { |
80 | if (auto * t = ast->as<ASTIdentifier>()) |
81 | visit(*t, ast, data); |
82 | if (auto * t = ast->as<ASTTableJoin>()) |
83 | visit(*t, ast, data); |
84 | if (auto * t = ast->as<ASTSelectQuery>()) |
85 | visit(*t, ast, data); |
86 | if (auto * node = ast->as<ASTExpressionList>()) |
87 | visit(*node, ast, data); |
88 | if (auto * node = ast->as<ASTFunction>()) |
89 | visit(*node, ast, data); |
90 | } |
91 | |
92 | void TranslateQualifiedNamesMatcher::visit(ASTIdentifier & identifier, ASTPtr &, Data & data) |
93 | { |
94 | if (IdentifierSemantic::getColumnName(identifier)) |
95 | { |
96 | String short_name = identifier.shortName(); |
97 | size_t table_pos = 0; |
98 | bool allow_ambiguous = data.join_using_columns.count(short_name); |
99 | if (IdentifierSemantic::chooseTable(identifier, data.tables, table_pos, allow_ambiguous)) |
100 | { |
101 | if (data.unknownColumn(table_pos, identifier)) |
102 | { |
103 | String table_name = data.tables[table_pos].table.getQualifiedNamePrefix(false); |
104 | throw Exception("There's no column '" + identifier.name + "' in table '" + table_name + "'" , |
105 | ErrorCodes::UNKNOWN_IDENTIFIER); |
106 | } |
107 | |
108 | IdentifierSemantic::setMembership(identifier, table_pos); |
109 | |
110 | /// In case if column from the joined table are in source columns, change it's name to qualified. |
111 | auto & table = data.tables[table_pos].table; |
112 | if (table_pos && data.hasColumn(short_name)) |
113 | IdentifierSemantic::setColumnLongName(identifier, table); |
114 | else |
115 | IdentifierSemantic::setColumnShortName(identifier, table); |
116 | } |
117 | } |
118 | } |
119 | |
120 | /// As special case, treat count(*) as count(), not as count(list of all columns). |
121 | void TranslateQualifiedNamesMatcher::visit(ASTFunction & node, const ASTPtr &, Data &) |
122 | { |
123 | ASTPtr & func_arguments = node.arguments; |
124 | |
125 | String func_name_lowercase = Poco::toLower(node.name); |
126 | if (func_name_lowercase == "count" && |
127 | func_arguments->children.size() == 1 && |
128 | func_arguments->children[0]->as<ASTAsterisk>()) |
129 | func_arguments->children.clear(); |
130 | } |
131 | |
132 | void TranslateQualifiedNamesMatcher::visit(const ASTQualifiedAsterisk & , const ASTPtr & ast, Data & data) |
133 | { |
134 | if (ast->children.size() != 1) |
135 | throw Exception("Logical error: qualified asterisk must have exactly one child" , ErrorCodes::LOGICAL_ERROR); |
136 | |
137 | auto & ident = ast->children[0]; |
138 | |
139 | /// @note it could contain table alias as table name. |
140 | DatabaseAndTableWithAlias db_and_table(ident); |
141 | |
142 | for (const auto & known_table : data.tables) |
143 | if (db_and_table.satisfies(known_table.table, true)) |
144 | return; |
145 | |
146 | throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER); |
147 | } |
148 | |
149 | void TranslateQualifiedNamesMatcher::visit(ASTTableJoin & join, const ASTPtr & , Data & data) |
150 | { |
151 | if (join.using_expression_list) |
152 | Visitor(data).visit(join.using_expression_list); |
153 | else if (join.on_expression) |
154 | Visitor(data).visit(join.on_expression); |
155 | } |
156 | |
157 | void TranslateQualifiedNamesMatcher::visit(ASTSelectQuery & select, const ASTPtr & , Data & data) |
158 | { |
159 | if (auto join = select.join()) |
160 | extractJoinUsingColumns(join->table_join, data); |
161 | |
162 | /// If the WHERE clause or HAVING consists of a single qualified column, the reference must be translated not only in children, |
163 | /// but also in where_expression and having_expression. |
164 | if (select.prewhere()) |
165 | Visitor(data).visit(select.refPrewhere()); |
166 | if (select.where()) |
167 | Visitor(data).visit(select.refWhere()); |
168 | if (select.having()) |
169 | Visitor(data).visit(select.refHaving()); |
170 | } |
171 | |
172 | static void addIdentifier(ASTs & nodes, const DatabaseAndTableWithAlias & table, const String & column_name, |
173 | AsteriskSemantic::RevertedAliasesPtr aliases) |
174 | { |
175 | String table_name = table.getQualifiedNamePrefix(false); |
176 | auto identifier = std::make_shared<ASTIdentifier>(std::vector<String>{table_name, column_name}); |
177 | |
178 | bool added = false; |
179 | if (aliases && aliases->count(identifier->name)) |
180 | { |
181 | for (const String & alias : (*aliases)[identifier->name]) |
182 | { |
183 | nodes.push_back(identifier->clone()); |
184 | nodes.back()->setAlias(alias); |
185 | added = true; |
186 | } |
187 | } |
188 | |
189 | if (!added) |
190 | nodes.emplace_back(identifier); |
191 | } |
192 | |
193 | /// Replace *, alias.*, database.table.* with a list of columns. |
194 | void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPtr &, Data & data) |
195 | { |
196 | const auto & tables_with_columns = data.tables; |
197 | |
198 | ASTs old_children; |
199 | if (data.processAsterisks()) |
200 | { |
201 | bool has_asterisk = false; |
202 | for (const auto & child : node.children) |
203 | { |
204 | if (child->as<ASTAsterisk>() || child->as<ASTColumnsMatcher>()) |
205 | { |
206 | if (tables_with_columns.empty()) |
207 | throw Exception("An asterisk cannot be replaced with empty columns." , ErrorCodes::LOGICAL_ERROR); |
208 | has_asterisk = true; |
209 | break; |
210 | } |
211 | else if (const auto * qa = child->as<ASTQualifiedAsterisk>()) |
212 | { |
213 | visit(*qa, child, data); /// check if it's OK before rewrite |
214 | has_asterisk = true; |
215 | break; |
216 | } |
217 | } |
218 | |
219 | if (has_asterisk) |
220 | { |
221 | old_children.swap(node.children); |
222 | node.children.reserve(old_children.size()); |
223 | } |
224 | } |
225 | |
226 | for (const auto & child : old_children) |
227 | { |
228 | if (const auto * asterisk = child->as<ASTAsterisk>()) |
229 | { |
230 | bool first_table = true; |
231 | for (const auto & table : tables_with_columns) |
232 | { |
233 | for (const auto & column_name : table.columns) |
234 | { |
235 | if (first_table || !data.join_using_columns.count(column_name)) |
236 | { |
237 | addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk)); |
238 | } |
239 | } |
240 | |
241 | first_table = false; |
242 | } |
243 | } |
244 | else if (const auto * asterisk_pattern = child->as<ASTColumnsMatcher>()) |
245 | { |
246 | bool first_table = true; |
247 | for (const auto & table : tables_with_columns) |
248 | { |
249 | for (const auto & column_name : table.columns) |
250 | { |
251 | if (asterisk_pattern->isColumnMatching(column_name) && (first_table || !data.join_using_columns.count(column_name))) |
252 | { |
253 | addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk_pattern)); |
254 | } |
255 | } |
256 | |
257 | first_table = false; |
258 | } |
259 | } |
260 | else if (const auto * qualified_asterisk = child->as<ASTQualifiedAsterisk>()) |
261 | { |
262 | DatabaseAndTableWithAlias ident_db_and_name(qualified_asterisk->children[0]); |
263 | |
264 | for (const auto & table : tables_with_columns) |
265 | { |
266 | if (ident_db_and_name.satisfies(table.table, true)) |
267 | { |
268 | for (const auto & column_name : table.columns) |
269 | { |
270 | addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*qualified_asterisk)); |
271 | } |
272 | break; |
273 | } |
274 | } |
275 | } |
276 | else |
277 | node.children.emplace_back(child); |
278 | } |
279 | } |
280 | |
281 | /// 'select * from a join b using id' should result one 'id' column |
282 | void TranslateQualifiedNamesMatcher::extractJoinUsingColumns(const ASTPtr ast, Data & data) |
283 | { |
284 | const auto & table_join = ast->as<ASTTableJoin &>(); |
285 | |
286 | if (table_join.using_expression_list) |
287 | { |
288 | const auto & keys = table_join.using_expression_list->as<ASTExpressionList &>(); |
289 | for (const auto & key : keys.children) |
290 | if (auto opt_column = tryGetIdentifierName(key)) |
291 | data.join_using_columns.insert(*opt_column); |
292 | else if (key->as<ASTLiteral>()) |
293 | data.join_using_columns.insert(key->getColumnName()); |
294 | else |
295 | { |
296 | String alias = key->tryGetAlias(); |
297 | if (alias.empty()) |
298 | throw Exception("Logical error: expected identifier or alias, got: " + key->getID(), ErrorCodes::LOGICAL_ERROR); |
299 | data.join_using_columns.insert(alias); |
300 | } |
301 | } |
302 | } |
303 | |
304 | void RestoreQualifiedNamesData::visit(ASTIdentifier & identifier, ASTPtr & ast) |
305 | { |
306 | if (IdentifierSemantic::getColumnName(identifier)) |
307 | { |
308 | if (IdentifierSemantic::getMembership(identifier)) |
309 | { |
310 | ast = identifier.clone(); |
311 | ast->as<ASTIdentifier>()->restoreCompoundName(); |
312 | } |
313 | } |
314 | } |
315 | |
316 | } |
317 | |