mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-18 05:32:52 +00:00
Fix converting join on keys, move actions into TableJoin
This commit is contained in:
parent
3a7eddcf3a
commit
456414beea
@ -95,27 +95,6 @@ bool allowEarlyConstantFolding(const ActionsDAG & actions, const Settings & sett
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns converting actions for tables that need to be performed before join
|
|
||||||
ActionsDAGPtr createJoinConvertingActions(const ColumnsWithTypeAndName & cols_src,
|
|
||||||
const TableJoin::NameToTypeMap & mapping,
|
|
||||||
bool has_using,
|
|
||||||
NameToNameMap & renames)
|
|
||||||
{
|
|
||||||
ColumnsWithTypeAndName cols_dst = cols_src;
|
|
||||||
for (auto & col : cols_dst)
|
|
||||||
{
|
|
||||||
if (auto it = mapping.find(col.name); it != mapping.end())
|
|
||||||
{
|
|
||||||
col.type = it->second;
|
|
||||||
col.column = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ActionsDAG::makeConvertingActions(
|
|
||||||
cols_src, cols_dst, ActionsDAG::MatchColumnsMode::Name, true, !has_using, &renames);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sanitizeBlock(Block & block, bool throw_if_cannot_create_column)
|
bool sanitizeBlock(Block & block, bool throw_if_cannot_create_column)
|
||||||
@ -741,15 +720,14 @@ bool SelectQueryExpressionAnalyzer::appendJoinLeftKeys(ExpressionActionsChain &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JoinPtr SelectQueryExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, ActionsDAGPtr & left_actions)
|
JoinPtr SelectQueryExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain)
|
||||||
{
|
{
|
||||||
const ColumnsWithTypeAndName & left_sample_columns = chain.getLastStep().getResultColumns();
|
const ColumnsWithTypeAndName & left_sample_columns = chain.getLastStep().getResultColumns();
|
||||||
JoinPtr table_join = makeTableJoin(*syntax->ast_join, left_sample_columns, left_actions);
|
JoinPtr table_join = makeTableJoin(*syntax->ast_join, left_sample_columns);
|
||||||
|
|
||||||
if (syntax->analyzed_join->needConvert())
|
if (syntax->analyzed_join->needConvert())
|
||||||
{
|
{
|
||||||
assert(left_actions);
|
chain.steps.push_back(std::make_unique<ExpressionActionsChain::ExpressionActionsStep>(syntax->analyzed_join->leftConvertingActions()));
|
||||||
chain.steps.push_back(std::make_unique<ExpressionActionsChain::ExpressionActionsStep>(left_actions));
|
|
||||||
chain.addStep();
|
chain.addStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,7 +798,7 @@ static std::shared_ptr<IJoin> makeJoin(std::shared_ptr<TableJoin> analyzed_join,
|
|||||||
}
|
}
|
||||||
|
|
||||||
JoinPtr SelectQueryExpressionAnalyzer::makeTableJoin(
|
JoinPtr SelectQueryExpressionAnalyzer::makeTableJoin(
|
||||||
const ASTTablesInSelectQueryElement & join_element, const ColumnsWithTypeAndName & left_sample_columns, ActionsDAGPtr & left_actions)
|
const ASTTablesInSelectQueryElement & join_element, const ColumnsWithTypeAndName & left_sample_columns)
|
||||||
{
|
{
|
||||||
/// Two JOINs are not supported with the same subquery, but different USINGs.
|
/// Two JOINs are not supported with the same subquery, but different USINGs.
|
||||||
auto join_hash = join_element.getTreeHash();
|
auto join_hash = join_element.getTreeHash();
|
||||||
@ -859,27 +837,9 @@ JoinPtr SelectQueryExpressionAnalyzer::makeTableJoin(
|
|||||||
subquery_for_join.addJoinActions(joined_block_actions); /// changes subquery_for_join.sample_block inside
|
subquery_for_join.addJoinActions(joined_block_actions); /// changes subquery_for_join.sample_block inside
|
||||||
|
|
||||||
const ColumnsWithTypeAndName & right_sample_columns = subquery_for_join.sample_block.getColumnsWithTypeAndName();
|
const ColumnsWithTypeAndName & right_sample_columns = subquery_for_join.sample_block.getColumnsWithTypeAndName();
|
||||||
/// For `USING` we already inferred common type an syntax analyzer stage
|
bool need_convert = syntax->analyzed_join->applyJoinKeyConvert(left_sample_columns, right_sample_columns);
|
||||||
if (!syntax->analyzed_join->hasUsing())
|
if (need_convert)
|
||||||
syntax->analyzed_join->inferJoinKeyCommonType(left_sample_columns, right_sample_columns);
|
subquery_for_join.addJoinActions(std::make_shared<ExpressionActions>(syntax->analyzed_join->rightConvertingActions()));
|
||||||
if (syntax->analyzed_join->needConvert())
|
|
||||||
{
|
|
||||||
NameToNameMap left_column_rename;
|
|
||||||
left_actions = createJoinConvertingActions(left_sample_columns,
|
|
||||||
syntax->analyzed_join->getLeftMapping(),
|
|
||||||
syntax->analyzed_join->hasUsing(),
|
|
||||||
left_column_rename);
|
|
||||||
syntax->analyzed_join->applyKeyColumnRename(left_column_rename, TableJoin::TableSide::Left);
|
|
||||||
|
|
||||||
NameToNameMap right_renames;
|
|
||||||
auto right_actions = createJoinConvertingActions(right_sample_columns,
|
|
||||||
syntax->analyzed_join->getRightMapping(),
|
|
||||||
syntax->analyzed_join->hasUsing(),
|
|
||||||
right_renames);
|
|
||||||
syntax->analyzed_join->applyKeyColumnRename(right_renames, TableJoin::TableSide::Right);
|
|
||||||
|
|
||||||
subquery_for_join.addJoinActions(std::make_shared<ExpressionActions>(right_actions));
|
|
||||||
}
|
|
||||||
|
|
||||||
subquery_for_join.join = makeJoin(syntax->analyzed_join, subquery_for_join.sample_block, context);
|
subquery_for_join.join = makeJoin(syntax->analyzed_join, subquery_for_join.sample_block, context);
|
||||||
|
|
||||||
@ -1476,7 +1436,8 @@ ExpressionAnalysisResult::ExpressionAnalysisResult(
|
|||||||
{
|
{
|
||||||
query_analyzer.appendJoinLeftKeys(chain, only_types || !first_stage);
|
query_analyzer.appendJoinLeftKeys(chain, only_types || !first_stage);
|
||||||
before_join = chain.getLastActions();
|
before_join = chain.getLastActions();
|
||||||
join = query_analyzer.appendJoin(chain, converting_join_columns);
|
join = query_analyzer.appendJoin(chain);
|
||||||
|
converting_join_columns = query_analyzer.analyzedJoin().leftConvertingActions();
|
||||||
chain.addStep();
|
chain.addStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,8 +317,7 @@ private:
|
|||||||
|
|
||||||
JoinPtr makeTableJoin(
|
JoinPtr makeTableJoin(
|
||||||
const ASTTablesInSelectQueryElement & join_element,
|
const ASTTablesInSelectQueryElement & join_element,
|
||||||
const ColumnsWithTypeAndName & left_sample_columns,
|
const ColumnsWithTypeAndName & left_sample_columns);
|
||||||
ActionsDAGPtr & left_actions);
|
|
||||||
|
|
||||||
const ASTSelectQuery * getAggregatingQuery() const;
|
const ASTSelectQuery * getAggregatingQuery() const;
|
||||||
|
|
||||||
@ -339,7 +338,7 @@ private:
|
|||||||
/// Before aggregation:
|
/// Before aggregation:
|
||||||
ArrayJoinActionPtr appendArrayJoin(ExpressionActionsChain & chain, ActionsDAGPtr & before_array_join, bool only_types);
|
ArrayJoinActionPtr appendArrayJoin(ExpressionActionsChain & chain, ActionsDAGPtr & before_array_join, bool only_types);
|
||||||
bool appendJoinLeftKeys(ExpressionActionsChain & chain, bool only_types);
|
bool appendJoinLeftKeys(ExpressionActionsChain & chain, bool only_types);
|
||||||
JoinPtr appendJoin(ExpressionActionsChain & chain, ActionsDAGPtr & left_actions);
|
JoinPtr appendJoin(ExpressionActionsChain & chain);
|
||||||
/// Add preliminary rows filtration. Actions are created in other expression analyzer to prevent any possible alias injection.
|
/// Add preliminary rows filtration. Actions are created in other expression analyzer to prevent any possible alias injection.
|
||||||
void appendPreliminaryFilter(ExpressionActionsChain & chain, ActionsDAGPtr actions_dag, String column_name);
|
void appendPreliminaryFilter(ExpressionActionsChain & chain, ActionsDAGPtr actions_dag, String column_name);
|
||||||
/// remove_filter is set in ExpressionActionsChain::finalize();
|
/// remove_filter is set in ExpressionActionsChain::finalize();
|
||||||
|
@ -338,18 +338,29 @@ bool TableJoin::allowDictJoin(const String & dict_key, const Block & sample_bloc
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TableJoin::inferJoinKeyCommonType(const ColumnsWithTypeAndName & left, const ColumnsWithTypeAndName & right)
|
bool TableJoin::applyJoinKeyConvert(const ColumnsWithTypeAndName & left_sample_columns, const ColumnsWithTypeAndName & right_sample_columns)
|
||||||
{
|
{
|
||||||
NamesAndTypesList left_list;
|
bool need_convert = needConvert();
|
||||||
NamesAndTypesList right_list;
|
if (!need_convert && !hasUsing())
|
||||||
|
{
|
||||||
|
/// For `USING` we already inferred common type an syntax analyzer stage
|
||||||
|
NamesAndTypesList left_list;
|
||||||
|
NamesAndTypesList right_list;
|
||||||
|
for (const auto & col : left_sample_columns)
|
||||||
|
left_list.emplace_back(col.name, col.type);
|
||||||
|
for (const auto & col : right_sample_columns)
|
||||||
|
right_list.emplace_back(col.name, col.type);
|
||||||
|
|
||||||
for (const auto & col : left)
|
need_convert = inferJoinKeyCommonType(left_list, right_list);
|
||||||
left_list.emplace_back(col.name, col.type);
|
}
|
||||||
|
|
||||||
for (const auto & col : right)
|
if (need_convert)
|
||||||
right_list.emplace_back(col.name, col.type);
|
{
|
||||||
|
left_converting_actions = applyKeyConvertToTable(left_sample_columns, left_type_map, key_names_left);
|
||||||
|
right_converting_actions = applyKeyConvertToTable(right_sample_columns, right_type_map, key_names_right);
|
||||||
|
}
|
||||||
|
|
||||||
return inferJoinKeyCommonType(left_list, right_list);
|
return need_convert;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TableJoin::inferJoinKeyCommonType(const NamesAndTypesList & left, const NamesAndTypesList & right)
|
bool TableJoin::inferJoinKeyCommonType(const NamesAndTypesList & left, const NamesAndTypesList & right)
|
||||||
@ -403,17 +414,33 @@ bool TableJoin::inferJoinKeyCommonType(const NamesAndTypesList & left, const Nam
|
|||||||
return !left_type_map.empty();
|
return !left_type_map.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableJoin::applyKeyColumnRename(const NameToNameMap & name_map, TableJoin::TableSide side)
|
ActionsDAGPtr
|
||||||
|
TableJoin::applyKeyConvertToTable(const ColumnsWithTypeAndName & cols_src, const NameToTypeMap & type_mapping, Names & names_to_rename)
|
||||||
{
|
{
|
||||||
assert(!hasUsing() || name_map.empty());
|
ColumnsWithTypeAndName cols_dst = cols_src;
|
||||||
|
for (auto & col : cols_dst)
|
||||||
Names & names = side == TableSide::Left ? key_names_left : key_names_right;
|
|
||||||
for (auto & name : names)
|
|
||||||
{
|
{
|
||||||
const auto it = name_map.find(name);
|
if (auto it = type_mapping.find(col.name); it != type_mapping.end())
|
||||||
if (it != name_map.end())
|
{
|
||||||
|
col.type = it->second;
|
||||||
|
col.column = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NameToNameMap key_column_rename;
|
||||||
|
/// Returns converting actions for tables that need to be performed before join
|
||||||
|
auto dag = ActionsDAG::makeConvertingActions(
|
||||||
|
cols_src, cols_dst, ActionsDAG::MatchColumnsMode::Name, true, !hasUsing(), &key_column_rename);
|
||||||
|
|
||||||
|
assert(!hasUsing() || key_column_rename.empty());
|
||||||
|
|
||||||
|
for (auto & name : names_to_rename)
|
||||||
|
{
|
||||||
|
const auto it = key_column_rename.find(name);
|
||||||
|
if (it != key_column_rename.end())
|
||||||
name = it->second;
|
name = it->second;
|
||||||
}
|
}
|
||||||
|
return dag;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,9 @@ private:
|
|||||||
NameToTypeMap left_type_map;
|
NameToTypeMap left_type_map;
|
||||||
NameToTypeMap right_type_map;
|
NameToTypeMap right_type_map;
|
||||||
|
|
||||||
|
ActionsDAGPtr left_converting_actions;
|
||||||
|
ActionsDAGPtr right_converting_actions;
|
||||||
|
|
||||||
/// Name -> original name. Names are the same as in columns_from_joined_table list.
|
/// Name -> original name. Names are the same as in columns_from_joined_table list.
|
||||||
std::unordered_map<String, String> original_names;
|
std::unordered_map<String, String> original_names;
|
||||||
/// Original name -> name. Only renamed columns.
|
/// Original name -> name. Only renamed columns.
|
||||||
@ -91,13 +94,12 @@ private:
|
|||||||
|
|
||||||
Names requiredJoinedNames() const;
|
Names requiredJoinedNames() const;
|
||||||
|
|
||||||
public:
|
/// Create converting actions and change key column names if required
|
||||||
enum class TableSide
|
ActionsDAGPtr applyKeyConvertToTable(const ColumnsWithTypeAndName & cols_src,
|
||||||
{
|
const NameToTypeMap & type_mapping,
|
||||||
Left,
|
Names & names_to_rename);
|
||||||
Right
|
|
||||||
};
|
|
||||||
|
|
||||||
|
public:
|
||||||
TableJoin() = default;
|
TableJoin() = default;
|
||||||
TableJoin(const Settings &, VolumePtr tmp_volume);
|
TableJoin(const Settings &, VolumePtr tmp_volume);
|
||||||
|
|
||||||
@ -157,19 +159,19 @@ public:
|
|||||||
bool rightBecomeNullable(const DataTypePtr & column_type) const;
|
bool rightBecomeNullable(const DataTypePtr & column_type) const;
|
||||||
void addJoinedColumn(const NameAndTypePair & joined_column);
|
void addJoinedColumn(const NameAndTypePair & joined_column);
|
||||||
|
|
||||||
void applyKeyColumnRename(const NameToNameMap & name_map, TableSide side);
|
|
||||||
|
|
||||||
void addJoinedColumnsAndCorrectTypes(NamesAndTypesList & names_and_types, bool correct_nullability = true) const;
|
void addJoinedColumnsAndCorrectTypes(NamesAndTypesList & names_and_types, bool correct_nullability = true) const;
|
||||||
void addJoinedColumnsAndCorrectTypes(ColumnsWithTypeAndName & columns, bool correct_nullability = true) const;
|
void addJoinedColumnsAndCorrectTypes(ColumnsWithTypeAndName & columns, bool correct_nullability = true) const;
|
||||||
|
|
||||||
/// Calculates common supertypes for corresponding join key columns.
|
/// Calculates common supertypes for corresponding join key columns.
|
||||||
bool inferJoinKeyCommonType(const NamesAndTypesList & left, const NamesAndTypesList & right);
|
bool inferJoinKeyCommonType(const NamesAndTypesList & left, const NamesAndTypesList & right);
|
||||||
bool inferJoinKeyCommonType(const ColumnsWithTypeAndName & left, const ColumnsWithTypeAndName & right);
|
///
|
||||||
|
bool applyJoinKeyConvert(const ColumnsWithTypeAndName & left_sample_columns, const ColumnsWithTypeAndName & right_sample_columns);
|
||||||
|
|
||||||
bool needConvert() const { return !left_type_map.empty(); }
|
bool needConvert() const { return !left_type_map.empty(); }
|
||||||
/// Key columns should be converted according to this mapping before join.
|
|
||||||
const NameToTypeMap & getLeftMapping() const { return left_type_map; }
|
/// Key columns should be converted before join.
|
||||||
const NameToTypeMap & getRightMapping() const { return right_type_map; }
|
ActionsDAGPtr leftConvertingActions() const { return left_converting_actions; }
|
||||||
|
ActionsDAGPtr rightConvertingActions() const { return right_converting_actions; }
|
||||||
|
|
||||||
void setAsofInequality(ASOF::Inequality inequality) { asof_inequality = inequality; }
|
void setAsofInequality(ASOF::Inequality inequality) { asof_inequality = inequality; }
|
||||||
ASOF::Inequality getAsofInequality() { return asof_inequality; }
|
ASOF::Inequality getAsofInequality() { return asof_inequality; }
|
||||||
|
Loading…
Reference in New Issue
Block a user