Skip to content

Commit 9918965

Browse files
Alexey Zatelepinalexey-milovidov
authored andcommitted
Allow metadata-only ALTER of primary key columns [#CLICKHOUSE-2027]
1 parent 7b57402 commit 9918965

File tree

2 files changed

+53
-15
lines changed

2 files changed

+53
-15
lines changed

dbms/include/DB/Interpreters/ExpressionActions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ class ExpressionActions
168168
/// - Если output_columns пуст, оставляет один произвольный столбец (чтобы не потерялось количество строк в блоке).
169169
void finalize(const Names & output_columns);
170170

171+
const Actions & getActions() const { return actions; }
172+
171173
/// Получить список входных столбцов.
172174
Names getRequiredColumns() const
173175
{

dbms/src/Storages/MergeTree/MergeTreeData.cpp

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <algorithm>
3131
#include <iomanip>
3232
#include <thread>
33+
#include <typeinfo>
34+
#include <typeindex>
3335

3436

3537
namespace ProfileEvents
@@ -594,14 +596,31 @@ namespace
594596

595597
/// If true, then in order to ALTER the type of the column from the type from to the type to
596598
/// we don't need to rewrite the data, we only need to update metadata and columns.txt in part directories.
599+
/// The function works for Arrays and Nullables of the same structure.
597600
bool isMetadataOnlyConversion(const IDataType * from, const IDataType * to)
598601
{
602+
if (from->getName() == to->getName())
603+
return true;
604+
605+
static const std::unordered_multimap<std::type_index, const std::type_info &> ALLOWED_CONVERSIONS =
606+
{
607+
{ typeid(DataTypeEnum8), typeid(DataTypeEnum8) },
608+
{ typeid(DataTypeEnum8), typeid(DataTypeInt8) },
609+
{ typeid(DataTypeEnum16), typeid(DataTypeEnum16) },
610+
{ typeid(DataTypeEnum16), typeid(DataTypeInt16) },
611+
{ typeid(DataTypeDateTime), typeid(DataTypeUInt32) },
612+
{ typeid(DataTypeUInt32), typeid(DataTypeDateTime) },
613+
{ typeid(DataTypeDate), typeid(DataTypeUInt16) },
614+
{ typeid(DataTypeUInt16), typeid(DataTypeDate) },
615+
};
616+
599617
while (true)
600618
{
601-
if ((typeid_cast<const DataTypeEnum8 *>(to) && typeid_cast<const DataTypeEnum8 *>(from))
602-
|| (typeid_cast<const DataTypeEnum16 *>(to) && typeid_cast<const DataTypeEnum16 *>(from)))
619+
auto it_range = ALLOWED_CONVERSIONS.equal_range(typeid(*from));
620+
for (auto it = it_range.first; it != it_range.second; ++it)
603621
{
604-
return true;
622+
if (it->second == typeid(*to))
623+
return true;
605624
}
606625

607626
const auto * arr_from = typeid_cast<const DataTypeArray *>(from);
@@ -640,20 +659,32 @@ void MergeTreeData::checkAlter(const AlterCommands & commands)
640659
checkNoMultidimensionalArrays(new_columns, /* attach = */ false);
641660
checkNoMultidimensionalArrays(new_materialized_columns, /* attach = */ false);
642661

643-
/// List of columns that shouldn't be altered.
644-
/// We don't add sampling_expression columns because they must be contained in the primary key columns.
662+
/// Set of columns that shouldn't be altered.
663+
NameSet columns_alter_forbidden;
645664

646-
Names keys;
647-
648-
if (primary_expr)
649-
keys = primary_expr->getRequiredColumns();
650-
651-
keys.push_back(date_column_name);
665+
columns_alter_forbidden.insert(date_column_name);
652666

653667
if (!merging_params.sign_column.empty())
654-
keys.push_back(merging_params.sign_column);
668+
columns_alter_forbidden.insert(merging_params.sign_column);
655669

656-
std::sort(keys.begin(), keys.end());
670+
/// Primary key columns can be ALTERed only if they are used in the primary key as-is
671+
/// (and not as a part of some expression) and if the ALTER only affects column metadata.
672+
/// We don't add sampling_expression columns here because they must be among the primary key columns.
673+
NameSet columns_alter_metadata_only;
674+
675+
if (primary_expr)
676+
{
677+
for (const auto & action : primary_expr->getActions())
678+
{
679+
auto action_columns = action.getNeededColumns();
680+
columns_alter_forbidden.insert(action_columns.begin(), action_columns.end());
681+
}
682+
for (const auto & col : primary_expr->getRequiredColumns())
683+
{
684+
if (!columns_alter_forbidden.count(col))
685+
columns_alter_metadata_only.insert(col);
686+
}
687+
}
657688

658689
std::map<String, const IDataType *> old_types;
659690
for (const auto & column : *columns)
@@ -663,7 +694,10 @@ void MergeTreeData::checkAlter(const AlterCommands & commands)
663694

664695
for (const AlterCommand & command : commands)
665696
{
666-
if (std::binary_search(keys.begin(), keys.end(), command.column_name))
697+
if (columns_alter_forbidden.count(command.column_name))
698+
throw Exception("trying to ALTER key column " + command.column_name, ErrorCodes::ILLEGAL_COLUMN);
699+
700+
if (columns_alter_metadata_only.count(command.column_name))
667701
{
668702
if (command.type == AlterCommand::MODIFY_COLUMN)
669703
{
@@ -672,7 +706,9 @@ void MergeTreeData::checkAlter(const AlterCommands & commands)
672706
continue;
673707
}
674708

675-
throw Exception("trying to ALTER key column " + command.column_name, ErrorCodes::ILLEGAL_COLUMN);
709+
throw Exception(
710+
"ALTER of key column " + command.column_name + " must be metadata-only",
711+
ErrorCodes::ILLEGAL_COLUMN);
676712
}
677713
}
678714

0 commit comments

Comments
 (0)