Skip to content

Commit 217c404

Browse files
author
Bin Su
committed
Bug#28089240 - AUTOINC NOT UPDATED PROPERLY DURING INSTANT ALTER TABLE LEADS TO DUPLICATE KEY
This is a regression from WL#11250. To alter table instantly, InnoDB forgot to remember the latest AUTOINC counter in dd::Table, thus after reopening the table, it picked a stale AUTOINC value which was already used. The solution is simply to remember the next AUTOINC counter for both normal table and partitioned table during ALTER TABLE, if this can be done instantly. RB: 19999 Reviewed-by: Jimmy Yang <jimmy.yang@oracle.com>
1 parent 77ef5e2 commit 217c404

File tree

3 files changed

+120
-7
lines changed

3 files changed

+120
-7
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
CREATE PROCEDURE test.sp2 (IN start BIGINT)
2+
BEGIN
3+
SET @idx =start;
4+
WHILE (@idx > 0) DO
5+
ALTER TABLE test.t1 ADD n2 BIGINT;
6+
ALTER TABLE test.t1 DROP COLUMN n2;
7+
SET @idx = @idx - 1;
8+
END WHILE;
9+
END|
10+
CREATE PROCEDURE test.sp1 (IN start BIGINT)
11+
BEGIN
12+
SET @idx =start;
13+
WHILE (@idx > 0) DO
14+
INSERT INTO test.t1 (c2,c3) VALUES(repeat('q',10),@idx);
15+
SET @idx = @idx - 1;
16+
END WHILE;
17+
END|
18+
CREATE TABLE t1 (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 TEXT(1024), c3 INT);
19+
call test.sp2(30);;
20+
call test.sp1(300);;
21+
CHECK TABLE t1;
22+
Table Op Msg_type Msg_text
23+
test.t1 check status OK
24+
DROP TABLE t1;
25+
CREATE TABLE t1 (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 TEXT(1024), c3 INT) PARTITION BY HASH (c1) PARTITIONS 2;
26+
call test.sp2(30);;
27+
call test.sp1(300);;
28+
CHECK TABLE t1;
29+
Table Op Msg_type Msg_text
30+
test.t1 check status OK
31+
DROP PROCEDURE IF EXISTS test.sp1;
32+
DROP PROCEDURE IF EXISTS test.sp2;
33+
DROP TABLE t1;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--source include/count_sessions.inc
2+
3+
delimiter |;
4+
#Procedure for alter(DDL)
5+
CREATE PROCEDURE test.sp2 (IN start BIGINT)
6+
BEGIN
7+
SET @idx =start;
8+
WHILE (@idx > 0) DO
9+
ALTER TABLE test.t1 ADD n2 BIGINT;
10+
ALTER TABLE test.t1 DROP COLUMN n2;
11+
SET @idx = @idx - 1;
12+
END WHILE;
13+
END|
14+
15+
#Procedure for INSERT
16+
CREATE PROCEDURE test.sp1 (IN start BIGINT)
17+
BEGIN
18+
SET @idx =start;
19+
WHILE (@idx > 0) DO
20+
INSERT INTO test.t1 (c2,c3) VALUES(repeat('q',10),@idx);
21+
SET @idx = @idx - 1;
22+
END WHILE;
23+
END|
24+
25+
delimiter ;|
26+
27+
28+
connect (con1,localhost,root,,);
29+
30+
connection default;
31+
CREATE TABLE t1 (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 TEXT(1024), c3 INT);
32+
--send call test.sp2(30);
33+
34+
connection con1;
35+
--send call test.sp1(300);
36+
--reap
37+
38+
connection default;
39+
--reap
40+
41+
CHECK TABLE t1;
42+
DROP TABLE t1;
43+
44+
CREATE TABLE t1 (c1 INT AUTO_INCREMENT PRIMARY KEY, c2 TEXT(1024), c3 INT) PARTITION BY HASH (c1) PARTITIONS 2;
45+
--send call test.sp2(30);
46+
47+
connection con1;
48+
--send call test.sp1(300);
49+
--reap
50+
51+
connection default;
52+
--reap
53+
54+
CHECK TABLE t1;
55+
56+
disconnect con1;
57+
58+
DROP PROCEDURE IF EXISTS test.sp1;
59+
DROP PROCEDURE IF EXISTS test.sp2;
60+
61+
DROP TABLE t1;
62+
63+
--source include/wait_until_count_sessions.inc

