diff options
-rw-r--r-- | mysql-test/r/innodb-autoinc.result | 385 | ||||
-rw-r--r-- | mysql-test/t/innodb-autoinc.test | 213 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.c | 4 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 126 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 6 | ||||
-rw-r--r-- | storage/innobase/include/row0mysql.h | 11 | ||||
-rw-r--r-- | storage/innobase/row/row0mysql.c | 8 |
7 files changed, 713 insertions, 40 deletions
diff --git a/mysql-test/r/innodb-autoinc.result b/mysql-test/r/innodb-autoinc.result index 70cdc67f77e..589bf2f30b0 100644 --- a/mysql-test/r/innodb-autoinc.result +++ b/mysql-test/r/innodb-autoinc.result @@ -196,3 +196,388 @@ c1 c2 3 8 5 9 DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 100 +auto_increment_offset 10 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (NULL),(5),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +c1 +5 +10 +110 +250 +310 +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +c1 +5 +10 +110 +250 +310 +400 +410 +1000 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(0); +SELECT * FROM t1; +c1 +1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +INSERT INTO t1 VALUES (-1), (NULL),(2),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +c1 +-1 +1 +2 +10 +110 +250 +410 +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +Got one of the listed errors +SELECT * FROM t1; +c1 +-1 +1 +2 +10 +110 +250 +410 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +SELECT * FROM t1; +c1 +-1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 100 +auto_increment_offset 10 +INSERT INTO t1 VALUES (-2), (NULL),(2),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +c1 +-2 +-1 +1 +2 +10 +250 +310 +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +c1 +-2 +-1 +1 +2 +10 +250 +310 +400 +410 +1000 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +SELECT * FROM t1; +c1 +1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 100 +auto_increment_offset 10 +INSERT INTO t1 VALUES (-2); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (2); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (250); +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +c1 +1 +2 +10 +110 +210 +250 +310 +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES(NULL); +SELECT * FROM t1; +c1 +1 +2 +10 +110 +210 +250 +310 +400 +1000 +1010 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +SELECT * FROM t1; +c1 +1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 100 +auto_increment_offset 10 +INSERT INTO t1 VALUES (-2),(NULL),(2),(NULL); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +c1 +1 +2 +10 +110 +210 +250 +410 +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +Got one of the listed errors +SELECT * FROM t1; +c1 +1 +2 +10 +110 +210 +250 +410 +1000 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES (9223372036854775794); +SELECT * FROM t1; +c1 +1 +9223372036854775794 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10; +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); +SELECT * FROM t1; +c1 +1 +9223372036854775794 +9223372036854775796 +9223372036854775798 +9223372036854775800 +9223372036854775802 +9223372036854775804 +9223372036854775806 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES (18446744073709551603); +SELECT * FROM t1; +c1 +1 +18446744073709551603 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10; +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),(NULL); +SELECT * FROM t1; +c1 +0 +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; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES (18446744073709551603); +SELECT * FROM t1; +c1 +1 +18446744073709551603 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=5, @@SESSION.AUTO_INCREMENT_OFFSET=7; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 5 +auto_increment_offset 7 +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +Got one of the listed errors +SELECT * FROM t1; +c1 +1 +18446744073709551603 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES(-9223372036854775806); +INSERT INTO t1 VALUES(-9223372036854775807); +INSERT INTO t1 VALUES(-9223372036854775808); +SELECT * FROM t1; +c1 +-9223372036854775808 +-9223372036854775807 +-9223372036854775806 +1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=3, @@SESSION.AUTO_INCREMENT_OFFSET=3; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 3 +auto_increment_offset 3 +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +SELECT * FROM t1; +c1 +-9223372036854775808 +-9223372036854775807 +-9223372036854775806 +1 +3 +6 +9 +DROP TABLE t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 1 +auto_increment_offset 1 +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES (18446744073709551610); +SELECT * FROM t1; +c1 +1 +18446744073709551610 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1152921504606846976, @@SESSION.AUTO_INCREMENT_OFFSET=1152921504606846976; +Warnings: +Warning 1292 Truncated incorrect auto_increment_increment value: '1152921504606846976' +Warning 1292 Truncated incorrect auto_increment_offset value: '1152921504606846976' +SHOW VARIABLES LIKE "%auto_inc%"; +Variable_name Value +auto_increment_increment 65535 +auto_increment_offset 65535 +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +SELECT * FROM t1; +c1 +1 +65534 +65535 +18446744073709551610 +18446744073709551615 +DROP TABLE t1; diff --git a/mysql-test/t/innodb-autoinc.test b/mysql-test/t/innodb-autoinc.test index 1c97364199b..172913349db 100644 --- a/mysql-test/t/innodb-autoinc.test +++ b/mysql-test/t/innodb-autoinc.test @@ -156,7 +156,218 @@ DROP TABLE IF EXISTS t1; CREATE TABLE t1 (c1 INT AUTO_INCREMENT, c2 INT, PRIMARY KEY(c1)) ENGINE=InnoDB; INSERT INTO t1 VALUES (NULL, 1); DELETE FROM t1 WHERE c1 = 1; -INSERT INTO t1 VALUES (2,1), (NULL, 8); +INSERT INTO t1 VALUES (2,1), (NULL, 8); INSERT INTO t1 VALUES (NULL,9); SELECT * FROM t1; DROP TABLE t1; + +# +# Test changes to AUTOINC next value calculation +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (NULL),(5),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# Test with SIGNED INT column, by inserting a 0 for the first column value +# 0 is treated in the same was NULL. +# Reset the AUTOINC session variables +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(0); +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +INSERT INTO t1 VALUES (-1), (NULL),(2),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +SET @@INSERT_ID=400; +# Duplicate error expected here for autoinc_lock_mode != TRADITIONAL +-- error ER_DUP_ENTRY,1062 +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# Test with SIGNED INT column +# Reset the AUTOINC session variables +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +INSERT INTO t1 VALUES (-2), (NULL),(2),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# Test with UNSIGNED INT column, single insert +# The sign in the value is ignored and a new column value is generated +# Reset the AUTOINC session variables +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +INSERT INTO t1 VALUES (-2); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (2); +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (250); +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# Test with UNSIGNED INT column, multi-value inserts +# The sign in the value is ignored and a new column value is generated +# Reset the AUTOINC session variables +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(-1); +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=100, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +INSERT INTO t1 VALUES (-2),(NULL),(2),(NULL); +INSERT INTO t1 VALUES (250),(NULL); +SELECT * FROM t1; +INSERT INTO t1 VALUES (1000); +SET @@INSERT_ID=400; +# Duplicate error expected here for autoinc_lock_mode != TRADITIONAL +-- error ER_DUP_ENTRY,1062 +INSERT INTO t1 VALUES(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# +# Check for overflow handling when increment is > 1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +# TODO: Fix the autoinc init code +# We have to do this because of a bug in the AUTOINC init code. +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES (9223372036854775794); -- 2^63 - 14 +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=2, @@SESSION.AUTO_INCREMENT_OFFSET=10; +SHOW VARIABLES LIKE "%auto_inc%"; +# This should just fit +INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# +# Check for overflow handling when increment and offser are > 1 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +# TODO: Fix the autoinc init code +# We have to do this because of a bug in the AUTOINC init code. +INSERT INTO t1 VALUES(NULL); +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 +INSERT INTO t1 VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# +# Check for overflow handling when increment and offset are odd numbers +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +# TODO: Fix the autoinc init code +# We have to do this because of a bug in the AUTOINC init code. +INSERT INTO t1 VALUES(NULL); +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 +-- error ER_DUP_ENTRY,1062 +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +SELECT * FROM t1; +DROP TABLE t1; + +# Check for overflow handling when increment and offset are odd numbers +# and check for large -ve numbers +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 BIGINT AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +# TODO: Fix the autoinc init code +# We have to do this because of a bug in the AUTOINC init code. +INSERT INTO t1 VALUES(NULL); +INSERT INTO t1 VALUES(-9223372036854775806); -- -2^63 + 2 +INSERT INTO t1 VALUES(-9223372036854775807); -- -2^63 + 1 +INSERT INTO t1 VALUES(-9223372036854775808); -- -2^63 +SELECT * FROM t1; +SET @@SESSION.AUTO_INCREMENT_INCREMENT=3, @@SESSION.AUTO_INCREMENT_OFFSET=3; +SHOW VARIABLES LIKE "%auto_inc%"; +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +SELECT * FROM t1; +DROP TABLE t1; +# +# Check for overflow handling when increment and offset are very +# large numbers 2^60 +SET @@SESSION.AUTO_INCREMENT_INCREMENT=1, @@SESSION.AUTO_INCREMENT_OFFSET=1; +SET @@INSERT_ID=1; +SHOW VARIABLES LIKE "%auto_inc%"; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (c1 BIGINT UNSIGNED AUTO_INCREMENT, PRIMARY KEY(c1)) ENGINE=InnoDB; +# TODO: Fix the autoinc init code +# We have to do this because of a bug in the AUTOINC init code. +INSERT INTO t1 VALUES(NULL); +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 +INSERT INTO t1 VALUES (NULL),(NULL), (NULL); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index 47cf7a0bc9c..f9935b8db19 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -91,10 +91,6 @@ dict_mem_table_create( table->autoinc_inited = FALSE; - /* The actual increment value will be set by MySQL, we simply - default to 1 here.*/ - table->autoinc_increment = 1; - /* The number of transactions that are either waiting on the AUTOINC lock or have been granted the lock. */ table->n_waiting_or_granted_auto_inc_locks = 0; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 379269ee07f..f79af1e274c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -922,6 +922,74 @@ innobase_convert_string( } /************************************************************************* +Compute the next autoinc value. + +For MySQL replication the autoincrement values can be partitioned among +the nodes. The offset is the start or origin of the autoincrement value +for a particular node. For n nodes the increment will be n and the offset +will be in the interval [1, n]. The formula tries to allocate the next +value for a particular node. + +Note: This function is also called with increment set to the number of +values we want to reserve for multi-value inserts e.g., + + INSERT INTO T VALUES(), (), (); + +innobase_next_autoinc() will be called with increment set to +n * 3 where autoinc_lock_mode != TRADITIONAL because we want +to reserve 3 values for the multi-value INSERT above. */ +static +ulonglong +innobase_next_autoinc( +/*==================*/ + /* out: the next value */ + ulonglong current, /* in: Current value */ + ulonglong increment, /* in: increment current by */ + ulonglong offset) /* in: AUTOINC offset */ +{ + ulonglong next_value; + + /* Should never be 0. */ + ut_a(increment > 0); + + if (offset <= 1) { + /* Offset 0 and 1 are the same, because there must be at + least one node in the system. */ + if (~0x0ULL - current <= increment) { + next_value = ~0x0ULL; + } else { + next_value = current + increment; + } + } else { + if (current > offset) { + next_value = ((current - offset) / increment) + 1; + } else { + next_value = ((offset - current) / increment) + 1; + } + + ut_a(increment > 0); + ut_a(next_value > 0); + + /* Check for multiplication overflow. */ + if (increment > (~0x0ULL / next_value)) { + + next_value = ~0x0ULL; + } else { + next_value *= increment; + + /* Check for overflow. */ + if (~0x0ULL - next_value <= offset) { + next_value = ~0x0ULL; + } else { + next_value += offset; + } + } + } + + return(next_value); +} + +/************************************************************************* Gets the InnoDB transaction handle for a MySQL handler object, creates an InnoDB transaction struct if the corresponding MySQL thread struct still lacks one. */ @@ -3565,22 +3633,18 @@ no_commit: update the table upper limit. Note: last_value will be 0 if get_auto_increment() was not called.*/ - if (auto_inc > prebuilt->last_value) { + if (auto_inc > prebuilt->autoinc_last_value) { set_max_autoinc: - ut_a(prebuilt->table->autoinc_increment > 0); + ut_a(prebuilt->autoinc_increment > 0); - ulonglong have; ulonglong need; + ulonglong offset; - /* Check for overflow conditions. */ - need = prebuilt->table->autoinc_increment; - have = ~0x0ULL - auto_inc; - - if (have < need) { - need = have; - } + offset = prebuilt->autoinc_offset; + need = prebuilt->autoinc_increment; - auto_inc += need; + auto_inc = innobase_next_autoinc( + auto_inc, need, offset); err = innobase_set_max_autoinc(auto_inc); @@ -3822,7 +3886,15 @@ ha_innobase::update_row( auto_inc = table->next_number_field->val_int(); if (auto_inc != 0) { - auto_inc += prebuilt->table->autoinc_increment; + + ulonglong need; + ulonglong offset; + + offset = prebuilt->autoinc_offset; + need = prebuilt->autoinc_increment; + + auto_inc = innobase_next_autoinc( + auto_inc, need, offset); error = innobase_set_max_autoinc(auto_inc); } @@ -5844,7 +5916,7 @@ ha_innobase::info( not be updated. This will force write_row() into attempting an update of the table's AUTOINC counter. */ - prebuilt->last_value = 0; + prebuilt->autoinc_last_value = 0; } stats.records = (ha_rows)n_rows; @@ -6474,7 +6546,7 @@ int ha_innobase::reset() it's safer to do it explicitly here. */ /* This is a statement level counter. */ - prebuilt->last_value = 0; + prebuilt->autoinc_last_value = 0; return(0); } @@ -7568,7 +7640,7 @@ ha_innobase::get_auto_increment( set_if_bigger(*first_value, autoinc); /* Not in the middle of a mult-row INSERT. */ - } else if (prebuilt->last_value == 0) { + } else if (prebuilt->autoinc_last_value == 0) { set_if_bigger(*first_value, autoinc); } @@ -7577,35 +7649,33 @@ ha_innobase::get_auto_increment( /* With old style AUTOINC locking we only update the table's AUTOINC counter after attempting to insert the row. */ if (innobase_autoinc_lock_mode != AUTOINC_OLD_STYLE_LOCKING) { - ulonglong have; ulonglong need; + ulonglong next_value; - /* Check for overflow conditions. */ need = *nb_reserved_values * increment; - have = ~0x0ULL - *first_value; - - if (have < need) { - need = have; - } /* Compute the last value in the interval */ - prebuilt->last_value = *first_value + need; + next_value = innobase_next_autoinc(*first_value, need, offset); + + prebuilt->autoinc_last_value = next_value; - ut_a(prebuilt->last_value >= *first_value); + ut_a(prebuilt->autoinc_last_value >= *first_value); /* Update the table autoinc variable */ dict_table_autoinc_update( - prebuilt->table, prebuilt->last_value); + prebuilt->table, prebuilt->autoinc_last_value); } else { /* This will force write_row() into attempting an update of the table's AUTOINC counter. */ - prebuilt->last_value = 0; + prebuilt->autoinc_last_value = 0; } /* The increment to be used to increase the AUTOINC value, we use this in write_row() and update_row() to increase the autoinc counter - for columns that are filled by the user.*/ - prebuilt->table->autoinc_increment = increment; + for columns that are filled by the user. We need the offset and + the increment. */ + prebuilt->autoinc_offset = offset; + prebuilt->autoinc_increment = increment; dict_table_autoinc_unlock(prebuilt->table); } diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 2fe72498989..6e5435493cb 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -411,11 +411,6 @@ struct dict_table_struct{ SELECT MAX(auto inc column) */ ib_ulonglong autoinc;/* autoinc counter value to give to the next inserted row */ - - ib_longlong autoinc_increment; - /* The increment step of the auto increment - column. Value must be greater than or equal - to 1 */ ulong n_waiting_or_granted_auto_inc_locks; /* This counter is used to track the number of granted and pending autoinc locks on this @@ -425,6 +420,7 @@ struct dict_table_struct{ acquired the AUTOINC lock or not. Of course only one transaction can be granted the lock but there can be multiple waiters. */ + /*----------------------*/ #ifdef UNIV_DEBUG ulint magic_n;/* magic number */ diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index ca9d9c6b8f8..dd6b1d00676 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -683,7 +683,16 @@ struct row_prebuilt_struct { to this heap */ mem_heap_t* old_vers_heap; /* memory heap where a previous version is built in consistent read */ - ulonglong last_value; /* last value of AUTO-INC interval */ + /*----------------------*/ + ulonglong autoinc_last_value;/* last value of AUTO-INC interval */ + ulonglong autoinc_increment;/* The increment step of the auto + increment column. Value must be + greater than or equal to 1. Required to + calculate the next value */ + ulonglong autoinc_offset; /* The offset passed to + get_auto_increment() by MySQL. Required + to calculate the next value */ + /*----------------------*/ ulint magic_n2; /* this should be the same as magic_n */ }; diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 74bf2267a3e..a3e2549f517 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -661,7 +661,13 @@ row_create_prebuilt( prebuilt->old_vers_heap = NULL; - prebuilt->last_value = 0; + prebuilt->autoinc_offset = 0; + + /* Default to 1, we will set the actual value later in + ha_innobase::get_auto_increment(). */ + prebuilt->autoinc_increment = 1; + + prebuilt->autoinc_last_value = 0; return(prebuilt); } |