mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 09:02:00 +00:00
Merge pull request #4412 from 4ertus2/joins
Support aliases in JOIN ON section for right table columns
This commit is contained in:
commit
15b03f6e12
@ -14,6 +14,24 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void AnalyzedJoin::addUsingKey(const ASTPtr & ast)
|
||||
{
|
||||
key_names_left.push_back(ast->getColumnName());
|
||||
key_names_right.push_back(ast->getAliasOrColumnName());
|
||||
|
||||
key_asts_left.push_back(ast);
|
||||
key_asts_right.push_back(ast);
|
||||
}
|
||||
|
||||
void AnalyzedJoin::addOnKeys(ASTPtr & left_table_ast, ASTPtr & right_table_ast)
|
||||
{
|
||||
key_names_left.push_back(left_table_ast->getColumnName());
|
||||
key_names_right.push_back(right_table_ast->getAliasOrColumnName());
|
||||
|
||||
key_asts_left.push_back(left_table_ast);
|
||||
key_asts_right.push_back(right_table_ast);
|
||||
}
|
||||
|
||||
ExpressionActionsPtr AnalyzedJoin::createJoinedBlockActions(
|
||||
const JoinedColumnsList & columns_added_by_join,
|
||||
const ASTSelectQuery * select_query_with_join,
|
||||
@ -49,7 +67,7 @@ ExpressionActionsPtr AnalyzedJoin::createJoinedBlockActions(
|
||||
ASTPtr query = expression_list;
|
||||
auto syntax_result = SyntaxAnalyzer(context).analyze(query, source_column_names, required_columns);
|
||||
ExpressionAnalyzer analyzer(query, syntax_result, context, {}, required_columns_set);
|
||||
return analyzer.getActions(false);
|
||||
return analyzer.getActions(true, false);
|
||||
}
|
||||
|
||||
Names AnalyzedJoin::getOriginalColumnNames(const NameSet & required_columns_from_joined_table) const
|
||||
@ -61,47 +79,32 @@ Names AnalyzedJoin::getOriginalColumnNames(const NameSet & required_columns_from
|
||||
return original_columns;
|
||||
}
|
||||
|
||||
const JoinedColumnsList & AnalyzedJoin::getColumnsFromJoinedTable(
|
||||
const NameSet & source_columns, const Context & context, const ASTSelectQuery * select_query_with_join)
|
||||
void AnalyzedJoin::calculateColumnsFromJoinedTable(
|
||||
const NameSet & source_columns, const DatabaseAndTableWithAlias & table_name_with_alias, const NamesAndTypesList & columns)
|
||||
{
|
||||
if (select_query_with_join && columns_from_joined_table.empty())
|
||||
{
|
||||
if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join())
|
||||
{
|
||||
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
||||
DatabaseAndTableWithAlias table_name_with_alias(table_expression, context.getCurrentDatabase());
|
||||
|
||||
auto columns = getNamesAndTypeListFromTableExpression(table_expression, context);
|
||||
|
||||
for (auto & column : columns)
|
||||
{
|
||||
JoinedColumn joined_column(column, column.name);
|
||||
|
||||
if (source_columns.count(column.name))
|
||||
{
|
||||
auto qualified_name = table_name_with_alias.getQualifiedNamePrefix() + column.name;
|
||||
joined_column.name_and_type.name = qualified_name;
|
||||
}
|
||||
|
||||
/// We don't want to select duplicate columns from the joined subquery if they appear
|
||||
if (std::find(columns_from_joined_table.begin(), columns_from_joined_table.end(), joined_column) == columns_from_joined_table.end())
|
||||
columns_from_joined_table.push_back(joined_column);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return columns_from_joined_table;
|
||||
}
|
||||
|
||||
void AnalyzedJoin::calculateAvailableJoinedColumns(
|
||||
const NameSet & source_columns, const Context & context, const ASTSelectQuery * select_query_with_join, bool make_nullable)
|
||||
{
|
||||
const auto & columns = getColumnsFromJoinedTable(source_columns, context, select_query_with_join);
|
||||
|
||||
NameSet joined_columns;
|
||||
columns_from_joined_table.clear();
|
||||
|
||||
for (auto & column : columns)
|
||||
{
|
||||
JoinedColumn joined_column(column, column.name);
|
||||
|
||||
if (source_columns.count(column.name))
|
||||
{
|
||||
auto qualified_name = table_name_with_alias.getQualifiedNamePrefix() + column.name;
|
||||
joined_column.name_and_type.name = qualified_name;
|
||||
}
|
||||
|
||||
/// We don't want to select duplicate columns from the joined subquery if they appear
|
||||
if (std::find(columns_from_joined_table.begin(), columns_from_joined_table.end(), joined_column) == columns_from_joined_table.end())
|
||||
columns_from_joined_table.push_back(joined_column);
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyzedJoin::calculateAvailableJoinedColumns(bool make_nullable)
|
||||
{
|
||||
NameSet joined_columns;
|
||||
|
||||
for (auto & column : columns_from_joined_table)
|
||||
{
|
||||
auto & column_name = column.name_and_type.name;
|
||||
auto & column_type = column.name_and_type.type;
|
||||
|
@ -12,6 +12,7 @@ namespace DB
|
||||
|
||||
class Context;
|
||||
class ASTSelectQuery;
|
||||
struct DatabaseAndTableWithAlias;
|
||||
|
||||
class ExpressionActions;
|
||||
using ExpressionActionsPtr = std::shared_ptr<ExpressionActions>;
|
||||
@ -61,14 +62,8 @@ struct AnalyzedJoin
|
||||
/// It's columns_from_joined_table without duplicate columns and possibly modified types.
|
||||
JoinedColumnsList available_joined_columns;
|
||||
|
||||
void addSimpleKey(const ASTPtr & ast)
|
||||
{
|
||||
key_names_left.push_back(ast->getColumnName());
|
||||
key_names_right.push_back(ast->getAliasOrColumnName());
|
||||
|
||||
key_asts_left.push_back(ast);
|
||||
key_asts_right.push_back(ast);
|
||||
}
|
||||
void addUsingKey(const ASTPtr & ast);
|
||||
void addOnKeys(ASTPtr & left_table_ast, ASTPtr & right_table_ast);
|
||||
|
||||
ExpressionActionsPtr createJoinedBlockActions(
|
||||
const JoinedColumnsList & columns_added_by_join, /// Subset of available_joined_columns.
|
||||
@ -77,13 +72,9 @@ struct AnalyzedJoin
|
||||
|
||||
Names getOriginalColumnNames(const NameSet & required_columns) const;
|
||||
|
||||
const JoinedColumnsList & getColumnsFromJoinedTable(const NameSet & source_columns,
|
||||
const Context & context,
|
||||
const ASTSelectQuery * select_query_with_join);
|
||||
void calculateAvailableJoinedColumns(const NameSet & source_columns,
|
||||
const Context & context,
|
||||
const ASTSelectQuery * select_query_with_join,
|
||||
bool make_nullable);
|
||||
void calculateColumnsFromJoinedTable(const NameSet & source_columns, const DatabaseAndTableWithAlias & table,
|
||||
const NamesAndTypesList & joined_columns);
|
||||
void calculateAvailableJoinedColumns(bool make_nullable);
|
||||
};
|
||||
|
||||
struct ASTTableExpression;
|
||||
|
@ -15,7 +15,7 @@ bool ColumnNamesContext::addTableAliasIfAny(const IAST & ast)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ColumnNamesContext::addColumnAliasIfAny(const IAST & ast, bool is_public)
|
||||
bool ColumnNamesContext::addColumnAliasIfAny(const IAST & ast)
|
||||
{
|
||||
String alias = ast.tryGetAlias();
|
||||
if (alias.empty())
|
||||
@ -24,21 +24,22 @@ bool ColumnNamesContext::addColumnAliasIfAny(const IAST & ast, bool is_public)
|
||||
if (required_names.count(alias))
|
||||
masked_columns.insert(alias);
|
||||
|
||||
if (is_public)
|
||||
public_columns.insert(alias);
|
||||
column_aliases.insert(alias);
|
||||
complex_aliases.insert(alias);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ColumnNamesContext::addColumnIdentifier(const ASTIdentifier & node, bool is_public)
|
||||
void ColumnNamesContext::addColumnIdentifier(const ASTIdentifier & node)
|
||||
{
|
||||
if (!IdentifierSemantic::getColumnName(node))
|
||||
return;
|
||||
|
||||
required_names.insert(node.name);
|
||||
/// There should be no complex cases after query normalization. Names to aliases: one-to-many.
|
||||
String alias = node.tryGetAlias();
|
||||
if (!alias.empty())
|
||||
required_names[node.name].insert(alias);
|
||||
|
||||
if (!addColumnAliasIfAny(node, is_public) && is_public)
|
||||
public_columns.insert(node.name);
|
||||
if (!required_names.count(node.name))
|
||||
required_names[node.name] = {};
|
||||
}
|
||||
|
||||
bool ColumnNamesContext::addArrayJoinAliasIfAny(const IAST & ast)
|
||||
@ -59,15 +60,16 @@ void ColumnNamesContext::addArrayJoinIdentifier(const ASTIdentifier & node)
|
||||
NameSet ColumnNamesContext::requiredColumns() const
|
||||
{
|
||||
NameSet required;
|
||||
for (const auto & name : required_names)
|
||||
for (const auto & pr : required_names)
|
||||
{
|
||||
const auto & name = pr.first;
|
||||
String table_name = Nested::extractTableName(name);
|
||||
|
||||
/// Tech debt. There's its own logic for ARRAY JOIN columns.
|
||||
if (array_join_columns.count(name) || array_join_columns.count(table_name))
|
||||
continue;
|
||||
|
||||
if (!column_aliases.count(name) || masked_columns.count(name))
|
||||
if (!complex_aliases.count(name) || masked_columns.count(name))
|
||||
required.insert(name);
|
||||
}
|
||||
return required;
|
||||
@ -76,9 +78,13 @@ NameSet ColumnNamesContext::requiredColumns() const
|
||||
std::ostream & operator << (std::ostream & os, const ColumnNamesContext & cols)
|
||||
{
|
||||
os << "required_names: ";
|
||||
for (const auto & x : cols.required_names)
|
||||
os << "'" << x << "' ";
|
||||
os << "source_tables: ";
|
||||
for (const auto & pr : cols.required_names)
|
||||
{
|
||||
os << "'" << pr.first << "'";
|
||||
for (auto & alias : pr.second)
|
||||
os << "/'" << alias << "'";
|
||||
}
|
||||
os << " source_tables: ";
|
||||
for (const auto & x : cols.tables)
|
||||
{
|
||||
auto alias = x.alias();
|
||||
@ -93,14 +99,8 @@ std::ostream & operator << (std::ostream & os, const ColumnNamesContext & cols)
|
||||
os << "table_aliases: ";
|
||||
for (const auto & x : cols.table_aliases)
|
||||
os << "'" << x << "' ";
|
||||
os << "private_aliases: ";
|
||||
for (const auto & x : cols.private_aliases)
|
||||
os << "'" << x << "' ";
|
||||
os << "column_aliases: ";
|
||||
for (const auto & x : cols.column_aliases)
|
||||
os << "'" << x << "' ";
|
||||
os << "public_columns: ";
|
||||
for (const auto & x : cols.public_columns)
|
||||
os << "complex_aliases: ";
|
||||
for (const auto & x : cols.complex_aliases)
|
||||
os << "'" << x << "' ";
|
||||
os << "masked_columns: ";
|
||||
for (const auto & x : cols.masked_columns)
|
||||
|
@ -51,20 +51,19 @@ struct ColumnNamesContext
|
||||
}
|
||||
};
|
||||
|
||||
NameSet required_names;
|
||||
std::unordered_map<String, std::set<String>> required_names; /// names with aliases
|
||||
NameSet table_aliases;
|
||||
NameSet private_aliases;
|
||||
NameSet column_aliases;
|
||||
NameSet complex_aliases;
|
||||
NameSet masked_columns;
|
||||
NameSet public_columns;
|
||||
NameSet array_join_columns;
|
||||
std::vector<JoinedTable> tables; /// ordered list of visited tables in FROM section with joins
|
||||
bool has_table_join = false;
|
||||
bool has_array_join = false;
|
||||
|
||||
bool addTableAliasIfAny(const IAST & ast);
|
||||
bool addColumnAliasIfAny(const IAST & ast, bool is_public = false);
|
||||
void addColumnIdentifier(const ASTIdentifier & node, bool is_public = false);
|
||||
bool addColumnAliasIfAny(const IAST & ast);
|
||||
void addColumnIdentifier(const ASTIdentifier & node);
|
||||
bool addArrayJoinAliasIfAny(const IAST & ast);
|
||||
void addArrayJoinIdentifier(const ASTIdentifier & node);
|
||||
|
||||
|
@ -957,8 +957,8 @@ void ExpressionAnalyzer::collectUsedColumns()
|
||||
for (const auto & name : source_columns)
|
||||
std::cerr << "'" << name.name << "' ";
|
||||
std::cerr << "required: ";
|
||||
for (const auto & name : required)
|
||||
std::cerr << "'" << name << "' ";
|
||||
for (const auto & pr : required)
|
||||
std::cerr << "'" << pr.first << "' ";
|
||||
std::cerr << std::endl;
|
||||
#endif
|
||||
|
||||
@ -991,7 +991,10 @@ void ExpressionAnalyzer::collectUsedColumns()
|
||||
for (const auto & joined_column : analyzed_join.available_joined_columns)
|
||||
{
|
||||
auto & name = joined_column.name_and_type.name;
|
||||
if (required.count(name) && !avaliable_columns.count(name))
|
||||
if (avaliable_columns.count(name))
|
||||
continue;
|
||||
|
||||
if (required.count(name))
|
||||
{
|
||||
columns_added_by_join.push_back(joined_column);
|
||||
required.erase(name);
|
||||
|
@ -47,6 +47,15 @@ bool IdentifierSemantic::canBeAlias(const ASTIdentifier & identifier)
|
||||
return identifier.semantic->can_be_alias;
|
||||
}
|
||||
|
||||
void IdentifierSemantic::setMembership(ASTIdentifier & identifier, size_t table_no)
|
||||
{
|
||||
identifier.semantic->membership = table_no;
|
||||
}
|
||||
|
||||
size_t IdentifierSemantic::getMembership(const ASTIdentifier & identifier)
|
||||
{
|
||||
return identifier.semantic->membership;
|
||||
}
|
||||
|
||||
std::pair<String, String> IdentifierSemantic::extractDatabaseAndTable(const ASTIdentifier & identifier)
|
||||
{
|
||||
|
@ -8,9 +8,10 @@ namespace DB
|
||||
|
||||
struct IdentifierSemanticImpl
|
||||
{
|
||||
bool special = false;
|
||||
bool need_long_name = false;
|
||||
bool can_be_alias = true;
|
||||
bool special = false; /// for now it's 'not a column': tables, subselects and some special stuff like FORMAT
|
||||
bool need_long_name = false;/// if column presents in multiple tables we need qualified names
|
||||
bool can_be_alias = true; /// if it's a cropped name it could not be an alias
|
||||
size_t membership = 0; /// table position in join (starting from 1) detected by qualifier or 0 if not detected.
|
||||
};
|
||||
|
||||
/// Static calss to manipulate IdentifierSemanticImpl via ASTIdentifier
|
||||
@ -30,6 +31,8 @@ struct IdentifierSemantic
|
||||
static void setColumnNormalName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static void setNeedLongName(ASTIdentifier & identifier, bool); /// if set setColumnNormalName makes qualified name
|
||||
static bool canBeAlias(const ASTIdentifier & identifier);
|
||||
static void setMembership(ASTIdentifier & identifier, size_t table_no);
|
||||
static size_t getMembership(const ASTIdentifier & identifier);
|
||||
|
||||
private:
|
||||
static bool doesIdentifierBelongTo(const ASTIdentifier & identifier, const String & database, const String & table);
|
||||
|
@ -18,7 +18,6 @@ namespace DB
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int TOO_DEEP_AST;
|
||||
extern const int CYCLIC_ALIASES;
|
||||
}
|
||||
@ -130,26 +129,38 @@ void QueryNormalizer::visit(ASTIdentifier & node, ASTPtr & ast, Data & data)
|
||||
}
|
||||
|
||||
/// mark table identifiers as 'not columns'
|
||||
void QueryNormalizer::visit(ASTTablesInSelectQueryElement & node, const ASTPtr &, Data &)
|
||||
void QueryNormalizer::visit(ASTTablesInSelectQueryElement & node, const ASTPtr &, Data & data)
|
||||
{
|
||||
/// mark table Identifiers as 'not a column'
|
||||
if (node.table_expression)
|
||||
{
|
||||
auto & expr = static_cast<ASTTableExpression &>(*node.table_expression);
|
||||
setIdentifierSpecial(expr.database_and_table_name);
|
||||
}
|
||||
|
||||
/// normalize JOIN ON section
|
||||
if (node.table_join)
|
||||
{
|
||||
auto & join = static_cast<ASTTableJoin &>(*node.table_join);
|
||||
if (join.on_expression)
|
||||
visit(join.on_expression, data);
|
||||
}
|
||||
}
|
||||
|
||||
static bool needVisitChild(const ASTPtr & child)
|
||||
{
|
||||
if (typeid_cast<const ASTSelectQuery *>(child.get()) ||
|
||||
typeid_cast<const ASTTableExpression *>(child.get()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// special visitChildren() for ASTSelectQuery
|
||||
void QueryNormalizer::visit(ASTSelectQuery & select, const ASTPtr & ast, Data & data)
|
||||
{
|
||||
for (auto & child : ast->children)
|
||||
{
|
||||
if (typeid_cast<const ASTSelectQuery *>(child.get()) ||
|
||||
typeid_cast<const ASTTableExpression *>(child.get()))
|
||||
continue;
|
||||
|
||||
visit(child, data);
|
||||
}
|
||||
if (needVisitChild(child))
|
||||
visit(child, data);
|
||||
|
||||
/// If the WHERE clause or HAVING consists of a single alias, the reference must be replaced not only in children,
|
||||
/// but also in where_expression and having_expression.
|
||||
@ -167,31 +178,28 @@ void QueryNormalizer::visit(ASTSelectQuery & select, const ASTPtr & ast, Data &
|
||||
/// on aliases in expressions of the form 123 AS x, arrayMap(x -> 1, [2]).
|
||||
void QueryNormalizer::visitChildren(const ASTPtr & node, Data & data)
|
||||
{
|
||||
ASTFunction * func_node = typeid_cast<ASTFunction *>(node.get());
|
||||
if (func_node && func_node->name == "lambda")
|
||||
if (ASTFunction * func_node = typeid_cast<ASTFunction *>(node.get()))
|
||||
{
|
||||
/// We skip the first argument. We also assume that the lambda function can not have parameters.
|
||||
for (size_t i = 1, size = func_node->arguments->children.size(); i < size; ++i)
|
||||
size_t first_pos = 0;
|
||||
if (func_node->name == "lambda")
|
||||
first_pos = 1;
|
||||
|
||||
auto & func_children = func_node->arguments->children;
|
||||
|
||||
for (size_t i = first_pos; i < func_children.size(); ++i)
|
||||
{
|
||||
auto & child = func_node->arguments->children[i];
|
||||
auto & child = func_children[i];
|
||||
|
||||
if (typeid_cast<const ASTSelectQuery *>(child.get()) ||
|
||||
typeid_cast<const ASTTableExpression *>(child.get()))
|
||||
continue;
|
||||
|
||||
visit(child, data);
|
||||
if (needVisitChild(child))
|
||||
visit(child, data);
|
||||
}
|
||||
}
|
||||
else if (!typeid_cast<ASTSelectQuery *>(node.get()))
|
||||
{
|
||||
for (auto & child : node->children)
|
||||
{
|
||||
if (typeid_cast<const ASTSelectQuery *>(child.get()) ||
|
||||
typeid_cast<const ASTTableExpression *>(child.get()))
|
||||
continue;
|
||||
|
||||
visit(child, data);
|
||||
}
|
||||
if (needVisitChild(child))
|
||||
visit(child, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,6 @@ std::vector<ASTPtr *> RequiredSourceColumnsMatcher::visit(ASTPtr & ast, Data & d
|
||||
|
||||
if (auto * t = typeid_cast<ASTIdentifier *>(ast.get()))
|
||||
{
|
||||
data.addColumnAliasIfAny(*ast);
|
||||
visit(*t, ast, data);
|
||||
return {};
|
||||
}
|
||||
@ -121,9 +120,9 @@ std::vector<ASTPtr *> RequiredSourceColumnsMatcher::visit(ASTSelectQuery & selec
|
||||
for (auto & node : select.select_expression_list->children)
|
||||
{
|
||||
if (auto * identifier = typeid_cast<ASTIdentifier *>(node.get()))
|
||||
data.addColumnIdentifier(*identifier, true);
|
||||
data.addColumnIdentifier(*identifier);
|
||||
else
|
||||
data.addColumnAliasIfAny(*node, true);
|
||||
data.addColumnAliasIfAny(*node);
|
||||
}
|
||||
|
||||
std::vector<ASTPtr *> out;
|
||||
|
@ -81,10 +81,11 @@ void collectSourceColumns(ASTSelectQuery * select_query, StoragePtr storage, Nam
|
||||
/// Translate qualified names such as db.table.column, table.column, table_alias.column to names' normal form.
|
||||
/// Expand asterisks and qualified asterisks with column names.
|
||||
/// There would be columns in normal form & column aliases after translation. Column & column alias would be normalized in QueryNormalizer.
|
||||
void translateQualifiedNames(ASTPtr & query, ASTSelectQuery * select_query, const Context & context, SyntaxAnalyzerResult & result,
|
||||
const Names & source_columns_list, const NameSet & source_columns_set)
|
||||
void translateQualifiedNames(ASTPtr & query, const ASTSelectQuery & select_query, const Context & context,
|
||||
const Names & source_columns_list, const NameSet & source_columns_set,
|
||||
const JoinedColumnsList & columns_from_joined_table)
|
||||
{
|
||||
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(*select_query, context);
|
||||
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(select_query, context);
|
||||
|
||||
if (tables_with_columns.empty())
|
||||
{
|
||||
@ -93,7 +94,6 @@ void translateQualifiedNames(ASTPtr & query, ASTSelectQuery * select_query, cons
|
||||
/// TODO: asterisk_left_columns_only probably does not work in some cases
|
||||
if (!context.getSettingsRef().asterisk_left_columns_only)
|
||||
{
|
||||
auto columns_from_joined_table = result.analyzed_join.getColumnsFromJoinedTable(source_columns_set, context, select_query);
|
||||
for (auto & column : columns_from_joined_table)
|
||||
all_columns_name.emplace_back(column.name_and_type.name);
|
||||
}
|
||||
@ -433,26 +433,11 @@ void getArrayJoinedColumns(ASTPtr & query, SyntaxAnalyzerResult & result, const
|
||||
}
|
||||
|
||||
/// Parse JOIN ON expression and collect ASTs for joined columns.
|
||||
void collectJoinedColumnsFromJoinOnExpr(AnalyzedJoin & analyzed_join, const ASTSelectQuery * select_query,
|
||||
const Context & context)
|
||||
void collectJoinedColumnsFromJoinOnExpr(AnalyzedJoin & analyzed_join, const ASTTableJoin & table_join)
|
||||
{
|
||||
const auto & tables = static_cast<const ASTTablesInSelectQuery &>(*select_query->tables);
|
||||
const auto * left_tables_element = static_cast<const ASTTablesInSelectQueryElement *>(tables.children.at(0).get());
|
||||
const auto * right_tables_element = select_query->join();
|
||||
|
||||
if (!left_tables_element || !right_tables_element)
|
||||
return;
|
||||
|
||||
const auto & table_join = static_cast<const ASTTableJoin &>(*right_tables_element->table_join);
|
||||
if (!table_join.on_expression)
|
||||
return;
|
||||
|
||||
const auto & left_table_expression = static_cast<const ASTTableExpression &>(*left_tables_element->table_expression);
|
||||
const auto & right_table_expression = static_cast<const ASTTableExpression &>(*right_tables_element->table_expression);
|
||||
|
||||
DatabaseAndTableWithAlias left_source_names(left_table_expression, context.getCurrentDatabase());
|
||||
DatabaseAndTableWithAlias right_source_names(right_table_expression, context.getCurrentDatabase());
|
||||
|
||||
/// Stores examples of columns which are only from one table.
|
||||
struct TableBelonging
|
||||
{
|
||||
@ -469,13 +454,15 @@ void collectJoinedColumnsFromJoinOnExpr(AnalyzedJoin & analyzed_join, const ASTS
|
||||
{
|
||||
auto * identifier = typeid_cast<const ASTIdentifier *>(ast.get());
|
||||
|
||||
size_t left_match_degree = IdentifierSemantic::canReferColumnToTable(*identifier, left_source_names);
|
||||
size_t right_match_degree = IdentifierSemantic::canReferColumnToTable(*identifier, right_source_names);
|
||||
|
||||
if (left_match_degree > right_match_degree)
|
||||
return {identifier, nullptr};
|
||||
if (left_match_degree < right_match_degree)
|
||||
return {nullptr, identifier};
|
||||
/// It's set in TranslateQualifiedNamesVisitor
|
||||
size_t membership = IdentifierSemantic::getMembership(*identifier);
|
||||
switch (membership)
|
||||
{
|
||||
case 1: return {identifier, nullptr};
|
||||
case 2: return {nullptr, identifier};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -524,19 +511,11 @@ void collectJoinedColumnsFromJoinOnExpr(AnalyzedJoin & analyzed_join, const ASTS
|
||||
bool can_be_right_part_from_left_table = right_table_belonging.example_only_from_right == nullptr;
|
||||
bool can_be_right_part_from_right_table = right_table_belonging.example_only_from_left == nullptr;
|
||||
|
||||
auto add_join_keys = [&](ASTPtr & ast_to_left_table, ASTPtr & ast_to_right_table)
|
||||
{
|
||||
analyzed_join.key_asts_left.push_back(ast_to_left_table);
|
||||
analyzed_join.key_names_left.push_back(ast_to_left_table->getColumnName());
|
||||
analyzed_join.key_asts_right.push_back(ast_to_right_table);
|
||||
analyzed_join.key_names_right.push_back(ast_to_right_table->getAliasOrColumnName());
|
||||
};
|
||||
|
||||
/// Default variant when all identifiers may be from any table.
|
||||
if (can_be_left_part_from_left_table && can_be_right_part_from_right_table)
|
||||
add_join_keys(left_ast, right_ast);
|
||||
analyzed_join.addOnKeys(left_ast, right_ast);
|
||||
else if (can_be_left_part_from_right_table && can_be_right_part_from_left_table)
|
||||
add_join_keys(right_ast, left_ast);
|
||||
analyzed_join.addOnKeys(right_ast, left_ast);
|
||||
else
|
||||
{
|
||||
auto * left_example = left_table_belonging.example_only_from_left ?
|
||||
@ -567,37 +546,34 @@ void collectJoinedColumnsFromJoinOnExpr(AnalyzedJoin & analyzed_join, const ASTS
|
||||
}
|
||||
|
||||
/// Find the columns that are obtained by JOIN.
|
||||
void collectJoinedColumns(AnalyzedJoin & analyzed_join, const ASTSelectQuery * select_query,
|
||||
const NameSet & source_columns, const Context & context)
|
||||
void collectJoinedColumns(AnalyzedJoin & analyzed_join, const ASTSelectQuery & select_query,
|
||||
const NameSet & source_columns, const String & current_database, bool join_use_nulls)
|
||||
{
|
||||
const ASTTablesInSelectQueryElement * node = select_query->join();
|
||||
const ASTTablesInSelectQueryElement * node = select_query.join();
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
const auto & table_join = static_cast<const ASTTableJoin &>(*node->table_join);
|
||||
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
||||
DatabaseAndTableWithAlias joined_table_name(table_expression, context.getCurrentDatabase());
|
||||
DatabaseAndTableWithAlias joined_table_name(table_expression, current_database);
|
||||
|
||||
if (table_join.using_expression_list)
|
||||
{
|
||||
auto & keys = typeid_cast<ASTExpressionList &>(*table_join.using_expression_list);
|
||||
for (const auto & key : keys.children)
|
||||
analyzed_join.addSimpleKey(key);
|
||||
analyzed_join.addUsingKey(key);
|
||||
|
||||
/// @warning wrong qualification if the right key is an alias
|
||||
for (auto & name : analyzed_join.key_names_right)
|
||||
if (source_columns.count(name))
|
||||
name = joined_table_name.getQualifiedNamePrefix() + name;
|
||||
}
|
||||
else if (table_join.on_expression)
|
||||
collectJoinedColumnsFromJoinOnExpr(analyzed_join, select_query, context);
|
||||
collectJoinedColumnsFromJoinOnExpr(analyzed_join, table_join);
|
||||
|
||||
auto & settings = context.getSettingsRef();
|
||||
bool make_nullable = settings.join_use_nulls && (table_join.kind == ASTTableJoin::Kind::Left ||
|
||||
table_join.kind == ASTTableJoin::Kind::Full);
|
||||
bool make_nullable = join_use_nulls && (table_join.kind == ASTTableJoin::Kind::Left || table_join.kind == ASTTableJoin::Kind::Full);
|
||||
|
||||
analyzed_join.calculateAvailableJoinedColumns(source_columns, context, select_query, make_nullable);
|
||||
analyzed_join.calculateAvailableJoinedColumns(make_nullable);
|
||||
}
|
||||
|
||||
}
|
||||
@ -635,8 +611,18 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze(
|
||||
|
||||
if (select_query)
|
||||
{
|
||||
translateQualifiedNames(query, select_query, context, result,
|
||||
(storage ? storage->getColumns().ordinary.getNames() : source_columns_list), source_columns_set);
|
||||
if (const ASTTablesInSelectQueryElement * node = select_query->join())
|
||||
{
|
||||
const auto & joined_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
||||
DatabaseAndTableWithAlias table(joined_expression, context.getCurrentDatabase());
|
||||
|
||||
NamesAndTypesList joined_columns = getNamesAndTypeListFromTableExpression(joined_expression, context);
|
||||
result.analyzed_join.calculateColumnsFromJoinedTable(source_columns_set, table, joined_columns);
|
||||
}
|
||||
|
||||
translateQualifiedNames(query, *select_query, context,
|
||||
(storage ? storage->getColumns().ordinary.getNames() : source_columns_list), source_columns_set,
|
||||
result.analyzed_join.columns_from_joined_table);
|
||||
|
||||
/// Depending on the user's profile, check for the execution rights
|
||||
/// distributed subqueries inside the IN or JOIN sections and process these subqueries.
|
||||
@ -692,7 +678,7 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze(
|
||||
/// Push the predicate expression down to the subqueries.
|
||||
result.rewrite_subqueries = PredicateExpressionsOptimizer(select_query, settings, context).optimize();
|
||||
|
||||
collectJoinedColumns(result.analyzed_join, select_query, source_columns_set, context);
|
||||
collectJoinedColumns(result.analyzed_join, *select_query, source_columns_set, context.getCurrentDatabase(), settings.join_use_nulls);
|
||||
}
|
||||
|
||||
return std::make_shared<const SyntaxAnalyzerResult>(result);
|
||||
|
@ -72,6 +72,9 @@ std::vector<ASTPtr *> TranslateQualifiedNamesMatcher::visit(ASTIdentifier & iden
|
||||
best_table_pos = i;
|
||||
}
|
||||
|
||||
if (best_match)
|
||||
IdentifierSemantic::setMembership(identifier, best_table_pos + 1);
|
||||
|
||||
/// In case if column from the joined table are in source columns, change it's name to qualified.
|
||||
if (best_table_pos && data.source_columns.count(identifier.shortName()))
|
||||
IdentifierSemantic::setNeedLongName(identifier, true);
|
||||
|
@ -8,6 +8,13 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
ASTPtr ASTIdentifier::clone() const
|
||||
{
|
||||
auto ret = std::make_shared<ASTIdentifier>(*this);
|
||||
ret->semantic = std::make_shared<IdentifierSemanticImpl>(*ret->semantic);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<ASTIdentifier> ASTIdentifier::createSpecial(const String & name, std::vector<String> && name_parts)
|
||||
{
|
||||
auto ret = std::make_shared<ASTIdentifier>(name, std::move(name_parts));
|
||||
@ -27,8 +34,9 @@ void ASTIdentifier::setShortName(const String & new_name)
|
||||
name = new_name;
|
||||
name_parts.clear();
|
||||
|
||||
semantic->need_long_name = false;
|
||||
semantic->can_be_alias = true;
|
||||
bool special = semantic->special;
|
||||
*semantic = IdentifierSemanticImpl();
|
||||
semantic->special = special;
|
||||
}
|
||||
|
||||
void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
/** Get the text that identifies this element. */
|
||||
String getID(char delim) const override { return "Identifier" + (delim + name); }
|
||||
|
||||
ASTPtr clone() const override { return std::make_shared<ASTIdentifier>(*this); }
|
||||
ASTPtr clone() const override;
|
||||
|
||||
void collectIdentifierNames(IdentifierNameSet & set) const override
|
||||
{
|
||||
|
@ -0,0 +1,29 @@
|
||||
0 0
|
||||
2 2
|
||||
4 4
|
||||
6 6
|
||||
8 8
|
||||
0 0
|
||||
2 2
|
||||
4 4
|
||||
6 6
|
||||
8 8
|
||||
0 0
|
||||
2 2
|
||||
4 4
|
||||
6 6
|
||||
8 8
|
||||
0 0
|
||||
2 2
|
||||
4 4
|
||||
6 6
|
||||
8 8
|
||||
0 0
|
||||
2 2
|
||||
4 4
|
||||
6 6
|
||||
8 8
|
||||
6 6 6 60
|
||||
8 8 8 80
|
||||
6 6 6 60
|
||||
8 8 8 80
|
43
dbms/tests/queries/0_stateless/00845_join_on_aliases.sql
Normal file
43
dbms/tests/queries/0_stateless/00845_join_on_aliases.sql
Normal file
@ -0,0 +1,43 @@
|
||||
USE test;
|
||||
|
||||
DROP TABLE IF EXISTS table1;
|
||||
DROP TABLE IF EXISTS table2;
|
||||
|
||||
CREATE TABLE table1 (a UInt32, b UInt32) ENGINE = Memory;
|
||||
CREATE TABLE table2 (a UInt32, b UInt32) ENGINE = Memory;
|
||||
|
||||
INSERT INTO table1 SELECT number, number FROM numbers(10);
|
||||
INSERT INTO table2 SELECT number * 2, number * 20 FROM numbers(6);
|
||||
|
||||
select t1.a t1_a, t2.a
|
||||
from table1 as t1
|
||||
join table2 as t2 on table1.a = table2.a and t1.a = table2.a and t1_a = table2.a;
|
||||
|
||||
select t1.a t1_a, t2.a
|
||||
from table1 as t1
|
||||
join table2 as t2 on table1.a = t2.a and t1.a = t2.a and t1_a = t2.a;
|
||||
|
||||
select t1.a as t1_a, t2.a t2_a
|
||||
from table1 as t1
|
||||
join table2 as t2 on table1.a = t2_a and t1.a = t2_a and t1_a = t2_a;
|
||||
|
||||
select t1.a t1_a, t2.a
|
||||
from table1 as t1
|
||||
join table2 as t2 on table1.a = table2.a and t1.a = t2.a and t1_a = t2.a;
|
||||
|
||||
select t1.a t1_a, t2.a as t2_a
|
||||
from table1 as t1
|
||||
join table2 as t2 on table1.a = table2.a and t1.a = t2.a and t1_a = t2_a;
|
||||
|
||||
select *
|
||||
from table1 as t1
|
||||
join table2 as t2 on t1_a = t2_a
|
||||
where (table1.a as t1_a) > 4 and (table2.a as t2_a) > 2;
|
||||
|
||||
select t1.*, t2.*
|
||||
from table1 as t1
|
||||
join table2 as t2 on t1_a = t2_a
|
||||
where (t1.a as t1_a) > 2 and (t2.a as t2_a) > 4;
|
||||
|
||||
DROP TABLE table1;
|
||||
DROP TABLE table2;
|
Loading…
Reference in New Issue
Block a user