Merge pull request #3202 from zhang2014/fix/ISSUES-3145

ISSUES-3145 support qualified asterisk
This commit is contained in:
alexey-milovidov 2018-09-26 17:41:01 +03:00 committed by GitHub
commit 6374094f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 49 deletions

View File

@ -281,26 +281,36 @@ ExpressionAnalyzer::ExpressionAnalyzer(
analyzeAggregation(); analyzeAggregation();
} }
static std::vector<ASTTableExpression> getTableExpressions(const ASTPtr & query)
{
ASTSelectQuery * select_query = typeid_cast<ASTSelectQuery *>(query.get());
std::vector<ASTTableExpression> tables_expression;
if (select_query && select_query->tables)
{
for (const auto & element : select_query->tables->children)
{
ASTTablesInSelectQueryElement & select_element = static_cast<ASTTablesInSelectQueryElement &>(*element);
if (select_element.table_expression)
tables_expression.emplace_back(static_cast<ASTTableExpression &>(*select_element.table_expression));
}
}
return tables_expression;
}
void ExpressionAnalyzer::translateQualifiedNames() void ExpressionAnalyzer::translateQualifiedNames()
{ {
if (!select_query || !select_query->tables || select_query->tables->children.empty()) if (!select_query || !select_query->tables || select_query->tables->children.empty())
return; return;
auto & element = static_cast<ASTTablesInSelectQueryElement &>(*select_query->tables->children[0]); std::vector<DatabaseAndTableWithAlias> tables;
std::vector<ASTTableExpression> tables_expression = getTableExpressions(query);
if (!element.table_expression) /// This is ARRAY JOIN without a table at the left side. for (const auto & table_expression : tables_expression)
return; tables.emplace_back(getTableNameWithAliasFromTableExpression(table_expression, context));
auto & table_expression = static_cast<ASTTableExpression &>(*element.table_expression);
auto * join = select_query->join();
std::vector<DatabaseAndTableWithAlias> tables = {getTableNameWithAliasFromTableExpression(table_expression, context)};
if (join)
{
const auto & join_table_expression = static_cast<const ASTTableExpression &>(*join->table_expression);
tables.emplace_back(getTableNameWithAliasFromTableExpression(join_table_expression, context));
}
translateQualifiedNamesImpl(query, tables); translateQualifiedNamesImpl(query, tables);
} }
@ -358,10 +368,11 @@ void ExpressionAnalyzer::translateQualifiedNamesImpl(ASTPtr & ast, const std::ve
&& ((!table_names.table.empty() && ident->name == table_names.table) && ((!table_names.table.empty() && ident->name == table_names.table)
|| (!table_names.alias.empty() && ident->name == table_names.alias)))) || (!table_names.alias.empty() && ident->name == table_names.alias))))
{ {
/// Replace to plain asterisk. return;
ast = std::make_shared<ASTAsterisk>();
} }
} }
throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER);
} }
else if (auto * join = typeid_cast<ASTTableJoin *>(ast.get())) else if (auto * join = typeid_cast<ASTTableJoin *>(ast.get()))
{ {
@ -862,6 +873,31 @@ static NamesAndTypesList::iterator findColumn(const String & name, NamesAndTypes
[&](const NamesAndTypesList::value_type & val) { return val.name == name; }); [&](const NamesAndTypesList::value_type & val) { return val.name == name; });
} }
static NamesAndTypesList getNamesAndTypeListFromTableExpression(const ASTTableExpression & table_expression, const Context & context)
{
NamesAndTypesList names_and_type_list;
if (table_expression.subquery)
{
const auto & subquery = table_expression.subquery->children.at(0);
names_and_type_list = InterpreterSelectWithUnionQuery::getSampleBlock(subquery, context).getNamesAndTypesList();
}
else if (table_expression.table_function)
{
const auto table_function = table_expression.table_function;
auto query_context = const_cast<Context *>(&context.getQueryContext());
const auto & function_storage = query_context->executeTableFunction(table_function);
names_and_type_list = function_storage->getSampleBlockNonMaterialized().getNamesAndTypesList();
}
else if (table_expression.database_and_table_name)
{
const auto & identifier = static_cast<const ASTIdentifier &>(*table_expression.database_and_table_name);
auto database_table = getDatabaseAndTableNameFromIdentifier(identifier);
const auto & table = context.getTable(database_table.first, database_table.second);
names_and_type_list = table->getSampleBlockNonMaterialized().getNamesAndTypesList();
}
return names_and_type_list;
}
void ExpressionAnalyzer::normalizeTree() void ExpressionAnalyzer::normalizeTree()
{ {
@ -879,7 +915,20 @@ void ExpressionAnalyzer::normalizeTree()
if (all_columns_name.empty()) if (all_columns_name.empty())
throw Exception("Logical error: an asterisk cannot be replaced with empty columns.", ErrorCodes::LOGICAL_ERROR); throw Exception("Logical error: an asterisk cannot be replaced with empty columns.", ErrorCodes::LOGICAL_ERROR);
QueryNormalizer(query, aliases, settings, all_columns_name).perform(); TableNamesAndColumnsName table_names_nad_columns_name;
if (select_query && select_query->tables && !select_query->tables->children.empty())
{
std::vector<ASTTableExpression> tables_expression = getTableExpressions(query);
for (const auto & table_expression : tables_expression)
{
const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
NamesAndTypesList names_and_types = getNamesAndTypeListFromTableExpression(table_expression, context);
table_names_nad_columns_name.emplace_back(std::pair(table_name, names_and_types.getNames()));
}
}
QueryNormalizer(query, aliases, settings, all_columns_name, table_names_nad_columns_name).perform();
} }
@ -2274,30 +2323,9 @@ NamesAndTypesList ExpressionAnalyzer::AnalyzedJoin::getColumnsFromJoinedTable(co
{ {
if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join()) if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join())
{ {
Block nested_result_sample;
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression); const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
if (table_expression.subquery) columns_from_joined_table = getNamesAndTypeListFromTableExpression(table_expression, context);
{
const auto & subquery = table_expression.subquery->children.at(0);
nested_result_sample = InterpreterSelectWithUnionQuery::getSampleBlock(subquery, context);
}
else if (table_expression.table_function)
{
const auto table_function = table_expression.table_function;
auto query_context = const_cast<Context *>(&context.getQueryContext());
const auto & join_storage = query_context->executeTableFunction(table_function);
nested_result_sample = join_storage->getSampleBlockNonMaterialized();
}
else if (table_expression.database_and_table_name)
{
const auto & identifier = static_cast<const ASTIdentifier &>(*table_expression.database_and_table_name);
auto database_table = getDatabaseAndTableNameFromIdentifier(identifier);
const auto & table = context.getTable(database_table.first, database_table.second);
nested_result_sample = table->getSampleBlockNonMaterialized();
}
columns_from_joined_table = nested_result_sample.getNamesAndTypesList();
} }
} }

