Bug#39828 : Autoinc wraps around when offset and increment > 1
Auto increment value wraps when performing a bulk insert with auto_increment_increment and auto_increment_offset greater than one. The fix: If overflow happened then return MAX_ULONGLONG value as an indication of overflow and check this before storing the value into the field in update_auto_increment().
This commit is contained in:
parent
4096f35a39
commit
0e77c3295a
@ -476,3 +476,24 @@ SELECT a FROM t2;
|
||||
a
|
||||
2
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#39828 autoinc wraps around when offset and increment > 1
|
||||
#
|
||||
CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) engine=MyISAM;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES (18446744073709551601);
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=10;
|
||||
SELECT @@SESSION.AUTO_INCREMENT_OFFSET;
|
||||
@@SESSION.AUTO_INCREMENT_OFFSET
|
||||
1
|
||||
INSERT INTO t1 VALUES (NULL), (NULL), (NULL);
|
||||
ERROR 22003: Out of range value for column 't1' at row 167
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551601
|
||||
18446744073709551611
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=default;
|
||||
SET @@SESSION.AUTO_INCREMENT_OFFSET=default;
|
||||
DROP TABLE t1;
|
||||
End of 5.1 tests
|
||||
|
@ -471,17 +471,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 2
|
||||
auto_increment_offset 10
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
ERROR HY000: Failed to read auto-increment value from storage engine
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551603
|
||||
18446744073709551604
|
||||
18446744073709551606
|
||||
18446744073709551608
|
||||
18446744073709551610
|
||||
18446744073709551612
|
||||
18446744073709551614
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
@ -504,13 +499,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 5
|
||||
auto_increment_offset 7
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
ERROR HY000: Failed to read auto-increment value from storage engine
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551603
|
||||
18446744073709551607
|
||||
18446744073709551612
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
@ -572,12 +566,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 65535
|
||||
auto_increment_offset 65535
|
||||
INSERT INTO t1 VALUES (NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
ERROR 22003: Out of range value for column 't1' at row 167
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551610
|
||||
18446744073709551615
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
|
@ -291,21 +291,8 @@ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't, it seems to be
|
||||
# a MySQL server bug. It wraps around to 0 for the last value.
|
||||
# See MySQL Bug# 39828
|
||||
#
|
||||
# Instead of wrapping around, it asserts when MySQL is compiled --with-debug
|
||||
# (see sql/handler.cc:handler::update_auto_increment()). Don't test for
|
||||
# overflow until Bug #39828 is fixed.
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
#endif
|
||||
--error ER_AUTOINC_READ_FAILED
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@ -323,20 +310,8 @@ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=5, @@SESSION.AUTO_INCREMENT_OFFSET=7;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't. It fails with
|
||||
# a duplicate entry message because of a MySQL server bug, it wraps
|
||||
# around. See MySQL Bug# 39828, once MySQL fix the bug we can replace
|
||||
# the ER_DUP_ENTRY, 1062 below with the appropriate error message
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
# Still need to fix this error code, error should mention overflow
|
||||
#-- error ER_DUP_ENTRY,1062
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
#endif
|
||||
--error ER_AUTOINC_READ_FAILED
|
||||
INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@ -374,20 +349,8 @@ INSERT INTO t1 VALUES (18446744073709551610); #-- 2^64 - 2
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1152921504606846976, @@SESSION.AUTO_INCREMENT_OFFSET=1152921504606846976;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't. It wraps around
|
||||
# and the autoinc values look bogus too.
|
||||
# See MySQL Bug# 39828, once MySQL fix the bug we can enable the error
|
||||
# code expected test.
|
||||
# -- error ER_AUTOINC_READ_FAILED,1467
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
#-- error ER_AUTOINC_READ_FAILED,1467
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL);
|
||||
#endif
|
||||
--error ER_WARN_DATA_OUT_OF_RANGE
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
@ -471,17 +471,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 2
|
||||
auto_increment_offset 10
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
ERROR HY000: Failed to read auto-increment value from storage engine
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551603
|
||||
18446744073709551604
|
||||
18446744073709551606
|
||||
18446744073709551608
|
||||
18446744073709551610
|
||||
18446744073709551612
|
||||
18446744073709551614
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
@ -504,13 +499,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 5
|
||||
auto_increment_offset 7
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
ERROR HY000: Failed to read auto-increment value from storage engine
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551603
|
||||
18446744073709551607
|
||||
18446744073709551612
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
@ -572,12 +566,12 @@ SHOW VARIABLES LIKE "%auto_inc%";
|
||||
Variable_name Value
|
||||
auto_increment_increment 65535
|
||||
auto_increment_offset 65535
|
||||
INSERT INTO t1 VALUES (NULL);
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
ERROR 22003: Out of range value for column 't1' at row 167
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
1
|
||||
18446744073709551610
|
||||
18446744073709551615
|
||||
DROP TABLE t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1;
|
||||
SET @@INSERT_ID=1;
|
||||
|
@ -293,21 +293,8 @@ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't, it seems to be
|
||||
# a MySQL server bug. It wraps around to 0 for the last value.
|
||||
# See MySQL Bug# 39828
|
||||
#
|
||||
# Instead of wrapping around, it asserts when MySQL is compiled --with-debug
|
||||
# (see sql/handler.cc:handler::update_auto_increment()). Don't test for
|
||||
# overflow until Bug #39828 is fixed.
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
#endif
|
||||
--error ER_AUTOINC_READ_FAILED
|
||||
INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@ -325,20 +312,8 @@ INSERT INTO t1 VALUES (18446744073709551603); #-- 2^64 - 13
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=5, @@SESSION.AUTO_INCREMENT_OFFSET=7;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't. It fails with
|
||||
# a duplicate entry message because of a MySQL server bug, it wraps
|
||||
# around. See MySQL Bug# 39828, once MySQL fix the bug we can replace
|
||||
# the ER_DUP_ENTRY, 1062 below with the appropriate error message
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
# Still need to fix this error code, error should mention overflow
|
||||
#-- error ER_DUP_ENTRY,1062
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
#endif
|
||||
--error ER_AUTOINC_READ_FAILED
|
||||
INSERT INTO t1 VALUES (NULL),(NULL), (NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@ -376,20 +351,8 @@ INSERT INTO t1 VALUES (18446744073709551610); #-- 2^64 - 2
|
||||
SELECT * FROM t1;
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=1152921504606846976, @@SESSION.AUTO_INCREMENT_OFFSET=1152921504606846976;
|
||||
SHOW VARIABLES LIKE "%auto_inc%";
|
||||
# This should fail because of overflow but it doesn't. It wraps around
|
||||
# and the autoinc values look bogus too.
|
||||
# See MySQL Bug# 39828, once MySQL fix the bug we can enable the error
|
||||
# code expected test.
|
||||
# -- error ER_AUTOINC_READ_FAILED,1467
|
||||
#
|
||||
# Since this asserts when compiled --with-debug, we can't properly test this
|
||||
# until Bug #39828 is fixed. For now, this test is meaningless.
|
||||
#if Bug #39828 is fixed
|
||||
#-- error ER_AUTOINC_READ_FAILED,1467
|
||||
#INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
#else
|
||||
INSERT INTO t1 VALUES (NULL);
|
||||
#endif
|
||||
--error ER_WARN_DATA_OUT_OF_RANGE
|
||||
INSERT INTO t1 VALUES (NULL),(NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
@ -342,3 +342,24 @@ SELECT a FROM t2;
|
||||
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#39828 autoinc wraps around when offset and increment > 1
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) engine=MyISAM;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES (18446744073709551601);
|
||||
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=10;
|
||||
|
||||
SELECT @@SESSION.AUTO_INCREMENT_OFFSET;
|
||||
--error ER_WARN_DATA_OUT_OF_RANGE
|
||||
INSERT INTO t1 VALUES (NULL), (NULL), (NULL);
|
||||
SELECT * FROM t1;
|
||||
|
||||
SET @@SESSION.AUTO_INCREMENT_INCREMENT=default;
|
||||
SET @@SESSION.AUTO_INCREMENT_OFFSET=default;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
@ -2166,7 +2166,8 @@ int handler::read_first_row(uchar * buf, uint primary_key)
|
||||
computes the lowest number
|
||||
- strictly greater than "nr"
|
||||
- of the form: auto_increment_offset + N * auto_increment_increment
|
||||
|
||||
If overflow happened then return MAX_ULONGLONG value as an
|
||||
indication of overflow.
|
||||
In most cases increment= offset= 1, in which case we get:
|
||||
@verbatim 1,2,3,4,5,... @endverbatim
|
||||
If increment=10 and offset=5 and previous number is 1, we get:
|
||||
@ -2175,13 +2176,23 @@ int handler::read_first_row(uchar * buf, uint primary_key)
|
||||
inline ulonglong
|
||||
compute_next_insert_id(ulonglong nr,struct system_variables *variables)
|
||||
{
|
||||
const ulonglong save_nr= nr;
|
||||
|
||||
if (variables->auto_increment_increment == 1)
|
||||
return (nr+1); // optimization of the formula below
|
||||
nr= (((nr+ variables->auto_increment_increment -
|
||||
variables->auto_increment_offset)) /
|
||||
(ulonglong) variables->auto_increment_increment);
|
||||
return (nr* (ulonglong) variables->auto_increment_increment +
|
||||
variables->auto_increment_offset);
|
||||
nr= nr + 1; // optimization of the formula below
|
||||
else
|
||||
{
|
||||
nr= (((nr+ variables->auto_increment_increment -
|
||||
variables->auto_increment_offset)) /
|
||||
(ulonglong) variables->auto_increment_increment);
|
||||
nr= (nr* (ulonglong) variables->auto_increment_increment +
|
||||
variables->auto_increment_offset);
|
||||
}
|
||||
|
||||
if (unlikely(nr <= save_nr))
|
||||
return ULONGLONG_MAX;
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
|
||||
@ -2392,7 +2403,7 @@ int handler::update_auto_increment()
|
||||
variables->auto_increment_increment,
|
||||
nb_desired_values, &nr,
|
||||
&nb_reserved_values);
|
||||
if (nr == ~(ulonglong) 0)
|
||||
if (nr == ULONGLONG_MAX)
|
||||
DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure
|
||||
|
||||
/*
|
||||
@ -2423,6 +2434,9 @@ int handler::update_auto_increment()
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(nr == ULONGLONG_MAX))
|
||||
DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
|
||||
|
||||
DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
|
||||
|
||||
if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user