mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 16:50:48 +00:00
Merge pull request #3270 from yandex/left-join-right-keys-fix
Left join right keys fix
This commit is contained in:
commit
36c3feea80
@ -125,9 +125,6 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery)
|
|||||||
|
|
||||||
if (!done_with_join)
|
if (!done_with_join)
|
||||||
{
|
{
|
||||||
if (subquery.joined_block_actions)
|
|
||||||
subquery.joined_block_actions->execute(block);
|
|
||||||
|
|
||||||
for (const auto & name_with_alias : subquery.joined_block_aliases)
|
for (const auto & name_with_alias : subquery.joined_block_aliases)
|
||||||
{
|
{
|
||||||
if (block.has(name_with_alias.first))
|
if (block.has(name_with_alias.first))
|
||||||
@ -140,6 +137,9 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subquery.joined_block_actions)
|
||||||
|
subquery.joined_block_actions->execute(block);
|
||||||
|
|
||||||
if (!subquery.join->insertFromBlock(block))
|
if (!subquery.join->insertFromBlock(block))
|
||||||
done_with_join = true;
|
done_with_join = true;
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ static std::vector<ASTTableExpression> getTableExpressions(const ASTPtr & query)
|
|||||||
{
|
{
|
||||||
ASTSelectQuery * select_query = typeid_cast<ASTSelectQuery *>(query.get());
|
ASTSelectQuery * select_query = typeid_cast<ASTSelectQuery *>(query.get());
|
||||||
|
|
||||||
std::vector<ASTTableExpression> tables_expression;
|
std::vector<ASTTableExpression> table_expressions;
|
||||||
|
|
||||||
if (select_query && select_query->tables)
|
if (select_query && select_query->tables)
|
||||||
{
|
{
|
||||||
@ -294,11 +294,11 @@ static std::vector<ASTTableExpression> getTableExpressions(const ASTPtr & query)
|
|||||||
ASTTablesInSelectQueryElement & select_element = static_cast<ASTTablesInSelectQueryElement &>(*element);
|
ASTTablesInSelectQueryElement & select_element = static_cast<ASTTablesInSelectQueryElement &>(*element);
|
||||||
|
|
||||||
if (select_element.table_expression)
|
if (select_element.table_expression)
|
||||||
tables_expression.emplace_back(static_cast<ASTTableExpression &>(*select_element.table_expression));
|
table_expressions.emplace_back(static_cast<ASTTableExpression &>(*select_element.table_expression));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tables_expression;
|
return table_expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionAnalyzer::translateQualifiedNames()
|
void ExpressionAnalyzer::translateQualifiedNames()
|
||||||
@ -908,27 +908,40 @@ void ExpressionAnalyzer::normalizeTree()
|
|||||||
|
|
||||||
if (!settings.asterisk_left_columns_only)
|
if (!settings.asterisk_left_columns_only)
|
||||||
{
|
{
|
||||||
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(context, select_query).getNames();
|
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query);
|
||||||
all_columns_name.insert(all_columns_name.end(), columns_from_joined_table.begin(), columns_from_joined_table.end());
|
for (auto & column : columns_from_joined_table)
|
||||||
|
all_columns_name.emplace_back(column.name_and_type.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
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("An asterisk cannot be replaced with empty columns.", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
|
||||||
TableNamesAndColumnsName table_names_nad_columns_name;
|
TableNamesAndColumnsName table_names_and_columns_name;
|
||||||
if (select_query && select_query->tables && !select_query->tables->children.empty())
|
if (select_query && select_query->tables && !select_query->tables->children.empty())
|
||||||
{
|
{
|
||||||
std::vector<ASTTableExpression> tables_expression = getTableExpressions(query);
|
std::vector<ASTTableExpression> tables_expression = getTableExpressions(query);
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
for (const auto & table_expression : tables_expression)
|
for (const auto & table_expression : tables_expression)
|
||||||
{
|
{
|
||||||
const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
|
const auto table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
|
||||||
NamesAndTypesList names_and_types = getNamesAndTypeListFromTableExpression(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()));
|
|
||||||
|
if (!first)
|
||||||
|
{
|
||||||
|
/// For joined tables qualify duplicating names.
|
||||||
|
for (auto & name_and_type : names_and_types)
|
||||||
|
if (source_columns.contains(name_and_type.name))
|
||||||
|
name_and_type.name = table_name.getQualifiedNamePrefix() + name_and_type.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
table_names_and_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();
|
QueryNormalizer(query, aliases, settings, all_columns_name, table_names_and_columns_name).perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2268,7 +2281,8 @@ void ExpressionAnalyzer::addJoinAction(ExpressionActionsPtr & actions, bool only
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const ASTSelectQuery * select_query_with_join,
|
void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const NamesAndTypesList & source_columns,
|
||||||
|
const ASTSelectQuery * select_query_with_join,
|
||||||
const Context & context)
|
const Context & context)
|
||||||
{
|
{
|
||||||
if (!select_query_with_join)
|
if (!select_query_with_join)
|
||||||
@ -2291,20 +2305,28 @@ void ExpressionAnalyzer::AnalyzedJoin::createJoinedBlockActions(const ASTSelectQ
|
|||||||
|
|
||||||
NameSet required_columns_set(key_names_right.begin(), key_names_right.end());
|
NameSet required_columns_set(key_names_right.begin(), key_names_right.end());
|
||||||
for (const auto & joined_column : columns_added_by_join)
|
for (const auto & joined_column : columns_added_by_join)
|
||||||
required_columns_set.insert(joined_column.original_name);
|
required_columns_set.insert(joined_column.name_and_type.name);
|
||||||
|
Names required_columns(required_columns_set.begin(), required_columns_set.end());
|
||||||
|
|
||||||
required_columns_set.insert(key_names_right.begin(), key_names_right.end());
|
const auto & columns_from_joined_table = getColumnsFromJoinedTable(source_columns, context, select_query_with_join);
|
||||||
|
NamesAndTypesList source_column_names;
|
||||||
|
for (auto & column : columns_from_joined_table)
|
||||||
|
source_column_names.emplace_back(column.name_and_type);
|
||||||
|
|
||||||
required_columns_from_joined_table.insert(required_columns_from_joined_table.end(),
|
ExpressionAnalyzer analyzer(expression_list, context, nullptr, source_column_names, required_columns);
|
||||||
required_columns_set.begin(), required_columns_set.end());
|
|
||||||
|
|
||||||
const auto & source_columns_name = getColumnsFromJoinedTable(context, select_query_with_join);
|
|
||||||
ExpressionAnalyzer analyzer(expression_list, context, nullptr, source_columns_name, required_columns_from_joined_table);
|
|
||||||
joined_block_actions = analyzer.getActions(false);
|
joined_block_actions = analyzer.getActions(false);
|
||||||
|
|
||||||
for (const auto & column_required_from_actions : joined_block_actions->getRequiredColumns())
|
auto required_action_columns = joined_block_actions->getRequiredColumns();
|
||||||
if (!required_columns_set.count(column_required_from_actions))
|
required_columns_from_joined_table.insert(required_action_columns.begin(), required_action_columns.end());
|
||||||
required_columns_from_joined_table.push_back(column_required_from_actions);
|
auto sample = joined_block_actions->getSampleBlock();
|
||||||
|
|
||||||
|
for (auto & column : key_names_right)
|
||||||
|
if (!sample.has(column))
|
||||||
|
required_columns_from_joined_table.insert(column);
|
||||||
|
|
||||||
|
for (auto & column : columns_added_by_join)
|
||||||
|
if (!sample.has(column.name_and_type.name))
|
||||||
|
required_columns_from_joined_table.insert(column.name_and_type.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2317,15 +2339,28 @@ NamesAndTypesList ExpressionAnalyzer::AnalyzedJoin::getColumnsAddedByJoin() cons
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
NamesAndTypesList ExpressionAnalyzer::AnalyzedJoin::getColumnsFromJoinedTable(const Context & context, const ASTSelectQuery * select_query_with_join)
|
const ExpressionAnalyzer::AnalyzedJoin::JoinedColumnsList & ExpressionAnalyzer::AnalyzedJoin::getColumnsFromJoinedTable(
|
||||||
|
const NamesAndTypesList & source_columns, const Context & context, const ASTSelectQuery * select_query_with_join)
|
||||||
{
|
{
|
||||||
if (select_query_with_join && !columns_from_joined_table.size())
|
if (select_query_with_join && columns_from_joined_table.empty())
|
||||||
{
|
{
|
||||||
if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join())
|
if (const ASTTablesInSelectQueryElement * node = select_query_with_join->join())
|
||||||
{
|
{
|
||||||
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
||||||
|
auto table_name_with_alias = getTableNameWithAliasFromTableExpression(table_expression, context);
|
||||||
|
|
||||||
columns_from_joined_table = getNamesAndTypeListFromTableExpression(table_expression, context);
|
auto columns = getNamesAndTypeListFromTableExpression(table_expression, context);
|
||||||
|
|
||||||
|
for (auto & column : columns)
|
||||||
|
{
|
||||||
|
columns_from_joined_table.emplace_back(column, column.name);
|
||||||
|
|
||||||
|
if (source_columns.contains(column.name))
|
||||||
|
{
|
||||||
|
auto qualified_name = table_name_with_alias.getQualifiedNamePrefix() + column.name;
|
||||||
|
columns_from_joined_table.back().name_and_type.name = qualified_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2411,22 +2446,23 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
|
|||||||
else if (table_to_join.database_and_table_name)
|
else if (table_to_join.database_and_table_name)
|
||||||
table = table_to_join.database_and_table_name;
|
table = table_to_join.database_and_table_name;
|
||||||
|
|
||||||
auto interpreter = interpretSubquery(table, context, subquery_depth, analyzed_join.required_columns_from_joined_table);
|
Names original_columns;
|
||||||
|
for (const auto & column : analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query))
|
||||||
|
if (analyzed_join.required_columns_from_joined_table.count(column.name_and_type.name))
|
||||||
|
original_columns.emplace_back(column.original_name);
|
||||||
|
|
||||||
|
auto interpreter = interpretSubquery(table, context, subquery_depth, original_columns);
|
||||||
subquery_for_set.source = std::make_shared<LazyBlockInputStream>(
|
subquery_for_set.source = std::make_shared<LazyBlockInputStream>(
|
||||||
interpreter->getSampleBlock(),
|
interpreter->getSampleBlock(),
|
||||||
[interpreter]() mutable { return interpreter->execute().in; });
|
[interpreter]() mutable { return interpreter->execute().in; });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Alias duplicating columns.
|
/// Alias duplicating columns as qualified.
|
||||||
for (const auto & joined_column : analyzed_join.columns_added_by_join)
|
for (const auto & column : analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query))
|
||||||
{
|
if (analyzed_join.required_columns_from_joined_table.count(column.name_and_type.name))
|
||||||
const auto & qualified_name = joined_column.name_and_type.name;
|
subquery_for_set.joined_block_aliases.emplace_back(column.original_name, column.name_and_type.name);
|
||||||
if (joined_column.original_name != qualified_name)
|
|
||||||
subquery_for_set.joined_block_aliases.emplace_back(joined_column.original_name, qualified_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sample_block = subquery_for_set.source->getHeader();
|
auto sample_block = subquery_for_set.source->getHeader();
|
||||||
analyzed_join.joined_block_actions->execute(sample_block);
|
|
||||||
for (const auto & name_with_alias : subquery_for_set.joined_block_aliases)
|
for (const auto & name_with_alias : subquery_for_set.joined_block_aliases)
|
||||||
{
|
{
|
||||||
if (sample_block.has(name_with_alias.first))
|
if (sample_block.has(name_with_alias.first))
|
||||||
@ -2439,6 +2475,8 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
analyzed_join.joined_block_actions->execute(sample_block);
|
||||||
|
|
||||||
/// TODO You do not need to set this up when JOIN is only needed on remote servers.
|
/// TODO You do not need to set this up when JOIN is only needed on remote servers.
|
||||||
subquery_for_set.join = join;
|
subquery_for_set.join = join;
|
||||||
subquery_for_set.join->setSampleBlock(sample_block);
|
subquery_for_set.join->setSampleBlock(sample_block);
|
||||||
@ -2836,7 +2874,7 @@ void ExpressionAnalyzer::collectUsedColumns()
|
|||||||
analyzed_join.columns_added_by_join.erase(it++);
|
analyzed_join.columns_added_by_join.erase(it++);
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzed_join.createJoinedBlockActions(select_query, context);
|
analyzed_join.createJoinedBlockActions(source_columns, select_query, context);
|
||||||
|
|
||||||
/// Some columns from right join key may be used in query. This columns will be appended to block during join.
|
/// Some columns from right join key may be used in query. This columns will be appended to block during join.
|
||||||
for (const auto & right_key_name : analyzed_join.key_names_right)
|
for (const auto & right_key_name : analyzed_join.key_names_right)
|
||||||
@ -2956,8 +2994,8 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
|
|||||||
return table_belonging;
|
return table_belonging;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<void(ASTPtr &, const DatabaseAndTableWithAlias &)> translate_qualified_names;
|
std::function<void(ASTPtr &, const DatabaseAndTableWithAlias &, bool)> translate_qualified_names;
|
||||||
translate_qualified_names = [&](ASTPtr & ast, const DatabaseAndTableWithAlias & source_names)
|
translate_qualified_names = [&](ASTPtr & ast, const DatabaseAndTableWithAlias & source_names, bool right_table)
|
||||||
{
|
{
|
||||||
auto * identifier = typeid_cast<const ASTIdentifier *>(ast.get());
|
auto * identifier = typeid_cast<const ASTIdentifier *>(ast.get());
|
||||||
if (identifier)
|
if (identifier)
|
||||||
@ -2966,12 +3004,16 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
|
|||||||
{
|
{
|
||||||
auto num_components = getNumComponentsToStripInOrderToTranslateQualifiedName(*identifier, source_names);
|
auto num_components = getNumComponentsToStripInOrderToTranslateQualifiedName(*identifier, source_names);
|
||||||
stripIdentifier(ast, num_components);
|
stripIdentifier(ast, num_components);
|
||||||
|
|
||||||
|
if (right_table && source_columns.contains(ast->getColumnName()))
|
||||||
|
source_names.makeQualifiedName(ast);
|
||||||
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & child : ast->children)
|
for (auto & child : ast->children)
|
||||||
translate_qualified_names(child, source_names);
|
translate_qualified_names(child, source_names, right_table);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto supported_syntax = " Supported syntax: JOIN ON Expr([table.]column, ...) = Expr([table.]column, ...) "
|
const auto supported_syntax = " Supported syntax: JOIN ON Expr([table.]column, ...) = Expr([table.]column, ...) "
|
||||||
@ -3001,8 +3043,8 @@ void ExpressionAnalyzer::collectJoinedColumnsFromJoinOnExpr()
|
|||||||
|
|
||||||
auto add_join_keys = [&](ASTPtr & ast_to_left_table, ASTPtr & ast_to_right_table)
|
auto add_join_keys = [&](ASTPtr & ast_to_left_table, ASTPtr & ast_to_right_table)
|
||||||
{
|
{
|
||||||
translate_qualified_names(ast_to_left_table, left_source_names);
|
translate_qualified_names(ast_to_left_table, left_source_names, false);
|
||||||
translate_qualified_names(ast_to_right_table, right_source_names);
|
translate_qualified_names(ast_to_right_table, right_source_names, true);
|
||||||
|
|
||||||
analyzed_join.key_asts_left.push_back(ast_to_left_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_names_left.push_back(ast_to_left_table->getColumnName());
|
||||||
@ -3058,8 +3100,18 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
|
|||||||
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
const auto & table_expression = static_cast<const ASTTableExpression &>(*node->table_expression);
|
||||||
auto joined_table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
|
auto joined_table_name = getTableNameWithAliasFromTableExpression(table_expression, context);
|
||||||
|
|
||||||
auto add_name_to_join_keys = [](Names & join_keys, ASTs & join_asts, const String & name, const ASTPtr & ast)
|
auto add_name_to_join_keys = [&](Names & join_keys, ASTs & join_asts, const ASTPtr & ast, bool right_table)
|
||||||
{
|
{
|
||||||
|
String name;
|
||||||
|
if (right_table)
|
||||||
|
{
|
||||||
|
name = ast->getAliasOrColumnName();
|
||||||
|
if (source_columns.contains(name))
|
||||||
|
name = joined_table_name.getQualifiedNamePrefix() + name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = ast->getColumnName();
|
||||||
|
|
||||||
join_keys.push_back(name);
|
join_keys.push_back(name);
|
||||||
join_asts.push_back(ast);
|
join_asts.push_back(ast);
|
||||||
};
|
};
|
||||||
@ -3069,8 +3121,8 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
|
|||||||
auto & keys = typeid_cast<ASTExpressionList &>(*table_join.using_expression_list);
|
auto & keys = typeid_cast<ASTExpressionList &>(*table_join.using_expression_list);
|
||||||
for (const auto & key : keys.children)
|
for (const auto & key : keys.children)
|
||||||
{
|
{
|
||||||
add_name_to_join_keys(analyzed_join.key_names_left, analyzed_join.key_asts_left, key->getColumnName(), key);
|
add_name_to_join_keys(analyzed_join.key_names_left, analyzed_join.key_asts_left, key, false);
|
||||||
add_name_to_join_keys(analyzed_join.key_names_right, analyzed_join.key_asts_right, key->getAliasOrColumnName(), key);
|
add_name_to_join_keys(analyzed_join.key_names_right, analyzed_join.key_asts_right, key, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (table_join.on_expression)
|
else if (table_join.on_expression)
|
||||||
@ -3081,29 +3133,26 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns)
|
|||||||
/// If we use USING syntax, join_key_names_left and join_key_names_right are almost the same, but we need to use
|
/// If we use USING syntax, join_key_names_left and join_key_names_right are almost the same, but we need to use
|
||||||
/// join_key_names_right in order to support aliases in USING list. Example:
|
/// join_key_names_right in order to support aliases in USING list. Example:
|
||||||
/// SELECT x FROM tab1 ANY LEFT JOIN tab2 USING (x as y) - will join column x from tab1 with column y from tab2.
|
/// SELECT x FROM tab1 ANY LEFT JOIN tab2 USING (x as y) - will join column x from tab1 with column y from tab2.
|
||||||
auto & not_joined_columns = table_join.using_expression_list ? analyzed_join.key_names_right : analyzed_join.key_names_left;
|
//auto & not_joined_columns = table_join.using_expression_list ? analyzed_join.key_names_right : analyzed_join.key_names_left;
|
||||||
auto columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(context, select_query);
|
auto & columns_from_joined_table = analyzed_join.getColumnsFromJoinedTable(source_columns, context, select_query);
|
||||||
|
|
||||||
for (auto & column_name_and_type : columns_from_joined_table)
|
for (auto & column : columns_from_joined_table)
|
||||||
{
|
{
|
||||||
auto & column_name = column_name_and_type.name;
|
auto & column_name = column.name_and_type.name;
|
||||||
auto & column_type = column_name_and_type.type;
|
auto & column_type = column.name_and_type.type;
|
||||||
if (not_joined_columns.end() == std::find(not_joined_columns.begin(), not_joined_columns.end(), column_name))
|
auto & original_name = column.original_name;
|
||||||
|
//if (table_join.on_expression
|
||||||
|
// || not_joined_columns.end() == std::find(not_joined_columns.begin(), not_joined_columns.end(), column_name))
|
||||||
{
|
{
|
||||||
auto qualified_name = column_name;
|
if (joined_columns.count(column_name)) /// Duplicate columns in the subquery for JOIN do not make sense.
|
||||||
/// Change name for duplicate column form joined table.
|
|
||||||
if (source_columns.contains(qualified_name))
|
|
||||||
qualified_name = joined_table_name.getQualifiedNamePrefix() + qualified_name;
|
|
||||||
|
|
||||||
if (joined_columns.count(qualified_name)) /// Duplicate columns in the subquery for JOIN do not make sense.
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
joined_columns.insert(qualified_name);
|
joined_columns.insert(column_name);
|
||||||
|
|
||||||
bool make_nullable = settings.join_use_nulls && (table_join.kind == ASTTableJoin::Kind::Left ||
|
bool make_nullable = settings.join_use_nulls && (table_join.kind == ASTTableJoin::Kind::Left ||
|
||||||
table_join.kind == ASTTableJoin::Kind::Full);
|
table_join.kind == ASTTableJoin::Kind::Full);
|
||||||
auto type = make_nullable ? makeNullable(column_type) : column_type;
|
auto type = make_nullable ? makeNullable(column_type) : column_type;
|
||||||
analyzed_join.columns_added_by_join.emplace_back(NameAndTypePair(qualified_name, std::move(type)), column_name);
|
analyzed_join.columns_added_by_join.emplace_back(NameAndTypePair(column_name, std::move(type)), original_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,7 @@ private:
|
|||||||
* It's possible to use name `expr(t2 columns)`.
|
* It's possible to use name `expr(t2 columns)`.
|
||||||
*/
|
*/
|
||||||
Names key_names_left;
|
Names key_names_left;
|
||||||
Names key_names_right;
|
Names key_names_right; /// Duplicating names are qualified.
|
||||||
ASTs key_asts_left;
|
ASTs key_asts_left;
|
||||||
ASTs key_asts_right;
|
ASTs key_asts_right;
|
||||||
|
|
||||||
@ -256,10 +256,11 @@ private:
|
|||||||
|
|
||||||
using JoinedColumnsList = std::list<JoinedColumn>;
|
using JoinedColumnsList = std::list<JoinedColumn>;
|
||||||
|
|
||||||
/// All columns which can be read from joined table.
|
/// All columns which can be read from joined table. Duplicating names are qualified.
|
||||||
NamesAndTypesList columns_from_joined_table;
|
JoinedColumnsList columns_from_joined_table;
|
||||||
/// Columns which will be used in query to the joined query.
|
/// Columns which will be used in query to the joined query. Duplicating names are qualified.
|
||||||
Names required_columns_from_joined_table;
|
NameSet required_columns_from_joined_table;
|
||||||
|
|
||||||
/// Columns which will be added to block, possible including some columns from right join key.
|
/// Columns which will be added to block, possible including some columns from right join key.
|
||||||
JoinedColumnsList columns_added_by_join;
|
JoinedColumnsList columns_added_by_join;
|
||||||
/// Such columns will be copied from left join keys during join.
|
/// Such columns will be copied from left join keys during join.
|
||||||
@ -267,11 +268,15 @@ private:
|
|||||||
/// Actions which need to be calculated on joined block.
|
/// Actions which need to be calculated on joined block.
|
||||||
ExpressionActionsPtr joined_block_actions;
|
ExpressionActionsPtr joined_block_actions;
|
||||||
|
|
||||||
void createJoinedBlockActions(const ASTSelectQuery * select_query_with_join, const Context & context);
|
void createJoinedBlockActions(const NamesAndTypesList & source_columns,
|
||||||
|
const ASTSelectQuery * select_query_with_join,
|
||||||
|
const Context & context);
|
||||||
|
|
||||||
NamesAndTypesList getColumnsAddedByJoin() const;
|
NamesAndTypesList getColumnsAddedByJoin() const;
|
||||||
|
|
||||||
NamesAndTypesList getColumnsFromJoinedTable(const Context & context, const ASTSelectQuery * select_query_with_join);
|
const JoinedColumnsList & getColumnsFromJoinedTable(const NamesAndTypesList & source_columns,
|
||||||
|
const Context & context,
|
||||||
|
const ASTSelectQuery * select_query_with_join);
|
||||||
};
|
};
|
||||||
|
|
||||||
AnalyzedJoin analyzed_join;
|
AnalyzedJoin analyzed_join;
|
||||||
|
@ -524,16 +524,20 @@ namespace
|
|||||||
struct Adder<ASTTableJoin::Kind::Left, ASTTableJoin::Strictness::Any, Map>
|
struct Adder<ASTTableJoin::Kind::Left, ASTTableJoin::Strictness::Any, Map>
|
||||||
{
|
{
|
||||||
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
|
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
|
||||||
size_t /*i*/, IColumn::Filter * /*filter*/, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/,
|
size_t i, IColumn::Filter * filter, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/,
|
||||||
const std::vector<size_t> & right_indexes)
|
const std::vector<size_t> & right_indexes)
|
||||||
{
|
{
|
||||||
|
(*filter)[i] = 1;
|
||||||
|
|
||||||
for (size_t j = 0; j < num_columns_to_add; ++j)
|
for (size_t j = 0; j < num_columns_to_add; ++j)
|
||||||
added_columns[j]->insertFrom(*it->second.block->getByPosition(right_indexes[j]).column.get(), it->second.row_num);
|
added_columns[j]->insertFrom(*it->second.block->getByPosition(right_indexes[j]).column.get(), it->second.row_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
|
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
|
||||||
size_t /*i*/, IColumn::Filter * /*filter*/, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/)
|
size_t i, IColumn::Filter * filter, IColumn::Offset & /*current_offset*/, IColumn::Offsets * /*offsets*/)
|
||||||
{
|
{
|
||||||
|
(*filter)[i] = 0;
|
||||||
|
|
||||||
for (size_t j = 0; j < num_columns_to_add; ++j)
|
for (size_t j = 0; j < num_columns_to_add; ++j)
|
||||||
added_columns[j]->insertDefault();
|
added_columns[j]->insertDefault();
|
||||||
}
|
}
|
||||||
@ -563,9 +567,11 @@ namespace
|
|||||||
struct Adder<KIND, ASTTableJoin::Strictness::All, Map>
|
struct Adder<KIND, ASTTableJoin::Strictness::All, Map>
|
||||||
{
|
{
|
||||||
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
|
static void addFound(const typename Map::const_iterator & it, size_t num_columns_to_add, MutableColumns & added_columns,
|
||||||
size_t i, IColumn::Filter * /*filter*/, IColumn::Offset & current_offset, IColumn::Offsets * offsets,
|
size_t i, IColumn::Filter * filter, IColumn::Offset & current_offset, IColumn::Offsets * offsets,
|
||||||
const std::vector<size_t> & right_indexes)
|
const std::vector<size_t> & right_indexes)
|
||||||
{
|
{
|
||||||
|
(*filter)[i] = 1;
|
||||||
|
|
||||||
size_t rows_joined = 0;
|
size_t rows_joined = 0;
|
||||||
for (auto current = &static_cast<const typename Map::mapped_type::Base_t &>(it->second); current != nullptr; current = current->next)
|
for (auto current = &static_cast<const typename Map::mapped_type::Base_t &>(it->second); current != nullptr; current = current->next)
|
||||||
{
|
{
|
||||||
@ -580,8 +586,10 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
|
static void addNotFound(size_t num_columns_to_add, MutableColumns & added_columns,
|
||||||
size_t i, IColumn::Filter * /*filter*/, IColumn::Offset & current_offset, IColumn::Offsets * offsets)
|
size_t i, IColumn::Filter * filter, IColumn::Offset & current_offset, IColumn::Offsets * offsets)
|
||||||
{
|
{
|
||||||
|
(*filter)[i] = 0;
|
||||||
|
|
||||||
if (KIND == ASTTableJoin::Kind::Inner)
|
if (KIND == ASTTableJoin::Kind::Inner)
|
||||||
{
|
{
|
||||||
(*offsets)[i] = current_offset;
|
(*offsets)[i] = current_offset;
|
||||||
@ -741,8 +749,8 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
|
|||||||
/// Used with ANY INNER JOIN
|
/// Used with ANY INNER JOIN
|
||||||
std::unique_ptr<IColumn::Filter> filter;
|
std::unique_ptr<IColumn::Filter> filter;
|
||||||
|
|
||||||
if ((kind == ASTTableJoin::Kind::Inner || kind == ASTTableJoin::Kind::Right) && strictness == ASTTableJoin::Strictness::Any)
|
bool filter_left_keys = (kind == ASTTableJoin::Kind::Inner || kind == ASTTableJoin::Kind::Right) && strictness == ASTTableJoin::Strictness::Any;
|
||||||
filter = std::make_unique<IColumn::Filter>(rows);
|
filter = std::make_unique<IColumn::Filter>(rows);
|
||||||
|
|
||||||
/// Used with ALL ... JOIN
|
/// Used with ALL ... JOIN
|
||||||
IColumn::Offset current_offset = 0;
|
IColumn::Offset current_offset = 0;
|
||||||
@ -771,14 +779,11 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
|
|||||||
block.insert(ColumnWithTypeAndName(std::move(added_columns[i]), added_type_name[i].first, added_type_name[i].second));
|
block.insert(ColumnWithTypeAndName(std::move(added_columns[i]), added_type_name[i].first, added_type_name[i].second));
|
||||||
|
|
||||||
/// If ANY INNER | RIGHT JOIN - filter all the columns except the new ones.
|
/// If ANY INNER | RIGHT JOIN - filter all the columns except the new ones.
|
||||||
if (filter)
|
if (filter_left_keys)
|
||||||
for (size_t i = 0; i < existing_columns; ++i)
|
for (size_t i = 0; i < existing_columns; ++i)
|
||||||
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->filter(*filter, -1);
|
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->filter(*filter, -1);
|
||||||
|
|
||||||
/// If ALL ... JOIN - we replicate all the columns except the new ones.
|
ColumnUInt64::Ptr mapping;
|
||||||
if (offsets_to_replicate)
|
|
||||||
for (size_t i = 0; i < existing_columns; ++i)
|
|
||||||
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->replicate(*offsets_to_replicate);
|
|
||||||
|
|
||||||
/// Add join key columns from right block if they has different name.
|
/// Add join key columns from right block if they has different name.
|
||||||
for (size_t i = 0; i < key_names_right.size(); ++i)
|
for (size_t i = 0; i < key_names_right.size(); ++i)
|
||||||
@ -789,9 +794,34 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
|
|||||||
if (needed_key_names_right.count(right_name) && !block.has(right_name))
|
if (needed_key_names_right.count(right_name) && !block.has(right_name))
|
||||||
{
|
{
|
||||||
const auto & col = block.getByName(left_name);
|
const auto & col = block.getByName(left_name);
|
||||||
block.insert({col.column, col.type, right_name});
|
auto column = col.column;
|
||||||
|
if (!filter_left_keys)
|
||||||
|
{
|
||||||
|
if (!mapping)
|
||||||
|
{
|
||||||
|
auto mut_mapping = ColumnUInt64::create(column->size());
|
||||||
|
auto & data = mut_mapping->getData();
|
||||||
|
size_t size = column->size();
|
||||||
|
for (size_t j = 0; j < size; ++j)
|
||||||
|
data[j] = (*filter)[j] ? j : size;
|
||||||
|
|
||||||
|
mapping = std::move(mut_mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mut_column = (*std::move(column)).mutate();
|
||||||
|
mut_column->insertDefault();
|
||||||
|
column = mut_column->index(*mapping, 0);
|
||||||
|
}
|
||||||
|
block.insert({column, col.type, right_name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If ALL ... JOIN - we replicate all the columns except the new ones.
|
||||||
|
if (offsets_to_replicate)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < existing_columns; ++i)
|
||||||
|
block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->replicate(*offsets_to_replicate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,6 +133,9 @@ std::pair<String, String> getDatabaseAndTableNameFromIdentifier(const ASTIdentif
|
|||||||
|
|
||||||
String DatabaseAndTableWithAlias::getQualifiedNamePrefix() const
|
String DatabaseAndTableWithAlias::getQualifiedNamePrefix() const
|
||||||
{
|
{
|
||||||
|
if (alias.empty() && table.empty())
|
||||||
|
return "";
|
||||||
|
|
||||||
return (!alias.empty() ? alias : (database + '.' + table)) + '.';
|
return (!alias.empty() ? alias : (database + '.' + table)) + '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,3 +32,4 @@
|
|||||||
1
|
1
|
||||||
-------Push to having expression, need check.-------
|
-------Push to having expression, need check.-------
|
||||||
-------Compatibility test-------
|
-------Compatibility test-------
|
||||||
|
1 2000-01-01 2000-01-01 1 test string 1 1
|
||||||
|
@ -70,7 +70,7 @@ SELECT * FROM (SELECT toUInt64(b), sum(id) AS b FROM test.test) WHERE `toUInt64(
|
|||||||
SELECT * FROM (SELECT toUInt64(table_alias.b) AS a, sum(id) AS b FROM test.test AS table_alias) AS outer_table_alias WHERE outer_table_alias.b = 3; -- { serverError 277 }
|
SELECT * FROM (SELECT toUInt64(table_alias.b) AS a, sum(id) AS b FROM test.test AS table_alias) AS outer_table_alias WHERE outer_table_alias.b = 3; -- { serverError 277 }
|
||||||
|
|
||||||
SELECT '-------Compatibility test-------';
|
SELECT '-------Compatibility test-------';
|
||||||
SELECT * FROM (SELECT 1 AS id, toDate('2000-01-01') AS date FROM system.numbers LIMIT 1) ANY LEFT JOIN (SELECT * FROM test.test) AS b USING date WHERE b.date = toDate('2000-01-01'); -- {serverError 47}
|
SELECT * FROM (SELECT toInt8(1) AS id, toDate('2000-01-01') AS date FROM system.numbers LIMIT 1) ANY LEFT JOIN (SELECT * FROM test.test) AS b USING date, id WHERE b.date = toDate('2000-01-01');
|
||||||
|
|
||||||
DROP TABLE IF EXISTS test.test;
|
DROP TABLE IF EXISTS test.test;
|
||||||
DROP TABLE IF EXISTS test.test_view;
|
DROP TABLE IF EXISTS test.test_view;
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
1 1 1 2
|
||||||
|
1 2 1 2
|
||||||
|
2 3 0 0
|
20
dbms/tests/queries/0_stateless/00725_join_on_bug_1.sql
Normal file
20
dbms/tests/queries/0_stateless/00725_join_on_bug_1.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
create database if not exists test;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS test.a1;
|
||||||
|
DROP TABLE IF EXISTS test.a2;
|
||||||
|
|
||||||
|
CREATE TABLE test.a1(a UInt8, b UInt8) ENGINE=Memory;
|
||||||
|
CREATE TABLE test.a2(a UInt8, b UInt8) ENGINE=Memory;
|
||||||
|
|
||||||
|
INSERT INTO test.a1 VALUES (1, 1);
|
||||||
|
INSERT INTO test.a1 VALUES (1, 2);
|
||||||
|
INSERT INTO test.a1 VALUES (2, 3);
|
||||||
|
INSERT INTO test.a2 VALUES (1, 2);
|
||||||
|
INSERT INTO test.a2 VALUES (1, 3);
|
||||||
|
INSERT INTO test.a2 VALUES (1, 4);
|
||||||
|
|
||||||
|
SELECT * FROM test.a1 as a left JOIN test.a2 as b on a.a=b.a ORDER BY b SETTINGS join_default_strictness='ANY';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS test.a1;
|
||||||
|
DROP TABLE IF EXISTS test.a2;
|
||||||
|
|
17
dbms/tests/queries/0_stateless/00725_join_on_bug_2.reference
Normal file
17
dbms/tests/queries/0_stateless/00725_join_on_bug_2.reference
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
||||||
|
-
|
||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
||||||
|
-
|
||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
||||||
|
-
|
||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
||||||
|
-
|
||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
||||||
|
-
|
||||||
|
1 1 1 1
|
||||||
|
2 2 0 0
|
25
dbms/tests/queries/0_stateless/00725_join_on_bug_2.sql
Normal file
25
dbms/tests/queries/0_stateless/00725_join_on_bug_2.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
create database if not exists test;
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.s;
|
||||||
|
|
||||||
|
create table test.t(a Int64, b Int64) engine = TinyLog;
|
||||||
|
insert into test.t values(1,1);
|
||||||
|
insert into test.t values(2,2);
|
||||||
|
create table test.s(a Int64, b Int64) engine = TinyLog;
|
||||||
|
insert into test.s values(1,1);
|
||||||
|
|
||||||
|
select a, b, s_a, s_b from test.t all left join (select a,b,a s_a, b s_b from test.s) using (a,b);
|
||||||
|
select '-';
|
||||||
|
select * from test.t all left join test.s using (a,b);
|
||||||
|
select '-';
|
||||||
|
select a,b,s_a,s_b from test.t all left join (select a, b, a s_a, b s_b from test.s) s on (s.a = t.a and s.b = t.b);
|
||||||
|
select '-';
|
||||||
|
select * from test.t all left join (select a s_a, b s_b from test.s) on (s_a = t.a and s_b = t.b);
|
||||||
|
select '-';
|
||||||
|
select a,b,s_a,s_b from test.t all left join (select a,b, a s_a, b s_b from test.s) on (s_a = t.a and s_b = t.b);
|
||||||
|
select '-';
|
||||||
|
select t.*, s.* from test.t all left join test.s on (s.a = t.a and s.b = t.b);
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.s;
|
@ -0,0 +1,2 @@
|
|||||||
|
1 1 1 1 1
|
||||||
|
2 2 0 0 0
|
16
dbms/tests/queries/0_stateless/00725_join_on_bug_3.sql
Normal file
16
dbms/tests/queries/0_stateless/00725_join_on_bug_3.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
create database if not exists test;
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.z;
|
||||||
|
|
||||||
|
create table test.t(a Int64, b Int64) engine = TinyLog;
|
||||||
|
insert into test.t values(1,1);
|
||||||
|
insert into test.t values(2,2);
|
||||||
|
create table test.z(c Int64, d Int64, e Int64) engine = TinyLog;
|
||||||
|
insert into test.z values(1,1,1);
|
||||||
|
|
||||||
|
select * from test.t all left join test.z on (z.c = t.a and z.d = t.b);
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.z;
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
2 2 b
|
15
dbms/tests/queries/0_stateless/00725_join_on_bug_4.sql
Normal file
15
dbms/tests/queries/0_stateless/00725_join_on_bug_4.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
create database if not exists test;
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.s;
|
||||||
|
|
||||||
|
create table test.t(a Int64, b Int64, c String) engine = TinyLog;
|
||||||
|
insert into test.t values(1,1,'a'),(2,2,'b');
|
||||||
|
create table test.s(a Int64, b Int64, c String) engine = TinyLog;
|
||||||
|
insert into test.s values(1,1,'a');
|
||||||
|
|
||||||
|
|
||||||
|
select t.* from test.t all left join test.s on (s.a = t.a and s.b = t.b) where s.a = 0 and s.b = 0;
|
||||||
|
|
||||||
|
drop table if exists test.t;
|
||||||
|
drop table if exists test.s;
|
Loading…
Reference in New Issue
Block a user