#pragma once #include #include #include #include #include #include #include #include #include namespace DB { /// Visits AST nodes, add default database to tables if not set. There's different logic for DDLs and selects. class AddDefaultDatabaseVisitor { public: AddDefaultDatabaseVisitor(const String & database_name_, std::ostream * ostr_ = nullptr) : database_name(database_name_), visit_depth(0), ostr(ostr_) {} void visitDDL(ASTPtr & ast) const { visitDDLChildren(ast); if (!tryVisitDynamicCast(ast) && !tryVisitDynamicCast(ast)) {} } void visit(ASTPtr & ast) const { if (!tryVisit(ast) && !tryVisit(ast)) visitChildren(ast); } void visit(ASTSelectQuery & select) const { ASTPtr unused; visit(select, unused); } void visit(ASTSelectWithUnionQuery & select) const { ASTPtr unused; visit(select, unused); } private: const String database_name; mutable size_t visit_depth; std::ostream * ostr; void visit(ASTSelectWithUnionQuery & select, ASTPtr &) const { for (auto & child : select.list_of_selects->children) tryVisit(child); } void visit(ASTSelectQuery & select, ASTPtr &) const { if (select.tables) tryVisit(select.tables); if (select.prewhere_expression) visitChildren(select.prewhere_expression); if (select.where_expression) visitChildren(select.where_expression); } void visit(ASTTablesInSelectQuery & tables, ASTPtr &) const { for (auto & child : tables.children) tryVisit(child); } void visit(ASTTablesInSelectQueryElement & tables_element, ASTPtr &) const { if (tables_element.table_expression) tryVisit(tables_element.table_expression); } void visit(ASTTableExpression & table_expression, ASTPtr &) const { if (table_expression.database_and_table_name) { tryVisit(table_expression.database_and_table_name); if (table_expression.database_and_table_name->children.size() != 2) throw Exception("Logical error: more than two components in table expression", ErrorCodes::LOGICAL_ERROR); } else if (table_expression.subquery) tryVisit(table_expression.subquery); } void visit(const ASTIdentifier & identifier, ASTPtr & ast) const { if (ast->children.empty()) ast = createDatabaseAndTableNode(database_name, identifier.name); } void visit(ASTSubquery & subquery, ASTPtr &) const { tryVisit(subquery.children[0]); } void visitChildren(ASTPtr & ast) const { for (auto & child : ast->children) visit(child); } template bool tryVisit(ASTPtr & ast) const { if (T * t = typeid_cast(ast.get())) { DumpASTNode dump(*ast, ostr, visit_depth, "addDefaultDatabaseName"); visit(*t, ast); return true; } return false; } void visitDDL(ASTQueryWithTableAndOutput & node, ASTPtr &) const { if (node.database.empty()) node.database = database_name; } void visitDDL(ASTRenameQuery & node, ASTPtr &) const { for (ASTRenameQuery::Element & elem : node.elements) { if (elem.from.database.empty()) elem.from.database = database_name; if (elem.to.database.empty()) elem.to.database = database_name; } } void visitDDLChildren(ASTPtr & ast) const { for (auto & child : ast->children) visitDDL(child); } template bool tryVisitDynamicCast(ASTPtr & ast) const { if (T * t = dynamic_cast(ast.get())) { visitDDL(*t, ast); return true; } return false; } }; }