diff options
-rw-r--r-- | include/mysql/service_wsrep.h | 3 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/innodb-autoinc.result | 2 | ||||
-rw-r--r-- | sql/sql_plugin_services.ic | 1 | ||||
-rw-r--r-- | sql/wsrep_dummy.cc | 8 | ||||
-rw-r--r-- | sql/wsrep_thd.cc | 21 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 308 |
6 files changed, 178 insertions, 165 deletions
diff --git a/include/mysql/service_wsrep.h b/include/mysql/service_wsrep.h index 54b9eb9250c..8432526a78b 100644 --- a/include/mysql/service_wsrep.h +++ b/include/mysql/service_wsrep.h @@ -107,7 +107,6 @@ extern struct wsrep_service_st { bool (*wsrep_thd_ignore_table_func)(THD *thd); long long (*wsrep_thd_trx_seqno_func)(THD *thd); struct wsrep_ws_handle * (*wsrep_thd_ws_handle_func)(THD *thd); - void (*wsrep_thd_auto_increment_variables_func)(THD *thd, unsigned long long *offset, unsigned long long *increment); void (*wsrep_set_load_multi_commit_func)(THD *thd, bool split); bool (*wsrep_is_load_multi_commit_func)(THD *thd); int (*wsrep_trx_is_aborting_func)(MYSQL_THD thd); @@ -155,7 +154,6 @@ extern struct wsrep_service_st { #define wsrep_thd_ignore_table(T) wsrep_service->wsrep_thd_ignore_table_func(T) #define wsrep_thd_trx_seqno(T) wsrep_service->wsrep_thd_trx_seqno_func(T) #define wsrep_thd_ws_handle(T) wsrep_service->wsrep_thd_ws_handle_func(T) -#define wsrep_thd_auto_increment_variables(T,O,I) wsrep_service->wsrep_thd_auto_increment_variables_func(T,O,I) #define wsrep_set_load_multi_commit(T,S) wsrep_service->wsrep_set_load_multi_commit_func(T,S) #define wsrep_is_load_multi_commit(T) wsrep_service->wsrep_is_load_multi_commit_func(T) #define wsrep_trx_is_aborting(T) wsrep_service->wsrep_trx_is_aborting_func(T) @@ -212,7 +210,6 @@ my_bool wsrep_thd_is_BF(MYSQL_THD thd, my_bool sync); my_bool wsrep_thd_is_wsrep(MYSQL_THD thd); struct wsrep *get_wsrep(); struct wsrep_ws_handle *wsrep_thd_ws_handle(THD *thd); -void wsrep_thd_auto_increment_variables(THD *thd, unsigned long long *offset, unsigned long long *increment); void wsrep_set_load_multi_commit(THD *thd, bool split); bool wsrep_is_load_multi_commit(THD *thd); void wsrep_aborting_thd_enqueue(THD *thd); diff --git a/mysql-test/suite/innodb/r/innodb-autoinc.result b/mysql-test/suite/innodb/r/innodb-autoinc.result index 87d7d1c60d3..6c405627e08 100644 --- a/mysql-test/suite/innodb/r/innodb-autoinc.result +++ b/mysql-test/suite/innodb/r/innodb-autoinc.result @@ -789,7 +789,7 @@ t2 CREATE TABLE `t2` ( `n` int(10) unsigned NOT NULL, `o` enum('FALSE','TRUE') DEFAULT NULL, PRIMARY KEY (`m`) -) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=latin1 +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1 INSERT INTO t1 (b,c) SELECT n,o FROM t2 ; SHOW CREATE TABLE t1; Table Create Table diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic index 43fe540731e..358052132a0 100644 --- a/sql/sql_plugin_services.ic +++ b/sql/sql_plugin_services.ic @@ -177,7 +177,6 @@ static struct wsrep_service_st wsrep_handler = { wsrep_thd_ignore_table, wsrep_thd_trx_seqno, wsrep_thd_ws_handle, - wsrep_thd_auto_increment_variables, wsrep_set_load_multi_commit, wsrep_is_load_multi_commit, wsrep_trx_is_aborting, diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 53941c06892..f5e88aeac1f 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -125,14 +125,6 @@ longlong wsrep_thd_trx_seqno(THD *) struct wsrep_ws_handle* wsrep_thd_ws_handle(THD *) { return 0; } -void wsrep_thd_auto_increment_variables(THD *thd, - unsigned long long *offset, - unsigned long long *increment) -{ - *offset= thd->variables.auto_increment_offset; - *increment= thd->variables.auto_increment_increment; -} - void wsrep_set_load_multi_commit(THD *thd, bool split) { } diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index d8ca70d1cbe..ef8c0e132f7 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -851,27 +851,6 @@ bool wsrep_thd_has_explicit_locks(THD *thd) return thd->mdl_context.has_explicit_locks(); } -/* - Get auto increment variables for THD. Use global settings for - applier threads. - */ -void wsrep_thd_auto_increment_variables(THD* thd, - unsigned long long* offset, - unsigned long long* increment) -{ - if (thd->wsrep_exec_mode == REPL_RECV && - thd->wsrep_conflict_state != REPLAYING) - { - *offset= global_system_variables.auto_increment_offset; - *increment= global_system_variables.auto_increment_increment; - } - else - { - *offset= thd->variables.auto_increment_offset; - *increment= thd->variables.auto_increment_increment; - } -} - my_bool wsrep_thd_is_applier(MYSQL_THD thd) { my_bool is_applier= false; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 11d8ec2b81e..423996c7fb1 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2556,6 +2556,72 @@ innobase_raw_format( return(ut_str_sql_format(buf_tmp, buf_tmp_used, buf, buf_size)); } +/* +The helper function nlz(x) calculates the number of leading zeros +in the binary representation of the number "x", either using a +built-in compiler function or a substitute trick based on the use +of the multiplication operation and a table indexed by the prefix +of the multiplication result: +*/ +#ifdef __GNUC__ +#define nlz(x) __builtin_clzll(x) +#elif defined(_MSC_VER) && !defined(_M_CEE_PURE) && \ + (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64)) +#ifndef __INTRIN_H_ +#pragma warning(push, 4) +#pragma warning(disable: 4255 4668) +#include <intrin.h> +#pragma warning(pop) +#endif +__forceinline unsigned int nlz (ulonglong x) +{ +#if defined(_M_IX86) || defined(_M_X64) + unsigned long n; +#ifdef _M_X64 + _BitScanReverse64(&n, x); + return (unsigned int) n ^ 63; +#else + unsigned long y = (unsigned long) (x >> 32); + unsigned int m = 31; + if (y == 0) + { + y = (unsigned long) x; + m = 63; + } + _BitScanReverse(&n, y); + return (unsigned int) n ^ m; +#endif +#elif defined(_M_ARM64) + return _CountLeadingZeros(x); +#endif +} +#else +inline unsigned int nlz (ulonglong x) +{ + static unsigned char table [48] = { + 32, 6, 5, 0, 4, 12, 0, 20, + 15, 3, 11, 0, 0, 18, 25, 31, + 8, 14, 2, 0, 10, 0, 0, 0, + 0, 0, 0, 21, 0, 0, 19, 26, + 7, 0, 13, 0, 16, 1, 22, 27, + 9, 0, 17, 23, 28, 24, 29, 30 + }; + unsigned int y= (unsigned int) (x >> 32); + unsigned int n= 0; + if (y == 0) { + y= (unsigned int) x; + n= 32; + } + y = y | (y >> 1); // Propagate leftmost 1-bit to the right. + y = y | (y >> 2); + y = y | (y >> 4); + y = y | (y >> 8); + y = y & ~(y >> 16); + y = y * 0x3EF5D037; + return n + table[y >> 26]; +} +#endif + /*********************************************************************//** Compute the next autoinc value. @@ -2584,85 +2650,93 @@ innobase_next_autoinc( ulonglong max_value) /*!< in: max value for type */ { ulonglong next_value; - ulonglong block = need * step; + ulonglong block; /* Should never be 0. */ ut_a(need > 0); - ut_a(block > 0); + ut_a(step > 0); ut_a(max_value > 0); - /* - Allow auto_increment to go over max_value up to max ulonglong. - This allows us to detect that all values are exhausted. - If we don't do this, we will return max_value several times - and get duplicate key errors instead of auto increment value - out of range. - */ - max_value= (~(ulonglong) 0); + /* + We need to calculate the "block" value equal to the product + "step * need". However, when calculating this product, an integer + overflow can occur, so we cannot simply use the usual multiplication + operation. The snippet below calculates the product of two numbers + and detects an unsigned integer overflow: + */ + unsigned int m= nlz(need); + unsigned int n= nlz(step); + if (m + n <= 8 * sizeof(ulonglong) - 2) { + // The bit width of the original values is too large, + // therefore we are guaranteed to get an overflow. + goto overflow; + } + block = need * (step >> 1); + if ((longlong) block < 0) { + goto overflow; + } + block += block; + if (step & 1) { + block += need; + if (block < need) { + goto overflow; + } + } + + /* Check for overflow. Current can be > max_value if the value + is in reality a negative value. Also, the visual studio compiler + converts large double values (which hypothetically can then be + passed here as the values of the "current" parameter) automatically + into unsigned long long datatype maximum value: */ + if (current > max_value) { + goto overflow; + } /* According to MySQL documentation, if the offset is greater than the step then the offset is ignored. */ - if (offset > block) { + if (offset > step) { offset = 0; } - /* Check for overflow. Current can be > max_value if the value is - in reality a negative value.The visual studio compilers converts - large double values automatically into unsigned long long datatype - maximum value */ - - if (block >= max_value - || offset > max_value - || current >= max_value - || max_value - offset <= offset) { - - next_value = max_value; + /* + Let's round the current value to within a step-size block: + */ + if (current > offset) { + next_value = current - offset; } else { - ut_a(max_value > current); - - ulonglong free = max_value - current; - - if (free < offset || free - offset <= block) { - next_value = max_value; - } else { - next_value = 0; - } + next_value = offset - current; } + next_value -= next_value % step; - if (next_value == 0) { - ulonglong next; - - if (current > offset) { - next = (current - offset) / step; - } else { - next = (offset - current) / step; - } - - ut_a(max_value > next); - next_value = next * step; - /* Check for multiplication overflow. */ - ut_a(next_value >= next); - ut_a(max_value > next_value); - - /* Check for overflow */ - if (max_value - next_value >= block) { - - next_value += block; - - if (max_value - next_value >= offset) { - next_value += offset; - } else { - next_value = max_value; - } - } else { - next_value = max_value; - } + /* + Add an offset to the next value and check that the addition + does not cause an integer overflow: + */ + next_value += offset; + if (next_value < offset) { + goto overflow; } - ut_a(next_value != 0); - ut_a(next_value <= max_value); + /* + Add a block to the next value and check that the addition + does not cause an integer overflow: + */ + next_value += block; + if (next_value < block) { + goto overflow; + } return(next_value); + +overflow: + /* + Allow auto_increment to go over max_value up to max ulonglong. + This allows us to detect that all values are exhausted. + If we don't do this, we will return max_value several times + and get duplicate key errors instead of auto increment value + out of range: + */ + return(~(ulonglong) 0); } /********************************************************************//** @@ -8169,7 +8243,6 @@ ha_innobase::write_row( /* Handling of errors related to auto-increment. */ if (auto_inc_used) { ulonglong auto_inc; - ulonglong col_max_value; /* Note the number of rows processed for this statement, used by get_auto_increment() to determine the number of AUTO-INC @@ -8179,11 +8252,6 @@ ha_innobase::write_row( --trx->n_autoinc_rows; } - /* We need the upper limit of the col type to check for - whether we update the table autoinc counter or not. */ - col_max_value = - table->next_number_field->get_max_int_value(); - /* Get the value that MySQL attempted to store in the table.*/ auto_inc = table->next_number_field->val_uint(); @@ -8250,38 +8318,25 @@ ha_innobase::write_row( if (auto_inc >= m_prebuilt->autoinc_last_value) { set_max_autoinc: + /* We need the upper limit of the col type to check for + whether we update the table autoinc counter or not. */ + ulonglong col_max_value = + table->next_number_field->get_max_int_value(); + /* This should filter out the negative values set explicitly by the user. */ if (auto_inc <= col_max_value) { + ut_ad(m_prebuilt->autoinc_increment > 0); ulonglong offset; ulonglong increment; dberr_t err; -#ifdef WITH_WSREP - /* Applier threads which are processing - ROW events and don't go through server - level autoinc processing, therefore - m_prebuilt autoinc values don't get - properly assigned. Fetch values from - server side. */ - if (trx->is_wsrep() && - wsrep_thd_exec_mode(m_user_thd) == REPL_RECV) - { - wsrep_thd_auto_increment_variables( - m_user_thd, &offset, &increment); - } - else - { -#endif /* WITH_WSREP */ - ut_a(m_prebuilt->autoinc_increment > 0); - offset = m_prebuilt->autoinc_offset; - increment = m_prebuilt->autoinc_increment; -#ifdef WITH_WSREP - } -#endif /* WITH_WSREP */ + + offset = m_prebuilt->autoinc_offset; + increment = m_prebuilt->autoinc_increment; + auto_inc = innobase_next_autoinc( - auto_inc, - 1, increment, offset, + auto_inc, 1, increment, offset, col_max_value); err = innobase_set_max_autoinc( @@ -8949,46 +9004,37 @@ ha_innobase::update_row( /* A value for an AUTO_INCREMENT column was specified in the UPDATE statement. */ - ulonglong offset; - ulonglong increment; -#ifdef WITH_WSREP - /* Applier threads which are processing - ROW events and don't go through server - level autoinc processing, therefore - m_prebuilt autoinc values don't get - properly assigned. Fetch values from - server side. */ - if (trx->is_wsrep() && - wsrep_thd_exec_mode(m_user_thd) == REPL_RECV) - { - wsrep_thd_auto_increment_variables( - m_user_thd, &offset, &increment); - } - else - { -#endif /* WITH_WSREP */ - offset = m_prebuilt->autoinc_offset; - increment = m_prebuilt->autoinc_increment; -#ifdef WITH_WSREP - } -#endif /* WITH_WSREP */ - - autoinc = innobase_next_autoinc( - autoinc, 1, increment, offset, - table->found_next_number_field->get_max_int_value()); - - error = innobase_set_max_autoinc(autoinc); - - if (m_prebuilt->table->persistent_autoinc) { - /* Update the PAGE_ROOT_AUTO_INC. Yes, we do - this even if dict_table_t::autoinc already was - greater than autoinc, because we cannot know - if any INSERT actually used (and wrote to - PAGE_ROOT_AUTO_INC) a value bigger than our - autoinc. */ - btr_write_autoinc(dict_table_get_first_index( - m_prebuilt->table), - autoinc); + /* We need the upper limit of the col type to check for + whether we update the table autoinc counter or not. */ + ulonglong col_max_value = + table->found_next_number_field->get_max_int_value(); + + /* This should filter out the negative + values set explicitly by the user. */ + if (autoinc <= col_max_value) { + ulonglong offset; + ulonglong increment; + + offset = m_prebuilt->autoinc_offset; + increment = m_prebuilt->autoinc_increment; + + autoinc = innobase_next_autoinc( + autoinc, 1, increment, offset, + col_max_value); + + error = innobase_set_max_autoinc(autoinc); + + if (m_prebuilt->table->persistent_autoinc) { + /* Update the PAGE_ROOT_AUTO_INC. Yes, we do + this even if dict_table_t::autoinc already was + greater than autoinc, because we cannot know + if any INSERT actually used (and wrote to + PAGE_ROOT_AUTO_INC) a value bigger than our + autoinc. */ + btr_write_autoinc(dict_table_get_first_index( + m_prebuilt->table), + autoinc); + } } } |