Merge pull request #4412 from 4ertus2/joins

Support aliases in JOIN ON section for right table columns
This commit is contained in:
alexey-milovidov 2019-02-18 18:31:01 +03:00 committed by GitHub
commit 15b03f6e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 252 additions and 168 deletions

View File

@ -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,17 +79,10 @@ 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);
columns_from_joined_table.clear();
for (auto & column : columns)
{
@ -86,22 +97,14 @@ const JoinedColumnsList & AnalyzedJoin::getColumnsFromJoinedTable(
/// 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)
void AnalyzedJoin::calculateAvailableJoinedColumns(bool make_nullable)
{
const auto & columns = getColumnsFromJoinedTable(source_columns, context, select_query_with_join);
NameSet joined_columns;
for (auto & column : columns)
for (auto & column : columns_from_joined_table)
{
auto & column_name = column.name_and_type.name;
auto & column_type = column.name_and_type.type;

View File

@ -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;

View File

@ -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,8 +78,12 @@ 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 << "' ";
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)
{
@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
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,33 +178,30 @@ 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];
if (typeid_cast<const ASTSelectQuery *>(child.get()) ||
typeid_cast<const ASTTableExpression *>(child.get()))
continue;
auto & child = func_children[i];
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;
if (needVisitChild(child))
visit(child, data);
}
}
}
void QueryNormalizer::visit(ASTPtr & ast, Data & data)
{

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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
{

View File

@ -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

View 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;