1
0
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:
Thirunarayanan Balathandayuthapani
2024-11-08 12:15:55 +05:30
parent 98d57719e2
commit 7afee25b08
6 changed files with 156 additions and 5 deletions

View 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 

View File

@ -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;

View File

@ -0,0 +1,2 @@
[COPY]
[INPLACE]

View File

@ -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;

View File

@ -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);

View File

@ -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,