2018-12-28 15:38:58 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2018-12-17 16:20:15 +00:00
|
|
|
#include <Interpreters/JoinToSubqueryTransformVisitor.h>
|
2019-02-20 12:12:36 +00:00
|
|
|
#include <Interpreters/IdentifierSemantic.h>
|
|
|
|
#include <Interpreters/AsteriskSemantic.h>
|
|
|
|
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
2018-12-17 16:20:15 +00:00
|
|
|
#include <Parsers/ASTSelectQuery.h>
|
|
|
|
#include <Parsers/ASTTablesInSelectQuery.h>
|
|
|
|
#include <Parsers/ASTIdentifier.h>
|
|
|
|
#include <Parsers/ASTExpressionList.h>
|
|
|
|
#include <Parsers/ParserTablesInSelectQuery.h>
|
|
|
|
#include <Parsers/ExpressionListParsers.h>
|
|
|
|
#include <Parsers/parseQuery.h>
|
|
|
|
#include <IO/WriteHelpers.h>
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
|
2018-12-17 16:20:15 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int TOO_DEEP_AST;
|
2019-02-20 12:12:36 +00:00
|
|
|
extern const int AMBIGUOUS_COLUMN_NAME;
|
2019-02-20 15:28:53 +00:00
|
|
|
extern const int NOT_IMPLEMENTED;
|
2018-12-17 16:20:15 +00:00
|
|
|
}
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
/// Find columns with aliases to push them into rewritten subselects.
|
|
|
|
/// Normalize table aliases: table_name.column_name -> table_alias.column_name
|
|
|
|
/// Make aliases maps (alias -> column_name, column_name -> alias)
|
2019-02-20 15:28:53 +00:00
|
|
|
struct ColumnAliasesMatcher
|
2019-02-20 12:12:36 +00:00
|
|
|
{
|
2019-02-20 15:28:53 +00:00
|
|
|
struct Data
|
|
|
|
{
|
|
|
|
const std::vector<DatabaseAndTableWithAlias> tables;
|
2019-02-21 12:45:31 +00:00
|
|
|
bool public_names;
|
2019-02-20 15:28:53 +00:00
|
|
|
AsteriskSemantic::RevertedAliases rev_aliases;
|
|
|
|
std::unordered_map<String, String> aliases;
|
2019-02-21 12:45:31 +00:00
|
|
|
std::vector<std::pair<ASTIdentifier *, bool>> compound_identifiers;
|
2019-02-20 15:28:53 +00:00
|
|
|
|
|
|
|
Data(std::vector<DatabaseAndTableWithAlias> && tables_)
|
|
|
|
: tables(tables_)
|
2019-02-21 12:45:31 +00:00
|
|
|
, public_names(false)
|
2019-02-20 15:28:53 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
void replaceIdentifiersWithAliases()
|
|
|
|
{
|
|
|
|
String hide_prefix = "--"; /// @note restriction: user should not use alises like `--table.column`
|
|
|
|
|
2019-02-21 12:45:31 +00:00
|
|
|
for (auto & [identifier, is_public] : compound_identifiers)
|
2019-02-20 15:28:53 +00:00
|
|
|
{
|
|
|
|
auto it = rev_aliases.find(identifier->name);
|
|
|
|
if (it == rev_aliases.end())
|
|
|
|
{
|
|
|
|
bool last_table = IdentifierSemantic::canReferColumnToTable(*identifier, tables.back());
|
|
|
|
if (!last_table)
|
|
|
|
{
|
|
|
|
String long_name = identifier->name;
|
|
|
|
String alias = hide_prefix + long_name;
|
|
|
|
aliases[alias] = long_name;
|
|
|
|
rev_aliases[long_name].push_back(alias);
|
|
|
|
|
|
|
|
identifier->setShortName(alias);
|
2019-02-21 12:45:31 +00:00
|
|
|
if (is_public)
|
|
|
|
identifier->setAlias(long_name);
|
2019-02-20 15:28:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (it->second.empty())
|
|
|
|
throw Exception("No alias for '" + identifier->name + "'", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
identifier->setShortName(it->second[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-02-20 12:12:36 +00:00
|
|
|
|
2019-02-20 15:28:53 +00:00
|
|
|
static constexpr const char * label = "ColumnAliases";
|
2019-02-20 12:12:36 +00:00
|
|
|
|
2019-02-20 15:28:53 +00:00
|
|
|
static bool needChildVisit(ASTPtr & node, const ASTPtr &)
|
|
|
|
{
|
|
|
|
if (typeid_cast<const ASTQualifiedAsterisk *>(node.get()))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2019-02-20 12:12:36 +00:00
|
|
|
|
2019-02-20 15:28:53 +00:00
|
|
|
static std::vector<ASTPtr *> visit(ASTPtr & ast, Data & data)
|
|
|
|
{
|
|
|
|
if (auto * t = typeid_cast<ASTIdentifier *>(ast.get()))
|
|
|
|
visit(*t, ast, data);
|
|
|
|
|
|
|
|
if (typeid_cast<ASTAsterisk *>(ast.get()) ||
|
|
|
|
typeid_cast<ASTQualifiedAsterisk *>(ast.get()))
|
|
|
|
throw Exception("Multiple JOIN do not support asterisks yet", ErrorCodes::NOT_IMPLEMENTED);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
static void visit(ASTIdentifier & node, ASTPtr &, Data & data)
|
2019-02-20 12:12:36 +00:00
|
|
|
{
|
|
|
|
if (node.isShort())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool last_table = false;
|
|
|
|
String long_name;
|
2019-02-20 15:28:53 +00:00
|
|
|
for (auto & table : data.tables)
|
2019-02-20 12:12:36 +00:00
|
|
|
{
|
|
|
|
if (IdentifierSemantic::canReferColumnToTable(node, table))
|
|
|
|
{
|
|
|
|
if (!long_name.empty())
|
|
|
|
throw Exception("Cannot refer column '" + node.name + "' to one table", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
|
|
|
|
IdentifierSemantic::setColumnLongName(node, table); /// table_name.column_name -> table_alias.column_name
|
|
|
|
long_name = node.name;
|
2019-02-20 15:28:53 +00:00
|
|
|
if (&table == &data.tables.back())
|
2019-02-20 12:12:36 +00:00
|
|
|
last_table = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (long_name.empty())
|
|
|
|
throw Exception("Cannot refer column '" + node.name + "' to table", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
|
|
|
|
|
|
|
|
String alias = node.tryGetAlias();
|
|
|
|
if (!alias.empty())
|
|
|
|
{
|
2019-02-20 15:28:53 +00:00
|
|
|
data.aliases[alias] = long_name;
|
|
|
|
data.rev_aliases[long_name].push_back(alias);
|
2019-02-20 12:12:36 +00:00
|
|
|
|
|
|
|
if (!last_table)
|
|
|
|
{
|
|
|
|
node.setShortName(alias);
|
|
|
|
node.setAlias("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2019-02-21 12:45:31 +00:00
|
|
|
data.compound_identifiers.emplace_back(&node, data.public_names);
|
2019-02-20 12:12:36 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Attach additional semantic info to generated selects.
|
2018-12-17 19:30:08 +00:00
|
|
|
struct AppendSemanticVisitorData
|
2018-12-17 16:20:15 +00:00
|
|
|
{
|
|
|
|
using TypeToVisit = ASTSelectQuery;
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
AsteriskSemantic::RevertedAliasesPtr rev_aliases = {};
|
2018-12-17 16:20:15 +00:00
|
|
|
bool done = false;
|
|
|
|
|
|
|
|
void visit(ASTSelectQuery & select, ASTPtr &)
|
|
|
|
{
|
2019-02-20 12:12:36 +00:00
|
|
|
if (done || !rev_aliases || !select.select_expression_list)
|
2018-12-17 16:20:15 +00:00
|
|
|
return;
|
2019-02-20 12:12:36 +00:00
|
|
|
|
|
|
|
for (auto & child : select.select_expression_list->children)
|
|
|
|
{
|
|
|
|
if (auto * node = typeid_cast<ASTAsterisk *>(child.get()))
|
|
|
|
AsteriskSemantic::setAliases(*node, rev_aliases);
|
|
|
|
if (auto * node = typeid_cast<ASTQualifiedAsterisk *>(child.get()))
|
|
|
|
AsteriskSemantic::setAliases(*node, rev_aliases);
|
|
|
|
}
|
|
|
|
|
2018-12-17 16:20:15 +00:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
|
|
|
|
/// Replaces table elements with pair.
|
2018-12-17 16:20:15 +00:00
|
|
|
struct RewriteTablesVisitorData
|
|
|
|
{
|
|
|
|
using TypeToVisit = ASTTablesInSelectQuery;
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
ASTPtr left;
|
|
|
|
ASTPtr right;
|
2018-12-17 16:20:15 +00:00
|
|
|
bool done = false;
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
/// @note Do not change ASTTablesInSelectQuery itself. No need to change select.tables.
|
2018-12-17 16:20:15 +00:00
|
|
|
void visit(ASTTablesInSelectQuery &, ASTPtr & ast)
|
|
|
|
{
|
|
|
|
if (done)
|
|
|
|
return;
|
2019-02-20 12:12:36 +00:00
|
|
|
std::vector<ASTPtr> new_tables{left, right};
|
|
|
|
ast->children.swap(new_tables);
|
2018-12-17 16:20:15 +00:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
bool needRewrite(ASTSelectQuery & select)
|
2018-12-18 18:28:02 +00:00
|
|
|
{
|
2019-02-01 14:26:36 +00:00
|
|
|
if (!select.tables)
|
|
|
|
return false;
|
|
|
|
|
2018-12-28 15:38:58 +00:00
|
|
|
auto tables = typeid_cast<const ASTTablesInSelectQuery *>(select.tables.get());
|
2018-12-18 18:28:02 +00:00
|
|
|
if (!tables)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size_t num_tables = tables->children.size();
|
|
|
|
if (num_tables <= 2)
|
|
|
|
return false;
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
for (size_t i = 1; i < tables->children.size(); ++i)
|
|
|
|
{
|
|
|
|
auto table = typeid_cast<const ASTTablesInSelectQueryElement *>(tables->children[i].get());
|
|
|
|
if (!table || !table->table_join)
|
|
|
|
throw Exception("Multiple JOIN expects joined tables", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
auto join = typeid_cast<const ASTTableJoin *>(table->table_join.get());
|
|
|
|
if (join->kind == ASTTableJoin::Kind::Comma)
|
2019-02-20 15:28:53 +00:00
|
|
|
throw Exception("Multiple COMMA JOIN is not supported", ErrorCodes::NOT_IMPLEMENTED);
|
2019-02-20 12:12:36 +00:00
|
|
|
|
|
|
|
/// it's not trivial to support mix of JOIN ON & JOIN USING cause of short names
|
|
|
|
if (!join || !join->on_expression)
|
2019-02-20 15:28:53 +00:00
|
|
|
throw Exception("Multiple JOIN expects JOIN with ON section", ErrorCodes::NOT_IMPLEMENTED);
|
2019-02-20 12:12:36 +00:00
|
|
|
}
|
|
|
|
|
2018-12-18 18:28:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
using RewriteMatcher = OneTypeMatcher<RewriteTablesVisitorData>;
|
|
|
|
using RewriteVisitor = InDepthNodeVisitor<RewriteMatcher, true>;
|
|
|
|
using ColumnAliasesVisitor = InDepthNodeVisitor<ColumnAliasesMatcher, true>;
|
|
|
|
using AppendSemanticMatcher = OneTypeMatcher<AppendSemanticVisitorData>;
|
|
|
|
using AppendSemanticVisitor = InDepthNodeVisitor<AppendSemanticMatcher, true>;
|
|
|
|
|
|
|
|
} /// namelesspace
|
2018-12-17 16:20:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
std::vector<ASTPtr *> JoinToSubqueryTransformMatcher::visit(ASTPtr & ast, Data & data)
|
|
|
|
{
|
|
|
|
if (auto * t = typeid_cast<ASTSelectQuery *>(ast.get()))
|
|
|
|
visit(*t, ast, data);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Data & data)
|
2018-12-17 16:20:15 +00:00
|
|
|
{
|
2019-02-20 12:12:36 +00:00
|
|
|
using RevertedAliases = AsteriskSemantic::RevertedAliases;
|
2018-12-18 18:28:02 +00:00
|
|
|
|
|
|
|
if (!needRewrite(select))
|
|
|
|
return;
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
ColumnAliasesVisitor::Data aliases_data(getDatabaseAndTables(select, ""));
|
|
|
|
if (select.select_expression_list)
|
2019-02-21 12:45:31 +00:00
|
|
|
{
|
|
|
|
aliases_data.public_names = true;
|
2019-02-20 12:12:36 +00:00
|
|
|
ColumnAliasesVisitor(aliases_data).visit(select.select_expression_list);
|
2019-02-21 12:45:31 +00:00
|
|
|
aliases_data.public_names = false;
|
|
|
|
}
|
2019-02-20 12:12:36 +00:00
|
|
|
if (select.where_expression)
|
|
|
|
ColumnAliasesVisitor(aliases_data).visit(select.where_expression);
|
|
|
|
if (select.prewhere_expression)
|
|
|
|
ColumnAliasesVisitor(aliases_data).visit(select.prewhere_expression);
|
|
|
|
if (select.having_expression)
|
|
|
|
ColumnAliasesVisitor(aliases_data).visit(select.having_expression);
|
|
|
|
|
|
|
|
/// JOIN sections
|
|
|
|
for (auto & child : select.tables->children)
|
|
|
|
{
|
|
|
|
auto table = typeid_cast<ASTTablesInSelectQueryElement *>(child.get());
|
|
|
|
if (table->table_join)
|
|
|
|
{
|
|
|
|
auto * join = typeid_cast<ASTTableJoin *>(table->table_join.get());
|
|
|
|
ColumnAliasesVisitor(aliases_data).visit(join->on_expression);
|
|
|
|
}
|
|
|
|
}
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
aliases_data.replaceIdentifiersWithAliases();
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
auto rev_aliases = std::make_shared<RevertedAliases>();
|
|
|
|
rev_aliases->swap(aliases_data.rev_aliases);
|
2018-12-17 19:30:08 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
auto & src_tables = select.tables->children;
|
|
|
|
ASTPtr left_table = src_tables[0];
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
for (size_t i = 1; i < src_tables.size() - 1; ++i)
|
|
|
|
{
|
|
|
|
left_table = replaceJoin(left_table, src_tables[i]);
|
|
|
|
if (!left_table)
|
|
|
|
throw Exception("Cannot replace tables with subselect", ErrorCodes::LOGICAL_ERROR);
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
/// attach data to generated asterisk
|
|
|
|
AppendSemanticVisitor::Data semantic_data{rev_aliases, false};
|
|
|
|
AppendSemanticVisitor(semantic_data).visit(left_table);
|
2018-12-17 16:20:15 +00:00
|
|
|
}
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
/// replace tables in select with generated two-table join
|
|
|
|
RewriteVisitor::Data visitor_data{left_table, src_tables.back()};
|
|
|
|
RewriteVisitor(visitor_data).visit(select.tables);
|
2018-12-17 16:20:15 +00:00
|
|
|
|
|
|
|
data.done = true;
|
|
|
|
}
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
static ASTPtr makeSubqueryTemplate()
|
2018-12-17 16:20:15 +00:00
|
|
|
{
|
2019-02-20 12:12:36 +00:00
|
|
|
ParserTablesInSelectQueryElement parser(true);
|
|
|
|
ASTPtr subquery_template = parseQuery(parser, "(select * from _t)", 0);
|
|
|
|
if (!subquery_template)
|
|
|
|
throw Exception("Cannot parse subquery template", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return subquery_template;
|
|
|
|
}
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_right)
|
|
|
|
{
|
2018-12-28 15:38:58 +00:00
|
|
|
auto left = typeid_cast<const ASTTablesInSelectQueryElement *>(ast_left.get());
|
|
|
|
auto right = typeid_cast<const ASTTablesInSelectQueryElement *>(ast_right.get());
|
2018-12-17 16:20:15 +00:00
|
|
|
if (!left || !right)
|
|
|
|
throw Exception("Two TablesInSelectQueryElements expected", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
if (!right->table_join)
|
|
|
|
throw Exception("Table join expected", ErrorCodes::LOGICAL_ERROR);
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
static ASTPtr subquery_template = makeSubqueryTemplate();
|
2018-12-17 16:20:15 +00:00
|
|
|
|
2019-02-20 12:12:36 +00:00
|
|
|
/// replace '_t' with pair of joined tables
|
|
|
|
ASTPtr res = subquery_template->clone();
|
2019-01-17 17:01:48 +00:00
|
|
|
RewriteVisitor::Data visitor_data{ast_left, ast_right};
|
2018-12-17 16:20:15 +00:00
|
|
|
RewriteVisitor(visitor_data).visit(res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|