Skip to content

Commit 869c132

Browse files
committed
Bug #14483484 INSERT ... ON DUPLICATE KEY UPDATE + INNODB_AUTOINC_LOCK_MODE=1
IS BROKEN Problem: When a multi-row INSERT ... ON DUPLICATE KEY UPDATE statement begins execution it reserves the required number of auto increment values. When one of the insert fails because of duplicate key error, then it will become an update operation. The auto increment value of this update operation will determine the auto increment values used for subsequent inserts. This can result in duplicate auto increment values. Solution: When an insert operation of a multi-row INSERT ... ON DUPLICATE KEY UPDATE statement fails, check if the update clause explicitly updates the auto increment value. If an explicit value is given for an auto increment field, then check if it will conflict with the values that we have reserved. If it might conflict then return an error. Also, the update clause will not bump the auto increment value for the rows inserted in the multi-row INSERT ... ON DUPLICATE KEY. rb#2219 approved by Guilhem and Dmitry Shulga.
1 parent c49e0df commit 869c132

4 files changed

Lines changed: 44 additions & 7 deletions

File tree

mysql-test/suite/rpl/r/rpl_insert_id.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,14 @@ select * from t1 order by n;
233233
n b
234234
2 100
235235
4 400
236+
5 600
236237
1000 350
237-
1001 600
238238
select * from t1 order by n;
239239
n b
240240
2 100
241241
4 400
242+
5 600
242243
1000 350
243-
1001 600
244244
drop table t1;
245245
create table t1 (n int primary key auto_increment not null,
246246
b int, unique(b)) engine=myisam;

sql/share/errmsg-utf8.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7057,6 +7057,9 @@ ER_WARN_PURGE_LOG_IN_USE
70577057
ER_WARN_PURGE_LOG_IS_ACTIVE
70587058
eng "file %s was not purged because it is the active log file."
70597059

7060+
ER_AUTO_INCREMENT_CONFLICT
7061+
eng "Auto-increment value in UPDATE conflicts with internally generated values"
7062+
70607063
#
70617064
# End of 5.6 error messages.
70627065
#

sql/sql_insert.cc

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,37 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
17121712
TRG_EVENT_UPDATE))
17131713
goto before_trg_err;
17141714

1715+
bool insert_id_consumed= false;
1716+
if (// UPDATE clause specifies a value for the auto increment field
1717+
table->auto_increment_field_not_null &&
1718+
// An auto increment value has been generated for this row
1719+
(insert_id_for_cur_row > 0))
1720+
{
1721+
// After-update value:
1722+
const ulonglong auto_incr_val= table->next_number_field->val_int();
1723+
if (auto_incr_val == insert_id_for_cur_row)
1724+
{
1725+
// UPDATE wants to use the generated value
1726+
insert_id_consumed= true;
1727+
}
1728+
else if (table->file->auto_inc_interval_for_cur_row.
1729+
in_range(auto_incr_val))
1730+
{
1731+
/*
1732+
UPDATE wants to use one auto generated value which we have already
1733+
reserved for another (previous or following) row. That may cause
1734+
a duplicate key error if we later try to insert the reserved
1735+
value. Such conflicts on auto generated values would be strange
1736+
behavior, so we return a clear error now.
1737+
*/
1738+
my_error(ER_AUTO_INCREMENT_CONFLICT, MYF(0));
1739+
goto before_trg_err;
1740+
}
1741+
}
1742+
1743+
if (!insert_id_consumed)
1744+
table->file->restore_auto_increment(prev_insert_id);
1745+
17151746
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
17161747
{
17171748
const TABLE_LIST *inserted_view=
@@ -1726,7 +1757,6 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
17261757
}
17271758
}
17281759

1729-
table->file->restore_auto_increment(prev_insert_id);
17301760
info->stats.touched++;
17311761
if (!records_are_comparable(table) || compare_records(table))
17321762
{
@@ -1763,9 +1793,6 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
17631793
info->stats.copied++;
17641794
}
17651795

1766-
if (table->next_number_field)
1767-
table->file->adjust_next_insert_id_after_explicit_value(
1768-
table->next_number_field->val_int());
17691796
goto ok_or_after_trg_err;
17701797
}
17711798
else /* DUP_REPLACE */

sql/structs.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef STRUCTS_INCLUDED
22
#define STRUCTS_INCLUDED
33

4-
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
4+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License as published by
@@ -292,6 +292,13 @@ class Discrete_interval {
292292
ulonglong interval_max; // excluded bound. Redundant.
293293
public:
294294
Discrete_interval *next; // used when linked into Discrete_intervals_list
295+
296+
/// Determine if the given value is within the interval
297+
bool in_range(const ulonglong value) const
298+
{
299+
return ((value >= interval_min) && (value < interval_max));
300+
}
301+
295302
void replace(ulonglong start, ulonglong val, ulonglong incr)
296303
{
297304
interval_min= start;

0 commit comments

Comments
 (0)