From a23f213fd4f69072c7d8a7133b7cf02c4c288b77 Mon Sep 17 00:00:00 2001 From: Raghunandan Bhat Date: Mon, 6 Apr 2026 19:18:21 +0530 Subject: [PATCH] MDEV-34951: InnoDB index corruption when renaming key name with same letter to upper case. Problem: InnoDB index corruption occurs when an index is renamed to a name that differs only in case (e.g., 'b' to 'B'). The SQL layer uses case-insensitive comparison and fails to recognize the change. Fix: Use case-sensitive comparison when matching index names during ALTER TABLE to correctly identify and handle case changes. --- mysql-test/main/alter_table.result | 23 ++++++++++++++++++ mysql-test/main/alter_table.test | 26 +++++++++++++++++++++ mysql-test/suite/parts/r/alter_table.result | 18 ++++++++++++++ mysql-test/suite/parts/t/alter_table.test | 24 +++++++++++++++++++ sql/sql_table.cc | 15 ++++-------- 5 files changed, 96 insertions(+), 10 deletions(-) diff --git a/mysql-test/main/alter_table.result b/mysql-test/main/alter_table.result index a312964c54747..99fda9c2ab51a 100644 --- a/mysql-test/main/alter_table.result +++ b/mysql-test/main/alter_table.result @@ -3131,5 +3131,28 @@ alter table t1 drop constraint t1_fk_t2_id, drop t2_id, drop t2_id; ERROR 42000: Can't DROP COLUMN `t2_id`; check that it exists drop table t1, t2; # +# MDEV-34951: Innodb index corruption when renaming key name with same letter to upper case. +# +CREATE TABLE t (a INT, b INT, KEY(a), KEY(b)) ENGINE=INNODB; +ALTER TABLE t RENAME KEY b TO B; +SELECT * FROM t; +a b +DROP TABLE t; +CREATE TABLE t (c INT UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c TO C; +SELECT * FROM t; +c +DROP TABLE t; +CREATE TABLE t (c POINT GENERATED ALWAYS AS (POINT(1,1)) UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c to C; +INSERT INTO t VALUES (1); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +DROP TABLE t; +CREATE TABLE t (c POINT GENERATED ALWAYS AS (POINT(1,1)) UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c to C; +INSERT INTO t VALUES (1,1); +ERROR 21S01: Column count doesn't match value count at row 1 +DROP TABLE t; +# # End of 10.6 tests # diff --git a/mysql-test/main/alter_table.test b/mysql-test/main/alter_table.test index 2375478341dc5..440deb2789327 100644 --- a/mysql-test/main/alter_table.test +++ b/mysql-test/main/alter_table.test @@ -2417,6 +2417,32 @@ create table t1(id int primary key, t2_id int, constraint t1_fk_t2_id foreign ke alter table t1 drop constraint t1_fk_t2_id, drop t2_id, drop t2_id; drop table t1, t2; +--echo # +--echo # MDEV-34951: Innodb index corruption when renaming key name with same letter to upper case. +--echo # + +CREATE TABLE t (a INT, b INT, KEY(a), KEY(b)) ENGINE=INNODB; +ALTER TABLE t RENAME KEY b TO B; +SELECT * FROM t; +DROP TABLE t; + +CREATE TABLE t (c INT UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c TO C; +SELECT * FROM t; +DROP TABLE t; + +CREATE TABLE t (c POINT GENERATED ALWAYS AS (POINT(1,1)) UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c to C; +--error ER_CANT_CREATE_GEOMETRY_OBJECT +INSERT INTO t VALUES (1); +DROP TABLE t; + +CREATE TABLE t (c POINT GENERATED ALWAYS AS (POINT(1,1)) UNIQUE) ENGINE=InnoDB; +ALTER TABLE t RENAME KEY c to C; +--error ER_WRONG_VALUE_COUNT_ON_ROW +INSERT INTO t VALUES (1,1); +DROP TABLE t; + --echo # --echo # End of 10.6 tests --echo # diff --git a/mysql-test/suite/parts/r/alter_table.result b/mysql-test/suite/parts/r/alter_table.result index 5f53245521948..c5eec602f15f6 100644 --- a/mysql-test/suite/parts/r/alter_table.result +++ b/mysql-test/suite/parts/r/alter_table.result @@ -74,3 +74,21 @@ PARTITION `p_31` VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB); ALTER online TABLE t1 MODIFY COLUMN `f4` VARCHAR(500) , ALGORITHM=INSTANT, LOCK=NONE; drop table t1; # End of 10.5 tests +# +# MDEV-34951: Innodb index corruption when renaming key name with same letter to upper case. +# +# Tests on partitioned tables +# partitioned table with index 'idx' (lowercase) +CREATE TABLE t_part (a INT, KEY idx(a)) ENGINE=InnoDB +PARTITION BY HASH(a) PARTITIONS 1; +# non-partitioned table with index 'IDX' (uppercase) +CREATE TABLE t_nonpart (a INT, KEY IDX(a)) ENGINE=InnoDB; +INSERT INTO t_part VALUES (1); +# Exchange partition +# ALTER TABLE should fail. Without fix, ALTER TABLE succeeds, SELECT fails. +ALTER TABLE t_part EXCHANGE PARTITION p0 WITH TABLE t_nonpart; +ERROR HY000: Tables have different definitions +SELECT * FROM t_nonpart; +a +DROP TABLE t_part, t_nonpart; +# End of 10.6 tests diff --git a/mysql-test/suite/parts/t/alter_table.test b/mysql-test/suite/parts/t/alter_table.test index 00ac3f0265eb6..878d218289af9 100644 --- a/mysql-test/suite/parts/t/alter_table.test +++ b/mysql-test/suite/parts/t/alter_table.test @@ -72,3 +72,27 @@ ALTER online TABLE t1 MODIFY COLUMN `f4` VARCHAR(500) , ALGORITHM=INSTANT, LOCK drop table t1; --echo # End of 10.5 tests + +--echo # +--echo # MDEV-34951: Innodb index corruption when renaming key name with same letter to upper case. +--echo # + +--echo # Tests on partitioned tables +--echo # partitioned table with index 'idx' (lowercase) +CREATE TABLE t_part (a INT, KEY idx(a)) ENGINE=InnoDB +PARTITION BY HASH(a) PARTITIONS 1; + +--echo # non-partitioned table with index 'IDX' (uppercase) +CREATE TABLE t_nonpart (a INT, KEY IDX(a)) ENGINE=InnoDB; + +INSERT INTO t_part VALUES (1); + +--echo # Exchange partition +--echo # ALTER TABLE should fail. Without fix, ALTER TABLE succeeds, SELECT fails. +--error ER_TABLES_DIFFERENT_METADATA +ALTER TABLE t_part EXCHANGE PARTITION p0 WITH TABLE t_nonpart; + +SELECT * FROM t_nonpart; +DROP TABLE t_part, t_nonpart; + +--echo # End of 10.6 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c8be7fb1baf56..cd013093f2279 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6935,8 +6935,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, new_key < new_key_end; new_key++) { - if (!lex_string_cmp(system_charset_info, &table_key->name, - &new_key->name)) + if (!cmp(&table_key->name, &new_key->name)) break; } if (new_key >= new_key_end) @@ -6986,8 +6985,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, /* Search an old key with the same name. */ for (table_key= table->key_info; table_key < table_key_end; table_key++) { - if (!lex_string_cmp(system_charset_info, &table_key->name, - &new_key->name)) + if (!cmp(&table_key->name, &new_key->name)) break; } if (table_key >= table_key_end) @@ -7019,8 +7017,7 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar, continue; } - DBUG_ASSERT( - lex_string_cmp(system_charset_info, &old_key->name, &new_key->name)); + DBUG_ASSERT(cmp(&old_key->name, &new_key->name)); ha_alter_info->handler_flags|= ALTER_RENAME_INDEX; ha_alter_info->rename_keys.push_back( @@ -7292,8 +7289,7 @@ bool mysql_compare_tables(TABLE *table, Alter_info *alter_info, /* Search a key with the same name. */ for (new_key= key_info_buffer; new_key < new_key_end; new_key++) { - if (!lex_string_cmp(system_charset_info, &table_key->name, - &new_key->name)) + if (!cmp(&table_key->name, &new_key->name)) break; } if (new_key >= new_key_end) @@ -7332,8 +7328,7 @@ bool mysql_compare_tables(TABLE *table, Alter_info *alter_info, /* Search a key with the same name. */ for (table_key= table->s->key_info; table_key < table_key_end; table_key++) { - if (!lex_string_cmp(system_charset_info, &table_key->name, - &new_key->name)) + if (!cmp(&table_key->name, &new_key->name)) break; } if (table_key >= table_key_end)