From e19ed7eacbe44c6accc23febdac067b6973a4659 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 1 Sep 2026 13:12:17 +0530 Subject: [PATCH] MDEV-40915 Table options (PAGE_COMPRESSED, ENCRYPTED) are ignored by ALTER TABLE on partitioned tables Problem: ========= ha_partition::check_if_supported_inplace_alter() sourced the per-partition engine options from m_part_info, which describes the table as it is now. The engine compares create_info->option_struct against handler::option_struct (old options), both are pointing to source table. It leads to make the engine believe that there is no change in table structure and allows to proceed PAGE_COMPRESSED=1 with INSTANT algorithm. Fix: ==== Take the new options from altered_table->part_info, which was opened from the new .frm with the new table-level options already merged into every partition_element by parse_engine_part_options(). Do the same in prepare_inplace_alter_table(), so that both phases agree and a per-partition option override reaches the rebuilt table. --- .../encryption/r/encryption_force.result | 25 ++++++++++++++++ .../suite/encryption/t/encryption_force.opt | 1 + .../suite/encryption/t/encryption_force.test | 10 +++++++ .../parts/r/partition_alter_innodb.result | 30 +++++++++++++++++++ .../suite/parts/t/partition_alter_innodb.opt | 1 + .../suite/parts/t/partition_alter_innodb.test | 18 +++++++++++ sql/ha_partition.cc | 23 +++++++++++++- 7 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/parts/t/partition_alter_innodb.opt diff --git a/mysql-test/suite/encryption/r/encryption_force.result b/mysql-test/suite/encryption/r/encryption_force.result index 40f612c9cc5c0..0bd6481453446 100644 --- a/mysql-test/suite/encryption/r/encryption_force.result +++ b/mysql-test/suite/encryption/r/encryption_force.result @@ -8,6 +8,7 @@ ERROR HY000: Can't create table `test`.`t3` (errno: 140 "Wrong create options") create table t4 (a int) engine=innodb encrypted=yes partition by hash(a) partitions 2; create table t5 (a int) engine=innodb encrypted=no partition by hash(a) partitions 2; ERROR HY000: Can't create table `test`.`t5` (errno: 140 "Wrong create options") +create table t5 (a int) engine=innodb partition by hash(a) partitions 2; set global innodb_encrypt_tables='ON'; create table t3 (a int) engine=innodb encrypted=no; set global innodb_encrypt_tables='FORCE'; @@ -34,12 +35,23 @@ t4 CREATE TABLE `t4` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci `encrypted`='yes' PARTITION BY HASH (`a`) PARTITIONS 2 +show create table t5; +Table Create Table +t5 CREATE TABLE `t5` ( + `a` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci + PARTITION BY HASH (`a`) +PARTITIONS 2 alter table t1 encrypted=no; ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'ENCRYPTED' alter table t2 encrypted=yes; alter table t3 encrypted=default; alter table t4 encrypted=no; ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'ENCRYPTED' +set global innodb_encrypt_tables='ON'; +alter table t5 encrypted=NO, algorithm=instant; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: Changing table options requires the table to be rebuilt. Try ALGORITHM=INPLACE +alter table t5 encrypted=NO, algorithm=inplace; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -55,7 +67,20 @@ Table Create Table t3 CREATE TABLE `t3` ( `a` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +show create table t5; +Table Create Table +t5 CREATE TABLE `t5` ( + `a` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci `encrypted`='NO' + PARTITION BY HASH (`a`) +PARTITIONS 2 +SELECT name, flag FROM information_schema.innodb_sys_tablespaces WHERE NAME like 'test/t5%'; +name flag +test/t5#P#p0 21 +test/t5#P#p1 21 drop table t1; drop table t2; drop table t3; drop table t4; +drop table t5; +set global innodb_encrypt_tables='FORCE'; diff --git a/mysql-test/suite/encryption/t/encryption_force.opt b/mysql-test/suite/encryption/t/encryption_force.opt index c9e532878e1df..b63d760b8d013 100644 --- a/mysql-test/suite/encryption/t/encryption_force.opt +++ b/mysql-test/suite/encryption/t/encryption_force.opt @@ -1,2 +1,3 @@ --innodb-encrypt-tables=FORCE --innodb-encrypt-log=ON +--innodb_sys_tablespaces diff --git a/mysql-test/suite/encryption/t/encryption_force.test b/mysql-test/suite/encryption/t/encryption_force.test index 862f69c13fae3..45e6e1e81cf04 100644 --- a/mysql-test/suite/encryption/t/encryption_force.test +++ b/mysql-test/suite/encryption/t/encryption_force.test @@ -12,6 +12,7 @@ create table t3 (a int) engine=innodb encrypted=no; create table t4 (a int) engine=innodb encrypted=yes partition by hash(a) partitions 2; --error ER_CANT_CREATE_TABLE create table t5 (a int) engine=innodb encrypted=no partition by hash(a) partitions 2; +create table t5 (a int) engine=innodb partition by hash(a) partitions 2; set global innodb_encrypt_tables='ON'; create table t3 (a int) engine=innodb encrypted=no; @@ -22,6 +23,7 @@ show create table t1; show create table t2; show create table t3; show create table t4; +show create table t5; --error ER_ILLEGAL_HA_CREATE_OPTION alter table t1 encrypted=no; @@ -29,12 +31,20 @@ alter table t2 encrypted=yes; alter table t3 encrypted=default; --error ER_ILLEGAL_HA_CREATE_OPTION alter table t4 encrypted=no; +set global innodb_encrypt_tables='ON'; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +alter table t5 encrypted=NO, algorithm=instant; +alter table t5 encrypted=NO, algorithm=inplace; show create table t1; show create table t2; show create table t3; +show create table t5; +SELECT name, flag FROM information_schema.innodb_sys_tablespaces WHERE NAME like 'test/t5%'; drop table t1; drop table t2; drop table t3; drop table t4; +drop table t5; +set global innodb_encrypt_tables='FORCE'; diff --git a/mysql-test/suite/parts/r/partition_alter_innodb.result b/mysql-test/suite/parts/r/partition_alter_innodb.result index 0bd5147623af7..5af8a15c951d8 100644 --- a/mysql-test/suite/parts/r/partition_alter_innodb.result +++ b/mysql-test/suite/parts/r/partition_alter_innodb.result @@ -86,3 +86,33 @@ CREATE TABLE t1(a INT) ENGINE=InnoDB PARTITION BY LIST (a) INSERT INTO t1 VALUES (1),(6); ALTER IGNORE TABLE t1 FORCE, ALGORITHM=COPY; DROP TABLE t1; +# +# MDEV-40915 Table options (PAGE_COMPRESSED, ENCRYPTED) +# are ignored by ALTER TABLE on partitioned tables +# +CREATE TABLE t1 (c1 INT,c2 TEXT, +FULLTEXT(c2),FULLTEXT(c2))Engine=InnoDB +PARTITION BY HASH(c1) PARTITIONS 1; +Warnings: +Note 1831 Duplicate index `c2_2`. This is deprecated and will be disallowed in a future release +INSERT INTO t1 VALUES (1, "test"); +ALTER TABLE t1 PAGE_COMPRESSED=1, ALGORITHM=INSTANT; +ERROR 0A000: ALGORITHM=INSTANT is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY +ALTER TABLE t1 PAGE_COMPRESSED=1, ALGORITHM=INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY +ALTER TABLE t1 PAGE_COMPRESSED=1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` text DEFAULT NULL, + FULLTEXT KEY `c2` (`c2`), + FULLTEXT KEY `c2_2` (`c2`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci `PAGE_COMPRESSED`='1' + PARTITION BY HASH (`c1`) +PARTITIONS 1 +SELECT name, flag FROM information_schema.innodb_sys_tablespaces +WHERE NAME like 'test/t1%'; +name flag +test/t1#P#p0 1610612789 +DROP TABLE t1; diff --git a/mysql-test/suite/parts/t/partition_alter_innodb.opt b/mysql-test/suite/parts/t/partition_alter_innodb.opt new file mode 100644 index 0000000000000..497233a1520df --- /dev/null +++ b/mysql-test/suite/parts/t/partition_alter_innodb.opt @@ -0,0 +1 @@ +--innodb_sys_tablespaces diff --git a/mysql-test/suite/parts/t/partition_alter_innodb.test b/mysql-test/suite/parts/t/partition_alter_innodb.test index 6e2d74dc35f43..66ad046f2f6ac 100644 --- a/mysql-test/suite/parts/t/partition_alter_innodb.test +++ b/mysql-test/suite/parts/t/partition_alter_innodb.test @@ -28,3 +28,21 @@ CREATE TABLE t1(a INT) ENGINE=InnoDB PARTITION BY LIST (a) INSERT INTO t1 VALUES (1),(6); ALTER IGNORE TABLE t1 FORCE, ALGORITHM=COPY; DROP TABLE t1; + +--echo # +--echo # MDEV-40915 Table options (PAGE_COMPRESSED, ENCRYPTED) +--echo # are ignored by ALTER TABLE on partitioned tables +--echo # +CREATE TABLE t1 (c1 INT,c2 TEXT, + FULLTEXT(c2),FULLTEXT(c2))Engine=InnoDB + PARTITION BY HASH(c1) PARTITIONS 1; +INSERT INTO t1 VALUES (1, "test"); +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +ALTER TABLE t1 PAGE_COMPRESSED=1, ALGORITHM=INSTANT; +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +ALTER TABLE t1 PAGE_COMPRESSED=1, ALGORITHM=INPLACE; +ALTER TABLE t1 PAGE_COMPRESSED=1; +SHOW CREATE TABLE t1; +SELECT name, flag FROM information_schema.innodb_sys_tablespaces +WHERE NAME like 'test/t1%'; +DROP TABLE t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 30b52ad0426a5..6af4fe1967874 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -10872,6 +10872,19 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, DBUG_RETURN(HA_ALTER_ERROR); ha_table_option_struct *orig_opst= ha_alter_info->create_info->option_struct; + /* + Per-partition engine options must be taken from the new table definition. + The engine compares create_info->option_struct (the new options) against + handler::option_struct (the options the partition was opened with), and the + latter is m_part_info's option_struct_part. Using m_part_info here would + make the engine compare the old options against themselves. + + altered_table was opened from the new .frm, where parse_engine_part_options() + has merged the new table-level options into every partition_element. + */ + DBUG_ASSERT(altered_table->part_info); + DBUG_ASSERT(altered_table->part_info->get_tot_partitions() == m_tot_parts); + do { result= HA_ALTER_INPLACE_NO_LOCK; /* Set all to NULL, including the terminating one. */ @@ -10880,7 +10893,7 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, ha_alter_info->handler_flags |= ALTER_PARTITIONED; orig_flags= ha_alter_info->handler_flags; - partition_element_iterator part_it(m_part_info->partitions); + partition_element_iterator part_it(altered_table->part_info->partitions); for (index= 0; index < m_tot_parts; index++) { ha_alter_info->create_info->option_struct= (part_it++)->option_struct_part; @@ -10940,14 +10953,22 @@ bool ha_partition::prepare_inplace_alter_table(TABLE *altered_table, part_inplace_ctx= static_cast(ha_alter_info->handler_ctx); + /* Per-partition engine options, see check_if_supported_inplace_alter(). */ + DBUG_ASSERT(altered_table->part_info); + DBUG_ASSERT(altered_table->part_info->get_tot_partitions() == m_tot_parts); + + ha_table_option_struct *orig_opst= ha_alter_info->create_info->option_struct; + partition_element_iterator part_it(altered_table->part_info->partitions); for (index= 0; index < m_tot_parts && !error; index++) { + ha_alter_info->create_info->option_struct= (part_it++)->option_struct_part; ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; if (m_file[index]->ha_prepare_inplace_alter_table(altered_table, ha_alter_info)) error= true; part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; } + ha_alter_info->create_info->option_struct= orig_opst; ha_alter_info->handler_ctx= part_inplace_ctx; DBUG_RETURN(error);