Skip to content

Commit 07ba556

Browse files
Thirunarayanandr-m
authored andcommitted
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
1 parent 82563c5 commit 07ba556

File tree

6 files changed

+253
-0
lines changed

6 files changed

+253
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
2+
# SETTING auto_increment_increment IN CONNECTION DEFAULT
3+
SET AUTO_INCREMENT_INCREMENT = 1;
4+
INSERT INTO t1 VALUES(NULL);
5+
SELECT * FROM t1;
6+
id
7+
1
8+
SHOW CREATE TABLE t1;
9+
Table Create Table
10+
t1 CREATE TABLE `t1` (
11+
`id` int(11) NOT NULL AUTO_INCREMENT,
12+
PRIMARY KEY (`id`)
13+
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
14+
# SETTING auto_increment_increment IN CONNECTION1
15+
SET AUTO_INCREMENT_INCREMENT = 2;
16+
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
17+
INSERT INTO t1 VALUES(NULL);
18+
SET AUTO_INCREMENT_INCREMENT = 2;
19+
SET DEBUG_SYNC= 'now WAIT_FOR opened';
20+
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
21+
insert into t1 values(NULL);
22+
# CONNECTION default
23+
SELECT * FROM t1;
24+
id
25+
1
26+
3
27+
SHOW CREATE TABLE t1;
28+
Table Create Table
29+
t1 CREATE TABLE `t1` (
30+
`id` int(11) NOT NULL AUTO_INCREMENT,
31+
PRIMARY KEY (`id`)
32+
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
33+
SET DEBUG_SYNC= 'now SIGNAL opened1';
34+
# CONNECTION con1
35+
SELECT * FROM t1;
36+
id
37+
1
38+
3
39+
5
40+
SHOW CREATE TABLE t1;
41+
Table Create Table
42+
t1 CREATE TABLE `t1` (
43+
`id` int(11) NOT NULL AUTO_INCREMENT,
44+
PRIMARY KEY (`id`)
45+
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
46+
DROP TABLE t1;
47+
CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
48+
# SETTING auto_increment_increment IN CONNECTION DEFAULT
49+
SET AUTO_INCREMENT_INCREMENT = 1;
50+
INSERT INTO t1 VALUES(NULL);
51+
SELECT * FROM t1;
52+
id
53+
1
54+
SHOW CREATE TABLE t1;
55+
Table Create Table
56+
t1 CREATE TABLE `t1` (
57+
`id` int(11) NOT NULL AUTO_INCREMENT,
58+
PRIMARY KEY (`id`)
59+
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
60+
SET DEBUG_SYNC = 'now SIGNAL flushed';
61+
# SETTING auto_increment_increment in connection1
62+
SET AUTO_INCREMENT_INCREMENT = 2;
63+
SET DEBUG_SYNC= 'now WAIT_FOR flushed';
64+
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
65+
INSERT INTO t1 values(NULL);
66+
# CONNECTION DEFAULT
67+
SET DEBUG_SYNC= 'now WAIT_FOR opened';
68+
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
69+
INSERT INTO t1 VALUES(NULL);
70+
# CONNECTION con1
71+
SELECT * FROM t1;
72+
id
73+
1
74+
3
75+
SHOW CREATE TABLE t1;
76+
Table Create Table
77+
t1 CREATE TABLE `t1` (
78+
`id` int(11) NOT NULL AUTO_INCREMENT,
79+
PRIMARY KEY (`id`)
80+
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
81+
SET DEBUG_SYNC= 'now SIGNAL opened1';
82+
# CONNECTION default
83+
SELECT * FROM t1;
84+
id
85+
1
86+
3
87+
5
88+
SHOW CREATE TABLE t1;
89+
Table Create Table
90+
t1 CREATE TABLE `t1` (
91+
`id` int(11) NOT NULL AUTO_INCREMENT,
92+
PRIMARY KEY (`id`)
93+
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
94+
SET DEBUG_SYNC= 'RESET';
95+
DROP TABLE t1;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
--source include/have_innodb.inc
2+
--source include/have_debug.inc
3+
--source include/have_debug_sync.inc
4+
--source include/not_embedded.inc
5+
6+
# Two parallel connection with autoinc column after restart.
7+
8+
CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
9+
10+
--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
11+
SET AUTO_INCREMENT_INCREMENT = 1;
12+
INSERT INTO t1 VALUES(NULL);
13+
SELECT * FROM t1;
14+
SHOW CREATE TABLE t1;
15+
16+
--source include/restart_mysqld.inc
17+
18+
--echo # SETTING auto_increment_increment IN CONNECTION1
19+
SET AUTO_INCREMENT_INCREMENT = 2;
20+
21+
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
22+
23+
SEND INSERT INTO t1 VALUES(NULL);
24+
25+
connect(con1, localhost, root,,);
26+
SET AUTO_INCREMENT_INCREMENT = 2;
27+
SET DEBUG_SYNC= 'now WAIT_FOR opened';
28+
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
29+
send insert into t1 values(NULL);
30+
31+
--echo # CONNECTION default
32+
connection default;
33+
reap;
34+
SELECT * FROM t1;
35+
SHOW CREATE TABLE t1;
36+
SET DEBUG_SYNC= 'now SIGNAL opened1';
37+
38+
--echo # CONNECTION con1
39+
connection con1;
40+
reap;
41+
SELECT * FROM t1;
42+
SHOW CREATE TABLE t1;
43+
connection default;
44+
disconnect con1;
45+
46+
DROP TABLE t1;
47+
48+
# Two parallel connection with autoinc column without restart.
49+
50+
CREATE TABLE t1(id INT AUTO_INCREMENT PRIMARY KEY)ENGINE=INNODB;
51+
52+
--echo # SETTING auto_increment_increment IN CONNECTION DEFAULT
53+
SET AUTO_INCREMENT_INCREMENT = 1;
54+
INSERT INTO t1 VALUES(NULL);
55+
SELECT * FROM t1;
56+
SHOW CREATE TABLE t1;
57+
SET DEBUG_SYNC = 'now SIGNAL flushed';
58+
59+
connect(con1, localhost, root,,);
60+
61+
--echo # SETTING auto_increment_increment in connection1
62+
SET AUTO_INCREMENT_INCREMENT = 2;
63+
64+
SET DEBUG_SYNC= 'now WAIT_FOR flushed';
65+
SET DEBUG_SYNC= 'ib_after_row_insert SIGNAL opened WAIT_FOR flushed1';
66+
67+
send INSERT INTO t1 values(NULL);
68+
69+
--echo # CONNECTION DEFAULT
70+
connection default;
71+
72+
SET DEBUG_SYNC= 'now WAIT_FOR opened';
73+
SET DEBUG_SYNC= 'ib_after_row_insert_step SIGNAL flushed1 WAIT_FOR opened1';
74+
75+
send INSERT INTO t1 VALUES(NULL);
76+
77+
--echo # CONNECTION con1
78+
connection con1;
79+
reap;
80+
SELECT * FROM t1;
81+
SHOW CREATE TABLE t1;
82+
SET DEBUG_SYNC= 'now SIGNAL opened1';
83+
84+
--echo # CONNECTION default
85+
connection default;
86+
reap;
87+
SELECT * FROM t1;
88+
SHOW CREATE TABLE t1;
89+
disconnect con1;
90+
SET DEBUG_SYNC= 'RESET';
91+
DROP TABLE t1;

storage/innobase/handler/ha_innodb.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5350,6 +5350,7 @@ ha_innobase::write_row(
53505350
innodb_srv_conc_enter_innodb(prebuilt->trx);
53515351

53525352
error = row_insert_for_mysql((byte*) record, prebuilt);
5353+
DEBUG_SYNC(user_thd, "ib_after_row_insert");
53535354

53545355
/* Handle duplicate key errors */
53555356
if (auto_inc_used) {
@@ -10461,6 +10462,37 @@ ha_innobase::get_auto_increment(
1046110462
ulonglong col_max_value = innobase_get_int_col_max_value(
1046210463
table->next_number_field);
1046310464

10465+
/** The following logic is needed to avoid duplicate key error
10466+
for autoincrement column.
10467+
10468+
(1) InnoDB gives the current autoincrement value with respect
10469+
to increment and offset value.
10470+
10471+
(2) Basically it does compute_next_insert_id() logic inside InnoDB
10472+
to avoid the current auto increment value changed by handler layer.
10473+
10474+
(3) It is restricted only for insert operations. */
10475+
10476+
if (increment > 1 && thd_sql_command(user_thd) != SQLCOM_ALTER_TABLE
10477+
&& autoinc < col_max_value) {
10478+
10479+
ulint prev_auto_inc = autoinc;
10480+
10481+
autoinc = ((autoinc - 1) + increment - offset)/ increment;
10482+
10483+
autoinc = autoinc * increment + offset;
10484+
10485+
/* If autoinc exceeds the col_max_value then reset
10486+
to old autoinc value. Because in case of non-strict
10487+
sql mode, boundary value is not considered as error. */
10488+
10489+
if (autoinc >= col_max_value) {
10490+
autoinc = prev_auto_inc;
10491+
}
10492+
10493+
ut_ad(autoinc > 0);
10494+
}
10495+
1046410496
/* Called for the first time ? */
1046510497
if (trx->n_autoinc_rows == 0) {
1046610498

storage/innobase/row/row0mysql.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,8 @@ row_insert_for_mysql(
12411241

12421242
row_ins_step(thr);
12431243

1244+
DEBUG_SYNC_C("ib_after_row_insert_step");
1245+
12441246
err = trx->error_state;
12451247

12461248
if (err != DB_SUCCESS) {

storage/xtradb/handler/ha_innodb.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11731,6 +11731,37 @@ ha_innobase::get_auto_increment(
1173111731
ulonglong col_max_value = innobase_get_int_col_max_value(
1173211732
table->next_number_field);
1173311733

11734+
/** The following logic is needed to avoid duplicate key error
11735+
for autoincrement column.
11736+
11737+
(1) InnoDB gives the current autoincrement value with respect
11738+
to increment and offset value.
11739+
11740+
(2) Basically it does compute_next_insert_id() logic inside InnoDB
11741+
to avoid the current auto increment value changed by handler layer.
11742+
11743+
(3) It is restricted only for insert operations. */
11744+
11745+
if (increment > 1 && thd_sql_command(user_thd) != SQLCOM_ALTER_TABLE
11746+
&& autoinc < col_max_value) {
11747+
11748+
ulint prev_auto_inc = autoinc;
11749+
11750+
autoinc = ((autoinc - 1) + increment - offset)/ increment;
11751+
11752+
autoinc = autoinc * increment + offset;
11753+
11754+
/* If autoinc exceeds the col_max_value then reset
11755+
to old autoinc value. Because in case of non-strict
11756+
sql mode, boundary value is not considered as error. */
11757+
11758+
if (autoinc >= col_max_value) {
11759+
autoinc = prev_auto_inc;
11760+
}
11761+
11762+
ut_ad(autoinc > 0);
11763+
}
11764+
1173411765
/* Called for the first time ? */
1173511766
if (trx->n_autoinc_rows == 0) {
1173611767

storage/xtradb/row/row0mysql.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,8 @@ row_insert_for_mysql(
12541254

12551255
row_ins_step(thr);
12561256

1257+
DEBUG_SYNC_C("ib_after_row_insert_step");
1258+
12571259
err = trx->error_state;
12581260

12591261
if (err != DB_SUCCESS) {

0 commit comments

Comments
 (0)