Commit 8646be88 authored by unknown's avatar unknown

Fix for BUG#20524 "auto_increment_* not observed when inserting

a too large value": the bug was that if MySQL generated a value for an
auto_increment column, based on auto_increment_* variables, and this value
was bigger than the column's max possible value, then that max possible
value was inserted (after issuing a warning). But this didn't honour
auto_increment_* variables (and so could cause conflicts in a master-master
replication where one master is supposed to generated only even numbers,
and the other only odd numbers), so now we "round down" this max possible
value to honour auto_increment_* variables, before inserting it.


mysql-test/r/rpl_auto_increment.result:
  result update. Before the fix, the result was that master inserted 127 in t1
  (which didn't honour auto_increment_* variables!),
  instead of failing with "duplicate key 125" like now.
mysql-test/t/rpl_auto_increment.test:
  Test for BUG#20524 "auto_increment_* not observed when inserting
  a too large value".
  We also check the pathological case (table t2) where it's impossible to
  "round down".
  The fixer of BUG#20573 will be able to use table t2 for testing his fix.
sql/handler.cc:
  If handler::update_auto_increment() generates a value larger than the field's
  max possible value, we used to simply insert this max possible value
  (after pushing a warning). Now we "round down" this max possible value to
  honour auto_increment_* variables (if at all possible), before trying the
  insertion.
parent 0f6a24ad
...@@ -183,3 +183,47 @@ a ...@@ -183,3 +183,47 @@ a
32 32
42 42
drop table t1; drop table t1;
create table t1 (a tinyint not null auto_increment primary key) engine=myisam;
insert into t1 values(103);
set auto_increment_increment=11;
set auto_increment_offset=4;
insert into t1 values(null);
insert into t1 values(null);
insert into t1 values(null);
ERROR 23000: Duplicate entry '125' for key 1
select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a;
a mod(a-@@auto_increment_offset,@@auto_increment_increment)
103 0
114 0
125 0
create table t2 (a tinyint unsigned not null auto_increment primary key) engine=myisam;
set auto_increment_increment=10;
set auto_increment_offset=1;
set insert_id=1000;
insert into t2 values(null);
Warnings:
Warning 1264 Out of range value adjusted for column 'a' at row 1
select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a;
a mod(a-@@auto_increment_offset,@@auto_increment_increment)
251 0
create table t3 like t1;
set auto_increment_increment=1000;
set auto_increment_offset=700;
insert into t3 values(null);
Warnings:
Warning 1264 Out of range value adjusted for column 'a' at row 1
select * from t3 order by a;
a
127
select * from t1 order by a;
a
103
114
125
select * from t2 order by a;
a
251
select * from t3 order by a;
a
127
drop table t1,t2,t3;
...@@ -96,9 +96,47 @@ select * from t1; ...@@ -96,9 +96,47 @@ select * from t1;
sync_slave_with_master; sync_slave_with_master;
select * from t1; select * from t1;
connection master;
# Test for BUG#20524 "auto_increment_* not observed when inserting
# a too large value". When an autogenerated value was bigger than the
# maximum possible value of the field, it was truncated to that max
# possible value, without being "rounded down" to still honour
# auto_increment_* variables.
connection master;
drop table t1; drop table t1;
create table t1 (a tinyint not null auto_increment primary key) engine=myisam;
insert into t1 values(103);
set auto_increment_increment=11;
set auto_increment_offset=4;
insert into t1 values(null);
insert into t1 values(null);
--error 1062
insert into t1 values(null);
select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t1 order by a;
# same but with a larger value
create table t2 (a tinyint unsigned not null auto_increment primary key) engine=myisam;
set auto_increment_increment=10;
set auto_increment_offset=1;
set insert_id=1000;
insert into t2 values(null);
select a, mod(a-@@auto_increment_offset,@@auto_increment_increment) from t2 order by a;
# An offset so big that even first value does not fit
create table t3 like t1;
set auto_increment_increment=1000;
set auto_increment_offset=700;
insert into t3 values(null);
select * from t3 order by a;
sync_slave_with_master;
select * from t1 order by a;
select * from t2 order by a;
select * from t3 order by a;
connection master;
drop table t1,t2,t3;
# End cleanup # End cleanup
sync_slave_with_master; sync_slave_with_master;
...@@ -1471,6 +1471,46 @@ next_insert_id(ulonglong nr,struct system_variables *variables) ...@@ -1471,6 +1471,46 @@ next_insert_id(ulonglong nr,struct system_variables *variables)
} }
/*
Computes the largest number X:
- smaller than or equal to "nr"
- of the form: auto_increment_offset + N * auto_increment_increment
where N>=0.
SYNOPSIS
prev_insert_id
nr Number to "round down"
variables variables struct containing auto_increment_increment and
auto_increment_offset
RETURN
The number X if it exists, "nr" otherwise.
*/
inline ulonglong
prev_insert_id(ulonglong nr, struct system_variables *variables)
{
if (unlikely(nr < variables->auto_increment_offset))
{
/*
There's nothing good we can do here. That is a pathological case, where
the offset is larger than the column's max possible value, i.e. not even
the first sequence value may be inserted. User will receive warning.
*/
DBUG_PRINT("info",("auto_increment: nr: %lu cannot honour "
"auto_increment_offset: %lu",
nr, variables->auto_increment_offset));
return nr;
}
if (variables->auto_increment_increment == 1)
return nr; // optimization of the formula below
nr= (((nr - variables->auto_increment_offset)) /
(ulonglong) variables->auto_increment_increment);
return (nr * (ulonglong) variables->auto_increment_increment +
variables->auto_increment_offset);
}
/* /*
Update the auto_increment field if necessary Update the auto_increment field if necessary
...@@ -1580,10 +1620,19 @@ bool handler::update_auto_increment() ...@@ -1580,10 +1620,19 @@ bool handler::update_auto_increment()
/* Mark that we should clear next_insert_id before next stmt */ /* Mark that we should clear next_insert_id before next stmt */
thd->clear_next_insert_id= 1; thd->clear_next_insert_id= 1;
if (!table->next_number_field->store((longlong) nr, TRUE)) if (likely(!table->next_number_field->store((longlong) nr, TRUE)))
thd->insert_id((ulonglong) nr); thd->insert_id((ulonglong) nr);
else else
thd->insert_id(table->next_number_field->val_int()); {
/*
overflow of the field; we'll use the max value, however we try to
decrease it to honour auto_increment_* variables:
*/
nr= prev_insert_id(table->next_number_field->val_int(), variables);
thd->insert_id(nr);
if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
thd->insert_id(nr= table->next_number_field->val_int());
}
/* /*
We can't set next_insert_id if the auto-increment key is not the We can't set next_insert_id if the auto-increment key is not the
......
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