dbms: allowed to JOIN with empty table [#METR-16476].

This commit is contained in:
Alexey Milovidov 2015-05-26 03:37:48 +03:00
parent c160b7e460
commit baaf5d0485
5 changed files with 63 additions and 23 deletions

View File

@ -74,6 +74,11 @@ public:
bool empty() { return type == Type::EMPTY; }
/** Передать информацию о структуре блока.
* Следует обязательно вызвать до вызовов insertFromBlock.
*/
void setSampleBlock(const Block & block);
/** Добавить в отображение для соединения блок "правой" таблицы.
* Возвращает false, если превышено какое-нибудь ограничение, и больше не нужно вставлять.
*/
@ -217,6 +222,8 @@ private:
bool keys_fit_128_bits;
Sizes key_sizes;
Block sample_block;
Logger * log;
/// Ограничения на максимальный размер множества

View File

@ -1591,6 +1591,7 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
{
auto interpreter = interpretSubquery(ast_join.table, context, subquery_depth, required_joined_columns);
subquery_for_set.source = new LazyBlockInputStream([interpreter]() mutable { return interpreter->execute(); });
join->setSampleBlock(interpreter->getSampleBlock());
}
subquery_for_set.join = join;

View File

@ -323,10 +323,45 @@ void Join::insertFromBlockImpl(Maps & maps, size_t rows, const ConstColumnPlainP
}
void Join::setSampleBlock(const Block & block)
{
Poco::ScopedWriteRWLock lock(rwlock);
if (!empty())
return;
size_t keys_size = key_names_right.size();
ConstColumnPlainPtrs key_columns(keys_size);
for (size_t i = 0; i < keys_size; ++i)
key_columns[i] = block.getByName(key_names_right[i]).column;
/// Выберем, какую структуру данных для множества использовать.
init(chooseMethod(key_columns, keys_fit_128_bits, key_sizes));
sample_block = block;
/// Удаляем из sample_block ключевые столбцы, так как они не нужны.
for (const auto & name : key_names_right)
sample_block.erase(sample_block.getPositionByName(name));
for (size_t i = 0, size = sample_block.columns(); i < size; ++i)
{
auto & column = sample_block.unsafeGetByPosition(i);
if (!column.column)
column.column = column.type->createColumn();
}
}
bool Join::insertFromBlock(const Block & block)
{
Poco::ScopedWriteRWLock lock(rwlock);
/// Какую структуру данных для множества использовать?
if (empty())
throw Exception("Logical error: Join was not initialized", ErrorCodes::LOGICAL_ERROR);
size_t keys_size = key_names_right.size();
ConstColumnPlainPtrs key_columns(keys_size);
@ -336,10 +371,6 @@ bool Join::insertFromBlock(const Block & block)
size_t rows = block.rows();
/// Какую структуру данных для множества использовать?
if (empty())
init(chooseMethod(key_columns, keys_fit_128_bits, key_sizes));
blocks.push_back(block);
Block * stored_block = &blocks.back();
@ -347,6 +378,14 @@ bool Join::insertFromBlock(const Block & block)
for (const auto & name : key_names_right)
stored_block->erase(stored_block->getPositionByName(name));
/// Редкий случай, когда соединяемые столбцы являеются константами. Чтобы не поддерживать отдельный код, материализуем их.
for (size_t i = 0, size = stored_block->columns(); i < size; ++i)
{
ColumnPtr col = stored_block->getByPosition(i).column;
if (col->isConst())
stored_block->getByPosition(i).column = dynamic_cast<IColumnConst &>(*col).convertToFullColumn();
}
if (!getFullness(kind))
{
if (strictness == ASTJoin::Any)
@ -473,9 +512,6 @@ struct Adder<KIND, ASTJoin::All, Map>
template <ASTJoin::Kind KIND, ASTJoin::Strictness STRICTNESS, typename Maps>
void Join::joinBlockImpl(Block & block, const Maps & maps) const
{
if (blocks.empty())
throw Exception("Attempt to JOIN with empty table", ErrorCodes::EMPTY_DATA_PASSED);
size_t keys_size = key_names_left.size();
ConstColumnPlainPtrs key_columns(keys_size);
@ -484,15 +520,14 @@ void Join::joinBlockImpl(Block & block, const Maps & maps) const
key_columns[i] = block.getByName(key_names_left[i]).column;
/// Добавляем в блок новые столбцы.
const Block & first_mapped_block = blocks.front();
size_t num_columns_to_add = first_mapped_block.columns();
size_t num_columns_to_add = sample_block.columns();
ColumnPlainPtrs added_columns(num_columns_to_add);
size_t existing_columns = block.columns();
for (size_t i = 0; i < num_columns_to_add; ++i)
{
const ColumnWithNameAndType & src_column = first_mapped_block.getByPosition(i);
const ColumnWithNameAndType & src_column = sample_block.getByPosition(i);
ColumnWithNameAndType new_column = src_column.cloneEmpty();
block.insert(new_column);
added_columns[i] = new_column.column;
@ -630,11 +665,8 @@ void Join::joinTotals(Block & block) const
}
else
{
if (blocks.empty())
return;
/// Будем присоединять пустые totals - из одной строчки со значениями по-умолчанию.
totals_without_keys = blocks.front().cloneEmpty();
totals_without_keys = sample_block.cloneEmpty();
for (size_t i = 0; i < totals_without_keys.columns(); ++i)
{
@ -739,13 +771,12 @@ private:
}
/// Добавляем в блок новые столбцы.
const Block & first_mapped_block = parent.blocks.front();
size_t num_columns_right = first_mapped_block.columns();
size_t num_columns_right = parent.sample_block.columns();
ColumnPlainPtrs columns_right(num_columns_right);
for (size_t i = 0; i < num_columns_right; ++i)
{
const ColumnWithNameAndType & src_column = first_mapped_block.getByPosition(i);
const ColumnWithNameAndType & src_column = parent.sample_block.getByPosition(i);
ColumnWithNameAndType new_column = src_column.cloneEmpty();
block.insert(new_column);
columns_right[i] = new_column.column;

View File

@ -33,6 +33,7 @@ StorageJoin::StorageJoin(
ErrorCodes::NO_SUCH_COLUMN_IN_TABLE};
join = new Join(key_names, key_names, Limits(), kind, strictness);
join->setSampleBlock(getSampleBlock());
restore();
}

View File

@ -1,10 +1,10 @@
0 0 0 Hello
1 0.5 0 Hello
1 0.5 0
2 1 3 Hello
3 1.5 0 Hello
3 1.5 0
4 2 6 Hello
5 2.5 0 Hello
5 2.5 0
6 3 9 Hello
7 3.5 0 Hello
8 4 0 Hello
9 4.5 0 Hello
7 3.5 0
8 4 0
9 4.5 0