diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 9fa19859c7f..888fbcc7bde 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1316,10 +1317,10 @@ void MergeTreeData::dropIfEmpty() namespace { -/// Conversion that is allowed for partition key. -/// Partition key should be serialized in the same way after conversion. +/// Conversion that is allowed for serializable key (primary key, sorting key). +/// Key should be serialized in the same way after conversion. /// NOTE: The list is not complete. -bool isSafeForPartitionKeyConversion(const IDataType * from, const IDataType * to) +bool isSafeForKeyConversion(const IDataType * from, const IDataType * to) { if (from->getName() == to->getName()) return true; @@ -1346,6 +1347,12 @@ bool isSafeForPartitionKeyConversion(const IDataType * from, const IDataType * t return false; } + if (const auto * from_lc = typeid_cast(from)) + return from_lc->getDictionaryType()->equals(*to); + + if (const auto * to_lc = typeid_cast(to)) + return to_lc->getDictionaryType()->equals(*from); + return false; } @@ -1540,7 +1547,7 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S auto it = old_types.find(command.column_name); assert(it != old_types.end()); - if (!isSafeForPartitionKeyConversion(it->second, command.data_type.get())) + if (!isSafeForKeyConversion(it->second, command.data_type.get())) throw Exception("ALTER of partition key column " + backQuoteIfNeed(command.column_name) + " from type " + it->second->getName() + " to type " + command.data_type->getName() + " is not safe because it can change the representation of partition key", @@ -1554,9 +1561,11 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S { auto it = old_types.find(command.column_name); assert(it != old_types.end()); - throw Exception("ALTER of key column " + backQuoteIfNeed(command.column_name) + " from type " - + it->second->getName() + " to type " + command.data_type->getName() + " must be metadata-only", - ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN); + if (!isSafeForKeyConversion(it->second, command.data_type.get())) + throw Exception("ALTER of key column " + backQuoteIfNeed(command.column_name) + " from type " + + it->second->getName() + " to type " + command.data_type->getName() + + " is not safe because it can change the representation of primary key", + ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN); } } } diff --git a/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.reference b/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.reference new file mode 100644 index 00000000000..07a160752d4 --- /dev/null +++ b/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.reference @@ -0,0 +1,8 @@ +CREATE TABLE default.table_with_lc_key\n(\n `enum_key` Enum8(\'y\' = 1, \'x\' = 2),\n `lc_key` String,\n `value` String\n)\nENGINE = MergeTree\nORDER BY (enum_key, lc_key)\nSETTINGS index_granularity = 8192 +y hello world +CREATE TABLE default.table_with_lc_key\n(\n `enum_key` Enum8(\'y\' = 1, \'x\' = 2, \'z\' = 3),\n `lc_key` String,\n `value` String\n)\nENGINE = MergeTree\nORDER BY (enum_key, lc_key)\nSETTINGS index_granularity = 8192 +y hello world +CREATE TABLE default.table_with_lc_key\n(\n `enum_key` Int8,\n `lc_key` String,\n `value` String\n)\nENGINE = MergeTree\nORDER BY (enum_key, lc_key)\nSETTINGS index_granularity = 8192 +1 hello world +CREATE TABLE default.table_with_string_key\n(\n `int_key` Int8,\n `str_key` LowCardinality(String),\n `value` String\n)\nENGINE = MergeTree\nORDER BY (int_key, str_key)\nSETTINGS index_granularity = 8192 +1 hello world diff --git a/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.sql b/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.sql new file mode 100644 index 00000000000..6478d33dfcc --- /dev/null +++ b/tests/queries/0_stateless/01611_string_to_low_cardinality_key_alter.sql @@ -0,0 +1,67 @@ +DROP TABLE IF EXISTS table_with_lc_key; + +CREATE TABLE table_with_lc_key +( + enum_key Enum8('x' = 2, 'y' = 1), + lc_key LowCardinality(String), + value String +) +ENGINE MergeTree() +ORDER BY (enum_key, lc_key); + +INSERT INTO table_with_lc_key VALUES(1, 'hello', 'world'); + +ALTER TABLE table_with_lc_key MODIFY COLUMN lc_key String; + +SHOW CREATE TABLE table_with_lc_key; + +DETACH TABLE table_with_lc_key; +ATTACH TABLE table_with_lc_key; + +SELECT * FROM table_with_lc_key WHERE enum_key > 0 and lc_key like 'h%'; + +ALTER TABLE table_with_lc_key MODIFY COLUMN enum_key Enum('x' = 2, 'y' = 1, 'z' = 3); +ALTER TABLE table_with_lc_key MODIFY COLUMN enum_key Enum16('x' = 2, 'y' = 1, 'z' = 3); --{serverError 524} +SHOW CREATE TABLE table_with_lc_key; + +DETACH TABLE table_with_lc_key; +ATTACH TABLE table_with_lc_key; + +SELECT * FROM table_with_lc_key WHERE enum_key > 0 and lc_key like 'h%'; + +ALTER TABLE table_with_lc_key MODIFY COLUMN enum_key Int8; + +SHOW CREATE TABLE table_with_lc_key; + +DETACH TABLE table_with_lc_key; +ATTACH TABLE table_with_lc_key; + +SELECT * FROM table_with_lc_key WHERE enum_key > 0 and lc_key like 'h%'; + +DROP TABLE IF EXISTS table_with_lc_key; + + +DROP TABLE IF EXISTS table_with_string_key; +CREATE TABLE table_with_string_key +( + int_key Int8, + str_key String, + value String +) +ENGINE MergeTree() +ORDER BY (int_key, str_key); + +INSERT INTO table_with_string_key VALUES(1, 'hello', 'world'); + +ALTER TABLE table_with_string_key MODIFY COLUMN str_key LowCardinality(String); + +SHOW CREATE TABLE table_with_string_key; + +DETACH TABLE table_with_string_key; +ATTACH TABLE table_with_string_key; + +SELECT * FROM table_with_string_key WHERE int_key > 0 and str_key like 'h%'; + +ALTER TABLE table_with_string_key MODIFY COLUMN int_key Enum8('y' = 1, 'x' = 2); --{serverError 524} + +DROP TABLE IF EXISTS table_with_string_key;