diff options
43 files changed, 1555 insertions, 329 deletions
diff --git a/client/readline.cc b/client/readline.cc index 0f3fab7e43d..7765a62766f 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -57,7 +57,7 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) char *batch_readline(LINE_BUFFER *line_buff) { char *pos; - ulong out_length; + ulong out_length= 0; if (!(pos=intern_read_line(line_buff, &out_length))) return 0; diff --git a/cmd-line-utils/libedit/histedit.h b/cmd-line-utils/libedit/histedit.h index 9f2b245f851..523d27b0be2 100644 --- a/cmd-line-utils/libedit/histedit.h +++ b/cmd-line-utils/libedit/histedit.h @@ -43,7 +43,12 @@ #define LIBEDIT_MAJOR 2 #define LIBEDIT_MINOR 11 +/* XXXMYSQL : stdint.h might not be available on older Solaris platforms. */ +#if defined(__sun) || defined(__sun__) +#include <sys/inttypes.h> +#else #include <stdint.h> +#endif #include <sys/types.h> #include <stdio.h> diff --git a/cmd-line-utils/libedit/np/unvis.c b/cmd-line-utils/libedit/np/unvis.c index 4c523608c27..812d280b2d8 100644 --- a/cmd-line-utils/libedit/np/unvis.c +++ b/cmd-line-utils/libedit/np/unvis.c @@ -47,14 +47,21 @@ static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93"; #include <assert.h> #include <ctype.h> + +/* XXXMYSQL : stdint.h might not be available on older Solaris platforms. */ +#if defined(__sun) || defined(__sun__) +#include <sys/inttypes.h> +#else #include <stdint.h> +#endif + #include <stdio.h> #include <errno.h> -#ifdef HAVE_VIS_H -#include <vis.h> -#else +/* + XXXMYSQL : Due to different versions of vis.h available, + use the one bundled with libedit. +*/ #include "np/vis.h" -#endif #ifdef __weak_alias __weak_alias(strnunvisx,_strnunvisx) diff --git a/cmd-line-utils/libedit/np/vis.c b/cmd-line-utils/libedit/np/vis.c index 6c75438c352..d7cc37b4f47 100644 --- a/cmd-line-utils/libedit/np/vis.c +++ b/cmd-line-utils/libedit/np/vis.c @@ -68,11 +68,11 @@ #include <sys/types.h> #include <assert.h> -#ifdef HAVE_VIS_H -#include <vis.h> -#else +/* + XXXMYSQL : Due to different versions of vis.h available, + use the one bundled with libedit. +*/ #include "np/vis.h" -#endif #include <errno.h> #include <stdlib.h> diff --git a/cmd-line-utils/libedit/read.c b/cmd-line-utils/libedit/read.c index 48bf4efd59b..a919b888965 100644 --- a/cmd-line-utils/libedit/read.c +++ b/cmd-line-utils/libedit/read.c @@ -333,7 +333,7 @@ memset(&state, 0, sizeof(mbstate_t)); #ifdef WIDECHAR ++cbp; - if (cbp > MB_CUR_MAX) { /* "shouldn't happen" */ + if (cbp > (size_t) MB_CUR_MAX) { /* "shouldn't happen" */ *cp = '\0'; return (-1); } diff --git a/include/my_stacktrace.h b/include/my_stacktrace.h index e7713f46fc3..30cac3871c5 100644 --- a/include/my_stacktrace.h +++ b/include/my_stacktrace.h @@ -1,5 +1,4 @@ -/* - Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,8 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _my_stacktrace_h_ #define _my_stacktrace_h_ @@ -63,6 +61,69 @@ void my_set_exception_pointers(EXCEPTION_POINTERS *ep); void my_write_core(int sig); #endif + + +/** + Async-signal-safe utility functions used by signal handler routines. + Declared here in order to unit-test them. + These are not general-purpose, but tailored to the signal handling routines. +*/ +/** + Converts a longlong value to string. + @param base 10 for decimal, 16 for hex values (0..9a..f) + @param val The value to convert + @param buf Assumed to point to the *end* of the buffer. + @returns Pointer to the first character of the converted string. + Negative values: + for base-10 the return string will be prepended with '-' + for base-16 the return string will contain 16 characters + Implemented with simplicity, and async-signal-safety in mind. +*/ +char *my_safe_itoa(int base, longlong val, char *buf); + +/** + Converts a ulonglong value to string. + @param base 10 for decimal, 16 for hex values (0..9a..f) + @param val The value to convert + @param buf Assumed to point to the *end* of the buffer. + @returns Pointer to the first character of the converted string. + Implemented with simplicity, and async-signal-safety in mind. +*/ +char *my_safe_utoa(int base, ulonglong val, char *buf); + +/** + A (very) limited version of snprintf. + @param to Destination buffer. + @param n Size of destination buffer. + @param fmt printf() style format string. + @returns Number of bytes written, including terminating '\0' + Supports 'd' 'i' 'u' 'x' 'p' 's' conversion. + Supports 'l' and 'll' modifiers for integral types. + Does not support any width/precision. + Implemented with simplicity, and async-signal-safety in mind. +*/ +size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...) + ATTRIBUTE_FORMAT(printf, 3, 4); + +/** + A (very) limited version of snprintf, which writes the result to STDERR. + @sa my_safe_snprintf + Implemented with simplicity, and async-signal-safety in mind. + @note Has an internal buffer capacity of 512 bytes, + which should suffice for our signal handling routines. +*/ +size_t my_safe_printf_stderr(const char* fmt, ...) + ATTRIBUTE_FORMAT(printf, 1, 2); + +/** + Writes up to count bytes from buffer to STDERR. + Implemented with simplicity, and async-signal-safety in mind. + @param buf Buffer containing data to be written. + @param count Number of bytes to write. + @returns Number of bytes written. +*/ +size_t my_write_stderr(const void *buf, size_t count); + C_MODE_END #endif /* _my_stacktrace_h_ */ diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 344079cd61e..a1a3dbd6658 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -109,6 +109,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/password.c ../sql/discover.cc ../sql/derror.cc ../sql/field.cc ../sql/field_conv.cc ../sql/filesort.cc ../sql/gstream.cc ../sql/ha_partition.cc + ../sql/signal_handler.cc ../sql/handler.cc ../sql/hash_filo.cc ../sql/hostname.cc ../sql/init.cc ../sql/item_buff.cc ../sql/item_cmpfunc.cc ../sql/item.cc ../sql/item_create.cc ../sql/item_func.cc diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 15ded03f414..69f5adf5b5d 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -12772,3 +12772,22 @@ a b c d e f -1 b c d e 1 DROP TABLE t1; SET sort_buffer_size=DEFAULT; +# +# BUG#11758979 - 51252: ARCHIVE TABLES CAUSE 100% CPU USAGE +# AND HANG IN SHOW TABLE STATUS +# (to be executed with valgrind) +CREATE TABLE t1(a BLOB, b VARCHAR(200)) ENGINE=ARCHIVE; +INSERT INTO t1 VALUES(NULL, ''); +FLUSH TABLE t1; +# we need this select to workaround BUG#11764364 +SELECT * FROM t1; +a b +NULL +CHECKSUM TABLE t1 EXTENDED; +Table Checksum +test.t1 286155052 +FLUSH TABLE t1; +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb-lock.result b/mysql-test/suite/innodb/r/innodb-lock.result index 4ace4065c34..5881d4c80b3 100644 --- a/mysql-test/suite/innodb/r/innodb-lock.result +++ b/mysql-test/suite/innodb/r/innodb-lock.result @@ -55,3 +55,20 @@ id x 1 1 2 10 drop table t1; +# +#Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE +#fixed by re-fixing Bug#7975 +#aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT... +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; +INSERT INTO t1 VALUES(3,1); +BEGIN; +INSERT IGNORE INTO t1 VALUES(3,14); +BEGIN; +INSERT IGNORE INTO t1 VALUES(3,23); +SELECT * FROM t1 FOR UPDATE; +COMMIT; +a b +3 1 +COMMIT; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/innodb_replace.result b/mysql-test/suite/innodb/r/innodb_replace.result new file mode 100644 index 00000000000..30009b8ddc9 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_replace.result @@ -0,0 +1,77 @@ +# +#Bug#11759688 52020: InnoDB can still deadlock +#on just INSERT...ON DUPLICATE KEY +#a.k.a. Bug#7975 deadlock without any locking, simple select and update +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; +INSERT INTO t1 VALUES(3,1); +BEGIN; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1'; +INSERT INTO t1 VALUES(3,2); +SET DEBUG_SYNC='now WAIT_FOR insert1'; +SELECT * FROM t1 LOCK IN SHARE MODE; +a b +3 1 +SELECT * FROM t1 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select1'; +ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +COMMIT; +SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2'; +REPLACE INTO t1 VALUES(3,4); +SET DEBUG_SYNC='now WAIT_FOR insert2'; +SELECT * FROM t1; +a b +3 11 +SELECT * FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select2'; +SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3'; +INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20; +a b +3 4 +SET DEBUG_SYNC='now WAIT_FOR insert3'; +SELECT b FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select3'; +b +24 +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert4'; +SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE; +b +24 +SELECT b FROM t1 WHERE a=3 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select4'; +b +24 +ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert5'; +SELECT * FROM t1; +a b +3 24 +SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE; +a b +3 24 +SELECT * FROM t1 WHERE a=3 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select5'; +a b +3 24 +SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert6'; +SELECT * FROM t1; +a b +1 2 +3 24 +5 6 +SELECT a,b FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select6'; +a b +1 2 +3 4 +5 6 +SET DEBUG_SYNC='RESET'; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb-lock.test b/mysql-test/suite/innodb/t/innodb-lock.test index eacf7e562be..8e4dd80e07e 100644 --- a/mysql-test/suite/innodb/t/innodb-lock.test +++ b/mysql-test/suite/innodb/t/innodb-lock.test @@ -100,3 +100,44 @@ select * from t1; drop table t1; # End of 4.1 tests + +--echo # +--echo #Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE +--echo #fixed by re-fixing Bug#7975 +--echo #aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT... +--echo # + +connection default; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; + +INSERT INTO t1 VALUES(3,1); + +BEGIN; +# this used to wrongly acquire an X lock; now it takes an S lock +INSERT IGNORE INTO t1 VALUES(3,14); + +connection con1; +BEGIN; +# this used to wrongly acquire an X lock; now it takes an S lock +INSERT IGNORE INTO t1 VALUES(3,23); +--send +SELECT * FROM t1 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 FOR UPDATE'; +--source include/wait_condition.inc + +connection default; +COMMIT; +connection con1; +reap; +COMMIT; +disconnect con1; +disconnect con2; + +connection default; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb_replace.test b/mysql-test/suite/innodb/t/innodb_replace.test new file mode 100644 index 00000000000..a35f423c85e --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_replace.test @@ -0,0 +1,186 @@ +--source include/have_innodb.inc +--source include/have_debug_sync.inc + +--echo # +--echo #Bug#11759688 52020: InnoDB can still deadlock +--echo #on just INSERT...ON DUPLICATE KEY +--echo #a.k.a. Bug#7975 deadlock without any locking, simple select and update +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; + +INSERT INTO t1 VALUES(3,1); + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connection con1; + +BEGIN; +# normal INSERT of a duplicate should only S-lock the existing record (3,1) +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1'; +--send +INSERT INTO t1 VALUES(3,2); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert1'; +# this should S-lock (3,1); no conflict +SELECT * FROM t1 LOCK IN SHARE MODE; +# this should X-lock (3,1), conflicting with con1 +--send +SELECT * FROM t1 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select1'; + +connection con1; +--error ER_DUP_ENTRY +reap; +# We are still holding an S-lock on (3,1) after the failed INSERT. +# The following will upgrade it to an X-lock, causing a deadlock. +# InnoDB should resolve the deadlock by aborting the blocked SELECT. +INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10; + +connection default; +--error ER_LOCK_DEADLOCK +reap; +connection con1; +COMMIT; + +SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2'; +--send +REPLACE INTO t1 VALUES(3,4); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert2'; +SELECT * FROM t1; +--send +SELECT * FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked because of X lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select2'; + +connection con1; +reap; + +SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3'; +--send +INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20; + +connection default; +reap; +SET DEBUG_SYNC='now WAIT_FOR insert3'; +--send +SELECT b FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked because of X lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT b FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select3'; + +connection default; +reap; + +connection con1; +reap; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert4'; +# this should S-lock (3,1); no conflict +SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE; +# this should X-lock (3,1), conflicting with con1 +--send +SELECT b FROM t1 WHERE a=3 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'statistics' and + info = 'SELECT b FROM t1 WHERE a=3 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select4'; + +connection default; +reap; + +connection con1; +--error ER_DUP_ENTRY +reap; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert5'; +SELECT * FROM t1; +# this should S-lock; no conflict +SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE; +# this should X-lock, conflicting with the S-lock of the IGNORE in con1 +--send +SELECT * FROM t1 WHERE a=3 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'statistics' and + info = 'SELECT * FROM t1 WHERE a=3 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select5'; + +connection con1; +reap; +connection default; +reap; + +connection con1; +SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert6'; +SELECT * FROM t1; +# this should conflict with the X-lock acquired by the REPLACE +--send +SELECT a,b FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT a,b FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select6'; + +connection con1; +reap; +connection default; +reap; + +disconnect con1; +disconnect con2; + +connection default; +SET DEBUG_SYNC='RESET'; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/r/innodb-lock.result b/mysql-test/suite/innodb_plugin/r/innodb-lock.result index 4ace4065c34..5881d4c80b3 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb-lock.result +++ b/mysql-test/suite/innodb_plugin/r/innodb-lock.result @@ -55,3 +55,20 @@ id x 1 1 2 10 drop table t1; +# +#Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE +#fixed by re-fixing Bug#7975 +#aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT... +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; +INSERT INTO t1 VALUES(3,1); +BEGIN; +INSERT IGNORE INTO t1 VALUES(3,14); +BEGIN; +INSERT IGNORE INTO t1 VALUES(3,23); +SELECT * FROM t1 FOR UPDATE; +COMMIT; +a b +3 1 +COMMIT; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_cmp_drop_table.result b/mysql-test/suite/innodb_plugin/r/innodb_cmp_drop_table.result new file mode 100644 index 00000000000..bae2a17bd02 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_cmp_drop_table.result @@ -0,0 +1,13 @@ +set global innodb_file_per_table=on; +set global innodb_file_format=`1`; +create table t1(a text) engine=innodb key_block_size=8; +SELECT page_size FROM information_schema.innodb_cmpmem WHERE pages_used > 0; +page_size +8192 +drop table t1; +SELECT page_size FROM information_schema.innodb_cmpmem WHERE pages_used > 0; +page_size +create table t2(a text) engine=innodb; +SELECT page_size FROM information_schema.innodb_cmpmem WHERE pages_used > 0; +page_size +drop table t2; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_replace.result b/mysql-test/suite/innodb_plugin/r/innodb_replace.result new file mode 100644 index 00000000000..30009b8ddc9 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_replace.result @@ -0,0 +1,77 @@ +# +#Bug#11759688 52020: InnoDB can still deadlock +#on just INSERT...ON DUPLICATE KEY +#a.k.a. Bug#7975 deadlock without any locking, simple select and update +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; +INSERT INTO t1 VALUES(3,1); +BEGIN; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1'; +INSERT INTO t1 VALUES(3,2); +SET DEBUG_SYNC='now WAIT_FOR insert1'; +SELECT * FROM t1 LOCK IN SHARE MODE; +a b +3 1 +SELECT * FROM t1 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select1'; +ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +COMMIT; +SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2'; +REPLACE INTO t1 VALUES(3,4); +SET DEBUG_SYNC='now WAIT_FOR insert2'; +SELECT * FROM t1; +a b +3 11 +SELECT * FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select2'; +SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3'; +INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20; +a b +3 4 +SET DEBUG_SYNC='now WAIT_FOR insert3'; +SELECT b FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select3'; +b +24 +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert4'; +SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE; +b +24 +SELECT b FROM t1 WHERE a=3 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select4'; +b +24 +ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert5'; +SELECT * FROM t1; +a b +3 24 +SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE; +a b +3 24 +SELECT * FROM t1 WHERE a=3 FOR UPDATE; +SET DEBUG_SYNC='now SIGNAL select5'; +a b +3 24 +SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6'; +LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); +SET DEBUG_SYNC='now WAIT_FOR insert6'; +SELECT * FROM t1; +a b +1 2 +3 24 +5 6 +SELECT a,b FROM t1 LOCK IN SHARE MODE; +SET DEBUG_SYNC='now SIGNAL select6'; +a b +1 2 +3 4 +5 6 +SET DEBUG_SYNC='RESET'; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/t/innodb-lock.test b/mysql-test/suite/innodb_plugin/t/innodb-lock.test index 20467236b1c..2198c02efb0 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb-lock.test +++ b/mysql-test/suite/innodb_plugin/t/innodb-lock.test @@ -100,3 +100,44 @@ select * from t1; drop table t1; # End of 4.1 tests + +--echo # +--echo #Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE +--echo #fixed by re-fixing Bug#7975 +--echo #aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT... +--echo # + +connection default; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; + +INSERT INTO t1 VALUES(3,1); + +BEGIN; +# this used to wrongly acquire an X lock; now it takes an S lock +INSERT IGNORE INTO t1 VALUES(3,14); + +connection con1; +BEGIN; +# this used to wrongly acquire an X lock; now it takes an S lock +INSERT IGNORE INTO t1 VALUES(3,23); +--send +SELECT * FROM t1 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 FOR UPDATE'; +--source include/wait_condition.inc + +connection default; +COMMIT; +connection con1; +reap; +COMMIT; +disconnect con1; +disconnect con2; + +connection default; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table-master.opt b/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table-master.opt new file mode 100644 index 00000000000..a9a3d8c3db8 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table-master.opt @@ -0,0 +1 @@ +--innodb-buffer-pool-size=8M diff --git a/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table.test b/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table.test new file mode 100644 index 00000000000..b40ab526615 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_cmp_drop_table.test @@ -0,0 +1,59 @@ +-- source include/have_innodb_plugin.inc + +let $per_table=`select @@innodb_file_per_table`; +let $format=`select @@innodb_file_format`; + +-- let $query_i_s = SELECT page_size FROM information_schema.innodb_cmpmem WHERE pages_used > 0 + +set global innodb_file_per_table=on; +set global innodb_file_format=`1`; + +create table t1(a text) engine=innodb key_block_size=8; + +-- disable_query_log + +# insert some rows so we are using compressed pages +-- let $i = 10 +while ($i) +{ + insert into t1 values(repeat('abcdefghijklmnopqrstuvwxyz',100)); + dec $i; +} +-- enable_query_log + +# we should be using some 8K pages +-- eval $query_i_s + +drop table t1; + +# no lazy eviction at drop table in 5.1 there should still be no +# used 8K pages +-- eval $query_i_s + +# create a non-compressed table and insert enough into it to evict +# compressed pages +create table t2(a text) engine=innodb; + +-- disable_query_log + +-- let $i = 200 +while ($i) +{ + insert into t2 values(repeat('abcdefghijklmnopqrstuvwxyz',1000)); + dec $i; +} + +-- enable_query_log + +# now there should be no 8K pages in the buffer pool +-- eval $query_i_s + +drop table t2; + +# +# restore environment to the state it was before this test execution +# + +-- disable_query_log +eval set global innodb_file_format=$format; +eval set global innodb_file_per_table=$per_table; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_replace.test b/mysql-test/suite/innodb_plugin/t/innodb_replace.test new file mode 100644 index 00000000000..f052edd1d24 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_replace.test @@ -0,0 +1,186 @@ +--source include/have_innodb_plugin.inc +--source include/have_debug_sync.inc + +--echo # +--echo #Bug#11759688 52020: InnoDB can still deadlock +--echo #on just INSERT...ON DUPLICATE KEY +--echo #a.k.a. Bug#7975 deadlock without any locking, simple select and update +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB; + +INSERT INTO t1 VALUES(3,1); + +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connection con1; + +BEGIN; +# normal INSERT of a duplicate should only S-lock the existing record (3,1) +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1'; +--send +INSERT INTO t1 VALUES(3,2); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert1'; +# this should S-lock (3,1); no conflict +SELECT * FROM t1 LOCK IN SHARE MODE; +# this should X-lock (3,1), conflicting with con1 +--send +SELECT * FROM t1 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select1'; + +connection con1; +--error ER_DUP_ENTRY +reap; +# We are still holding an S-lock on (3,1) after the failed INSERT. +# The following will upgrade it to an X-lock, causing a deadlock. +# InnoDB should resolve the deadlock by aborting the blocked SELECT. +INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10; + +connection default; +--error ER_LOCK_DEADLOCK +reap; +connection con1; +COMMIT; + +SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2'; +--send +REPLACE INTO t1 VALUES(3,4); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert2'; +SELECT * FROM t1; +--send +SELECT * FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked because of X lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT * FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select2'; + +connection con1; +reap; + +SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3'; +--send +INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20; + +connection default; +reap; +SET DEBUG_SYNC='now WAIT_FOR insert3'; +--send +SELECT b FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked because of X lock. +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT b FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select3'; + +connection default; +reap; + +connection con1; +reap; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert4'; +# this should S-lock (3,1); no conflict +SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE; +# this should X-lock (3,1), conflicting with con1 +--send +SELECT b FROM t1 WHERE a=3 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'statistics' and + info = 'SELECT b FROM t1 WHERE a=3 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select4'; + +connection default; +reap; + +connection con1; +--error ER_DUP_ENTRY +reap; +SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert5'; +SELECT * FROM t1; +# this should S-lock; no conflict +SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE; +# this should X-lock, conflicting with the S-lock of the IGNORE in con1 +--send +SELECT * FROM t1 WHERE a=3 FOR UPDATE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'statistics' and + info = 'SELECT * FROM t1 WHERE a=3 FOR UPDATE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select5'; + +connection con1; +reap; +connection default; +reap; + +connection con1; +SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6'; +--send +LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b); + +connection default; +SET DEBUG_SYNC='now WAIT_FOR insert6'; +SELECT * FROM t1; +# this should conflict with the X-lock acquired by the REPLACE +--send +SELECT a,b FROM t1 LOCK IN SHARE MODE; + +connection con2; +# Check that the above SELECT is blocked +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where state = 'Sending data' and + info = 'SELECT a,b FROM t1 LOCK IN SHARE MODE'; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL select6'; + +connection con1; +reap; +connection default; +reap; + +disconnect con1; +disconnect con2; + +connection default; +SET DEBUG_SYNC='RESET'; +DROP TABLE t1; diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index 98ba5e03ede..0364c95db0b 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1693,3 +1693,17 @@ INSERT INTO t1 SELECT t1.* FROM t1,t1 t2,t1 t3,t1 t4,t1 t5,t1 t6; SELECT * FROM t1 ORDER BY f LIMIT 1; DROP TABLE t1; SET sort_buffer_size=DEFAULT; + +--echo # +--echo # BUG#11758979 - 51252: ARCHIVE TABLES CAUSE 100% CPU USAGE +--echo # AND HANG IN SHOW TABLE STATUS +--echo # (to be executed with valgrind) +CREATE TABLE t1(a BLOB, b VARCHAR(200)) ENGINE=ARCHIVE; +INSERT INTO t1 VALUES(NULL, ''); +FLUSH TABLE t1; +--echo # we need this select to workaround BUG#11764364 +SELECT * FROM t1; +CHECKSUM TABLE t1 EXTENDED; +FLUSH TABLE t1; +OPTIMIZE TABLE t1; +DROP TABLE t1; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 84c981c08c1..bfa051de4b6 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -12,4 +12,4 @@ kill : Bug#11748945 2008-12-03 HHunger need some changes to be robust enough for pushbuild. read_many_rows_innodb : Bug#11748886 2010-11-15 mattiasj report already exists main.log_tables-big : Bug#11756699 2010-11-15 mattiasj report already exists - +query_cache_28249 : Bug#12584161 2011-11-17 joh fails sporadically in 5.1 only diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index 95f06937faa..b2eae0fb4c6 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,8 +11,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /* Workaround for Bug#32082: VOID redefinition on Win results in compile errors*/ #define DONT_DEFINE_VOID 1 @@ -57,10 +56,11 @@ void my_init_stacktrace() static void print_buffer(char *buffer, size_t count) { + const char s[]= " "; for (; count && *buffer; --count) { - int c= (int) *buffer++; - fputc(isprint(c) ? c : ' ', stderr); + my_write_stderr(isprint(*buffer) ? buffer : s, 1); + ++buffer; } } @@ -124,10 +124,10 @@ static int safe_print_str(const char *addr, int max_len) /* Output a new line if something was printed. */ if (total != (size_t) max_len) - fputc('\n', stderr); + my_safe_printf_stderr("%s", "\n"); if (nbytes == -1) - fprintf(stderr, "Can't read from address %p: %m.\n", addr); + my_safe_printf_stderr("Can't read from address %p\n", addr); close(fd); @@ -149,13 +149,13 @@ void my_safe_print_str(const char* val, int max_len) if (!PTR_SANE(val)) { - fprintf(stderr, "is an invalid pointer\n"); + my_safe_printf_stderr("%s", "is an invalid pointer\n"); return; } for (; max_len && PTR_SANE(val) && *val; --max_len) - fputc(*val++, stderr); - fputc('\n', stderr); + my_write_stderr((val++), 1); + my_safe_printf_stderr("%s", "\n"); } #if defined(HAVE_PRINTSTACK) @@ -167,14 +167,15 @@ void my_print_stacktrace(uchar* stack_bottom __attribute__((unused)), ulong thread_stack __attribute__((unused))) { if (printstack(fileno(stderr)) == -1) - fprintf(stderr, "Error when traversing the stack, stack appears corrupt.\n"); + my_safe_printf_stderr("%s", + "Error when traversing the stack, stack appears corrupt.\n"); else - fprintf(stderr, - "Please read " - "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" - "and follow instructions on how to resolve the stack trace.\n" - "Resolved stack trace is much more helpful in diagnosing the\n" - "problem, so please do resolve it\n"); + my_safe_printf_stderr("%s" + "Please read " + "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" + "and follow instructions on how to resolve the stack trace.\n" + "Resolved stack trace is much more helpful in diagnosing the\n" + "problem, so please do resolve it\n"); } #elif HAVE_BACKTRACE && (HAVE_BACKTRACE_SYMBOLS || HAVE_BACKTRACE_SYMBOLS_FD) @@ -212,9 +213,9 @@ static void my_demangle_symbols(char **addrs, int n) } if (demangled) - fprintf(stderr, "%s(%s+%s\n", addrs[i], demangled, end); + my_safe_printf_stderr("%s(%s+%s\n", addrs[i], demangled, end); else - fprintf(stderr, "%s\n", addrs[i]); + my_safe_printf_stderr("%s\n", addrs[i]); } } @@ -225,8 +226,8 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) void *addrs[128]; char **strings= NULL; int n = backtrace(addrs, array_elements(addrs)); - fprintf(stderr, "stack_bottom = %p thread_stack 0x%lx\n", - stack_bottom, thread_stack); + my_safe_printf_stderr("stack_bottom = %p thread_stack 0x%lx\n", + stack_bottom, thread_stack); #if BACKTRACE_DEMANGLE if ((strings= backtrace_symbols(addrs, n))) { @@ -319,8 +320,9 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) #endif if (!fp) { - fprintf(stderr, "frame pointer is NULL, did you compile with\n\ --fomit-frame-pointer? Aborting backtrace!\n"); + my_safe_printf_stderr("%s", + "frame pointer is NULL, did you compile with\n" + "-fomit-frame-pointer? Aborting backtrace!\n"); return; } @@ -328,24 +330,28 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) { ulong tmp= min(0x10000,thread_stack); /* Assume that the stack starts at the previous even 65K */ - stack_bottom= (uchar*) (((ulong) &fp + tmp) & - ~(ulong) 0xFFFF); - fprintf(stderr, "Cannot determine thread, fp=%p, backtrace may not be correct.\n", fp); + stack_bottom= (uchar*) (((ulong) &fp + tmp) & ~(ulong) 0xFFFF); + my_safe_printf_stderr("Cannot determine thread, fp=%p, " + "backtrace may not be correct.\n", fp); } if (fp > (uchar**) stack_bottom || fp < (uchar**) stack_bottom - thread_stack) { - fprintf(stderr, "Bogus stack limit or frame pointer,\ - fp=%p, stack_bottom=%p, thread_stack=%ld, aborting backtrace.\n", - fp, stack_bottom, thread_stack); + my_safe_printf_stderr("Bogus stack limit or frame pointer, " + "fp=%p, stack_bottom=%p, thread_stack=%ld, " + "aborting backtrace.\n", + fp, stack_bottom, thread_stack); return; } - fprintf(stderr, "Stack range sanity check OK, backtrace follows:\n"); + my_safe_printf_stderr("%s", + "Stack range sanity check OK, backtrace follows:\n"); #if defined(__alpha__) && defined(__GNUC__) - fprintf(stderr, "Warning: Alpha stacks are difficult -\ - will be taking some wild guesses, stack trace may be incorrect or \ - terminate abruptly\n"); + my_safe_printf_stderr("%s", + "Warning: Alpha stacks are difficult -" + "will be taking some wild guesses, stack trace may be incorrect or " + "terminate abruptly\n"); + /* On Alpha, we need to get pc */ __asm __volatile__ ("bsr %0, do_next; do_next: " :"=r"(pc) @@ -359,8 +365,9 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) { #if defined(__i386__) || defined(__x86_64__) uchar** new_fp = (uchar**)*fp; - fprintf(stderr, "%p\n", frame_count == sigreturn_frame_count ? - *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1)); + my_safe_printf_stderr("%p\n", + frame_count == sigreturn_frame_count ? + *(fp + SIGRETURN_FRAME_OFFSET) : *(fp + 1)); #endif /* defined(__386__) || defined(__x86_64__) */ #if defined(__alpha__) && defined(__GNUC__) @@ -374,38 +381,40 @@ void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) { pc = find_prev_pc(pc, fp); if (pc) - fprintf(stderr, "%p\n", pc); + my_safe_printf_stderr("%p\n", pc); else { - fprintf(stderr, "Not smart enough to deal with the rest\ - of this stack\n"); + my_safe_printf_stderr("%s", + "Not smart enough to deal with the rest of this stack\n"); goto end; } } else { - fprintf(stderr, "Not smart enough to deal with the rest of this stack\n"); + my_safe_printf_stderr("%s", + "Not smart enough to deal with the rest of this stack\n"); goto end; } #endif /* defined(__alpha__) && defined(__GNUC__) */ if (new_fp <= fp ) { - fprintf(stderr, "New value of fp=%p failed sanity check,\ - terminating stack trace!\n", new_fp); + my_safe_printf_stderr("New value of fp=%p failed sanity check, " + "terminating stack trace!\n", new_fp); goto end; } fp = new_fp; ++frame_count; } - - fprintf(stderr, "Stack trace seems successful - bottom reached\n"); + my_safe_printf_stderr("%s", + "Stack trace seems successful - bottom reached\n"); end: - fprintf(stderr, - "Please read http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" - "and follow instructions on how to resolve the stack trace.\n" - "Resolved stack trace is much more helpful in diagnosing the\n" - "problem, so please do resolve it\n"); + my_safe_printf_stderr("%s", + "Please read " + "http://dev.mysql.com/doc/refman/5.1/en/resolve-stack-dump.html\n" + "and follow instructions on how to resolve the stack trace.\n" + "Resolved stack trace is much more helpful in diagnosing the\n" + "problem, so please do resolve it\n"); } #endif /* TARGET_OS_LINUX */ #endif /* HAVE_STACKTRACE */ @@ -670,7 +679,7 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) &(package.sym)); have_source= pSymGetLineFromAddr64(hProcess, addr, &line_offset, &line); - fprintf(stderr, "%p ", addr); + my_safe_printf_stderr("%p ", addr); if(have_module) { char *base_image_name= strrchr(module.ImageName, '\\'); @@ -678,12 +687,13 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) base_image_name++; else base_image_name= module.ImageName; - fprintf(stderr, "%s!", base_image_name); + my_safe_printf_stderr("%s!", base_image_name); } if(have_symbol) - fprintf(stderr, "%s()", package.sym.Name); + my_safe_printf_stderr("%s()", package.sym.Name); + else if(have_module) - fprintf(stderr, "???"); + my_safe_printf_stderr("%s", "???"); if(have_source) { @@ -692,11 +702,11 @@ void my_print_stacktrace(uchar* unused1, ulong unused2) base_file_name++; else base_file_name= line.FileName; - fprintf(stderr,"[%s:%u]", base_file_name, line.LineNumber); + my_safe_printf_stderr("[%s:%u]", + base_file_name, line.LineNumber); } - fprintf(stderr, "\n"); + my_safe_printf_stderr("%s", "\n"); } - fflush(stderr); } @@ -733,22 +743,22 @@ void my_write_core(int unused) if(pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &info, 0, 0)) { - fprintf(stderr, "Minidump written to %s\n", - _fullpath(path, dump_fname, sizeof(path)) ? path : dump_fname); + my_safe_printf_stderr("Minidump written to %s\n", + _fullpath(path, dump_fname, sizeof(path)) ? + path : dump_fname); } else { - fprintf(stderr,"MiniDumpWriteDump() failed, last error %u\n", - GetLastError()); + my_safe_printf_stderr("MiniDumpWriteDump() failed, last error %u\n", + (uint) GetLastError()); } CloseHandle(hFile); } else { - fprintf(stderr, "CreateFile(%s) failed, last error %u\n", dump_fname, - GetLastError()); + my_safe_printf_stderr("CreateFile(%s) failed, last error %u\n", + dump_fname, (uint) GetLastError()); } - fflush(stderr); } @@ -756,11 +766,212 @@ void my_safe_print_str(const char *val, int len) { __try { - fprintf(stderr,"=%.*s\n", len, val); + my_write_stderr(val, len); } __except(EXCEPTION_EXECUTE_HANDLER) { - fprintf(stderr,"is an invalid string pointer\n"); + my_safe_printf_stderr("%s", "is an invalid string pointer\n"); } } #endif /*__WIN__*/ + + +#ifdef __WIN__ +size_t my_write_stderr(const void *buf, size_t count) +{ + DWORD bytes_written; + SetFilePointer(GetStdHandle(STD_ERROR_HANDLE), 0, NULL, FILE_END); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), buf, count, &bytes_written, NULL); + return bytes_written; +} +#else +size_t my_write_stderr(const void *buf, size_t count) +{ + return (size_t) write(STDERR_FILENO, buf, count); +} +#endif + + +static const char digits[]= "0123456789abcdef"; + +char *my_safe_utoa(int base, ulonglong val, char *buf) +{ + *buf--= 0; + do { + *buf--= digits[val % base]; + } while ((val /= base) != 0); + return buf + 1; +} + + +char *my_safe_itoa(int base, longlong val, char *buf) +{ + char *orig_buf= buf; + const my_bool is_neg= (val < 0); + *buf--= 0; + + if (is_neg) + val= -val; + if (is_neg && base == 16) + { + int ix; + val-= 1; + for (ix= 0; ix < 16; ++ix) + buf[-ix]= '0'; + } + + do { + *buf--= digits[val % base]; + } while ((val /= base) != 0); + + if (is_neg && base == 10) + *buf--= '-'; + + if (is_neg && base == 16) + { + int ix; + buf= orig_buf - 1; + for (ix= 0; ix < 16; ++ix, --buf) + { + switch (*buf) + { + case '0': *buf= 'f'; break; + case '1': *buf= 'e'; break; + case '2': *buf= 'd'; break; + case '3': *buf= 'c'; break; + case '4': *buf= 'b'; break; + case '5': *buf= 'a'; break; + case '6': *buf= '9'; break; + case '7': *buf= '8'; break; + case '8': *buf= '7'; break; + case '9': *buf= '6'; break; + case 'a': *buf= '5'; break; + case 'b': *buf= '4'; break; + case 'c': *buf= '3'; break; + case 'd': *buf= '2'; break; + case 'e': *buf= '1'; break; + case 'f': *buf= '0'; break; + } + } + } + return buf+1; +} + + +static const char *check_longlong(const char *fmt, my_bool *have_longlong) +{ + *have_longlong= FALSE; + if (*fmt == 'l') + { + fmt++; + if (*fmt != 'l') + *have_longlong= (sizeof(long) == sizeof(longlong)); + else + { + fmt++; + *have_longlong= TRUE; + } + } + return fmt; +} + +static size_t my_safe_vsnprintf(char *to, size_t size, + const char* format, va_list ap) +{ + char *start= to; + char *end= start + size - 1; + for (; *format; ++format) + { + my_bool have_longlong = FALSE; + if (*format != '%') + { + if (to == end) /* end of buffer */ + break; + *to++= *format; /* copy ordinary char */ + continue; + } + ++format; /* skip '%' */ + + format= check_longlong(format, &have_longlong); + + switch (*format) + { + case 'd': + case 'i': + case 'u': + case 'x': + case 'p': + { + longlong ival= 0; + ulonglong uval = 0; + if (*format == 'p') + have_longlong= (sizeof(void *) == sizeof(longlong)); + if (have_longlong) + { + if (*format == 'u') + uval= va_arg(ap, ulonglong); + else + ival= va_arg(ap, longlong); + } + else + { + if (*format == 'u') + uval= va_arg(ap, unsigned int); + else + ival= va_arg(ap, int); + } + + { + char buff[22]; + const int base= (*format == 'x' || *format == 'p') ? 16 : 10; + char *val_as_str= (*format == 'u') ? + my_safe_utoa(base, uval, &buff[sizeof(buff)-1]) : + my_safe_itoa(base, ival, &buff[sizeof(buff)-1]); + + /* Strip off "ffffffff" if we have 'x' format without 'll' */ + if (*format == 'x' && !have_longlong && ival < 0) + val_as_str+= 8; + + while (*val_as_str && to < end) + *to++= *val_as_str++; + continue; + } + } + case 's': + { + const char *val= va_arg(ap, char*); + if (!val) + val= "(null)"; + while (*val && to < end) + *to++= *val++; + continue; + } + } + } + *to= 0; + return to - start; +} + + +size_t my_safe_snprintf(char* to, size_t n, const char* fmt, ...) +{ + size_t result; + va_list args; + va_start(args,fmt); + result= my_safe_vsnprintf(to, n, fmt, args); + va_end(args); + return result; +} + + +size_t my_safe_printf_stderr(const char* fmt, ...) +{ + char to[512]; + size_t result; + va_list args; + va_start(args,fmt); + result= my_safe_vsnprintf(to, sizeof(to), fmt, args); + va_end(args); + my_write_stderr(to, result); + return result; +} diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c050b329787..8f6e9cc7b32 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -65,6 +65,7 @@ SET (SQL_SOURCE sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc debug_sync.cc debug_sync.h + signal_handler.cc sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc @@ -113,7 +114,7 @@ IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK COMMAND cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js ${PLATFORM} ${LIB_LOCATIONS} > mysqld.def - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/sql) + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ENDIF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS) ADD_DEPENDENCIES(sql GenError) diff --git a/sql/Makefile.am b/sql/Makefile.am index 6040e4d498d..829523c7ba3 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -103,6 +103,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ records.cc filesort.cc handler.cc \ ha_partition.cc \ debug_sync.cc \ + signal_handler.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ diff --git a/sql/field.h b/sql/field.h index 8db965a8270..ca23702fd8c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -2128,6 +2128,23 @@ public: uchar *from_null_ptr,*to_null_ptr; my_bool *null_row; uint from_bit,to_bit; + /** + Number of bytes in the fields pointed to by 'from_ptr' and + 'to_ptr'. Usually this is the number of bytes that are copied from + 'from_ptr' to 'to_ptr'. + + For variable-length fields (VARCHAR), the first byte(s) describe + the actual length of the text. For VARCHARs with length + < 256 there is 1 length byte + >= 256 there is 2 length bytes + Thus, if from_field is VARCHAR(10), from_length (and in most cases + to_length) is 11. For VARCHAR(1024), the length is 1026. @see + Field_varstring::length_bytes + + Note that for VARCHARs, do_copy() will be do_varstring*() which + only copies the length-bytes (1 or 2) + the actual length of the + text instead of from/to_length bytes. @see get_copy_func() + */ uint from_length,to_length; Field *from_field,*to_field; String tmp; // For items diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 1dc7ec57944..ff56a1fdda8 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -707,7 +707,7 @@ Copy_field::get_copy_func(Field *to,Field *from) if (((Field_varstring*) to)->length_bytes != ((Field_varstring*) from)->length_bytes) return do_field_string; - if (to_length != from_length) + else return (((Field_varstring*) to)->length_bytes == 1 ? (from->charset()->mbmaxlen == 1 ? do_varstring1 : do_varstring1_mb) : diff --git a/sql/log.cc b/sql/log.cc index 77d12641442..cca403ca1be 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1854,7 +1854,7 @@ static void setup_windows_event_source() static int find_uniq_filename(char *name) { - long number; + long number= 0; uint i; char buff[FN_REFLEN]; struct st_my_dir *dir_info; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 37eb6d95ce8..c47d39721b9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -124,9 +124,6 @@ extern "C" { // Because of SCO 3.2V4.2 #ifdef __WIN__ #include <crtdbg.h> -#define SIGNAL_FMT "exception 0x%x" -#else -#define SIGNAL_FMT "signal %d" #endif #ifdef __NETWARE__ @@ -269,7 +266,7 @@ inline void setup_fpu() extern "C" int gethostname(char *name, int namelen); #endif -extern "C" sig_handler handle_segfault(int sig); +extern "C" sig_handler handle_fatal_signal(int sig); #if defined(__linux__) #define ENABLE_TEMP_POOL 1 @@ -415,6 +412,10 @@ TYPELIB log_output_typelib= {array_elements(log_output_names)-1,"", /* static variables */ +#ifdef HAVE_NPTL +volatile sig_atomic_t ld_assume_kernel_is_set= 0; +#endif + /* the default log output is log tables */ static bool lower_case_table_names_used= 0; static bool max_long_data_size_used= false; @@ -424,7 +425,7 @@ static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_short_log_format= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; -static ulong max_used_connections; + ulong max_used_connections; static ulong my_bind_addr; /**< the address we bind to */ static volatile ulong cached_thread_count= 0; static const char *sql_mode_str= "OFF"; @@ -553,7 +554,7 @@ TYPELIB binlog_format_typelib= ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC; const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id]; #ifdef HAVE_INITGROUPS -static bool calling_initgroups= FALSE; /**< Used in SIGSEGV handler. */ +volatile sig_atomic_t calling_initgroups= 0; /**< Used in SIGSEGV handler. */ #endif uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; uint mysqld_port_timeout; @@ -727,7 +728,7 @@ char *opt_logname, *opt_slow_logname; /* Static variables */ -static bool kill_in_progress, segfaulted; +static volatile sig_atomic_t kill_in_progress; #ifdef HAVE_STACK_TRACE_ON_SEGV static my_bool opt_do_pstack; #endif /* HAVE_STACK_TRACE_ON_SEGV */ @@ -1616,9 +1617,9 @@ static void set_user(const char *user, struct passwd *user_info_arg) calling_initgroups as a flag to the SIGSEGV handler that is then used to output a specific message to help the user resolve this problem. */ - calling_initgroups= TRUE; + calling_initgroups= 1; initgroups((char*) user, user_info_arg->pw_gid); - calling_initgroups= FALSE; + calling_initgroups= 0; #endif if (setgid(user_info_arg->pw_gid) == -1) { @@ -2170,7 +2171,7 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers) __try { my_set_exception_pointers(ex_pointers); - handle_segfault(ex_pointers->ExceptionRecord->ExceptionCode); + handle_fatal_signal(ex_pointers->ExceptionRecord->ExceptionCode); } __except(EXCEPTION_EXECUTE_HANDLER) { @@ -2481,161 +2482,6 @@ extern "C" char *my_demangle(const char *mangled_name, int *status) #endif -extern "C" sig_handler handle_segfault(int sig) -{ - time_t curr_time; - struct tm tm; - - /* - Strictly speaking, one needs a mutex here - but since we have got SIGSEGV already, things are a mess - so not having the mutex is not as bad as possibly using a buggy - mutex - so we keep things simple - */ - if (segfaulted) - { - fprintf(stderr, "Fatal " SIGNAL_FMT " while backtracing\n", sig); - exit(1); - } - - segfaulted = 1; - - curr_time= my_time(0); - localtime_r(&curr_time, &tm); - - fprintf(stderr,"\ -%02d%02d%02d %2d:%02d:%02d - mysqld got " SIGNAL_FMT " ;\n\ -This could be because you hit a bug. It is also possible that this binary\n\ -or one of the libraries it was linked against is corrupt, improperly built,\n\ -or misconfigured. This error can also be caused by malfunctioning hardware.\n", - tm.tm_year % 100, tm.tm_mon+1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - sig); - fprintf(stderr, "\ -We will try our best to scrape up some info that will hopefully help diagnose\n\ -the problem, but since we have already crashed, something is definitely wrong\n\ -and this may fail.\n\n"); - fprintf(stderr, "key_buffer_size=%lu\n", - (ulong) dflt_key_cache->key_cache_mem_size); - fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); - fprintf(stderr, "max_used_connections=%lu\n", max_used_connections); - fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads); - fprintf(stderr, "threads_connected=%u\n", thread_count); - fprintf(stderr, "It is possible that mysqld could use up to \n\ -key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\ -bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size + - (global_system_variables.read_buff_size + - global_system_variables.sortbuff_size) * - thread_scheduler.max_threads + - max_connections * sizeof(THD)) / 1024); - fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); - -#if defined(HAVE_LINUXTHREADS) - if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) - { - fprintf(stderr, "\ -You seem to be running 32-bit Linux and have %d concurrent connections.\n\ -If you have not changed STACK_SIZE in LinuxThreads and built the binary \n\ -yourself, LinuxThreads is quite likely to steal a part of the global heap for\n\ -the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", - thread_count); - } -#endif /* HAVE_LINUXTHREADS */ - -#ifdef HAVE_STACKTRACE - THD *thd=current_thd; - - if (!(test_flags & TEST_NO_STACKTRACE)) - { - fprintf(stderr, "Thread pointer: 0x%lx\n", (long) thd); - fprintf(stderr, "Attempting backtrace. You can use the following " - "information to find out\nwhere mysqld died. If " - "you see no messages after this, something went\n" - "terribly wrong...\n"); - my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, - my_thread_stack_size); - } - if (thd) - { - const char *kreason= "UNKNOWN"; - switch (thd->killed) { - case THD::NOT_KILLED: - kreason= "NOT_KILLED"; - break; - case THD::KILL_BAD_DATA: - kreason= "KILL_BAD_DATA"; - break; - case THD::KILL_CONNECTION: - kreason= "KILL_CONNECTION"; - break; - case THD::KILL_QUERY: - kreason= "KILL_QUERY"; - break; - case THD::KILLED_NO_VALUE: - kreason= "KILLED_NO_VALUE"; - break; - } - fprintf(stderr, "\nTrying to get some variables.\n" - "Some pointers may be invalid and cause the dump to abort.\n"); - fprintf(stderr, "Query (%p): ", thd->query()); - my_safe_print_str(thd->query(), min(1024, thd->query_length())); - fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id); - fprintf(stderr, "Status: %s\n", kreason); - fputc('\n', stderr); - } - fprintf(stderr, "\ -The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\ -information that should help you find out what is causing the crash.\n"); - fflush(stderr); -#endif /* HAVE_STACKTRACE */ - -#ifdef HAVE_INITGROUPS - if (calling_initgroups) - fprintf(stderr, "\n\ -This crash occured while the server was calling initgroups(). This is\n\ -often due to the use of a mysqld that is statically linked against glibc\n\ -and configured to use LDAP in /etc/nsswitch.conf. You will need to either\n\ -upgrade to a version of glibc that does not have this problem (2.3.4 or\n\ -later when used with nscd), disable LDAP in your nsswitch.conf, or use a\n\ -mysqld that is not statically linked.\n"); -#endif - -#ifdef HAVE_NPTL - if (thd_lib_detected == THD_LIB_LT && !getenv("LD_ASSUME_KERNEL")) - fprintf(stderr,"\n\ -You are running a statically-linked LinuxThreads binary on an NPTL system.\n\ -This can result in crashes on some distributions due to LT/NPTL conflicts.\n\ -You should either build a dynamically-linked binary, or force LinuxThreads\n\ -to be used with the LD_ASSUME_KERNEL environment variable. Please consult\n\ -the documentation for your distribution on how to do that.\n"); -#endif - - if (locked_in_memory) - { - fprintf(stderr, "\n\ -The \"--memlock\" argument, which was enabled, uses system calls that are\n\ -unreliable and unstable on some operating systems and operating-system\n\ -versions (notably, some versions of Linux). This crash could be due to use\n\ -of those buggy OS calls. You should consider whether you really need the\n\ -\"--memlock\" parameter and/or consult the OS distributer about \"mlockall\"\n\ -bugs.\n"); - } - -#ifdef HAVE_WRITE_CORE - if (test_flags & TEST_CORE_ON_SIGNAL) - { - fprintf(stderr, "Writing a core file\n"); - fflush(stderr); - my_write_core(sig); - } -#endif - -#ifndef __WIN__ - /* On Windows, do not terminate, but pass control to exception filter */ - exit(1); -#endif -} - #if !defined(__WIN__) && !defined(__NETWARE__) #ifndef SA_RESETHAND #define SA_RESETHAND 0 @@ -2664,9 +2510,9 @@ static void init_signals(void) my_init_stacktrace(); #endif #if defined(__amiga__) - sa.sa_handler=(void(*)())handle_segfault; + sa.sa_handler=(void(*)())handle_fatal_signal; #else - sa.sa_handler=handle_segfault; + sa.sa_handler=handle_fatal_signal; #endif sigaction(SIGSEGV, &sa, NULL); sigaction(SIGABRT, &sa, NULL); @@ -4363,6 +4209,10 @@ int win_main(int argc, char **argv) int main(int argc, char **argv) #endif { +#ifdef HAVE_NPTL + ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0); +#endif + MY_INIT(argv[0]); // init my_sys library & pthreads /* nothing should come before this line ^^^ */ @@ -7876,7 +7726,7 @@ static int mysql_init_variables(void) opt_secure_file_priv= 0; opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; - segfaulted= kill_in_progress= 0; + kill_in_progress= 0; cleanup_done= 0; defaults_argc= 0; defaults_argv= 0; diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc new file mode 100644 index 00000000000..54b456ec2c4 --- /dev/null +++ b/sql/signal_handler.cc @@ -0,0 +1,257 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +#include "my_global.h" +#include <signal.h> + +#include "mysql_priv.h" +#include "my_stacktrace.h" + +#ifdef __WIN__ +#include <crtdbg.h> +#define SIGNAL_FMT "exception 0x%x" +#else +#define SIGNAL_FMT "signal %d" +#endif + +/* + We are handling signals in this file. + Any global variables we read should be 'volatile sig_atomic_t' + to guarantee that we read some consistent value. + */ +static volatile sig_atomic_t segfaulted= 0; +extern ulong max_used_connections; +extern volatile sig_atomic_t calling_initgroups; +#ifdef HAVE_NPTL +extern volatile sig_atomic_t ld_assume_kernel_is_set; +#endif + +/** + * Handler for fatal signals + * + * Fatal events (seg.fault, bus error etc.) will trigger + * this signal handler. The handler will try to dump relevant + * debugging information to stderr and dump a core image. + * + * Signal handlers can only use a set of 'safe' system calls + * and library functions. A list of safe calls in POSIX systems + * are available at: + * http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html + * For MS Windows, guidelines are available at: + * http://msdn.microsoft.com/en-us/library/xdkz3x12(v=vs.71).aspx + * + * @param sig Signal number +*/ +extern "C" sig_handler handle_fatal_signal(int sig) +{ + if (segfaulted) + { + my_safe_printf_stderr("Fatal " SIGNAL_FMT " while backtracing\n", sig); + _exit(1); /* Quit without running destructors */ + } + + segfaulted = 1; + +#ifdef __WIN__ + SYSTEMTIME utc_time; + GetSystemTime(&utc_time); + const long hrs= utc_time.wHour; + const long mins= utc_time.wMinute; + const long secs= utc_time.wSecond; +#else + /* Using time() instead of my_time() to avoid looping */ + const time_t curr_time= time(NULL); + /* Calculate time of day */ + const long tmins = curr_time / 60; + const long thrs = tmins / 60; + const long hrs = thrs % 24; + const long mins = tmins % 60; + const long secs = curr_time % 60; +#endif + + char hrs_buf[3]= "00"; + char mins_buf[3]= "00"; + char secs_buf[3]= "00"; + my_safe_itoa(10, hrs, &hrs_buf[2]); + my_safe_itoa(10, mins, &mins_buf[2]); + my_safe_itoa(10, secs, &secs_buf[2]); + + my_safe_printf_stderr("%s:%s:%s UTC - mysqld got " SIGNAL_FMT " ;\n", + hrs_buf, mins_buf, secs_buf, sig); + + my_safe_printf_stderr("%s", + "This could be because you hit a bug. It is also possible that this binary\n" + "or one of the libraries it was linked against is corrupt, improperly built,\n" + "or misconfigured. This error can also be caused by malfunctioning hardware.\n"); + + my_safe_printf_stderr("%s", + "We will try our best to scrape up some info that will hopefully help\n" + "diagnose the problem, but since we have already crashed, \n" + "something is definitely wrong and this may fail.\n\n"); + + my_safe_printf_stderr("key_buffer_size=%lu\n", + (ulong) dflt_key_cache->key_cache_mem_size); + + my_safe_printf_stderr("read_buffer_size=%ld\n", + (long) global_system_variables.read_buff_size); + + my_safe_printf_stderr("max_used_connections=%lu\n", + (ulong) max_used_connections); + + my_safe_printf_stderr("max_threads=%u\n", + (uint) thread_scheduler.max_threads); + + my_safe_printf_stderr("thread_count=%u\n", (uint) thread_count); + + my_safe_printf_stderr("connection_count=%u\n", (uint) connection_count); + + my_safe_printf_stderr("It is possible that mysqld could use up to \n" + "key_buffer_size + " + "(read_buffer_size + sort_buffer_size)*max_threads = " + "%lu K bytes of memory\n", + ((ulong) dflt_key_cache->key_cache_mem_size + + (global_system_variables.read_buff_size + + global_system_variables.sortbuff_size) * + thread_scheduler.max_threads + + max_connections * sizeof(THD)) / 1024); + + my_safe_printf_stderr("%s", + "Hope that's ok; if not, decrease some variables in the equation.\n\n"); + +#if defined(HAVE_LINUXTHREADS) + if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) + { + my_safe_printf_stderr( + "You seem to be running 32-bit Linux and have " + "%d concurrent connections.\n" + "If you have not changed STACK_SIZE in LinuxThreads " + "and built the binary \n" + "yourself, LinuxThreads is quite likely to steal " + "a part of the global heap for\n" + "the thread stack. Please read " + "http://dev.mysql.com/doc/mysql/en/linux-installation.html\n\n" + thread_count); + } +#endif /* HAVE_LINUXTHREADS */ + +#ifdef HAVE_STACKTRACE + THD *thd=current_thd; + + if (!(test_flags & TEST_NO_STACKTRACE)) + { + my_safe_printf_stderr("Thread pointer: 0x%p\n", thd); + my_safe_printf_stderr("%s", + "Attempting backtrace. You can use the following " + "information to find out\n" + "where mysqld died. If you see no messages after this, something went\n" + "terribly wrong...\n"); + my_print_stacktrace(thd ? (uchar*) thd->thread_stack : NULL, + my_thread_stack_size); + } + if (thd) + { + const char *kreason= "UNKNOWN"; + switch (thd->killed) { + case THD::NOT_KILLED: + kreason= "NOT_KILLED"; + break; + case THD::KILL_BAD_DATA: + kreason= "KILL_BAD_DATA"; + break; + case THD::KILL_CONNECTION: + kreason= "KILL_CONNECTION"; + break; + case THD::KILL_QUERY: + kreason= "KILL_QUERY"; + break; + case THD::KILLED_NO_VALUE: + kreason= "KILLED_NO_VALUE"; + break; + } + my_safe_printf_stderr("%s", "\n" + "Trying to get some variables.\n" + "Some pointers may be invalid and cause the dump to abort.\n"); + + my_safe_printf_stderr("Query (%p): ", thd->query()); + my_safe_print_str(thd->query(), min(1024U, thd->query_length())); + my_safe_printf_stderr("Connection ID (thread ID): %lu\n", + (ulong) thd->thread_id); + my_safe_printf_stderr("Status: %s\n\n", kreason); + } + my_safe_printf_stderr("%s", + "The manual page at " + "http://dev.mysql.com/doc/mysql/en/crashing.html contains\n" + "information that should help you find out what is causing the crash.\n"); + +#endif /* HAVE_STACKTRACE */ + +#ifdef HAVE_INITGROUPS + if (calling_initgroups) + { + my_safe_printf_stderr("%s", "\n" + "This crash occured while the server was calling initgroups(). This is\n" + "often due to the use of a mysqld that is statically linked against \n" + "glibc and configured to use LDAP in /etc/nsswitch.conf.\n" + "You will need to either upgrade to a version of glibc that does not\n" + "have this problem (2.3.4 or later when used with nscd),\n" + "disable LDAP in your nsswitch.conf, or use a " + "mysqld that is not statically linked.\n"); + } +#endif + +#ifdef HAVE_NPTL + if (thd_lib_detected == THD_LIB_LT && !ld_assume_kernel_is_set) + { + my_safe_printf_stderr("%s", + "You are running a statically-linked LinuxThreads binary on an NPTL\n" + "system. This can result in crashes on some distributions due to " + "LT/NPTL conflicts.\n" + "You should either build a dynamically-linked binary, " + "or force LinuxThreads\n" + "to be used with the LD_ASSUME_KERNEL environment variable.\n" + "Please consult the documentation for your distribution " + "on how to do that.\n"); + } +#endif + + if (locked_in_memory) + { + my_safe_printf_stderr("%s", "\n" + "The \"--memlock\" argument, which was enabled, " + "uses system calls that are\n" + "unreliable and unstable on some operating systems and " + "operating-system versions (notably, some versions of Linux).\n" + "This crash could be due to use of those buggy OS calls.\n" + "You should consider whether you really need the " + "\"--memlock\" parameter and/or consult the OS distributer about " + "\"mlockall\" bugs.\n"); + } + +#ifdef HAVE_WRITE_CORE + if (test_flags & TEST_CORE_ON_SIGNAL) + { + my_safe_printf_stderr("%s", "Writing a core file\n"); + my_write_core(sig); + } +#endif + +#ifndef __WIN__ + /* + Quit, without running destructors (etc.) + On Windows, do not terminate, but pass control to exception filter. + */ + _exit(1); // Using _exit(), since exit() is not async signal safe +#endif +} diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e176e5c9b6d..4cd456829ba 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -63,6 +63,8 @@ #include "slave.h" #include "rpl_mi.h" +#include "debug_sync.h" + #ifndef EMBEDDED_LIBRARY static bool delayed_get_table(THD *thd, TABLE_LIST *table_list); static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, @@ -1411,6 +1413,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) error= HA_ERR_FOUND_DUPP_KEY; /* Database can't find key */ goto err; } + DEBUG_SYNC(thd, "write_row_replace"); + /* Read all columns for the row we are going to replace */ table->use_all_columns(); /* @@ -1604,6 +1608,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } else if ((error=table->file->ha_write_row(table->record[0]))) { + DEBUG_SYNC(thd, "write_row_noreplace"); if (!info->ignore || table->file->is_fatal_error(error, HA_CHECK_DUP)) goto err; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bf0cd7c9db8..72c826ff32d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10647,6 +10647,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (open_tmp_table(table)) goto err; + // Make empty record so random data is not written to disk + empty_record(table); + thd->mem_root= mem_root_save; DBUG_RETURN(table); diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index df5bd8bafb1..79053f284b3 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -760,6 +760,7 @@ uint32 ha_archive::max_row_length(const uchar *buf) ptr != end ; ptr++) { + if (!table->field[*ptr]->is_null()) length += 2 + ((Field_blob*)table->field[*ptr])->get_length(); } @@ -1110,6 +1111,17 @@ int ha_archive::unpack_row(azio_stream *file_to_read, uchar *record) /* Copy null bits */ const uchar *ptr= record_buffer->buffer; + /* + Field::unpack() is not called when field is NULL. For VARCHAR + Field::unpack() only unpacks as much bytes as occupied by field + value. In these cases respective memory area on record buffer is + not initialized. + + These uninitialized areas may be accessed by CHECKSUM TABLE or + by optimizer using temporary table (BUG#12997905). We may remove + this memset() when they're fixed. + */ + memset(record, 0, table->s->reclength); memcpy(record, ptr, table->s->null_bytes); ptr+= table->s->null_bytes; for (Field **field=table->field ; *field ; field++) @@ -1578,13 +1590,15 @@ int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt) { int rc= 0; const char *old_proc_info; - ha_rows count= share->rows_recorded; + ha_rows count; DBUG_ENTER("ha_archive::check"); old_proc_info= thd_proc_info(thd, "Checking table"); - /* Flush any waiting data */ pthread_mutex_lock(&share->mutex); - azflush(&(share->archive_write), Z_SYNC_FLUSH); + count= share->rows_recorded; + /* Flush any waiting data */ + if (share->archive_write_open) + azflush(&(share->archive_write), Z_SYNC_FLUSH); pthread_mutex_unlock(&share->mutex); if (init_archive_reader()) @@ -1594,18 +1608,34 @@ int ha_archive::check(THD* thd, HA_CHECK_OPT* check_opt) start of the file. */ read_data_header(&archive); + for (ha_rows cur_count= count; cur_count; cur_count--) + { + if ((rc= get_row(&archive, table->record[0]))) + goto error; + } + /* + Now read records that may have been inserted concurrently. + Acquire share->mutex so tail of the table is not modified by + concurrent writers. + */ + pthread_mutex_lock(&share->mutex); + count= share->rows_recorded - count; + if (share->archive_write_open) + azflush(&(share->archive_write), Z_SYNC_FLUSH); while (!(rc= get_row(&archive, table->record[0]))) count--; - - thd_proc_info(thd, old_proc_info); + pthread_mutex_unlock(&share->mutex); if ((rc && rc != HA_ERR_END_OF_FILE) || count) - { - share->crashed= FALSE; - DBUG_RETURN(HA_ADMIN_CORRUPT); - } + goto error; + thd_proc_info(thd, old_proc_info); DBUG_RETURN(HA_ADMIN_OK); + +error: + thd_proc_info(thd, old_proc_info); + share->crashed= FALSE; + DBUG_RETURN(HA_ADMIN_CORRUPT); } /* diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 44b9a7fba36..6709f790994 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4087,8 +4087,7 @@ no_commit: switch (sql_command) { case SQLCOM_LOAD: - if ((trx->duplicates - & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) { + if (trx->duplicates) { goto set_max_autoinc; } @@ -4368,8 +4367,7 @@ ha_innobase::update_row( && table->next_number_field && new_row == table->record[0] && thd_sql_command(user_thd) == SQLCOM_INSERT - && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) - == TRX_DUP_IGNORE) { + && trx->duplicates) { ulonglong auto_inc; ulonglong col_max_value; @@ -4717,6 +4715,7 @@ ha_innobase::index_read( index, (byte*) key_ptr, (ulint) key_len, prebuilt->trx); + DBUG_ASSERT(prebuilt->search_tuple->n_fields > 0); } else { /* We position the cursor to the last or the first entry in the index */ @@ -6172,6 +6171,7 @@ ha_innobase::records_in_range( void* heap2; DBUG_ENTER("records_in_range"); + DBUG_ASSERT(min_key || max_key); ut_a(prebuilt->trx == thd_to_trx(ha_thd())); @@ -6202,6 +6202,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (min_key ? min_key->length : 0), prebuilt->trx); + DBUG_ASSERT(min_key + ? range_start->n_fields > 0 + : range_start->n_fields == 0); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, @@ -6210,6 +6213,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (max_key ? max_key->length : 0), prebuilt->trx); + DBUG_ASSERT(max_key + ? range_end->n_fields > 0 + : range_end->n_fields == 0); mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : HA_READ_KEY_EXACT); @@ -7095,6 +7101,7 @@ ha_innobase::extra( break; case HA_EXTRA_RESET_STATE: reset_template(prebuilt); + thd_to_trx(ha_thd())->duplicates = 0; break; case HA_EXTRA_NO_KEYREAD: prebuilt->read_just_key = 0; @@ -7112,19 +7119,18 @@ ha_innobase::extra( parameters below. We must not invoke update_thd() either, because the calling threads may change. CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */ - case HA_EXTRA_IGNORE_DUP_KEY: + case HA_EXTRA_INSERT_WITH_UPDATE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE; break; + case HA_EXTRA_NO_IGNORE_DUP_KEY: + thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_IGNORE; + break; case HA_EXTRA_WRITE_CAN_REPLACE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE; break; case HA_EXTRA_WRITE_CANNOT_REPLACE: thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE; break; - case HA_EXTRA_NO_IGNORE_DUP_KEY: - thd_to_trx(ha_thd())->duplicates &= - ~(TRX_DUP_IGNORE | TRX_DUP_REPLACE); - break; default:/* Do nothing */ ; } diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 4f5f948f218..db134ca7a41 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -1673,7 +1673,7 @@ row_ins_scan_sec_index_for_duplicate( btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); - allow_duplicates = thr_get_trx(thr)->duplicates & TRX_DUP_IGNORE; + allow_duplicates = thr_get_trx(thr)->duplicates; /* Scan index records and check if there is a duplicate */ @@ -1813,7 +1813,7 @@ row_ins_duplicate_error_in_clust( sure that in roll-forward we get the same duplicate errors as in original execution */ - if (trx->duplicates & TRX_DUP_IGNORE) { + if (trx->duplicates) { /* If the SQL-query will update or replace duplicate key we will take X-lock for @@ -1855,7 +1855,7 @@ row_ins_duplicate_error_in_clust( offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); - if (trx->duplicates & TRX_DUP_IGNORE) { + if (trx->duplicates) { /* If the SQL-query will update or replace duplicate key we will take X-lock for diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 1cf3222a008..df017b54298 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,15 @@ +2011-11-10 The InnoDB Team + + * handler/ha_innodb.cc, row/row0ins.c, innodb_replace.test: + Fix Bug#11759688 52020: InnoDB can still deadlock + on just INSERT...ON DUPLICATE KEY a.k.a. the reintroduction of + Bug#7975 deadlock without any locking, simple select and update + +2011-11-08 The InnoDB Team + + * btr/btr0pcur.c, include/btr0pcur.h, include/btr0pcur.ic: + Fix Bug#13358468 ASSERTION FAILURE IN BTR_PCUR_GET_BLOCK + 2011-10-27 The InnoDB Team * row/row0mysql.c: @@ -55,6 +67,15 @@ * include/trx0undo.h, trx/trx0rec.c, trx/trx0undo.c: Fix Bug#12547647 UPDATE LOGGING COULD EXCEED LOG PAGE SIZE +2011-08-29 The InnoDB Team + + * btr/btr0btr.c, btr/btr0cur.c, fsp/fsp0fsp.c, + include/btr0btr.h, include/btr0cur.h, include/fsp0fsp.h, + include/mtr0mtr.h, include/mtr0mtr.ic, mtr/mtr0mtr.c, + row/row0ins.c, row/row0row.c, row/row0upd.c, trx/trx0undo.c: + Fix Bug#12704861 Corruption after a crash during BLOB update + and other regressions from the fix of Bug#12612184 + 2011-08-15 The InnoDB Team * btr/btr0btr.c, btr/btr0cur.c, btr/btr0pcur.c, btr/btr0sea.c, @@ -106,6 +127,16 @@ * page/page0zip.c, rem/rem0rec.c: Fix Bug#61191 question about page_zip_available() +2011-06-16 The InnoDB Team + + * btr/btr0btr.c, btr/btr0cur.c, include/btr0btr.h, include/btr0cur.h, + include/btr0cur.ic, include/buf0buf.h, include/buf0buf.ic, + include/page0cur.ic, include/page0page.h, include/page0page.ic, + include/sync0rw.ic, include/sync0sync.h, page/page0cur.c, + page/page0page.c, row/row0ins.c, row/row0upd.c, + sync/sync0rw.c, sync/sync0sync.c: + Fix Bug#12612184 Race condition after btr_cur_pessimistic_update() + 2011-06-09 The InnoDB Team * btr/btr0cur.c, include/rem0rec.h, include/rem0rec.ic, * row/row0row.c, row/row0vers.c, trx/trx0rec.c: diff --git a/storage/innodb_plugin/btr/btr0pcur.c b/storage/innodb_plugin/btr/btr0pcur.c index cbb0d21a7ed..57d9752649f 100644 --- a/storage/innodb_plugin/btr/btr0pcur.c +++ b/storage/innodb_plugin/btr/btr0pcur.c @@ -247,6 +247,8 @@ btr_pcur_restore_position_func( cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE, index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr); + cursor->latch_mode = latch_mode; + cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->block_when_stored = btr_pcur_get_block(cursor); return(FALSE); diff --git a/storage/innodb_plugin/buf/buf0buf.c b/storage/innodb_plugin/buf/buf0buf.c index 7d03f08f61f..d88860b807b 100644 --- a/storage/innodb_plugin/buf/buf0buf.c +++ b/storage/innodb_plugin/buf/buf0buf.c @@ -946,6 +946,24 @@ buf_pool_free(void) { buf_chunk_t* chunk; buf_chunk_t* chunks; + buf_page_t* bpage; + + bpage = UT_LIST_GET_LAST(buf_pool->LRU); + while (bpage != NULL) { + buf_page_t* prev_bpage = UT_LIST_GET_PREV(LRU, bpage); + enum buf_page_state state = buf_page_get_state(bpage); + + ut_ad(buf_page_in_file(bpage)); + ut_ad(bpage->in_LRU_list); + + if (state != BUF_BLOCK_FILE_PAGE) { + /* We must not have any dirty block. */ + ut_ad(state == BUF_BLOCK_ZIP_PAGE); + buf_page_free_descriptor(bpage); + } + + bpage = prev_bpage; + } chunks = buf_pool->chunks; chunk = chunks + buf_pool->n_chunks; diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 566c54e0175..d84bd38ba7e 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -4793,8 +4793,7 @@ no_commit: switch (sql_command) { case SQLCOM_LOAD: - if ((trx->duplicates - & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) { + if (trx->duplicates) { goto set_max_autoinc; } @@ -5070,8 +5069,7 @@ ha_innobase::update_row( && table->next_number_field && new_row == table->record[0] && thd_sql_command(user_thd) == SQLCOM_INSERT - && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) - == TRX_DUP_IGNORE) { + && trx->duplicates) { ulonglong auto_inc; ulonglong col_max_value; @@ -5429,6 +5427,7 @@ ha_innobase::index_read( (byte*) key_ptr, (ulint) key_len, prebuilt->trx); + DBUG_ASSERT(prebuilt->search_tuple->n_fields > 0); } else { /* We position the cursor to the last or the first entry in the index */ @@ -7227,6 +7226,7 @@ ha_innobase::records_in_range( mem_heap_t* heap; DBUG_ENTER("records_in_range"); + DBUG_ASSERT(min_key || max_key); ut_a(prebuilt->trx == thd_to_trx(ha_thd())); @@ -7272,6 +7272,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (min_key ? min_key->length : 0), prebuilt->trx); + DBUG_ASSERT(min_key + ? range_start->n_fields > 0 + : range_start->n_fields == 0); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, @@ -7280,6 +7283,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (max_key ? max_key->length : 0), prebuilt->trx); + DBUG_ASSERT(max_key + ? range_end->n_fields > 0 + : range_end->n_fields == 0); mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : HA_READ_KEY_EXACT); @@ -8416,6 +8422,7 @@ ha_innobase::extra( break; case HA_EXTRA_RESET_STATE: reset_template(prebuilt); + thd_to_trx(ha_thd())->duplicates = 0; break; case HA_EXTRA_NO_KEYREAD: prebuilt->read_just_key = 0; @@ -8433,19 +8440,18 @@ ha_innobase::extra( parameters below. We must not invoke update_thd() either, because the calling threads may change. CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */ - case HA_EXTRA_IGNORE_DUP_KEY: + case HA_EXTRA_INSERT_WITH_UPDATE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE; break; + case HA_EXTRA_NO_IGNORE_DUP_KEY: + thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_IGNORE; + break; case HA_EXTRA_WRITE_CAN_REPLACE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE; break; case HA_EXTRA_WRITE_CANNOT_REPLACE: thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE; break; - case HA_EXTRA_NO_IGNORE_DUP_KEY: - thd_to_trx(ha_thd())->duplicates &= - ~(TRX_DUP_IGNORE | TRX_DUP_REPLACE); - break; default:/* Do nothing */ ; } diff --git a/storage/innodb_plugin/include/btr0pcur.h b/storage/innodb_plugin/include/btr0pcur.h index f59514d04b3..40ecc77dcd1 100644 --- a/storage/innodb_plugin/include/btr0pcur.h +++ b/storage/innodb_plugin/include/btr0pcur.h @@ -279,14 +279,6 @@ btr_pcur_commit_specify_mtr( /*========================*/ btr_pcur_t* pcur, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr to commit */ -/**************************************************************//** -Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES. -@return TRUE if detached */ -UNIV_INLINE -ibool -btr_pcur_is_detached( -/*=================*/ - btr_pcur_t* pcur); /*!< in: persistent cursor */ /*********************************************************//** Moves the persistent cursor to the next record in the tree. If no records are left, the cursor stays 'after last in tree'. diff --git a/storage/innodb_plugin/include/btr0pcur.ic b/storage/innodb_plugin/include/btr0pcur.ic index 0f9b969e7c5..f49e155f97e 100644 --- a/storage/innodb_plugin/include/btr0pcur.ic +++ b/storage/innodb_plugin/include/btr0pcur.ic @@ -416,38 +416,6 @@ btr_pcur_commit_specify_mtr( } /**************************************************************//** -Sets the pcur latch mode to BTR_NO_LATCHES. */ -UNIV_INLINE -void -btr_pcur_detach( -/*============*/ - btr_pcur_t* pcur) /*!< in: persistent cursor */ -{ - ut_a(pcur->pos_state == BTR_PCUR_IS_POSITIONED); - - pcur->latch_mode = BTR_NO_LATCHES; - - pcur->pos_state = BTR_PCUR_WAS_POSITIONED; -} - -/**************************************************************//** -Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES. -@return TRUE if detached */ -UNIV_INLINE -ibool -btr_pcur_is_detached( -/*=================*/ - btr_pcur_t* pcur) /*!< in: persistent cursor */ -{ - if (pcur->latch_mode == BTR_NO_LATCHES) { - - return(TRUE); - } - - return(FALSE); -} - -/**************************************************************//** Sets the old_rec_buf field to NULL. */ UNIV_INLINE void diff --git a/storage/innodb_plugin/row/row0ins.c b/storage/innodb_plugin/row/row0ins.c index 82c3c931bef..2cbe1e13edc 100644 --- a/storage/innodb_plugin/row/row0ins.c +++ b/storage/innodb_plugin/row/row0ins.c @@ -1665,7 +1665,7 @@ row_ins_scan_sec_index_for_duplicate( ulint n_fields_cmp; btr_pcur_t pcur; ulint err = DB_SUCCESS; - unsigned allow_duplicates; + ulint allow_duplicates; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; @@ -1696,7 +1696,7 @@ row_ins_scan_sec_index_for_duplicate( btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); - allow_duplicates = thr_get_trx(thr)->duplicates & TRX_DUP_IGNORE; + allow_duplicates = thr_get_trx(thr)->duplicates; /* Scan index records and check if there is a duplicate */ @@ -1830,7 +1830,7 @@ row_ins_duplicate_error_in_clust( sure that in roll-forward we get the same duplicate errors as in original execution */ - if (trx->duplicates & TRX_DUP_IGNORE) { + if (trx->duplicates) { /* If the SQL-query will update or replace duplicate key we will take X-lock for @@ -1874,7 +1874,7 @@ row_ins_duplicate_error_in_clust( offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); - if (trx->duplicates & TRX_DUP_IGNORE) { + if (trx->duplicates) { /* If the SQL-query will update or replace duplicate key we will take X-lock for diff --git a/storage/myisam/mi_close.c b/storage/myisam/mi_close.c index bb95aff24d7..a6911814608 100644 --- a/storage/myisam/mi_close.c +++ b/storage/myisam/mi_close.c @@ -87,7 +87,12 @@ int mi_close(register MI_INFO *info) } #ifdef HAVE_MMAP if (share->file_map) - _mi_unmap_file(info); + { + if (share->options & HA_OPTION_COMPRESS_RECORD) + _mi_unmap_file(info); + else + mi_munmap_file(info); + } #endif if (share->decode_trees) { diff --git a/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c index e6ad4c3defc..005f8c65d71 100644 --- a/storage/myisam/mi_packrec.c +++ b/storage/myisam/mi_packrec.c @@ -1553,13 +1553,14 @@ my_bool _mi_memmap_file(MI_INFO *info) void _mi_unmap_file(MI_INFO *info) { - VOID(my_munmap((char*) info->s->file_map, - (size_t) info->s->mmaped_length + MEMMAP_EXTRA_MARGIN)); + DBUG_ASSERT(info->s->options & HA_OPTION_COMPRESS_RECORD); + + VOID(my_munmap((char*) info->s->file_map, (size_t) info->s->mmaped_length)); if (myisam_mmap_size != SIZE_T_MAX) { pthread_mutex_lock(&THR_LOCK_myisam_mmap); - myisam_mmap_used-= info->s->mmaped_length + MEMMAP_EXTRA_MARGIN; + myisam_mmap_used-= info->s->mmaped_length; pthread_mutex_unlock(&THR_LOCK_myisam_mmap); } } |