From b657301e96716fd26b8a8eb00707ac05ab133ef6 Mon Sep 17 00:00:00 2001 From: sunny Date: Wed, 5 Mar 2008 20:23:39 +0000 Subject: [PATCH] branches/5.1: Change the InnoDB autoinc type to ulint64. For this added a new typedef to univ.i (ib_ulonglong). Added checks for overflow and removed the assertion where it crashed previously, since the type has now changed to unsigned, it doesn't make sense to check for < 0. Added new tests, to check for overflow, for the different INT types supported for both signed and unsigned. Fixes Bug# 34335 --- dict/dict0dict.c | 6 +- handler/ha_innodb.cc | 48 ++++++++++---- handler/ha_innodb.h | 2 +- include/dict0dict.h | 6 +- include/dict0mem.h | 2 +- include/row0sel.h | 2 +- include/univ.i | 3 + mysql-test/innodb-autoinc.result | 60 +++++++++++++++++ mysql-test/innodb-autoinc.test | 107 +++++++++++++++++++++++++++++++ row/row0sel.c | 10 +-- 10 files changed, 218 insertions(+), 28 deletions(-) create mode 100644 mysql-test/innodb-autoinc.result create mode 100644 mysql-test/innodb-autoinc.test diff --git a/dict/dict0dict.c b/dict/dict0dict.c index 8aba5ac1c85..10cef688705 100644 --- a/dict/dict0dict.c +++ b/dict/dict0dict.c @@ -429,7 +429,7 @@ void dict_table_autoinc_initialize( /*==========================*/ dict_table_t* table, /* in: table */ - ib_longlong value) /* in: next value to assign to a row */ + ib_ulonglong value) /* in: next value to assign to a row */ { ut_ad(mutex_own(&table->autoinc_mutex)); @@ -441,7 +441,7 @@ dict_table_autoinc_initialize( Reads the next autoinc value (== autoinc counter value), 0 if not yet initialized. */ -ib_longlong +ib_ulonglong dict_table_autoinc_read( /*====================*/ /* out: value for a new row, or 0 */ @@ -470,7 +470,7 @@ dict_table_autoinc_update( /*======================*/ dict_table_t* table, /* in: table */ - ib_longlong value) /* in: value which was assigned to a row */ + ib_ulonglong value) /* in: value which was assigned to a row */ { if (table->autoinc_inited && value > table->autoinc) { diff --git a/handler/ha_innodb.cc b/handler/ha_innodb.cc index ab8b7248056..77acfaf6df3 100644 --- a/handler/ha_innodb.cc +++ b/handler/ha_innodb.cc @@ -3549,7 +3549,19 @@ no_commit: if (auto_inc > prebuilt->last_value) { set_max_autoinc: ut_a(prebuilt->table->autoinc_increment > 0); - auto_inc += prebuilt->table->autoinc_increment; + + ulonglong have; + ulonglong need; + + /* Check for overflow conditions. */ + need = prebuilt->table->autoinc_increment; + have = ~0x0ULL - auto_inc; + + if (have < need) { + need = have; + } + + auto_inc += need; err = innobase_set_max_autoinc(auto_inc); @@ -5867,7 +5879,7 @@ ha_innobase::info( } if (flag & HA_STATUS_AUTO && table->found_next_number_field) { - longlong auto_inc; + ulonglong auto_inc; int ret; /* The following function call can the first time fail in @@ -7218,9 +7230,9 @@ ha_innobase::innobase_read_and_init_auto_inc( /*=========================================*/ /* out: 0 or generic MySQL error code */ - longlong* value) /* out: the autoinc value */ + ulonglong* value) /* out: the autoinc value */ { - longlong auto_inc; + ulonglong auto_inc; ibool stmt_start; int mysql_error = 0; dict_table_t* innodb_table = prebuilt->table; @@ -7271,7 +7283,9 @@ ha_innobase::innobase_read_and_init_auto_inc( index, autoinc_col_name, &auto_inc); if (error == DB_SUCCESS) { - ++auto_inc; + if (auto_inc < ~0x0ULL) { + ++auto_inc; + } dict_table_autoinc_initialize(innodb_table, auto_inc); } else { ut_print_timestamp(stderr); @@ -7324,14 +7338,14 @@ ha_innobase::innobase_get_auto_increment( error = innobase_autoinc_lock(); if (error == DB_SUCCESS) { - ib_longlong autoinc; + ulonglong autoinc; /* Determine the first value of the interval */ autoinc = dict_table_autoinc_read(prebuilt->table); /* We need to initialize the AUTO-INC value, for that we release all locks.*/ - if (autoinc <= 0) { + if (autoinc == 0) { trx_t* trx; trx = prebuilt->trx; @@ -7350,14 +7364,11 @@ ha_innobase::innobase_get_auto_increment( mysql_error = innobase_read_and_init_auto_inc( &autoinc); - if (!mysql_error) { - /* Should have read the proper value */ - ut_a(autoinc > 0); - } else { + if (mysql_error) { error = DB_ERROR; } } else { - *value = (ulonglong) autoinc; + *value = autoinc; } /* A deadlock error during normal processing is OK and can be ignored. */ @@ -7442,10 +7453,19 @@ 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; + + /* 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 + - (*nb_reserved_values * increment); + prebuilt->last_value = *first_value + need; ut_a(prebuilt->last_value >= *first_value); diff --git a/handler/ha_innodb.h b/handler/ha_innodb.h index 773884b6584..4ffcdb1e3c2 100644 --- a/handler/ha_innodb.h +++ b/handler/ha_innodb.h @@ -72,7 +72,7 @@ class ha_innobase: public handler int update_thd(THD* thd); int change_active_index(uint keynr); int general_fetch(uchar* buf, uint direction, uint match_mode); - int innobase_read_and_init_auto_inc(longlong* ret); + int innobase_read_and_init_auto_inc(ulonglong* ret); ulong innobase_autoinc_lock(); ulong innobase_set_max_autoinc(ulonglong auto_inc); ulong innobase_reset_autoinc(ulonglong auto_inc); diff --git a/include/dict0dict.h b/include/dict0dict.h index 2f038b21e8e..f60775c8c2f 100644 --- a/include/dict0dict.h +++ b/include/dict0dict.h @@ -185,12 +185,12 @@ void dict_table_autoinc_initialize( /*==========================*/ dict_table_t* table, /* in: table */ - ib_longlong value); /* in: next value to assign to a row */ + ib_ulonglong value); /* in: next value to assign to a row */ /************************************************************************ Reads the next autoinc value (== autoinc counter value), 0 if not yet initialized. */ -ib_longlong +ib_ulonglong dict_table_autoinc_read( /*====================*/ /* out: value for a new row, or 0 */ @@ -204,7 +204,7 @@ dict_table_autoinc_update( /*======================*/ dict_table_t* table, /* in: table */ - ib_longlong value); /* in: value which was assigned to a row */ + ib_ulonglong value); /* in: value which was assigned to a row */ /************************************************************************ Release the autoinc lock.*/ diff --git a/include/dict0mem.h b/include/dict0mem.h index a05bc513efd..2fe72498989 100644 --- a/include/dict0mem.h +++ b/include/dict0mem.h @@ -409,7 +409,7 @@ struct dict_table_struct{ /* TRUE if the autoinc counter has been inited; MySQL gets the init value by executing SELECT MAX(auto inc column) */ - ib_longlong autoinc;/* autoinc counter value to give to the + ib_ulonglong autoinc;/* autoinc counter value to give to the next inserted row */ ib_longlong autoinc_increment; diff --git a/include/row0sel.h b/include/row0sel.h index 4bde648f18e..a0a4ccb973b 100644 --- a/include/row0sel.h +++ b/include/row0sel.h @@ -181,7 +181,7 @@ row_search_max_autoinc( error code */ dict_index_t* index, /* in: index to search */ const char* col_name, /* in: autoinc column name */ - ib_longlong* value); /* out: AUTOINC value read */ + ib_ulonglong* value); /* out: AUTOINC value read */ /* A structure for caching column values for prefetched rows */ struct sel_buf_struct{ diff --git a/include/univ.i b/include/univ.i index 8163ae16e4e..8ab62e655ff 100644 --- a/include/univ.i +++ b/include/univ.i @@ -212,8 +212,11 @@ typedef long int lint; #ifdef __WIN__ typedef __int64 ib_longlong; +typedef unsigned __int64 ib_ulonglong; #else +/* Note: longlong and ulonglong come from MySQL headers. */ typedef longlong ib_longlong; +typedef ulonglong ib_ulonglong; #endif typedef unsigned long long int ullint; diff --git a/mysql-test/innodb-autoinc.result b/mysql-test/innodb-autoinc.result new file mode 100644 index 00000000000..4fc12538046 --- /dev/null +++ b/mysql-test/innodb-autoinc.result @@ -0,0 +1,60 @@ +drop table if exists t1; +CREATE TABLE t1 (c1 BIGINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (9223372036854775807, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 TINYINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (127, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 TINYINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (255, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 SMALLINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (32767, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 SMALLINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (65535, null); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 MEDIUMINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (8388607, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 MEDIUMINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (16777215, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (2147483647, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (4294967295, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 BIGINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (9223372036854775807, null); +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; +CREATE TABLE t1 (c1 INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (18446744073709551615, null); +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +INSERT INTO t1 (c2) VALUES ('innodb'); +Got one of the listed errors +DROP TABLE t1; diff --git a/mysql-test/innodb-autoinc.test b/mysql-test/innodb-autoinc.test new file mode 100644 index 00000000000..b6bb9c6b0b7 --- /dev/null +++ b/mysql-test/innodb-autoinc.test @@ -0,0 +1,107 @@ +-- source include/have_innodb.inc +# embedded server ignores 'delayed', so skip this +-- source include/not_embedded.inc + +--disable_warnings +drop table if exists t1; +--enable_warnings + +# +# Bug #34335 +# +CREATE TABLE t1 (c1 BIGINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (9223372036854775807, null); +-- error ER_DUP_ENTRY,1062 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; +# +## Test AUTOINC overflow +## + +# TINYINT +CREATE TABLE t1 (c1 TINYINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (127, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (c1 TINYINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (255, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; +# +# SMALLINT +# +CREATE TABLE t1 (c1 SMALLINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (32767, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (c1 SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (65535, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; +# +# MEDIUMINT +# +CREATE TABLE t1 (c1 MEDIUMINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (8388607, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (c1 MEDIUMINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (16777215, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; +# +# INT +# +CREATE TABLE t1 (c1 INT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (2147483647, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (c1 INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (4294967295, null); +-- error ER_DUP_ENTRY,1062 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; +# +# BIGINT +# +CREATE TABLE t1 (c1 BIGINT PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (9223372036854775807, null); +-- error ER_DUP_ENTRY,1062 +-- warning ER_WARN_DATA_OUT_OF_RANGE,1264 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (c1 BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, c2 VARCHAR(10)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (18446744073709551615, null); +-- error ER_AUTOINC_READ_FAILED,1467 +INSERT INTO t1 (c2) VALUES ('innodb'); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/row/row0sel.c b/row/row0sel.c index 29bded114e0..b050838aad2 100644 --- a/row/row0sel.c +++ b/row/row0sel.c @@ -4529,7 +4529,7 @@ row_search_check_if_query_cache_permitted( Read the AUTOINC column from the current row. If the value is less than 0 and the type is not unsigned then we reset the value to 0. */ static -ib_longlong +ib_ulonglong row_search_autoinc_read_column( /*===========================*/ /* out: value read from the column */ @@ -4540,7 +4540,7 @@ row_search_autoinc_read_column( { ulint len; const byte* data; - ib_longlong value; + ib_ulonglong value; mem_heap_t* heap = NULL; /* Our requirement is that dest should be word aligned. */ byte dest[sizeof(value)]; @@ -4567,7 +4567,7 @@ row_search_autoinc_read_column( and that dest is word aligned. */ switch (len) { case 8: - value = *(ib_longlong*) dest; + value = *(ib_ulonglong*) dest; break; case 4: @@ -4595,7 +4595,7 @@ row_search_autoinc_read_column( mem_heap_free(heap); } - if (!unsigned_type && value < 0) { + if (!unsigned_type && (ib_longlong) value < 0) { value = 0; } @@ -4634,7 +4634,7 @@ row_search_max_autoinc( column name can't be found in index */ dict_index_t* index, /* in: index to search */ const char* col_name, /* in: name of autoinc column */ - ib_longlong* value) /* out: AUTOINC value read */ + ib_ulonglong* value) /* out: AUTOINC value read */ { ulint i; ulint n_cols;