mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-35115 Inconsistent Replace behaviour when multiple unique index exist
- Replace statement fails with duplicate key error when multiple unique key conflict happens. Reason is that Server expects the InnoDB engine to store the confliciting keys in ascending order. But the InnoDB doesn't store the conflicting keys in ascending order. Fix: === - Enable HA_DUPLICATE_KEY_NOT_IN_ORDER for InnoDB storage engine only when unique index order is different in .frm and innodb dictionary.
This commit is contained in:
16
mysql-test/suite/innodb/r/innodb-replace,INPLACE.rdiff
Normal file
16
mysql-test/suite/innodb/r/innodb-replace,INPLACE.rdiff
Normal file
@ -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
|
@ -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;
|
||||
|
2
mysql-test/suite/innodb/t/innodb-replace.combinations
Normal file
2
mysql-test/suite/innodb/t/innodb-replace.combinations
Normal file
@ -0,0 +1,2 @@
|
||||
[COPY]
|
||||
[INPLACE]
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user