mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Fix converting right key type in join using
This commit is contained in:
parent
4c36cd1737
commit
f5b98015a8
@ -728,12 +728,13 @@ SelectQueryExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, const
|
||||
{
|
||||
JoinCommon::JoinConvertActions converting_actions;
|
||||
JoinPtr table_join = makeTableJoin(*syntax->ast_join, sample_block, converting_actions);
|
||||
|
||||
if (converting_actions.first)
|
||||
if (converting_actions.needConvert())
|
||||
{
|
||||
before_join_dag = ActionsDAG::merge(std::move(*before_join_dag->clone()), std::move(*converting_actions.first->clone()));
|
||||
syntax->analyzed_join->setConvertedRightType(converting_actions.right_target_types);
|
||||
|
||||
chain.steps.push_back(std::make_unique<ExpressionActionsChain::ExpressionActionsStep>(converting_actions.first));
|
||||
before_join_dag = ActionsDAG::merge(std::move(*before_join_dag->clone()), std::move(*converting_actions.left_actions->clone()));
|
||||
|
||||
chain.steps.push_back(std::make_unique<ExpressionActionsChain::ExpressionActionsStep>(converting_actions.left_actions));
|
||||
chain.addStep();
|
||||
}
|
||||
|
||||
@ -849,8 +850,8 @@ JoinPtr SelectQueryExpressionAnalyzer::makeTableJoin(const ASTTablesInSelectQuer
|
||||
left_sample_block, syntax->analyzed_join->keyNamesLeft(),
|
||||
right_sample_block, syntax->analyzed_join->keyNamesRight(),
|
||||
has_using);
|
||||
if (converting_actions.second)
|
||||
subquery_for_join.addJoinActions(std::make_shared<ExpressionActions>(converting_actions.second));
|
||||
if (converting_actions.needConvert())
|
||||
subquery_for_join.addJoinActions(std::make_shared<ExpressionActions>(converting_actions.right_actions));
|
||||
|
||||
subquery_for_join.join = makeJoin(syntax->analyzed_join, subquery_for_join.sample_block, context);
|
||||
|
||||
|
@ -241,9 +241,15 @@ void TableJoin::addJoinedColumnsAndCorrectNullability(ColumnsWithTypeAndName & c
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<String, DataTypePtr> type_map;
|
||||
for (const auto & [name, type] : converted_right_types)
|
||||
type_map[name] = type;
|
||||
|
||||
for (const auto & col : columns_added_by_join)
|
||||
{
|
||||
auto res_type = col.type;
|
||||
if (const auto it = type_map.find(col.name); it != type_map.end())
|
||||
res_type = it->second;
|
||||
|
||||
if (rightBecomeNullable(res_type))
|
||||
res_type = makeNullable(res_type);
|
||||
|
@ -68,10 +68,12 @@ class TableJoin
|
||||
NamesAndTypesList columns_from_joined_table;
|
||||
/// Columns will be added to block by JOIN. It's a subset of columns_from_joined_table with corrected Nullability
|
||||
NamesAndTypesList columns_added_by_join;
|
||||
/// Columns from right table that requres type conversion
|
||||
NamesAndTypesList converted_right_types;
|
||||
|
||||
/// Name -> original name. Names are the same as in columns_from_joined_table list.
|
||||
std::unordered_map<String, String> original_names;
|
||||
/// Original name -> name. Only ranamed columns.
|
||||
/// Original name -> name. Only renamed columns.
|
||||
std::unordered_map<String, String> renames;
|
||||
|
||||
VolumePtr tmp_volume;
|
||||
@ -124,6 +126,7 @@ public:
|
||||
|
||||
bool hasUsing() const { return table_join.using_expression_list != nullptr; }
|
||||
bool hasOn() const { return table_join.on_expression != nullptr; }
|
||||
bool hasJoinedStorage() const { return joined_storage != nullptr; }
|
||||
|
||||
NameSet getQualifiedColumnsSet() const;
|
||||
NamesWithAliases getNamesWithAliases(const NameSet & required_columns) const;
|
||||
@ -137,6 +140,7 @@ public:
|
||||
bool rightBecomeNullable(const DataTypePtr & column_type) const;
|
||||
void addJoinedColumn(const NameAndTypePair & joined_column);
|
||||
void addJoinedColumnsAndCorrectNullability(ColumnsWithTypeAndName & columns) const;
|
||||
void setConvertedRightType(NamesAndTypesList columns) { converted_right_types = columns; }
|
||||
|
||||
void setAsofInequality(ASOF::Inequality inequality) { asof_inequality = inequality; }
|
||||
ASOF::Inequality getAsofInequality() { return asof_inequality; }
|
||||
|
@ -302,6 +302,8 @@ JoinConvertActions columnsNeedConvert(const Block & left_block, const Names & le
|
||||
if (!has_using)
|
||||
return {};
|
||||
|
||||
JoinConvertActions actions;
|
||||
|
||||
Block left_block_dst = left_block;
|
||||
Block right_block_dst = right_block;
|
||||
|
||||
@ -338,6 +340,9 @@ JoinConvertActions columnsNeedConvert(const Block & left_block, const Names & le
|
||||
+ right_keys[i] + ": " + rtype->getName() + " at right",
|
||||
ErrorCodes::TYPE_MISMATCH);
|
||||
}
|
||||
actions.left_target_types.emplace_back(left_keys[i], supertype);
|
||||
actions.right_target_types.emplace_back(right_keys[i], supertype);
|
||||
|
||||
auto & lcol_dst = left_block_dst.getByName(left_keys[i]);
|
||||
auto & rcol_dst = right_block_dst.getByName(right_keys[i]);
|
||||
lcol_dst.column = rcol_dst.column = nullptr;
|
||||
@ -347,18 +352,18 @@ JoinConvertActions columnsNeedConvert(const Block & left_block, const Names & le
|
||||
if (!any_need_cast)
|
||||
return {};
|
||||
|
||||
auto convert_left_actions_dag = ActionsDAG::makeConvertingActions(
|
||||
actions.left_actions = ActionsDAG::makeConvertingActions(
|
||||
left_block.getColumnsWithTypeAndName(),
|
||||
left_block_dst.getColumnsWithTypeAndName(),
|
||||
ActionsDAG::MatchColumnsMode::Name,
|
||||
true);
|
||||
auto convert_right_actions_dag = ActionsDAG::makeConvertingActions(
|
||||
actions.right_actions = ActionsDAG::makeConvertingActions(
|
||||
right_block.getColumnsWithTypeAndName(),
|
||||
right_block_dst.getColumnsWithTypeAndName(),
|
||||
ActionsDAG::MatchColumnsMode::Name,
|
||||
true);
|
||||
|
||||
return std::make_pair(convert_left_actions_dag, convert_right_actions_dag);
|
||||
return actions;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,16 @@ using ColumnRawPtrs = std::vector<const IColumn *>;
|
||||
namespace JoinCommon
|
||||
{
|
||||
|
||||
using JoinConvertActions = std::pair<ActionsDAGPtr, ActionsDAGPtr>;
|
||||
struct JoinConvertActions
|
||||
{
|
||||
ActionsDAGPtr left_actions;
|
||||
ActionsDAGPtr right_actions;
|
||||
|
||||
NamesAndTypesList left_target_types;
|
||||
NamesAndTypesList right_target_types;
|
||||
|
||||
bool needConvert() const { return left_actions && right_actions; }
|
||||
};
|
||||
|
||||
void convertColumnToNullable(ColumnWithTypeAndName & column, bool low_card_nullability = false);
|
||||
void convertColumnsToNullable(Block & block, size_t starting_pos = 0);
|
||||
@ -40,9 +49,10 @@ void joinTotals(const Block & totals, const Block & columns_to_add, const Names
|
||||
|
||||
void addDefaultValues(IColumn & column, const DataTypePtr & type, size_t count);
|
||||
|
||||
/// Return converting actions for left and right tables that need to be performed before join
|
||||
JoinConvertActions columnsNeedConvert(const Block & left_block, const Names & left_keys,
|
||||
const Block & right_block, const Names & right_keys,
|
||||
bool has_using);
|
||||
const Block & right_block, const Names & right_keys,
|
||||
bool has_using);
|
||||
}
|
||||
|
||||
/// Creates result from right table data in RIGHT and FULL JOIN when keys are not present in left table.
|
||||
|
@ -1,5 +1,5 @@
|
||||
CREATE DATABASE IF NOT EXISTS test_01655;
|
||||
USE test_01655;
|
||||
CREATE DATABASE IF NOT EXISTS test_01674;
|
||||
USE test_01674;
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
@ -79,50 +79,50 @@ SELECT * FROM t1 INNER JOIN t2 ON (t1.a == t2.a) ORDER BY (a); -- { serverError
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
|
||||
CREATE TABLE t1 (id Nullable(Int32), a UInt16, b UInt8) ENGINE = TinyLog;
|
||||
CREATE TABLE t2 (id Nullable(Int32), a Int16, b Nullable(Int64)) ENGINE = TinyLog;
|
||||
INSERT INTO t1 VALUES (0, 1, 1), (1, 2, 2);
|
||||
INSERT INTO t2 VALUES (2, -1, 1), (3, 1, NULL), (4, 1, 257), (5, 1, -1), (6, 1, 1);
|
||||
CREATE TABLE t_ab1 (id Nullable(Int32), a UInt16, b UInt8) ENGINE = TinyLog;
|
||||
CREATE TABLE t_ab2 (id Nullable(Int32), a Int16, b Nullable(Int64)) ENGINE = TinyLog;
|
||||
INSERT INTO t_ab1 VALUES (0, 1, 1), (1, 2, 2);
|
||||
INSERT INTO t_ab2 VALUES (2, -1, 1), (3, 1, NULL), (4, 1, 257), (5, 1, -1), (6, 1, 1);
|
||||
|
||||
SELECT '--- hash ---';
|
||||
|
||||
SELECT '- full -';
|
||||
SELECT a, b FROM t1 FULL JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 FULL JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- left -';
|
||||
SELECT a, b FROM t1 LEFT JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 LEFT JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- right -';
|
||||
SELECT a, b FROM t1 RIGHT JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 RIGHT JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- inner -';
|
||||
SELECT a, b FROM t1 INNER JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 INNER JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
|
||||
SELECT '- types -';
|
||||
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 FULL JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 LEFT JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 RIGHT JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 INNER JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 FULL JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 LEFT JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 RIGHT JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 INNER JOIN t_ab2 USING (a, b);
|
||||
|
||||
SELECT '--- partial_merge ---';
|
||||
|
||||
SET join_algorithm = 'partial_merge';
|
||||
|
||||
SELECT '- full -';
|
||||
SELECT a, b FROM t1 FULL JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 FULL JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- left -';
|
||||
SELECT a, b FROM t1 LEFT JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 LEFT JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- right -';
|
||||
SELECT a, b FROM t1 RIGHT JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 RIGHT JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
SELECT '- inner -';
|
||||
SELECT a, b FROM t1 INNER JOIN t2 USING (a, b) ORDER BY ifNull(t1.id, t2.id);
|
||||
SELECT a, b FROM t_ab1 INNER JOIN t_ab2 USING (a, b) ORDER BY ifNull(t_ab1.id, t_ab2.id);
|
||||
|
||||
SELECT '- types -';
|
||||
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 FULL JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 LEFT JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 RIGHT JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t1 INNER JOIN t2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 FULL JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 LEFT JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 RIGHT JOIN t_ab2 USING (a, b);
|
||||
SELECT any(toTypeName(a)) == 'Int32' AND any(toTypeName(b)) == 'Nullable(Int64)' FROM t_ab1 INNER JOIN t_ab2 USING (a, b);
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t_ab1;
|
||||
DROP TABLE IF EXISTS t_ab2;
|
||||
|
||||
DROP DATABASE IF EXISTS test_01655;
|
||||
DROP DATABASE IF EXISTS test_01674;
|
||||
|
Loading…
Reference in New Issue
Block a user