Commit 8abc9536 authored by tsmith@siva.hindu.god's avatar tsmith@siva.hindu.god

NULL MERGE this to 5.1

Apply the following InnoDB snapshots:
innodb-5.0-ss1319
innodb-5.0-ss1331
innodb-5.0-ss1333
innodb-5.0-ss1341

Fixes:
- Bug #21409: Incorrect result returned when in READ-COMMITTED with query_cache ON
  At low transaction isolation levels we let each consistent read set
  its own snapshot.
- Bug #23666: strange Innodb_row_lock_time_% values in show status; also millisecs wrong
  On Windows ut_usectime returns secs and usecs relative to the UNIX
  epoch (which is Jan, 1 1970).

- Bug #25494: LATEST DEADLOCK INFORMATION is not always cleared
  lock_deadlock_recursive(): When the search depth or length is exceeded,
  rewind lock_latest_err_file and display the two transactions at the
  point of aborting the search.

- Bug #25927: Foreign key with ON DELETE SET NULL on NOT NULL can crash server
  Prevent ALTER TABLE ... MODIFY ... NOT NULL on columns for which
  there is a foreign key constraint ON ... SET NULL.

- Bug #26835: Repeatable corruption of utf8-enabled tables inside InnoDB
  The bug could be reproduced as follows:

  Define a table so that the first column of the clustered index is
  a VARCHAR or a UTF-8 CHAR in a collation where sequences of bytes
  of differing length are considered equivalent.

  Insert and delete a record.  Before the delete-marked record is
  purged, insert another record whose first column is of different
  length but equivalent to the first record.  Under certain conditions,
  the insertion can be incorrectly performed as update-in-place.

  Likewise, an operation that could be done as update-in-place can
  unnecessarily be performed as delete and insert, but that would not
  cause corruption but merely degraded performance.
parent d645496e
...@@ -2139,9 +2139,12 @@ dict_foreign_find_index( ...@@ -2139,9 +2139,12 @@ dict_foreign_find_index(
ulint n_cols, /* in: number of columns */ ulint n_cols, /* in: number of columns */
dict_index_t* types_idx, /* in: NULL or an index to whose types the dict_index_t* types_idx, /* in: NULL or an index to whose types the
column types must match */ 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 != only has an effect if types_idx !=
NULL. */ NULL. */
ulint check_null)
/* in: nonzero if none of the columns must
be declared NOT NULL */
{ {
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
dict_index_t* index; dict_index_t* index;
...@@ -2154,10 +2157,11 @@ dict_foreign_find_index( ...@@ -2154,10 +2157,11 @@ dict_foreign_find_index(
if (dict_index_get_n_fields(index) >= n_cols) { if (dict_index_get_n_fields(index) >= n_cols) {
for (i = 0; i < n_cols; i++) { for (i = 0; i < n_cols; i++) {
col_name = dict_index_get_nth_field(index, i) dict_field_t* field
->col->name; = dict_index_get_nth_field(index, i);
if (dict_index_get_nth_field(index, i)
->prefix_len != 0) { col_name = field->col->name;
if (field->prefix_len != 0) {
/* We do not accept column prefix /* We do not accept column prefix
indexes here */ indexes here */
...@@ -2169,6 +2173,13 @@ dict_foreign_find_index( ...@@ -2169,6 +2173,13 @@ dict_foreign_find_index(
break; break;
} }
if (check_null
&& (field->col->type.prtype
& DATA_NOT_NULL)) {
return(NULL);
}
if (types_idx && !cmp_types_are_equal( if (types_idx && !cmp_types_are_equal(
dict_index_get_nth_type(index, i), dict_index_get_nth_type(index, i),
dict_index_get_nth_type(types_idx, i), dict_index_get_nth_type(types_idx, i),
...@@ -2290,7 +2301,7 @@ dict_foreign_add_to_cache( ...@@ -2290,7 +2301,7 @@ dict_foreign_add_to_cache(
index = dict_foreign_find_index(ref_table, index = dict_foreign_find_index(ref_table,
(const char**) for_in_cache->referenced_col_names, (const char**) for_in_cache->referenced_col_names,
for_in_cache->n_fields, for_in_cache->n_fields,
for_in_cache->foreign_index, check_charsets); for_in_cache->foreign_index, check_charsets, FALSE);
if (index == NULL) { if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache, dict_foreign_error_report(ef, for_in_cache,
...@@ -2317,13 +2328,17 @@ dict_foreign_add_to_cache( ...@@ -2317,13 +2328,17 @@ dict_foreign_add_to_cache(
index = dict_foreign_find_index(for_table, index = dict_foreign_find_index(for_table,
(const char**) for_in_cache->foreign_col_names, (const char**) for_in_cache->foreign_col_names,
for_in_cache->n_fields, 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) { if (index == NULL) {
dict_foreign_error_report(ef, for_in_cache, dict_foreign_error_report(ef, for_in_cache,
"there is no index in the table which would contain\n" "there is no index in the table which would contain\n"
"the columns as the first columns, or the data types in the\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 (for_in_cache == foreign) {
if (added_to_referenced_list) { if (added_to_referenced_list) {
...@@ -3125,7 +3140,8 @@ dict_create_foreign_constraints_low( ...@@ -3125,7 +3140,8 @@ dict_create_foreign_constraints_low(
/* Try to find an index which contains the columns /* Try to find an index which contains the columns
as the first fields and in the right order */ 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) { if (!index) {
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
...@@ -3390,7 +3406,7 @@ dict_create_foreign_constraints_low( ...@@ -3390,7 +3406,7 @@ dict_create_foreign_constraints_low(
if (referenced_table) { if (referenced_table) {
index = dict_foreign_find_index(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) { if (!index) {
dict_foreign_free(foreign); dict_foreign_free(foreign);
mutex_enter(&dict_foreign_err_mutex); mutex_enter(&dict_foreign_err_mutex);
......
...@@ -982,6 +982,9 @@ rec_offs_nth_size( ...@@ -982,6 +982,9 @@ rec_offs_nth_size(
{ {
ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(rec_offs_validate(NULL, NULL, offsets));
ut_ad(n < rec_offs_n_fields(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]) return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n])
& REC_OFFS_MASK); & REC_OFFS_MASK);
} }
......
...@@ -6,6 +6,16 @@ Mutex, the basic synchronization primitive ...@@ -6,6 +6,16 @@ Mutex, the basic synchronization primitive
Created 9/5/1995 Heikki Tuuri 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. */ Sets the waiters field in a mutex. */
...@@ -85,20 +95,10 @@ mutex_test_and_set( ...@@ -85,20 +95,10 @@ mutex_test_and_set(
return(res); return(res);
#elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86) #elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86)
ulint* lw;
ulint res; ulint res;
lw = &(mutex->lock_word); TAS(&mutex->lock_word, 1, res);
/* 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. */
asm volatile("movl $1, %%eax; xchgl (%%ecx), %%eax" :
"=eax" (res), "=m" (*lw) :
"ecx" (lw));
return(res); return(res);
#else #else
ibool ret; ibool ret;
...@@ -137,20 +137,9 @@ mutex_reset_lock_word( ...@@ -137,20 +137,9 @@ mutex_reset_lock_word(
__asm MOV ECX, lw __asm MOV ECX, lw
__asm XCHG EDX, DWORD PTR [ECX] __asm XCHG EDX, DWORD PTR [ECX]
#elif defined(not_defined) && defined(__GNUC__) && defined(UNIV_INTEL_X86) #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. */
asm volatile("movl $0, %%eax; xchgl (%%ecx), %%eax" : TAS(&mutex->lock_word, 0, res);
"=m" (*lw) :
"ecx" (lw) :
"eax"); /* gcc does not seem to understand
that our asm code resets eax: tell it
explicitly that after the third ':' */
#else #else
mutex->lock_word = 0; mutex->lock_word = 0;
......
...@@ -3259,12 +3259,6 @@ lock_deadlock_recursive( ...@@ -3259,12 +3259,6 @@ lock_deadlock_recursive(
*cost = *cost + 1; *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; lock = wait_lock;
if (lock_get_type(wait_lock) == LOCK_REC) { if (lock_get_type(wait_lock) == LOCK_REC) {
...@@ -3296,11 +3290,18 @@ lock_deadlock_recursive( ...@@ -3296,11 +3290,18 @@ lock_deadlock_recursive(
if (lock_has_to_wait(wait_lock, lock)) { 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; lock_trx = lock->trx;
if (lock_trx == start) { if (lock_trx == start || too_far) {
/* We came back to the recursion starting /* 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; FILE* ef = lock_latest_err_file;
rewind(ef); rewind(ef);
...@@ -3342,9 +3343,20 @@ lock_deadlock_recursive( ...@@ -3342,9 +3343,20 @@ lock_deadlock_recursive(
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (lock_print_waits) { if (lock_print_waits) {
fputs("Deadlock detected\n", stderr); fputs("Deadlock detected"
" or too long search\n",
stderr);
} }
#endif /* UNIV_DEBUG */ #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, if (ut_dulint_cmp(wait_lock->trx->undo_no,
start->undo_no) >= 0) { start->undo_no) >= 0) {
/* Our recursion starting point /* Our recursion starting point
......
...@@ -1822,14 +1822,14 @@ srv_export_innodb_status(void) ...@@ -1822,14 +1822,14 @@ srv_export_innodb_status(void)
export_vars.innodb_pages_written= buf_pool->n_pages_written; 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_waits= srv_n_lock_wait_count;
export_vars.innodb_row_lock_current_waits= srv_n_lock_wait_current_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) { if (srv_n_lock_wait_count > 0) {
export_vars.innodb_row_lock_time_avg = (ulint) 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 { } else {
export_vars.innodb_row_lock_time_avg = 0; 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_read= srv_n_rows_read;
export_vars.innodb_rows_inserted= srv_n_rows_inserted; export_vars.innodb_rows_inserted= srv_n_rows_inserted;
export_vars.innodb_rows_updated= srv_n_rows_updated; export_vars.innodb_rows_updated= srv_n_rows_updated;
......
...@@ -20,6 +20,55 @@ Created 5/11/1994 Heikki Tuuri ...@@ -20,6 +20,55 @@ Created 5/11/1994 Heikki Tuuri
ibool ut_always_false = FALSE; 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. Get the quote character to be used in SQL identifiers.
This definition must match the one in sql/ha_innodb.cc! */ This definition must match the one in sql/ha_innodb.cc! */
...@@ -82,17 +131,11 @@ ut_usectime( ...@@ -82,17 +131,11 @@ ut_usectime(
ulint* sec, /* out: seconds since the Epoch */ ulint* sec, /* out: seconds since the Epoch */
ulint* ms) /* out: microseconds since the Epoch+*sec */ 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; struct timeval tv;
gettimeofday(&tv,NULL);
ut_gettimeofday(&tv, NULL);
*sec = (ulint) tv.tv_sec; *sec = (ulint) tv.tv_sec;
*ms = (ulint) tv.tv_usec; *ms = (ulint) tv.tv_usec;
#endif
} }
/************************************************************** /**************************************************************
......
...@@ -2968,3 +2968,21 @@ a ...@@ -2968,3 +2968,21 @@ a
drop table t2, t1; drop table t2, t1;
create table t1 (g geometry not null, spatial gk(g)) engine=innodb; create table t1 (g geometry not null, spatial gk(g)) engine=innodb;
ERROR HY000: The used table type doesn't support SPATIAL indexes 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;
...@@ -1976,6 +1976,34 @@ drop table t2, t1; ...@@ -1976,6 +1976,34 @@ drop table t2, t1;
--error ER_TABLE_CANT_HANDLE_SPKEYS --error ER_TABLE_CANT_HANDLE_SPKEYS
create table t1 (g geometry not null, spatial gk(g)) engine=innodb; 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. # # Please, DO NOT TOUCH this file as well as the innodb.result file. #
......
...@@ -6066,6 +6066,15 @@ ha_innobase::external_lock( ...@@ -6066,6 +6066,15 @@ ha_innobase::external_lock(
trx->isolation_level = innobase_map_isolation_level( trx->isolation_level = innobase_map_isolation_level(
(enum_tx_isolation) (enum_tx_isolation)
thd->variables.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 if (trx->isolation_level == TRX_ISO_SERIALIZABLE
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment