diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index df48a8a4b5a508194c36b556f992af828a1f58e1..33aebb250711f71e5198914776909283f3079df8 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -2139,9 +2139,12 @@ dict_foreign_find_index( ulint n_cols, /* in: number of columns */ dict_index_t* types_idx, /* in: NULL or an index to whose types the column types must match */ - ibool check_charsets) /* in: whether to check charsets. + ibool check_charsets, /* in: whether to check charsets. only has an effect if types_idx != NULL. */ + ulint check_null) + /* in: nonzero if none of the columns must + be declared NOT NULL */ { #ifndef UNIV_HOTBACKUP dict_index_t* index; @@ -2154,10 +2157,11 @@ dict_foreign_find_index( if (dict_index_get_n_fields(index) >= n_cols) { for (i = 0; i < n_cols; i++) { - col_name = dict_index_get_nth_field(index, i) - ->col->name; - if (dict_index_get_nth_field(index, i) - ->prefix_len != 0) { + dict_field_t* field + = dict_index_get_nth_field(index, i); + + col_name = field->col->name; + if (field->prefix_len != 0) { /* We do not accept column prefix indexes here */ @@ -2169,6 +2173,13 @@ dict_foreign_find_index( break; } + if (check_null + && (field->col->type.prtype + & DATA_NOT_NULL)) { + + return(NULL); + } + if (types_idx && !cmp_types_are_equal( dict_index_get_nth_type(index, i), dict_index_get_nth_type(types_idx, i), @@ -2290,7 +2301,7 @@ dict_foreign_add_to_cache( index = dict_foreign_find_index(ref_table, (const char**) for_in_cache->referenced_col_names, for_in_cache->n_fields, - for_in_cache->foreign_index, check_charsets); + for_in_cache->foreign_index, check_charsets, FALSE); if (index == NULL) { dict_foreign_error_report(ef, for_in_cache, @@ -2317,13 +2328,17 @@ dict_foreign_add_to_cache( index = dict_foreign_find_index(for_table, (const char**) for_in_cache->foreign_col_names, for_in_cache->n_fields, - for_in_cache->referenced_index, check_charsets); + for_in_cache->referenced_index, check_charsets, + for_in_cache->type + & (DICT_FOREIGN_ON_DELETE_SET_NULL + | DICT_FOREIGN_ON_UPDATE_SET_NULL)); if (index == NULL) { dict_foreign_error_report(ef, for_in_cache, "there is no index in the table which would contain\n" "the columns as the first columns, or the data types in the\n" -"table do not match to the ones in the referenced table."); +"table do not match to the ones in the referenced table\n" +"or one of the ON ... SET NULL columns is declared NOT NULL."); if (for_in_cache == foreign) { if (added_to_referenced_list) { @@ -3125,7 +3140,8 @@ dict_create_foreign_constraints_low( /* Try to find an index which contains the columns as the first fields and in the right order */ - index = dict_foreign_find_index(table, column_names, i, NULL, TRUE); + index = dict_foreign_find_index(table, column_names, i, + NULL, TRUE, FALSE); if (!index) { mutex_enter(&dict_foreign_err_mutex); @@ -3390,7 +3406,7 @@ dict_create_foreign_constraints_low( if (referenced_table) { index = dict_foreign_find_index(referenced_table, - column_names, i, foreign->foreign_index, TRUE); + column_names, i, foreign->foreign_index, TRUE, FALSE); if (!index) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index 9c24f385f4fffe3ee91b13df5fec4da1b54457ef..1abbb503bab151ba541b4136440362621d762fbe 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -982,6 +982,9 @@ rec_offs_nth_size( { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); + if (!n) { + return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK); + } return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n]) & REC_OFFS_MASK); } diff --git a/innobase/include/sync0sync.ic b/innobase/include/sync0sync.ic index a32a82d6e8b0cd5fdcbd6b1a9099b36c132b1125..e5c6f56d8ba19049f767e59f33200d798241e230 100644 --- a/innobase/include/sync0sync.ic +++ b/innobase/include/sync0sync.ic @@ -6,6 +6,16 @@ Mutex, the basic synchronization primitive Created 9/5/1995 Heikki Tuuri *******************************************************/ +#if defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86) +/* %z0: Use the size of operand %0 which in our case is *m to determine +instruction size, it should end up as xchgl. "1" in the input constraint, +says that "in" has to go in the same place as "out".*/ +#define TAS(m, in, out) \ + asm volatile ("xchg%z0 %2, %0" \ + : "=g" (*(m)), "=r" (out) \ + : "1" (in)) /* Note: "1" here refers to "=r" (out) */ +#endif + /********************************************************************** Sets the waiters field in a mutex. */ @@ -85,20 +95,10 @@ mutex_test_and_set( return(res); #elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86) - ulint* lw; ulint res; - lw = &(mutex->lock_word); - - /* In assembly we use the so-called AT & T syntax where - the order of operands is inverted compared to the ordinary Intel - syntax. The 'l' after the mnemonics denotes a 32-bit operation. - The line after the code tells which values come out of the asm - code, and the second line tells the input to the asm code. */ + TAS(&mutex->lock_word, 1, res); - asm volatile("movl $1, %%eax; xchgl (%%ecx), %%eax" : - "=eax" (res), "=m" (*lw) : - "ecx" (lw)); return(res); #else ibool ret; @@ -137,20 +137,9 @@ mutex_reset_lock_word( __asm MOV ECX, lw __asm XCHG EDX, DWORD PTR [ECX] #elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86) - ulint* lw; - - lw = &(mutex->lock_word); - - /* In assembly we use the so-called AT & T syntax where - the order of operands is inverted compared to the ordinary Intel - syntax. The 'l' after the mnemonics denotes a 32-bit operation. */ + ulint res; - asm volatile("movl $0, %%eax; xchgl (%%ecx), %%eax" : - "=m" (*lw) : - "ecx" (lw) : - "eax"); /* gcc does not seem to understand - that our asm code resets eax: tell it - explicitly that after the third ':' */ + TAS(&mutex->lock_word, 0, res); #else mutex->lock_word = 0; diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 06475c8ef7e9591dfcc9025500912ffca5607f9f..77dfca5fdf4ccc84da3dfeeaafa16903480dac6c 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -3259,12 +3259,6 @@ lock_deadlock_recursive( *cost = *cost + 1; - if ((depth > LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK) - || (*cost > LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK)) { - - return(LOCK_VICTIM_IS_START); - } - lock = wait_lock; if (lock_get_type(wait_lock) == LOCK_REC) { @@ -3296,11 +3290,18 @@ lock_deadlock_recursive( if (lock_has_to_wait(wait_lock, lock)) { + ibool too_far + = depth > LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK + || *cost > LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK; + lock_trx = lock->trx; - if (lock_trx == start) { + if (lock_trx == start || too_far) { + /* We came back to the recursion starting - point: a deadlock detected */ + point: a deadlock detected; or we have + searched the waits-for graph too long */ + FILE* ef = lock_latest_err_file; rewind(ef); @@ -3342,9 +3343,20 @@ lock_deadlock_recursive( } #ifdef UNIV_DEBUG if (lock_print_waits) { - fputs("Deadlock detected\n", stderr); + fputs("Deadlock detected" + " or too long search\n", + stderr); } #endif /* UNIV_DEBUG */ + if (too_far) { + + fputs("TOO DEEP OR LONG SEARCH" + " IN THE LOCK TABLE" + " WAITS-FOR GRAPH\n", ef); + + return(LOCK_VICTIM_IS_START); + } + if (ut_dulint_cmp(wait_lock->trx->undo_no, start->undo_no) >= 0) { /* Our recursion starting point diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index fe9e08d65bed11d895c9a4d0974b5c2246053a48..96c0f05111b71d1bedd924a47e3203698345c679 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -1822,14 +1822,14 @@ srv_export_innodb_status(void) export_vars.innodb_pages_written= buf_pool->n_pages_written; export_vars.innodb_row_lock_waits= srv_n_lock_wait_count; export_vars.innodb_row_lock_current_waits= srv_n_lock_wait_current_count; - export_vars.innodb_row_lock_time= srv_n_lock_wait_time / 10000; + export_vars.innodb_row_lock_time= srv_n_lock_wait_time / 1000; if (srv_n_lock_wait_count > 0) { export_vars.innodb_row_lock_time_avg = (ulint) - (srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count); + (srv_n_lock_wait_time / 1000 / srv_n_lock_wait_count); } else { export_vars.innodb_row_lock_time_avg = 0; } - export_vars.innodb_row_lock_time_max= srv_n_lock_max_wait_time / 10000; + export_vars.innodb_row_lock_time_max= srv_n_lock_max_wait_time / 1000; export_vars.innodb_rows_read= srv_n_rows_read; export_vars.innodb_rows_inserted= srv_n_rows_inserted; export_vars.innodb_rows_updated= srv_n_rows_updated; diff --git a/innobase/ut/ut0ut.c b/innobase/ut/ut0ut.c index 1be5939303a06c18db5ad560a051039b29869a4e..feb03269d916cc06983a41317bd01e3afebc73be 100644 --- a/innobase/ut/ut0ut.c +++ b/innobase/ut/ut0ut.c @@ -20,6 +20,55 @@ Created 5/11/1994 Heikki Tuuri ibool ut_always_false = FALSE; +#ifdef __WIN__ +/********************************************************************* +NOTE: The Windows epoch starts from 1601/01/01 whereas the Unix +epoch starts from 1970/1/1. For selection of constant see: +http://support.microsoft.com/kb/167296/ */ +#define WIN_TO_UNIX_DELTA_USEC ((ib_longlong) 11644473600000000ULL) + + +/********************************************************************* +This is the Windows version of gettimeofday(2).*/ +static +int +ut_gettimeofday( +/*============*/ + /* out: 0 if all OK else -1 */ + struct timeval* tv, /* out: Values are relative to Unix epoch */ + void* tz) /* in: not used */ +{ + FILETIME ft; + ib_longlong tm; + + if (!tv) { + errno = EINVAL; + return(-1); + } + + GetSystemTimeAsFileTime(&ft); + + tm = (ib_longlong) ft.dwHighDateTime << 32; + tm |= ft.dwLowDateTime; + + ut_a(tm >= 0); /* If tm wraps over to negative, the quotient / 10 + does not work */ + + tm /= 10; /* Convert from 100 nsec periods to usec */ + + /* If we don't convert to the Unix epoch the value for + struct timeval::tv_sec will overflow.*/ + tm -= WIN_TO_UNIX_DELTA_USEC; + + tv->tv_sec = (long) (tm / 1000000L); + tv->tv_usec = (long) (tm % 1000000L); + + return(0); +} +#else +#define ut_gettimeofday gettimeofday +#endif + /********************************************************************* Get the quote character to be used in SQL identifiers. This definition must match the one in sql/ha_innodb.cc! */ @@ -82,17 +131,11 @@ ut_usectime( ulint* sec, /* out: seconds since the Epoch */ ulint* ms) /* out: microseconds since the Epoch+*sec */ { -#ifdef __WIN__ - SYSTEMTIME st; - GetLocalTime(&st); - *sec = (ulint) st.wSecond; - *ms = (ulint) st.wMilliseconds; -#else struct timeval tv; - gettimeofday(&tv,NULL); + + ut_gettimeofday(&tv, NULL); *sec = (ulint) tv.tv_sec; *ms = (ulint) tv.tv_usec; -#endif } /************************************************************** diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 99f0d4100eef478166776db062062d94c3774dd1..0638152ba425dd5e9e07ccbf493731ce71e33022 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2968,3 +2968,21 @@ a drop table t2, t1; create table t1 (g geometry not null, spatial gk(g)) engine=innodb; ERROR HY000: The used table type doesn't support SPATIAL indexes +CREATE TABLE t1 (a INT, INDEX(a)) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, INDEX(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1 (a) ON DELETE SET NULL; +ALTER TABLE t2 MODIFY a INT NOT NULL; +ERROR HY000: Error on rename of '#sql-temporary' to './test/t2' (errno: 150) +DELETE FROM t1; +DROP TABLE t2,t1; +CREATE TABLE t1 (a VARCHAR(5) COLLATE utf8_unicode_ci PRIMARY KEY) +ENGINE=InnoDB; +INSERT INTO t1 VALUES (0xEFBCA4EFBCA4EFBCA4); +DELETE FROM t1; +INSERT INTO t1 VALUES ('DDD'); +SELECT * FROM t1; +a +DDD +DROP TABLE t1; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index aa25677bd998c89d9d46d197dc4c925d3c9002d1..e762d740d667a8b427bf210f0fc8cd66b79219c2 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1976,6 +1976,34 @@ drop table t2, t1; --error ER_TABLE_CANT_HANDLE_SPKEYS create table t1 (g geometry not null, spatial gk(g)) engine=innodb; +# +# Bug #25927: Prevent ALTER TABLE ... MODIFY ... NOT NULL on columns +# for which there is a foreign key constraint ON ... SET NULL. +# + +CREATE TABLE t1 (a INT, INDEX(a)) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, INDEX(a)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (1); +ALTER TABLE t2 ADD FOREIGN KEY (a) REFERENCES t1 (a) ON DELETE SET NULL; +--replace_regex /'\.\/test\/#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error 1025 +ALTER TABLE t2 MODIFY a INT NOT NULL; +DELETE FROM t1; +DROP TABLE t2,t1; + +# +# Bug #26835: table corruption after delete+insert +# + +CREATE TABLE t1 (a VARCHAR(5) COLLATE utf8_unicode_ci PRIMARY KEY) +ENGINE=InnoDB; +INSERT INTO t1 VALUES (0xEFBCA4EFBCA4EFBCA4); +DELETE FROM t1; +INSERT INTO t1 VALUES ('DDD'); +SELECT * FROM t1; +DROP TABLE t1; + ####################################################################### # # # Please, DO NOT TOUCH this file as well as the innodb.result file. # diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index cbefa9d39493bac0479e47552eebdde165f64bac..217f59d4b7ea3ec228db2273e4555b0ba293b572 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -6066,6 +6066,15 @@ ha_innobase::external_lock( trx->isolation_level = innobase_map_isolation_level( (enum_tx_isolation) thd->variables.tx_isolation); + + if (trx->isolation_level <= TRX_ISO_READ_COMMITTED + && trx->global_read_view) { + + /* At low transaction isolation levels we let + each consistent read set its own snapshot */ + + read_view_close_for_mysql(trx); + } } if (trx->isolation_level == TRX_ISO_SERIALIZABLE