3030#include < algorithm>
3131#include < iomanip>
3232#include < thread>
33+ #include < typeinfo>
34+ #include < typeindex>
3335
3436
3537namespace 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.
597600bool 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