Commit 223eb5fb authored by Thirunarayanan Balathandayuthapani's avatar Thirunarayanan Balathandayuthapani Committed by Marko Mäkelä

Bug #20989615 INNODB AUTO_INCREMENT PRODUCES SAME VALUE TWICE

Problem:
=======
Autoincrement value gives duplicate values because of the following reasons.

(1) In InnoDB handler function, current autoincrement value is not changed
based on newly set auto_increment_increment or auto_increment_offset variable.

(2) Handler function does the rounding logic and changes the current
autoincrement value and InnoDB doesn't aware of the change in current
autoincrement value.

Solution:
========
Fix the problem(1), InnoDB always respect the auto_increment_increment
and auto_increment_offset value in case of current autoincrement value.
By fixing the problem (2), handler layer won't change any current
autoincrement value.
Reviewed-by: default avatarJimmy Yang <jimmy.yang@oracle.com>
RB: 13748
parent 01389ee8
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
# SETTING auto_increment_increment IN CONNECTION DEFAULT
SET AUTO_INCREMENT_INCREMENT = 1;
INSERT INTO t1 VALUES(NULL);
SELECT * FROM t1;
id
1
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
# SETTING auto_increment_increment IN CONNECTION1
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
INSERT INTO t1 VALUES(NULL);
connect con1, localhost, root,,;
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
insert into t1 values(NULL);
connection default;
SELECT * FROM t1;
id
1
3
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
SET DEBUG_SYNC= 'now SIGNAL opened1';
connection con1;
SELECT * FROM t1;
id
1
3
5
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
connection default;
disconnect con1;
DROP TABLE t1;
CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
# SETTING auto_increment_increment IN CONNECTION DEFAULT
SET AUTO_INCREMENT_INCREMENT = 1;
INSERT INTO t1 VALUES(NULL);
SELECT * FROM t1;
id
1
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
SET DEBUG_SYNC = 'now SIGNAL flushed';
connect con1, localhost, root,,;
# SETTING auto_increment_increment in connection1
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'now WAIT_FOR flushed';
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
INSERT INTO t1 values(NULL);
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
INSERT INTO t1 VALUES(NULL);
connection con1;
SELECT * FROM t1;
id
1
3
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
SET DEBUG_SYNC= 'now SIGNAL opened1';
disconnect con1;
connection default;
SELECT * FROM t1;
id
1
3
5
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
DROP TABLE t1;
SET DEBUG_SYNC='RESET';
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/not_embedded.inc
# Two parallel connection with autoinc column after restart.
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
SET AUTO_INCREMENT_INCREMENT = 1;
INSERT INTO t1 VALUES(NULL);
SELECT * FROM t1;
SHOW CREATE TABLE t1;
--source include/restart_mysqld.inc
--echo # SETTING auto_increment_increment IN CONNECTION1
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
SEND INSERT INTO t1 VALUES(NULL);
connect(con1, localhost, root,,);
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
send insert into t1 values(NULL);
connection default;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
SET DEBUG_SYNC= 'now SIGNAL opened1';
connection con1;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
connection default;
disconnect con1;
DROP TABLE t1;
# Two parallel connection with autoinc column without restart.
CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
SET AUTO_INCREMENT_INCREMENT = 1;
INSERT INTO t1 VALUES(NULL);
SELECT * FROM t1;
SHOW CREATE TABLE t1;
SET DEBUG_SYNC = 'now SIGNAL flushed';
connect(con1, localhost, root,,);
--echo # SETTING auto_increment_increment in connection1
SET AUTO_INCREMENT_INCREMENT = 2;
SET DEBUG_SYNC= 'now WAIT_FOR flushed';
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
send INSERT INTO t1 values(NULL);
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR opened';
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
send INSERT INTO t1 VALUES(NULL);
connection con1;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
SET DEBUG_SYNC= 'now SIGNAL opened1';
disconnect con1;
connection default;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
DROP TABLE t1;
SET DEBUG_SYNC='RESET';
......@@ -17667,6 +17667,37 @@ ha_innobase::get_auto_increment(
whether we update the table autoinc counter or not. */
ulonglong col_max_value = innobase_get_int_col_max_value(table->next_number_field);
/** The following logic is needed to avoid duplicate key error
for autoincrement column.
(1) InnoDB gives the current autoincrement value with respect
to increment and offset value.
(2) Basically it does compute_next_insert_id() logic inside InnoDB
to avoid the current auto increment value changed by handler layer.
(3) It is restricted only for insert operations. */
if (increment > 1 && thd_sql_command(m_user_thd) != SQLCOM_ALTER_TABLE
&& autoinc < col_max_value) {
ulonglong prev_auto_inc = autoinc;
autoinc = ((autoinc - 1) + increment - offset)/ increment;
autoinc = autoinc * increment + offset;
/* If autoinc exceeds the col_max_value then reset
to old autoinc value. Because in case of non-strict
sql mode, boundary value is not considered as error. */
if (autoinc >= col_max_value) {
autoinc = prev_auto_inc;
}
ut_ad(autoinc > 0);
}
/* Called for the first time ? */
if (trx->n_autoinc_rows == 0) {
......
......@@ -1505,6 +1505,8 @@ row_insert_for_mysql(
row_ins_step(thr);
DEBUG_SYNC_C("ib_after_row_insert_step");
err = trx->error_state;
if (err != DB_SUCCESS) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment