Skip to content
Permalink
Browse files

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: Jimmy Yang <jimmy.yang@oracle.com>
RB: 13748
  • Loading branch information...
Thirunarayanan authored and dr-m committed Aug 26, 2016
1 parent 82563c5 commit 07ba5560da805c766da68811692272dd9eebdaf7
@@ -0,0 +1,95 @@
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);
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
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';
# 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';
# 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
SET DEBUG_SYNC= 'RESET';
DROP TABLE t1;
@@ -0,0 +1,91 @@
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_debug_sync.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);

--echo # CONNECTION default
connection default;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
SET DEBUG_SYNC= 'now SIGNAL opened1';

--echo # CONNECTION con1
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);

--echo # CONNECTION DEFAULT
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);

--echo # CONNECTION con1
connection con1;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
SET DEBUG_SYNC= 'now SIGNAL opened1';

--echo # CONNECTION default
connection default;
reap;
SELECT * FROM t1;
SHOW CREATE TABLE t1;
disconnect con1;
SET DEBUG_SYNC= 'RESET';
DROP TABLE t1;
@@ -5350,6 +5350,7 @@ ha_innobase::write_row(
innodb_srv_conc_enter_innodb(prebuilt->trx);

error = row_insert_for_mysql((byte*) record, prebuilt);
DEBUG_SYNC(user_thd, "ib_after_row_insert");

/* Handle duplicate key errors */
if (auto_inc_used) {
@@ -10461,6 +10462,37 @@ ha_innobase::get_auto_increment(
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(user_thd) != SQLCOM_ALTER_TABLE
&& autoinc < col_max_value) {

ulint 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) {

@@ -1241,6 +1241,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) {
@@ -11731,6 +11731,37 @@ ha_innobase::get_auto_increment(
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(user_thd) != SQLCOM_ALTER_TABLE
&& autoinc < col_max_value) {

ulint 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) {

@@ -1254,6 +1254,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) {

0 comments on commit 07ba556

Please sign in to comment.
You can’t perform that action at this time.