2019-09-18 12:46:57 +00:00
|
|
|
#include <Interpreters/join_common.h>
|
2019-09-10 18:39:10 +00:00
|
|
|
#include <Columns/ColumnNullable.h>
|
2019-09-11 18:03:21 +00:00
|
|
|
#include <DataTypes/DataTypeNullable.h>
|
2019-09-10 18:39:10 +00:00
|
|
|
#include <DataTypes/DataTypeLowCardinality.h>
|
2019-09-11 18:03:21 +00:00
|
|
|
#include <DataStreams/materializeBlock.h>
|
2019-09-10 18:39:10 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2019-09-11 18:03:21 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int TYPE_MISMATCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace JoinCommon
|
|
|
|
{
|
|
|
|
|
|
|
|
void convertColumnToNullable(ColumnWithTypeAndName & column)
|
|
|
|
{
|
|
|
|
if (column.type->isNullable() || !column.type->canBeInsideNullable())
|
|
|
|
return;
|
|
|
|
|
|
|
|
column.type = makeNullable(column.type);
|
|
|
|
if (column.column)
|
|
|
|
column.column = makeNullable(column.column);
|
|
|
|
}
|
|
|
|
|
|
|
|
void convertColumnsToNullable(Block & block, size_t starting_pos)
|
|
|
|
{
|
|
|
|
for (size_t i = starting_pos; i < block.columns(); ++i)
|
|
|
|
convertColumnToNullable(block.getByPosition(i));
|
|
|
|
}
|
|
|
|
|
2019-10-29 19:39:42 +00:00
|
|
|
/// @warning It assumes that every NULL has default value in nested column (or it does not matter)
|
|
|
|
void removeColumnNullability(ColumnWithTypeAndName & column)
|
|
|
|
{
|
|
|
|
if (!column.type->isNullable())
|
|
|
|
return;
|
|
|
|
|
|
|
|
column.type = static_cast<const DataTypeNullable &>(*column.type).getNestedType();
|
|
|
|
if (column.column)
|
|
|
|
{
|
|
|
|
auto * nullable_column = checkAndGetColumn<ColumnNullable>(*column.column);
|
|
|
|
ColumnPtr nested_column = nullable_column->getNestedColumnPtr();
|
|
|
|
MutableColumnPtr mutable_column = (*std::move(nested_column)).mutate();
|
|
|
|
column.column = std::move(mutable_column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-17 15:02:42 +00:00
|
|
|
ColumnRawPtrs materializeColumnsInplace(Block & block, const Names & names)
|
2019-09-12 12:24:19 +00:00
|
|
|
{
|
|
|
|
ColumnRawPtrs ptrs;
|
|
|
|
ptrs.reserve(names.size());
|
2019-12-17 15:02:42 +00:00
|
|
|
|
|
|
|
for (auto & column_name : names)
|
|
|
|
{
|
|
|
|
auto & column = block.getByName(column_name).column;
|
|
|
|
column = recursiveRemoveLowCardinality(column->convertToFullColumnIfConst());
|
|
|
|
ptrs.push_back(column.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
Columns materializeColumns(const Block & block, const Names & names)
|
|
|
|
{
|
|
|
|
Columns materialized;
|
2019-09-12 12:24:19 +00:00
|
|
|
materialized.reserve(names.size());
|
|
|
|
|
|
|
|
for (auto & column_name : names)
|
|
|
|
{
|
|
|
|
const auto & src_column = block.getByName(column_name).column;
|
|
|
|
materialized.emplace_back(recursiveRemoveLowCardinality(src_column->convertToFullColumnIfConst()));
|
|
|
|
}
|
|
|
|
|
2019-12-17 15:02:42 +00:00
|
|
|
return materialized;
|
|
|
|
}
|
|
|
|
|
|
|
|
ColumnRawPtrs getRawPointers(const Columns & columns)
|
|
|
|
{
|
|
|
|
ColumnRawPtrs ptrs;
|
|
|
|
ptrs.reserve(columns.size());
|
|
|
|
|
|
|
|
for (auto & column : columns)
|
|
|
|
ptrs.push_back(column.get());
|
|
|
|
|
2019-09-12 12:24:19 +00:00
|
|
|
return ptrs;
|
|
|
|
}
|
|
|
|
|
2019-09-17 18:53:52 +00:00
|
|
|
void removeLowCardinalityInplace(Block & block)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < block.columns(); ++i)
|
|
|
|
{
|
|
|
|
auto & col = block.getByPosition(i);
|
|
|
|
col.column = recursiveRemoveLowCardinality(col.column);
|
|
|
|
col.type = recursiveRemoveLowCardinality(col.type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 18:39:10 +00:00
|
|
|
ColumnRawPtrs extractKeysForJoin(const Names & key_names_right, const Block & right_sample_block,
|
|
|
|
Block & sample_block_with_keys, Block & sample_block_with_columns_to_add)
|
|
|
|
{
|
|
|
|
size_t keys_size = key_names_right.size();
|
|
|
|
ColumnRawPtrs key_columns(keys_size);
|
|
|
|
|
|
|
|
sample_block_with_columns_to_add = materializeBlock(right_sample_block);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < keys_size; ++i)
|
|
|
|
{
|
|
|
|
const String & column_name = key_names_right[i];
|
|
|
|
|
|
|
|
/// there could be the same key names
|
|
|
|
if (sample_block_with_keys.has(column_name))
|
|
|
|
{
|
|
|
|
key_columns[i] = sample_block_with_keys.getByName(column_name).column.get();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto & col = sample_block_with_columns_to_add.getByName(column_name);
|
|
|
|
col.column = recursiveRemoveLowCardinality(col.column);
|
|
|
|
col.type = recursiveRemoveLowCardinality(col.type);
|
|
|
|
|
|
|
|
/// Extract right keys with correct keys order.
|
|
|
|
sample_block_with_keys.insert(col);
|
|
|
|
sample_block_with_columns_to_add.erase(column_name);
|
|
|
|
|
|
|
|
key_columns[i] = sample_block_with_keys.getColumns().back().get();
|
|
|
|
|
|
|
|
/// We will join only keys, where all components are not NULL.
|
|
|
|
if (auto * nullable = checkAndGetColumn<ColumnNullable>(*key_columns[i]))
|
|
|
|
key_columns[i] = &nullable->getNestedColumn();
|
|
|
|
}
|
|
|
|
|
|
|
|
return key_columns;
|
|
|
|
}
|
|
|
|
|
2019-09-11 18:03:21 +00:00
|
|
|
void checkTypesOfKeys(const Block & block_left, const Names & key_names_left, const Block & block_right, const Names & key_names_right)
|
|
|
|
{
|
|
|
|
size_t keys_size = key_names_left.size();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < keys_size; ++i)
|
|
|
|
{
|
|
|
|
DataTypePtr left_type = removeNullable(recursiveRemoveLowCardinality(block_left.getByName(key_names_left[i]).type));
|
|
|
|
DataTypePtr right_type = removeNullable(recursiveRemoveLowCardinality(block_right.getByName(key_names_right[i]).type));
|
|
|
|
|
|
|
|
if (!left_type->equals(*right_type))
|
|
|
|
throw Exception("Type mismatch of columns to JOIN by: "
|
|
|
|
+ key_names_left[i] + " " + left_type->getName() + " at left, "
|
|
|
|
+ key_names_right[i] + " " + right_type->getName() + " at right",
|
|
|
|
ErrorCodes::TYPE_MISMATCH);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 18:39:10 +00:00
|
|
|
void createMissedColumns(Block & block)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < block.columns(); ++i)
|
|
|
|
{
|
|
|
|
auto & column = block.getByPosition(i);
|
|
|
|
if (!column.column)
|
|
|
|
column.column = column.type->createColumn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-19 14:53:03 +00:00
|
|
|
void joinTotals(const Block & totals, const Block & columns_to_add, const Names & key_names_right, Block & block)
|
|
|
|
{
|
|
|
|
if (Block totals_without_keys = totals)
|
|
|
|
{
|
|
|
|
for (const auto & name : key_names_right)
|
|
|
|
totals_without_keys.erase(totals_without_keys.getPositionByName(name));
|
|
|
|
|
|
|
|
for (size_t i = 0; i < totals_without_keys.columns(); ++i)
|
|
|
|
block.insert(totals_without_keys.safeGetByPosition(i));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/// We will join empty `totals` - from one row with the default values.
|
|
|
|
|
|
|
|
for (size_t i = 0; i < columns_to_add.columns(); ++i)
|
|
|
|
{
|
|
|
|
const auto & col = columns_to_add.getByPosition(i);
|
|
|
|
block.insert({
|
|
|
|
col.type->createColumnConstWithDefaultValue(1)->convertToFullColumnIfConst(),
|
|
|
|
col.type,
|
|
|
|
col.name});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 18:39:10 +00:00
|
|
|
}
|
2019-09-11 18:03:21 +00:00
|
|
|
}
|