Skip to content

Commit 4543fe4

Browse files
Merge pull request ClickHouse#86695 from ClickHouse/backport/25.8/86005
Backport ClickHouse#86005 to 25.8: Forbid altering columns whose subcolumns are used in PK or partition expression
2 parents 7528177 + 66ae5ee commit 4543fe4

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

src/Interpreters/MutationsInterpreter.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,8 @@ static void validateUpdateColumns(
533533
}
534534
}
535535

536-
if (!storage_columns.tryGetColumn(GetColumnsOptions::Ordinary, column_name))
536+
auto ordinary_storage_column = storage_columns.tryGetColumn(GetColumnsOptions::Ordinary, column_name);
537+
if (!ordinary_storage_column)
537538
{
538539
/// Allow to override value of lightweight delete filter virtual column
539540
if (column_name == RowExistsColumn::name)
@@ -550,6 +551,16 @@ static void validateUpdateColumns(
550551
throw Exception(ErrorCodes::NO_SUCH_COLUMN_IN_TABLE, "There is no column {} in table", backQuote(column_name));
551552
}
552553
}
554+
else
555+
{
556+
/// Check if we have a subcolumn of this column as a key column.
557+
for (const auto & key_column : key_columns)
558+
{
559+
auto [key_column_name, key_subcolumn_name] = Nested::splitName(key_column);
560+
if (key_column_name == column_name && ordinary_storage_column->type->hasSubcolumn(key_subcolumn_name))
561+
throw Exception(ErrorCodes::CANNOT_UPDATE_COLUMN, "Cannot UPDATE column {} because its subcolumn {} is a key column", backQuote(column_name), backQuote(key_column));
562+
}
563+
}
553564
}
554565
}
555566

src/Storages/MergeTree/MergeTreeData.cpp

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4018,6 +4018,11 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
40184018
/// Columns to check that the type change is safe for partition key.
40194019
NameSet columns_alter_type_check_safe_for_partition;
40204020

4021+
/// Columns with subcolumns used in primary key or partition key.
4022+
std::unordered_map<String, std::vector<String>> column_to_subcolumns_used_in_keys;
4023+
4024+
const auto & old_columns = old_metadata.getColumns();
4025+
40214026
if (old_metadata.hasPartitionKey())
40224027
{
40234028
/// Forbid altering columns inside partition key expressions because it can change partition ID format.
@@ -4030,7 +4035,13 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
40304035

40314036
/// But allow to alter columns without expressions under certain condition.
40324037
for (const String & col : partition_key_expr->getRequiredColumns())
4033-
columns_alter_type_check_safe_for_partition.insert(col);
4038+
{
4039+
auto storage_column = old_columns.tryGetColumnOrSubcolumn(GetColumnsOptions::All, col);
4040+
if (storage_column && storage_column->isSubcolumn())
4041+
column_to_subcolumns_used_in_keys[storage_column->getNameInStorage()].push_back(backQuoteIfNeed(col));
4042+
else
4043+
columns_alter_type_check_safe_for_partition.insert(col);
4044+
}
40344045
}
40354046

40364047
if (old_metadata.hasSortingKey())
@@ -4042,7 +4053,13 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
40424053
columns_alter_type_forbidden.insert(child->result_name);
40434054
}
40444055
for (const String & col : sorting_key_expr->getRequiredColumns())
4045-
columns_alter_type_metadata_only.insert(col);
4056+
{
4057+
auto storage_column = old_columns.tryGetColumnOrSubcolumn(GetColumnsOptions::All, col);
4058+
if (storage_column && storage_column->isSubcolumn())
4059+
column_to_subcolumns_used_in_keys[storage_column->getNameInStorage()].push_back(backQuoteIfNeed(col));
4060+
else
4061+
columns_alter_type_metadata_only.insert(col);
4062+
}
40464063

40474064
/// We don't process sample_by_ast separately because it must be among the primary key columns
40484065
/// and we don't process primary_key_expr separately because it is a prefix of sorting_key_expr.
@@ -4172,6 +4189,15 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
41724189
backQuoteIfNeed(command.column_name));
41734190
}
41744191

4192+
if (column_to_subcolumns_used_in_keys.contains(command.column_name))
4193+
{
4194+
throw Exception(
4195+
ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN,
4196+
"Trying to ALTER RENAME column {} whose subcolumns ({}) are part of key expression",
4197+
backQuoteIfNeed(command.column_name),
4198+
boost::join(column_to_subcolumns_used_in_keys[command.column_name], ", "));
4199+
}
4200+
41754201
/// Don't check columns in indices here. RENAME works fine with index columns.
41764202

41774203
if (auto it = columns_in_projections.find(command.column_name); it != columns_in_projections.end())
@@ -4191,6 +4217,15 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
41914217
"Trying to ALTER DROP key {} column which is a part of key expression", backQuoteIfNeed(command.column_name));
41924218
}
41934219

4220+
if (column_to_subcolumns_used_in_keys.contains(command.column_name))
4221+
{
4222+
throw Exception(
4223+
ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN,
4224+
"Trying to ALTER DROP column {} whose subcolumns ({}) are part of key expression",
4225+
backQuoteIfNeed(command.column_name),
4226+
boost::join(column_to_subcolumns_used_in_keys[command.column_name], ", "));
4227+
}
4228+
41944229
/// Don't check columns in indices or projections here. If required columns of indices
41954230
/// or projections get dropped, it will be checked later in AlterCommands::apply. This
41964231
/// allows projections with * to drop columns. One example can be found in
@@ -4239,6 +4274,15 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
42394274
throw Exception(ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN, "ALTER of key column {} is forbidden",
42404275
backQuoteIfNeed(command.column_name));
42414276

4277+
if (column_to_subcolumns_used_in_keys.contains(command.column_name))
4278+
{
4279+
throw Exception(
4280+
ErrorCodes::ALTER_OF_COLUMN_IS_FORBIDDEN,
4281+
"Trying to ALTER column {} whose subcolumns ({}) are part of key expression",
4282+
backQuoteIfNeed(command.column_name),
4283+
boost::join(column_to_subcolumns_used_in_keys[command.column_name], ", "));
4284+
}
4285+
42424286
if (auto it = columns_in_indices.find(command.column_name); it != columns_in_indices.end())
42434287
{
42444288
throw Exception(

tests/queries/0_stateless/03597_alter_column_with_subcolumn_in_key.reference

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
drop table if exists test;
2+
create table test (id UInt32, t Tuple(a UInt32)) engine=MergeTree order by t.a;
3+
insert into test select 1, tuple(1);
4+
alter table test update t = tuple(2) where 1; -- {serverError CANNOT_UPDATE_COLUMN}
5+
alter table test modify column t Tuple(a String); -- {serverError ALTER_OF_COLUMN_IS_FORBIDDEN}
6+
drop table test;
7+
8+
drop table if exists test;
9+
create table test (id UInt32, json JSON) engine=MergeTree order by json.a::Int64;
10+
insert into test select 1, '{"a" : 42}';
11+
alter table test update json = '{}' where 1; -- {serverError CANNOT_UPDATE_COLUMN}
12+
alter table test modify column json JSON(a String); -- {serverError ALTER_OF_COLUMN_IS_FORBIDDEN}
13+
drop table test;
14+

0 commit comments

Comments
 (0)