Skip to content

Commit

Permalink
Bug#15851528 DUPLICATE KEY ERROR ON AUTO-INC PK WITH MIXED AUTO_INCRE…
Browse files Browse the repository at this point in the history
…MENT_INCREMENT CLIENTS

Problem: Clients running different values for auto_increment_increment
and doing concurrent inserts leads to "Duplicate key error" in one of them.

Analysis:
When auto_increment_increment value is reduced in a session,
InnoDB uses last auto_increment_increment value
to recalculate the autoinc value.
In case, some other session has inserted a value
with different auto_increment_increment, InnoDB recalculate
autoinc values based on current session previous auto_increment_increment
instead of considering the auto_increment_increment used for last insert
across all session

Fix:
revert 7acdf29
a.k.a. 7c12a9e
as it causing the bug.

Reviewed By:
Bin <bin.x.su@oracle.com>
Kevin <kevin.lewis@oracle.com>
RB#21777

Note: In MariaDB Server, earlier changes in
ae5bc05
for MDEV-533 require that the original test in
mysql/mysql-server@1ccd472
be adjusted for MariaDB.

Also, ef47b62 (MDEV-8827)
had to be reverted after the upstream fix had been backported.
  • Loading branch information
Rahul Malik authored and dr-m committed Jul 23, 2019
1 parent 7153e15 commit 739f523
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 34 deletions.
132 changes: 130 additions & 2 deletions mysql-test/suite/innodb/r/innodb-autoinc.result
Original file line number Diff line number Diff line change
Expand Up @@ -1340,11 +1340,139 @@ SELECT * FROM t;
i
1
301
351
601
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`i` int(11) NOT NULL AUTO_INCREMENT,
KEY `i` (`i`)
) ENGINE=InnoDB AUTO_INCREMENT=401 DEFAULT CHARSET=latin1
) ENGINE=InnoDB AUTO_INCREMENT=651 DEFAULT CHARSET=latin1
DROP TABLE t;
#
# Bug#15851528 DUPLICATE KEY ERROR ON AUTO-INC PK WITH MIXED AUTO_INCREMENT_INCREMENT CLIENTS
#
# This test shows that the next record to be inserted is not affected
# by a change in auto_increment_increment.
# In addition, current value of auto_increment_increment by the client
# that uses the existing autoinc value with be used to set next autoinc
# value, which will be used by next client reguardless of its own session
# setting for auto_increment_increment.
#
# Client 1: Insert a record with auto_increment_increment=2
CREATE TABLE t(
a SERIAL PRIMARY KEY,
b VARCHAR(200)) ENGINE=InnoDB;
SET SESSION auto_increment_increment=2;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S1');
SELECT a,b FROM t;
a b
1 S1
# Client 2: Insert records with auto_increment_increment 2,1
SET SESSION auto_increment_increment=2;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S2');
SELECT a,b FROM t;
a b
1 S1
3 S2
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S2');
SELECT a,b FROM t;
a b
1 S1
3 S2
5 S2
# Client 1: Insert a record with auto_increment_increment=1
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S1');
SELECT a,b FROM t;
a b
1 S1
3 S2
5 S2
6 S1
DROP TABLE t;
# Autoincrement behaviour with mixed insert.
CREATE TABLE t(
a TINYINT AUTO_INCREMENT PRIMARY KEY,
b VARCHAR(200)) ENGINE=InnoDB;
SET SESSION auto_increment_increment=10;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` tinyint(4) NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S0'),('S1');
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` tinyint(4) NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=latin1
INSERT INTO t(a,b) VALUES(28,'S2');
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` tinyint(4) NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S3');
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` tinyint(4) NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=latin1
INSERT INTO t(b) VALUES('S4');
SELECT * FROM t;
a b
1 S0
11 S1
28 S2
31 S3
32 S4
SHOW CREATE TABLE t;
Table Create Table
t CREATE TABLE `t` (
`a` tinyint(4) NOT NULL AUTO_INCREMENT,
`b` varchar(200) DEFAULT NULL,
PRIMARY KEY (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=latin1
DROP TABLE t;
60 changes: 60 additions & 0 deletions mysql-test/suite/innodb/t/innodb-autoinc.test
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,63 @@ INSERT INTO t VALUES (NULL);
SELECT * FROM t;
SHOW CREATE TABLE t;
DROP TABLE t;

--echo #
--echo # Bug#15851528 DUPLICATE KEY ERROR ON AUTO-INC PK WITH MIXED AUTO_INCREMENT_INCREMENT CLIENTS
--echo #
--echo # This test shows that the next record to be inserted is not affected
--echo # by a change in auto_increment_increment.
--echo # In addition, current value of auto_increment_increment by the client
--echo # that uses the existing autoinc value with be used to set next autoinc
--echo # value, which will be used by next client reguardless of its own session
--echo # setting for auto_increment_increment.
--echo #

--connection default
--echo # Client 1: Insert a record with auto_increment_increment=2
CREATE TABLE t(
a SERIAL PRIMARY KEY,
b VARCHAR(200)) ENGINE=InnoDB;
SET SESSION auto_increment_increment=2;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S1');
SELECT a,b FROM t;
--connect(con1,localhost,root,,)

--connection con1
--echo # Client 2: Insert records with auto_increment_increment 2,1
SET SESSION auto_increment_increment=2;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S2');
SELECT a,b FROM t;
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S2');
SELECT a,b FROM t;
disconnect con1;

--connection default
--echo # Client 1: Insert a record with auto_increment_increment=1
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S1');
SELECT a,b FROM t;
DROP TABLE t;

--echo # Autoincrement behaviour with mixed insert.
CREATE TABLE t(
a TINYINT AUTO_INCREMENT PRIMARY KEY,
b VARCHAR(200)) ENGINE=InnoDB;
SET SESSION auto_increment_increment=10;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S0'),('S1');
SHOW CREATE TABLE t;
INSERT INTO t(a,b) VALUES(28,'S2');
SET SESSION auto_increment_increment=1;
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S3');
SHOW CREATE TABLE t;
INSERT INTO t(b) VALUES('S4');
SELECT * FROM t;
SHOW CREATE TABLE t;
DROP TABLE t;
18 changes: 2 additions & 16 deletions storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*****************************************************************************
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2010, 2019, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
Expand Down Expand Up @@ -10529,21 +10530,6 @@ ha_innobase::get_auto_increment(

current = *first_value;

/* If the increment step of the auto increment column
decreases then it is not affecting the immediate
next value in the series. */
if (prebuilt->autoinc_increment > increment) {

current = autoinc - prebuilt->autoinc_increment;

current = innobase_next_autoinc(
current, 1, increment, 1, col_max_value);

dict_table_autoinc_initialize(prebuilt->table, current);

*first_value = current;
}

/* Compute the last value in the interval */
next_value = innobase_next_autoinc(
current, *nb_reserved_values, increment, offset,
Expand Down
18 changes: 2 additions & 16 deletions storage/xtradb/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/*****************************************************************************
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2010, 2019, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
Expand Down Expand Up @@ -11798,21 +11799,6 @@ ha_innobase::get_auto_increment(

current = *first_value;

/* If the increment step of the auto increment column
decreases then it is not affecting the immediate
next value in the series. */
if (prebuilt->autoinc_increment > increment) {

current = autoinc - prebuilt->autoinc_increment;

current = innobase_next_autoinc(
current, 1, increment, 1, col_max_value);

dict_table_autoinc_initialize(prebuilt->table, current);

*first_value = current;
}

/* Compute the last value in the interval */
next_value = innobase_next_autoinc(
current, *nb_reserved_values, increment, offset,
Expand Down

0 comments on commit 739f523

Please sign in to comment.