View File

@ -303,7 +303,7 @@ ASTs PredicateExpressionsOptimizer::getSelectQueryProjectionColumns(ASTPtr & ast
/// first should normalize query tree. /// first should normalize query tree.
std::unordered_map<String, ASTPtr> aliases; std::unordered_map<String, ASTPtr> aliases;
getQueryAliases(ast, aliases, 0); getQueryAliases(ast, aliases, 0);
QueryNormalizer(ast, aliases, settings, {}).perform(); QueryNormalizer(ast, aliases, settings, {}, {}).perform();
ASTs projection_columns; ASTs projection_columns;
auto select_query = static_cast<ASTSelectQuery *>(ast.get()); auto select_query = static_cast<ASTSelectQuery *>(ast.get());

View File

@ -8,6 +8,8 @@
#include <Common/StringUtils/StringUtils.h> #include <Common/StringUtils/StringUtils.h>
#include <Common/typeid_cast.h> #include <Common/typeid_cast.h>
#include <Poco/String.h> #include <Poco/String.h>
#include <Parsers/ASTQualifiedAsterisk.h>
#include <iostream>
namespace DB namespace DB
{ {
@ -29,9 +31,11 @@ bool functionIsInOrGlobalInOperator(const String & name)
} }
QueryNormalizer::QueryNormalizer( QueryNormalizer::QueryNormalizer(ASTPtr & query, const QueryNormalizer::Aliases & aliases,
ASTPtr & query, const QueryNormalizer::Aliases & aliases, const Settings & settings, const Names & all_columns_name) const Settings & settings, const Names & all_columns_name,
: query(query), aliases(aliases), settings(settings), all_columns_name(all_columns_name) const TableNamesAndColumnsName & table_names_and_columns_name)
: query(query), aliases(aliases), settings(settings), all_columns_name(all_columns_name),
table_names_and_columns_name(table_names_and_columns_name)
{ {
} }
@ -143,7 +147,7 @@ void QueryNormalizer::performImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOf
} }
else if (ASTExpressionList * expr_list = typeid_cast<ASTExpressionList *>(ast.get())) else if (ASTExpressionList * expr_list = typeid_cast<ASTExpressionList *>(ast.get()))
{ {
/// Replace * with a list of columns. /// Replace *, alias.*, database.table.* with a list of columns.
ASTs & asts = expr_list->children; ASTs & asts = expr_list->children;
for (int i = static_cast<int>(asts.size()) - 1; i >= 0; --i) for (int i = static_cast<int>(asts.size()) - 1; i >= 0; --i)
{ {
@ -154,6 +158,31 @@ void QueryNormalizer::performImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOf
for (size_t idx = 0; idx < all_columns_name.size(); idx++) for (size_t idx = 0; idx < all_columns_name.size(); idx++)
asts.insert(asts.begin() + idx + i, std::make_shared<ASTIdentifier>(all_columns_name[idx])); asts.insert(asts.begin() + idx + i, std::make_shared<ASTIdentifier>(all_columns_name[idx]));
} }
else if (typeid_cast<ASTQualifiedAsterisk *>(asts[i].get()) && !table_names_and_columns_name.empty())
{
ASTQualifiedAsterisk * qualified_asterisk = static_cast<ASTQualifiedAsterisk *>(asts[i].get());
ASTIdentifier * identifier = typeid_cast<ASTIdentifier *>(qualified_asterisk->children[0].get());
size_t num_components = identifier->children.size();
for (const auto table_name_and_columns_name : table_names_and_columns_name)
{
const auto table_name = table_name_and_columns_name.first;
const auto table_all_columns_name = table_name_and_columns_name.second;
if ((num_components == 2
&& !table_name.database.empty()
&& static_cast<const ASTIdentifier &>(*identifier->children[0]).name == table_name.database
&& static_cast<const ASTIdentifier &>(*identifier->children[1]).name == table_name.table)
|| (num_components == 0
&& ((!table_name.table.empty() && identifier->name == table_name.table)
|| (!table_name.alias.empty() && identifier->name == table_name.alias))))
{
asts.erase(asts.begin() + i);
for (size_t idx = 0; idx < table_all_columns_name.size(); idx++)
asts.insert(asts.begin() + idx + i, std::make_shared<ASTIdentifier>(table_all_columns_name[idx]));
}
}
}
} }
} }
else if (ASTTablesInSelectQueryElement * tables_elem = typeid_cast<ASTTablesInSelectQueryElement *>(ast.get())) else if (ASTTablesInSelectQueryElement * tables_elem = typeid_cast<ASTTablesInSelectQueryElement *>(ast.get()))

