Merge pull request #3270 from yandex/left-join-right-keys-fix

Left join right keys fix
This commit is contained in:
alexey-milovidov 2018-10-07 23:26:40 +03:00 committed by GitHub
commit 36c3feea80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 265 additions and 78 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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)) + '.';
} }

View File

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

View File

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

View File

@ -0,0 +1,3 @@
1 1 1 2
1 2 1 2
2 3 0 0

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

View 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

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

View File

@ -0,0 +1,2 @@
1 1 1 1 1
2 2 0 0 0

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

View File

@ -0,0 +1 @@
2 2 b

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