Fix converting right key type in join using

This commit is contained in:
vdimir 2021-02-01 16:53:54 +03:00
parent 4c36cd1737
commit f5b98015a8
No known key found for this signature in database
GPG Key ID: F57B3E10A21DBB31
6 changed files with 64 additions and 38 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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