View File

@ -1,17 +1,22 @@
#pragma once #pragma once
#include <Parsers/IAST.h> #include <Parsers/IAST.h>
#include "Settings.h" #include <Interpreters/Settings.h>
#include <Interpreters/evaluateQualified.h>
namespace DB namespace DB
{ {
using TableNameAndColumnsName = std::pair<DatabaseAndTableWithAlias, Names>;
using TableNamesAndColumnsName = std::vector<TableNameAndColumnsName>;
class QueryNormalizer class QueryNormalizer
{ {
public: public:
using Aliases = std::unordered_map<String, ASTPtr>; using Aliases = std::unordered_map<String, ASTPtr>;
QueryNormalizer(ASTPtr & query, const Aliases & aliases, const Settings & settings, const Names & all_columns_name); QueryNormalizer(ASTPtr & query, const Aliases & aliases, const Settings & settings, const Names & all_columns_name,
const TableNamesAndColumnsName & table_names_and_columns_name);
void perform(); void perform();
@ -23,8 +28,9 @@ private:
const Aliases & aliases; const Aliases & aliases;
const Settings & settings; const Settings & settings;
const Names & all_columns_name; const Names & all_columns_name;
const std::vector<std::pair<DatabaseAndTableWithAlias, Names>> & table_names_and_columns_name;
void performImpl(ASTPtr &ast, MapOfASTs &finished_asts, SetOfASTs &current_asts, std::string current_alias, size_t level); void performImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias, size_t level);
}; };
} }

View File

@ -1,4 +1,4 @@
1 2 1 2
1 2 3 1 4 5 1 2 3 1 4 5
1 2 1 3 1 2 1 3 1 2 1 3 1 3
1 2 1 3 1 2 1 3 3 1 2 1 3 1 3 3