diff --git a/mysql-test/suite/innodb/r/innodb-replace,INPLACE.rdiff b/mysql-test/suite/innodb/r/innodb-replace,INPLACE.rdiff new file mode 100644 index 00000000000..a05e4bec7e9 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb-replace,INPLACE.rdiff @@ -0,0 +1,16 @@ +--- innodb-replace.result ++++ innodb-replace,INPLACE.result +@@ -31,10 +31,10 @@ + REPLACE INTO t1 (c1,c2,c3) VALUES (0,1,b'11'); + SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); + VARIABLE_NAME VARIABLE_VALUE +-HANDLER_DELETE 1 ++HANDLER_DELETE 2 + HANDLER_READ_KEY 2 +-HANDLER_UPDATE 1 +-HANDLER_WRITE 2 ++HANDLER_UPDATE 0 ++HANDLER_WRITE 3 + SELECT * FROM t1; + c1 c2 c3 + 0 1  diff --git a/mysql-test/suite/innodb/r/innodb-replace.result b/mysql-test/suite/innodb/r/innodb-replace.result index c926bb89a2e..16b1df73a85 100644 --- a/mysql-test/suite/innodb/r/innodb-replace.result +++ b/mysql-test/suite/innodb/r/innodb-replace.result @@ -11,3 +11,53 @@ ERROR HY000: DELAYED option not supported for table 't1' select * from t1; c1 c2 stamp drop table t1; +# +# MDEV-35115 Inconsistent Replace behaviour when multiple +# unique index exist +# +CREATE TABLE t1 (c1 NUMERIC UNSIGNED NOT NULL, +c2 INT3 UNIQUE, +c3 BIT(2) PRIMARY KEY)ENGINE=InnoDB; +ALTER TABLE t1 ADD UNIQUE INDEX(c1); +INSERT INTO t1 (c1,c2,c3) VALUES (0,0,b'01'); +INSERT INTO t1 (c1,c2,c3) VALUES (1,1,b'10'); +FLUSH STATUS; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); +VARIABLE_NAME VARIABLE_VALUE +HANDLER_DELETE 0 +HANDLER_READ_KEY 0 +HANDLER_UPDATE 0 +HANDLER_WRITE 0 +REPLACE INTO t1 (c1,c2,c3) VALUES (0,1,b'11'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); +VARIABLE_NAME VARIABLE_VALUE +HANDLER_DELETE 1 +HANDLER_READ_KEY 2 +HANDLER_UPDATE 1 +HANDLER_WRITE 2 +SELECT * FROM t1; +c1 c2 c3 +0 1  +DROP TABLE t1; +CREATE TABLE t1 (f1 INT NOT NULL PRIMARY KEY, +f2 INT, f3 INT, f4 INT, +UNIQUE INDEX i1(f2))ENGINE=InnoDB; +ALTER TABLE t1 ADD INDEX i3(f4); +ALTER TABLE t1 ADD UNIQUE INDEX i2(f3); +INSERT INTO t1 VALUES (0,0,0,0); +INSERT INTO t1 VALUES (1,1,1,1); +FLUSH STATUS; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); +VARIABLE_NAME VARIABLE_VALUE +HANDLER_DELETE 0 +HANDLER_READ_KEY 0 +HANDLER_UPDATE 0 +HANDLER_WRITE 0 +REPLACE INTO t1 VALUES (0,0,1,1); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); +VARIABLE_NAME VARIABLE_VALUE +HANDLER_DELETE 1 +HANDLER_READ_KEY 2 +HANDLER_UPDATE 1 +HANDLER_WRITE 2 +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb-replace.combinations b/mysql-test/suite/innodb/t/innodb-replace.combinations new file mode 100644 index 00000000000..e84e17b06ac --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb-replace.combinations @@ -0,0 +1,2 @@ +[COPY] +[INPLACE] diff --git a/mysql-test/suite/innodb/t/innodb-replace.test b/mysql-test/suite/innodb/t/innodb-replace.test index 8c3aacde5e8..ee3a508786d 100644 --- a/mysql-test/suite/innodb/t/innodb-replace.test +++ b/mysql-test/suite/innodb/t/innodb-replace.test @@ -20,3 +20,67 @@ select * from t1; drop table t1; # End of 4.1 tests + +--echo # +--echo # MDEV-35115 Inconsistent Replace behaviour when multiple +--echo # unique index exist +--echo # +let $get_handler_status_counts= SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME IN ('HANDLER_DELETE','HANDLER_WRITE','HANDLER_READ_KEY','HANDLER_UPDATE'); + +let $MYSQLD_DATADIR= `select @@datadir`; +let $algorithm=`select regexp_replace('$MTR_COMBINATIONS', 'innodb,\|,innodb', '')`; + +CREATE TABLE t1 (c1 NUMERIC UNSIGNED NOT NULL, + c2 INT3 UNIQUE, + c3 BIT(2) PRIMARY KEY)ENGINE=InnoDB; + +replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE ''; +eval ALTER TABLE t1 ADD UNIQUE INDEX(c1),ALGORITHM=$algorithm; +INSERT INTO t1 (c1,c2,c3) VALUES (0,0,b'01'); +INSERT INTO t1 (c1,c2,c3) VALUES (1,1,b'10'); + +FLUSH STATUS; + +--disable_ps2_protocol +eval $get_handler_status_counts; +--enable_ps2_protocol + +# INPLACE algorithm appends the index, so unique index +# reordering happened between innodb and .frm file. This +# lead to deletion of 2 existing rows for the replace statement + +# COPY algorithm does table rebuild everytime. No reordering +# happened in this case. This lead to 1 deletion of record +# and 1 update on the existing record +REPLACE INTO t1 (c1,c2,c3) VALUES (0,1,b'11'); + +--disable_ps2_protocol +eval $get_handler_status_counts; +--enable_ps2_protocol +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (f1 INT NOT NULL PRIMARY KEY, + f2 INT, f3 INT, f4 INT, + UNIQUE INDEX i1(f2))ENGINE=InnoDB; +replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE ''; +eval ALTER TABLE t1 ADD INDEX i3(f4),ALGORITHM=$algorithm; + +replace_result ,ALGORITHM=COPY '' ,ALGORITHM=INPLACE ''; +eval ALTER TABLE t1 ADD UNIQUE INDEX i2(f3),ALGORITHM=$algorithm; + +INSERT INTO t1 VALUES (0,0,0,0); +INSERT INTO t1 VALUES (1,1,1,1); + +FLUSH STATUS; +--disable_ps2_protocol +eval $get_handler_status_counts; +--enable_ps2_protocol + +REPLACE INTO t1 VALUES (0,0,1,1); + +--disable_ps2_protocol +eval $get_handler_status_counts; +--enable_ps2_protocol + +DROP TABLE t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f12212c1aeb..6fd3d6d8cdf 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -5590,15 +5590,15 @@ innobase_build_v_templ( } /** Check consistency between .frm indexes and InnoDB indexes. -@param[in] table table object formed from .frm @param[in] ib_table InnoDB table definition @retval true if not errors were found */ -static bool -check_index_consistency(const TABLE* table, const dict_table_t* ib_table) +bool +ha_innobase::check_index_consistency(const dict_table_t* ib_table) noexcept { ulint mysql_num_index = table->s->keys; ulint ib_num_index = UT_LIST_GET_LEN(ib_table->indexes); bool ret = true; + ulint last_unique = 0; /* If there exists inconsistency between MySQL and InnoDB dictionary (metadata) information, the number of index defined in MySQL @@ -5633,8 +5633,21 @@ check_index_consistency(const TABLE* table, const dict_table_t* ib_table) ret = false; goto func_exit; } - } + if (index->is_unique()) { + ulint i = 0; + while ((index = UT_LIST_GET_PREV(indexes, index))) i++; + /* Check if any unique index in InnoDB + dictionary are re-ordered compared to + the index in .frm */ + if (last_unique > i) { + m_int_table_flags + |= HA_DUPLICATE_KEY_NOT_IN_ORDER; + } + + last_unique = i; + } + } func_exit: return ret; } @@ -5874,7 +5887,7 @@ ha_innobase::open(const char* name, int, uint) mutex_exit(&dict_sys.mutex); } - if (!check_index_consistency(table, ib_table)) { + if (!check_index_consistency(ib_table)) { sql_print_error("InnoDB indexes are inconsistent with what " "defined in .frm for table %s", name); diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index bd99b230a67..ea0018cad25 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -437,6 +437,12 @@ public: const KEY_PART_INFO& old_part, const KEY_PART_INFO& new_part) const override; + /** Check consistency between .frm indexes and InnoDB indexes + Set HA_DUPLICATE_KEY_NOT_IN_ORDER if multiple unique index + are not in the correct order. + @param ib_table InnoDB table definition + @retval true if not errors were found */ + bool check_index_consistency(const dict_table_t* ib_table) noexcept; protected: bool can_convert_string(const Field_string* field,