2023-08-22 16:24:41 +00:00
|
|
|
#include <unordered_map>
|
|
|
|
#include <Analyzer/createUniqueTableAliases.h>
|
|
|
|
#include <Analyzer/InDepthQueryTreeVisitor.h>
|
|
|
|
#include <Analyzer/IQueryTreeNode.h>
|
2023-08-28 16:44:25 +00:00
|
|
|
#include "Common/logger_useful.h"
|
2023-08-22 16:24:41 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
class CreateUniqueTableAliasesVisitor : public InDepthQueryTreeVisitorWithContext<CreateUniqueTableAliasesVisitor>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using Base = InDepthQueryTreeVisitorWithContext<CreateUniqueTableAliasesVisitor>;
|
|
|
|
|
|
|
|
explicit CreateUniqueTableAliasesVisitor(const ContextPtr & context)
|
|
|
|
: Base(context)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void enterImpl(QueryTreeNodePtr & node)
|
|
|
|
{
|
2023-08-28 16:44:25 +00:00
|
|
|
auto node_type = node->getNodeType();
|
|
|
|
switch (node_type)
|
2023-08-22 16:24:41 +00:00
|
|
|
{
|
|
|
|
case QueryTreeNodeType::QUERY:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QueryTreeNodeType::UNION:
|
2023-08-22 17:14:24 +00:00
|
|
|
{
|
2023-08-25 18:01:25 +00:00
|
|
|
/// Queries like `(SELECT 1) as t` have invalid syntax. To avoid creating such queries (e.g. in StorageDistributed)
|
|
|
|
/// we need to remove aliases for top level queries.
|
|
|
|
/// N.B. Subquery depth starts count from 1, so the following condition checks if it's a top level.
|
2023-08-22 16:24:41 +00:00
|
|
|
if (getSubqueryDepth() == 1)
|
2023-08-22 17:14:24 +00:00
|
|
|
{
|
|
|
|
node->removeAlias();
|
2023-08-22 16:24:41 +00:00
|
|
|
break;
|
2023-08-22 17:14:24 +00:00
|
|
|
}
|
2023-08-22 16:24:41 +00:00
|
|
|
[[fallthrough]];
|
2023-08-22 17:14:24 +00:00
|
|
|
}
|
2023-08-22 16:24:41 +00:00
|
|
|
case QueryTreeNodeType::TABLE:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QueryTreeNodeType::TABLE_FUNCTION:
|
2023-08-24 20:32:40 +00:00
|
|
|
[[fallthrough]];
|
|
|
|
case QueryTreeNodeType::ARRAY_JOIN:
|
2023-08-22 16:24:41 +00:00
|
|
|
{
|
|
|
|
auto & alias = table_expression_to_alias[node];
|
|
|
|
if (alias.empty())
|
|
|
|
{
|
2023-08-28 16:44:25 +00:00
|
|
|
scope_to_nodes_with_aliases[scope_nodes_stack.back()].push_back(node);
|
2023-08-22 16:24:41 +00:00
|
|
|
alias = fmt::format("__table{}", table_expression_to_alias.size());
|
|
|
|
node->setAlias(alias);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2023-08-28 16:44:25 +00:00
|
|
|
|
|
|
|
switch (node_type)
|
|
|
|
{
|
|
|
|
case QueryTreeNodeType::QUERY:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QueryTreeNodeType::UNION:
|
|
|
|
[[fallthrough]];
|
|
|
|
case QueryTreeNodeType::LAMBDA:
|
|
|
|
scope_nodes_stack.push_back(node);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void leaveImpl(QueryTreeNodePtr & node)
|
|
|
|
{
|
|
|
|
if (scope_nodes_stack.back() == node)
|
|
|
|
{
|
|
|
|
if (auto it = scope_to_nodes_with_aliases.find(scope_nodes_stack.back());
|
|
|
|
it != scope_to_nodes_with_aliases.end())
|
|
|
|
{
|
|
|
|
for (const auto & node_with_alias : it->second)
|
|
|
|
{
|
|
|
|
table_expression_to_alias.erase(node_with_alias);
|
|
|
|
}
|
|
|
|
scope_to_nodes_with_aliases.erase(it);
|
|
|
|
}
|
|
|
|
scope_nodes_stack.pop_back();
|
|
|
|
}
|
2023-08-22 16:24:41 +00:00
|
|
|
}
|
2023-08-28 16:44:25 +00:00
|
|
|
|
2023-08-22 16:24:41 +00:00
|
|
|
private:
|
2023-08-28 16:44:25 +00:00
|
|
|
// Stack of nodes which create scopes: QUERY, UNION and LAMBDA.
|
|
|
|
QueryTreeNodes scope_nodes_stack;
|
|
|
|
|
|
|
|
std::unordered_map<QueryTreeNodePtr, QueryTreeNodes> scope_to_nodes_with_aliases;
|
|
|
|
|
2023-08-22 16:24:41 +00:00
|
|
|
// We need to use raw pointer as a key, not a QueryTreeNodePtrWithHash.
|
|
|
|
std::unordered_map<QueryTreeNodePtr, String> table_expression_to_alias;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void createUniqueTableAliases(QueryTreeNodePtr & node, const ContextPtr & context)
|
|
|
|
{
|
|
|
|
CreateUniqueTableAliasesVisitor(context).visit(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|