storage/innobase/handler/handler0alter.cc

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,14 +1065,16 @@ static void dd_commit_inplace_no_change(const Table *old_dd_tab,
10651065
@param[in] old_table MySQL table as it is before the ALTER operation
10661066
@param[in] altered_table MySQL table that is being altered
10671067
@param[in] old_dd_tab Old dd::Table or dd::Partition
1068-
@param[in,out] new_dd_tab New dd::Table or dd::Partition */
1068+
@param[in,out] new_dd_tab New dd::Table or dd::Partition
1069+
@param[in] autoinc autoinc counter pointer if AUTO_INCREMENT
1070+
is defined for the table, otherwise nullptr */
10691071
template <typename Table>
10701072
static void dd_commit_inplace_instant(Alter_inplace_info *ha_alter_info,
10711073
THD *thd, trx_t *trx, dict_table_t *table,
10721074
const TABLE *old_table,
10731075
const TABLE *altered_table,
10741076
const Table *old_dd_tab,
1075-
Table *new_dd_tab);
1077+
Table *new_dd_tab, uint64_t *autoinc);
10761078

10771079
/** Update table level instant metadata in commit phase
10781080
@param[in] table InnoDB table object
@@ -1220,7 +1222,10 @@ bool ha_innobase::commit_inplace_alter_table(TABLE *altered_table,
12201222
ut_ad(!res);
12211223
dd_commit_inplace_instant(ha_alter_info, m_user_thd, m_prebuilt->trx,
12221224
m_prebuilt->table, table, altered_table,
1223-
old_dd_tab, new_dd_tab);
1225+
old_dd_tab, new_dd_tab,
1226+
altered_table->found_next_number_field != nullptr
1227+
? &m_prebuilt->table->autoinc
1228+
: nullptr);
12241229
} else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
12251230
ctx == nullptr) {
12261231
ut_ad(!res);
@@ -4012,7 +4017,7 @@ static void dd_commit_inplace_instant(Alter_inplace_info *ha_alter_info,
40124017
const TABLE *old_table,
40134018
const TABLE *altered_table,
40144019
const Table *old_dd_tab,
4015-
Table *new_dd_tab) {
4020+
Table *new_dd_tab, uint64_t *autoinc) {
40164021
ut_ad(is_instant(ha_alter_info));
40174022

40184023
Instant_Type type =
@@ -4056,6 +4061,14 @@ static void dd_commit_inplace_instant(Alter_inplace_info *ha_alter_info,
40564061
default:
40574062
ut_ad(0);
40584063
}
4064+
4065+
if (autoinc != nullptr) {
4066+
ut_ad(altered_table->found_next_number_field != nullptr);
4067+
if (!dd_table_is_partitioned(new_dd_tab->table()) ||
4068+
dd_part_is_first(reinterpret_cast<dd::Partition *>(new_dd_tab))) {
4069+
dd_set_autoinc(new_dd_tab->table().se_private_data(), *autoinc);
4070+
}
4071+
}
40594072
}
40604073

40614074
/** Check if a new table's index will exceed the index limit for the table
@@ -9963,9 +9976,13 @@ bool ha_innopart::commit_inplace_alter_table(TABLE *altered_table,
99639976
static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
99649977

99659978
if (is_instant(ha_alter_info)) {
9966-
dd_commit_inplace_instant(ha_alter_info, m_user_thd, m_prebuilt->trx,
9967-
m_part_share->get_table_part(i), table,
9968-
altered_table, old_part, new_part);
9979+
dd_commit_inplace_instant(
9980+
ha_alter_info, m_user_thd, m_prebuilt->trx,
9981+
m_part_share->get_table_part(i), table, altered_table, old_part,
9982+
new_part,
9983+
altered_table->found_next_number_field != nullptr
9984+
? reinterpret_cast<uint64_t *>(&m_part_share->next_auto_inc_val)
9985+
: nullptr);
99699986
} else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
99709987
ctx == nullptr) {
99719988
dd_commit_inplace_no_change(old_part, new_part, true);

0 commit comments

Comments
 (0)