diff --git a/CMakeLists.txt b/CMakeLists.txt index 036d0cdb9d6..d20939f33ac 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,26 +32,32 @@ IF(DEFINED CMAKE_BUILD_TYPE) SET(HAVE_CMAKE_BUILD_TYPE TRUE) ENDIF() SET(CUSTOM_C_FLAGS $ENV{CFLAGS}) -IF(NOT CUSTOM_C_FLAGS) - SET(CUSTOM_C_FLAGS ${CMAKE_C_FLAGS}) -ENDIF() -OPTION(WITH_DEBUG "Use dbug" OFF) +OPTION(WITH_DEBUG "Use dbug/safemutex" OFF) OPTION(WITH_DEBUG_FULL "Use dbug and safemalloc/safemutex. Slow" OFF) -IF(NOT HAVE_CMAKE_BUILD_TYPE) - IF(BUILD_CONFIG OR NOT CUSTOM_C_FLAGS) - IF(WITH_DEBUG) - SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Debug build" FORCE) - ELSE() - SET(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING - "RelWithDebInfo build" FORCE) - ENDIF() - ENDIF() -ENDIF() -IF(WITH_DEBUG_FULL) - SET(WITH_DEBUG ON CACHE BOOL "Use DBUG") +# We choose to provide WITH_DEBUG as alias to standard CMAKE_BUILD_TYPE=Debug +# which turns out to be not trivial, as this involves synchronization +# between CMAKE_BUILD_TYPE and WITH_DEBUG. Besides, we have to deal with cases +# where WITH_DEBUG is reset from ON to OFF and here we need to reset +# CMAKE_BUILD_TYPE to either none or default RelWithDebInfo + +SET(BUILDTYPE_DOCSTRING + "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or + CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel") + +IF(WITH_DEBUG OR WITH_DEBUG_FULL) + SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING ${BUILDTYPE_DOCSTRING} FORCE) + SET(OLD_WITH_DEBUG 1 CACHE INTERNAL "" FORCE) +ELSEIF(NOT HAVE_CMAKE_BUILD_TYPE OR OLD_WITH_DEBUG) + IF(CUSTOM_C_FLAGS) + SET(CMAKE_BUILD_TYPE "" CACHE STRING ${BUILDTYPE_DOCSTRING} FORCE) + ELSE(CMAKE_BUILD_TYPE MATCHES "Debug" OR NOT HAVE_CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING + ${BUILDTYPE_DOCSTRING} FORCE) + ENDIF() + SET(OLD_WITH_DEBUG 0 CACHE INTERNAL "" FORCE) ENDIF() IF(BUILD_CONFIG) diff --git a/config.h.cmake b/config.h.cmake index eb1a5e24ec8..f0473c83af6 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -205,6 +205,7 @@ #cmakedefine HAVE_PTHREAD_KEY_DELETE 1 #cmakedefine HAVE_PTHREAD_KILL 1 #cmakedefine HAVE_PTHREAD_RWLOCK_RDLOCK 1 +#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP 1 #cmakedefine HAVE_PTHREAD_SETPRIO_NP 1 #cmakedefine HAVE_PTHREAD_SETSCHEDPARAM 1 #cmakedefine HAVE_PTHREAD_SIGMASK 1 diff --git a/configure.cmake b/configure.cmake index 243d3b03b45..31e38aaa2cd 100644 --- a/configure.cmake +++ b/configure.cmake @@ -308,6 +308,7 @@ CHECK_FUNCTION_EXISTS (pthread_condattr_setclock HAVE_PTHREAD_CONDATTR_SETCLOCK) CHECK_FUNCTION_EXISTS (pthread_init HAVE_PTHREAD_INIT) CHECK_FUNCTION_EXISTS (pthread_key_delete HAVE_PTHREAD_KEY_DELETE) CHECK_FUNCTION_EXISTS (pthread_rwlock_rdlock HAVE_PTHREAD_RWLOCK_RDLOCK) +CHECK_FUNCTION_EXISTS (pthread_rwlockattr_setkind_np HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) CHECK_FUNCTION_EXISTS (pthread_sigmask HAVE_PTHREAD_SIGMASK) CHECK_FUNCTION_EXISTS (pthread_threadmask HAVE_PTHREAD_THREADMASK) CHECK_FUNCTION_EXISTS (pthread_yield_np HAVE_PTHREAD_YIELD_NP) diff --git a/configure.in b/configure.in index 3280c979c8c..c2a8ce86e08 100644 --- a/configure.in +++ b/configure.in @@ -2266,7 +2266,8 @@ AC_CHECK_FUNCS(alarm bcmp bfill bmove bsearch bzero \ locking longjmp lrand48 madvise mallinfo memcpy memmove \ mkstemp mlockall perror poll pread pthread_attr_create mmap mmap64 getpagesize \ pthread_attr_getstacksize pthread_attr_setstacksize pthread_condattr_create \ - pthread_getsequence_np pthread_key_delete pthread_rwlock_rdlock pthread_sigmask \ + pthread_getsequence_np pthread_key_delete pthread_rwlock_rdlock \ + pthread_rwlockattr_setkind_np pthread_sigmask \ readlink realpath rename rint rwlock_init setupterm \ shmget shmat shmdt shmctl sigaction sigemptyset sigaddset \ sighold sigset sigthreadmask port_create sleep thr_yield \ diff --git a/include/my_pthread.h b/include/my_pthread.h index 0e421f678b0..0b55c70b0fa 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -600,30 +600,76 @@ int my_pthread_fastmutex_lock(my_pthread_fastmutex_t *mp); #define my_rwlock_init(A,B) rwlock_init((A),USYNC_THREAD,0) #else /* Use our own version of read/write locks */ -typedef struct _my_rw_lock_t { - pthread_mutex_t lock; /* lock for structure */ - pthread_cond_t readers; /* waiting readers */ - pthread_cond_t writers; /* waiting writers */ - int state; /* -1:writer,0:free,>0:readers */ - int waiters; /* number of waiting writers */ -} my_rw_lock_t; - +#define NEED_MY_RW_LOCK 1 #define rw_lock_t my_rw_lock_t +#define my_rwlock_init(A,B) my_rw_init((A), 0) #define rw_rdlock(A) my_rw_rdlock((A)) #define rw_wrlock(A) my_rw_wrlock((A)) #define rw_tryrdlock(A) my_rw_tryrdlock((A)) #define rw_trywrlock(A) my_rw_trywrlock((A)) #define rw_unlock(A) my_rw_unlock((A)) -#define rwlock_destroy(A) my_rwlock_destroy((A)) +#define rwlock_destroy(A) my_rw_destroy((A)) +#endif /* USE_MUTEX_INSTEAD_OF_RW_LOCKS */ -extern int my_rwlock_init(my_rw_lock_t *, void *); -extern int my_rwlock_destroy(my_rw_lock_t *); + +/* + Portable read-write locks which prefer readers. + + Required by some algorithms in order to provide correctness. +*/ + +#if defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) +/* + On systems which have a way to specify that readers should + be preferred through attribute mechanism (e.g. Linux) we use + system implementation of read/write locks. +*/ +#define rw_pr_lock_t pthread_rwlock_t +extern int rw_pr_init(rw_pr_lock_t *); +#define rw_pr_rdlock(A) pthread_rwlock_rdlock(A) +#define rw_pr_wrlock(A) pthread_rwlock_wrlock(A) +#define rw_pr_tryrdlock(A) pthread_rwlock_tryrdlock(A) +#define rw_pr_trywrlock(A) pthread_rwlock_trywrlock(A) +#define rw_pr_unlock(A) pthread_rwlock_unlock(A) +#define rw_pr_destroy(A) pthread_rwlock_destroy(A) +#else +/* Otherwise we have to use our own implementation of read/write locks. */ +#define NEED_MY_RW_LOCK 1 +struct st_my_rw_lock_t; +#define rw_pr_lock_t my_rw_lock_t +extern int rw_pr_init(struct st_my_rw_lock_t *); +#define rw_pr_rdlock(A) my_rw_rdlock((A)) +#define rw_pr_wrlock(A) my_rw_wrlock((A)) +#define rw_pr_tryrdlock(A) my_rw_tryrdlock((A)) +#define rw_pr_trywrlock(A) my_rw_trywrlock((A)) +#define rw_pr_unlock(A) my_rw_unlock((A)) +#define rw_pr_destroy(A) my_rw_destroy((A)) +#endif /* defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) */ + + +#ifdef NEED_MY_RW_LOCK +/* + On systems which don't support native read/write locks, or don't support + read/write locks which prefer readers we have to use own implementation. +*/ +typedef struct st_my_rw_lock_t { + pthread_mutex_t lock; /* lock for structure */ + pthread_cond_t readers; /* waiting readers */ + pthread_cond_t writers; /* waiting writers */ + int state; /* -1:writer,0:free,>0:readers */ + int waiters; /* number of waiting writers */ + my_bool prefer_readers; +} my_rw_lock_t; + +extern int my_rw_init(my_rw_lock_t *, my_bool *); +extern int my_rw_destroy(my_rw_lock_t *); extern int my_rw_rdlock(my_rw_lock_t *); extern int my_rw_wrlock(my_rw_lock_t *); extern int my_rw_unlock(my_rw_lock_t *); extern int my_rw_tryrdlock(my_rw_lock_t *); extern int my_rw_trywrlock(my_rw_lock_t *); -#endif /* USE_MUTEX_INSTEAD_OF_RW_LOCKS */ +#endif /* NEED_MY_RW_LOCK */ + #define GETHOSTBYADDR_BUFF_SIZE 2048 diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 2136bcd92f1..fd23bfa0562 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -111,3 +111,99 @@ commit; # which was already released by commit. unlock tables; drop tables t1, t2; +# +# Tests for WL#5000 FLUSH TABLES|TABLE table_list WITH READ LOCK +# +# I. Check the incompatible changes in the grammar. +# +flush tables with read lock, hosts; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' hosts' at line 1 +flush privileges, tables; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables' at line 1 +flush privileges, tables with read lock; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables with read lock' at line 1 +flush privileges, tables; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tables' at line 1 +flush tables with read lock, tables; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' tables' at line 1 +show tables; +Tables_in_test +# +# II. Check the allowed syntax. +# +drop table if exists t1, t2, t3; +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +lock table t1 read, t2 read; +flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +unlock tables; +flush tables with read lock; +flush tables t1, t2 with read lock; +flush tables t1, t2 with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +flush tables with read lock; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +select * from t1; +a +select * from t2; +a +select * from t3; +ERROR HY000: Table 't3' was not locked with LOCK TABLES +insert into t1 (a) values (1); +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +insert into t2 (a) values (1); +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +insert into t3 (a) values (1); +ERROR HY000: Table 't3' was not locked with LOCK TABLES +lock table no_such_table read; +ERROR 42S02: Table 'test.no_such_table' doesn't exist +# +# We implicitly left the locked tables +# mode but still have the read lock. +# +insert into t2 (a) values (1); +ERROR HY000: Can't execute the query because you have a conflicting read lock +unlock tables; +insert into t1 (a) values (1); +insert into t2 (a) values (1); +flush table t1, t2 with read lock; +select * from t1; +a +1 +select * from t2; +a +1 +select * from t3; +ERROR HY000: Table 't3' was not locked with LOCK TABLES +insert into t1 (a) values (2); +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +insert into t2 (a) values (2); +ERROR HY000: Table 't2' was locked with a READ lock and can't be updated +insert into t3 (a) values (2); +ERROR HY000: Table 't3' was not locked with LOCK TABLES +lock table no_such_table read; +ERROR 42S02: Table 'test.no_such_table' doesn't exist +insert into t3 (a) values (2); +# +# III. Concurrent tests. +# +# --> connection default +# +# Check that flush tables with read lock +# does not affect non-locked tables. +# +flush tables t1 with read lock; +# --> connection con1; +select * from t1; +a +1 +select * from t2; +a +1 +insert into t2 (a) values (3); +# --> connection default; +unlock tables; +# --> connection con1 +drop table t1, t2, t3; diff --git a/mysql-test/r/greedy_optimizer.result b/mysql-test/r/greedy_optimizer.result index c0012c297d1..be4b06396a8 100644 --- a/mysql-test/r/greedy_optimizer.result +++ b/mysql-test/r/greedy_optimizer.result @@ -115,6 +115,8 @@ select @@optimizer_prune_level; @@optimizer_prune_level 1 set optimizer_search_depth=63; +Warnings: +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead select @@optimizer_search_depth; @@optimizer_search_depth 63 diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 5c4a13a3c7e..711402c764f 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -396,8 +396,9 @@ The following options may be given as the first argument: relation result in faster optimization, but may produce very bad query plans. If set to 0, the system will automatically pick a reasonable value; if set to 63, the - optimizer will switch to the original find_best - search(used for testing/comparison) + optimizer will switch to the original find_best search. + NOTE: The value 63 and its associated behaviour is + deprecated --optimizer-switch=name optimizer_switch=option=val[,option=val...], where option is one of {index_merge, index_merge_union, diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 15649cbc0fa..9804cfce78b 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -396,8 +396,9 @@ The following options may be given as the first argument: relation result in faster optimization, but may produce very bad query plans. If set to 0, the system will automatically pick a reasonable value; if set to 63, the - optimizer will switch to the original find_best - search(used for testing/comparison) + optimizer will switch to the original find_best search. + NOTE: The value 63 and its associated behaviour is + deprecated --optimizer-switch=name optimizer_switch=option=val[,option=val...], where option is one of {index_merge, index_merge_union, diff --git a/mysql-test/suite/large_tests/r/rpl_slave_net_timeout.result b/mysql-test/suite/large_tests/r/rpl_slave_net_timeout.result new file mode 100644 index 00000000000..81de6e228e0 --- /dev/null +++ b/mysql-test/suite/large_tests/r/rpl_slave_net_timeout.result @@ -0,0 +1,25 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +set @save_general_log = @@global.general_log; +set @save_log_output = @@global.log_output; +set @save_slave_net_timeout = @@global.slave_net_timeout; +set @@global.general_log = ON; +set @@global.log_output = 'table,file'; +include/stop_slave.inc +set @@global.slave_net_timeout = @@global.net_read_timeout * 2; +change master to master_host = '127.0.0.1',master_port = MASTER_PORT, +master_user = 'root', master_heartbeat_period = 0; +include/start_slave.inc +include/stop_slave.inc +select event_time from (select event_time from mysql.general_log as t_1 where command_type like 'Connect' order by event_time desc limit 2) as t_2 order by event_time desc limit 1 into @ts_last; +select event_time from (select event_time from mysql.general_log as t_1 where command_type like 'Connect' order by event_time desc limit 2) as t_2 order by event_time asc limit 1 into @ts_prev; +select @result as 'Must be 1'; +Must be 1 +1 +set @@global.general_log = @save_general_log; +set @@global.log_output = @save_log_output; +set @@global.slave_net_timeout = @save_slave_net_timeout; diff --git a/mysql-test/suite/large_tests/t/rpl_slave_net_timeout-slave.opt b/mysql-test/suite/large_tests/t/rpl_slave_net_timeout-slave.opt new file mode 100644 index 00000000000..281566c9ad9 --- /dev/null +++ b/mysql-test/suite/large_tests/t/rpl_slave_net_timeout-slave.opt @@ -0,0 +1 @@ +--net_read_timeout=5 diff --git a/mysql-test/suite/large_tests/t/rpl_slave_net_timeout.test b/mysql-test/suite/large_tests/t/rpl_slave_net_timeout.test new file mode 100644 index 00000000000..9a2cdc3b596 --- /dev/null +++ b/mysql-test/suite/large_tests/t/rpl_slave_net_timeout.test @@ -0,0 +1,81 @@ +# +# Testing reconnecting by slave as specified by `slave_net_timeout' +# +# Bug #50296 Slave reconnects earlier than the prescribed slave_net_timeout value +# +--source include/have_csv.inc +--source include/master-slave.inc + + +# save global env +connection master; +set @save_general_log = @@global.general_log; +set @save_log_output = @@global.log_output; + +connection slave; +set @save_slave_net_timeout = @@global.slave_net_timeout; + +connection master; +set @@global.general_log = ON; +set @@global.log_output = 'table,file'; + +connection slave; +--source include/stop_slave.inc +--disable_warnings +set @@global.slave_net_timeout = @@global.net_read_timeout * 2; +--enable_warnings +let $idle_time=`select @@global.slave_net_timeout * 2`; + +# +# if heartbeat is disabled then reconnecting to the idle master +# should happen with `slave_net_timeout' period. +# Since it's the real time that is measured, `slave_net_timeout' +# merely guarantees that reconnecting can *not* happen earlier of a value specified. +# That is there can't an exact estimate for how many time it will happen. +# +# The following lines verify that having idle master +# for more than 2 * slave_net_timeout seconds and +# slave.net_read_timeout < slave_net_timeout +# won't cause reconnecting by the slave within at least +# slave_net_timeout interval. +--replace_result $MASTER_MYPORT MASTER_PORT +eval change master to master_host = '127.0.0.1',master_port = $MASTER_MYPORT, +master_user = 'root', master_heartbeat_period = 0; + +let $slave_net_timeout = `select @@global.slave_net_timeout`; + +--source include/start_slave.inc + +--disable_query_log +--disable_result_log +eval select 'master is idle for ', sleep($idle_time); +--enable_result_log +--enable_query_log + +--source include/stop_slave.inc + +# querying general-log + +connection master; + +# In particular the last reconnection timestamp must be greater or equal to +# the previous one + slave_net_timeout + +select event_time from (select event_time from mysql.general_log as t_1 where command_type like 'Connect' order by event_time desc limit 2) as t_2 order by event_time desc limit 1 into @ts_last; +select event_time from (select event_time from mysql.general_log as t_1 where command_type like 'Connect' order by event_time desc limit 2) as t_2 order by event_time asc limit 1 into @ts_prev; + +--disable_query_log +eval select time_to_sec(@ts_last) - $slave_net_timeout >= time_to_sec(@ts_prev) into @result; +--enable_query_log + +select @result as 'Must be 1'; + +# cleanup + +# restore global env +connection master; +set @@global.general_log = @save_general_log; +set @@global.log_output = @save_log_output; +connection slave; +set @@global.slave_net_timeout = @save_slave_net_timeout; + diff --git a/mysql-test/suite/perfschema/r/misc.result b/mysql-test/suite/perfschema/r/misc.result index d944b4b1d3d..3c0a4a58e08 100644 --- a/mysql-test/suite/perfschema/r/misc.result +++ b/mysql-test/suite/perfschema/r/misc.result @@ -11,3 +11,17 @@ create table test.t1 like performance_schema.EVENTS_WAITS_CURRENT; ERROR HY000: Invalid performance_schema usage. create table performance_schema.t1(a int); ERROR 42000: CREATE command denied to user 'root'@'localhost' for table 't1' +drop table if exists test.ghost; +create table test.ghost (a int, b int); +alter table test.ghost add index index_a(a); +alter table test.ghost add index index_b(b); +insert into test.ghost values (1, 3); +insert into test.ghost values (2, 4); +select * from test.ghost; +a b +1 3 +2 4 +drop table test.ghost; +select * from performance_schema.FILE_INSTANCES +where file_name like "%ghost%"; +FILE_NAME EVENT_NAME OPEN_COUNT diff --git a/mysql-test/suite/perfschema/t/misc.test b/mysql-test/suite/perfschema/t/misc.test index d9b97d8441e..749eccca793 100644 --- a/mysql-test/suite/perfschema/t/misc.test +++ b/mysql-test/suite/perfschema/t/misc.test @@ -55,3 +55,24 @@ create table test.t1 like performance_schema.EVENTS_WAITS_CURRENT; --error ER_TABLEACCESS_DENIED_ERROR create table performance_schema.t1(a int); +# +# Bug#51447 performance schema evil twin files +# + +--disable_warnings +drop table if exists test.ghost; +--enable_warnings + +create table test.ghost (a int, b int); +alter table test.ghost add index index_a(a); +alter table test.ghost add index index_b(b); +insert into test.ghost values (1, 3); +insert into test.ghost values (2, 4); +select * from test.ghost; + +drop table test.ghost; + +# Shoud return nothing +select * from performance_schema.FILE_INSTANCES + where file_name like "%ghost%"; + diff --git a/mysql-test/suite/sys_vars/r/optimizer_search_depth_basic.result b/mysql-test/suite/sys_vars/r/optimizer_search_depth_basic.result index 4d94fb02be7..de448d3e2fc 100644 --- a/mysql-test/suite/sys_vars/r/optimizer_search_depth_basic.result +++ b/mysql-test/suite/sys_vars/r/optimizer_search_depth_basic.result @@ -10,6 +10,7 @@ SELECT @start_session_value; SET @@global.optimizer_search_depth = 100; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '100' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SET @@global.optimizer_search_depth = DEFAULT; SELECT @@global.optimizer_search_depth; @@global.optimizer_search_depth @@ -17,6 +18,7 @@ SELECT @@global.optimizer_search_depth; SET @@session.optimizer_search_depth = 200; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '200' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SET @@session.optimizer_search_depth = DEFAULT; SELECT @@session.optimizer_search_depth; @@session.optimizer_search_depth @@ -44,6 +46,8 @@ SELECT @@global.optimizer_search_depth; @@global.optimizer_search_depth 62 SET @@global.optimizer_search_depth = 63; +Warnings: +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@global.optimizer_search_depth; @@global.optimizer_search_depth 63 @@ -61,6 +65,8 @@ SELECT @@session.optimizer_search_depth; @@session.optimizer_search_depth 62 SET @@session.optimizer_search_depth = 63; +Warnings: +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@session.optimizer_search_depth; @@session.optimizer_search_depth 63 @@ -68,6 +74,7 @@ SELECT @@session.optimizer_search_depth; SET @@global.optimizer_search_depth = 64; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '64' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@global.optimizer_search_depth; @@global.optimizer_search_depth 63 @@ -80,6 +87,7 @@ SELECT @@global.optimizer_search_depth; SET @@global.optimizer_search_depth = 65536; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '65536' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@global.optimizer_search_depth; @@global.optimizer_search_depth 63 @@ -96,6 +104,7 @@ SELECT @@global.optimizer_search_depth; SET @@session.optimizer_search_depth = 64; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '64' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@session.optimizer_search_depth; @@session.optimizer_search_depth 63 @@ -110,6 +119,7 @@ ERROR 42000: Incorrect argument type to variable 'optimizer_search_depth' SET @@session.optimizer_search_depth = 65550; Warnings: Warning 1292 Truncated incorrect optimizer_search_depth value: '65550' +Warning 1287 'optimizer-search-depth=63' is deprecated and will be removed in a future release. Please use a search depth less than 63 instead SELECT @@session.optimizer_search_depth; @@session.optimizer_search_depth 63 diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index d41ac3100b0..582d2562fc6 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -224,3 +224,103 @@ commit; --echo # which was already released by commit. unlock tables; drop tables t1, t2; + + + +--echo # +--echo # Tests for WL#5000 FLUSH TABLES|TABLE table_list WITH READ LOCK +--echo # +--echo # I. Check the incompatible changes in the grammar. +--echo # +--error ER_PARSE_ERROR +flush tables with read lock, hosts; +--error ER_PARSE_ERROR +flush privileges, tables; +--error ER_PARSE_ERROR +flush privileges, tables with read lock; +--error ER_PARSE_ERROR +flush privileges, tables; +--error ER_PARSE_ERROR +flush tables with read lock, tables; +show tables; +--echo # +--echo # II. Check the allowed syntax. +--echo # +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +create table t3 (a int); +lock table t1 read, t2 read; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +flush tables with read lock; +unlock tables; +flush tables with read lock; +flush tables t1, t2 with read lock; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +flush tables t1, t2 with read lock; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +flush tables with read lock; +select * from t1; +select * from t2; +--error ER_TABLE_NOT_LOCKED +select * from t3; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t1 (a) values (1); +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t2 (a) values (1); +--error ER_TABLE_NOT_LOCKED +insert into t3 (a) values (1); +--error ER_NO_SUCH_TABLE +lock table no_such_table read; +--echo # +--echo # We implicitly left the locked tables +--echo # mode but still have the read lock. +--echo # +--error ER_CANT_UPDATE_WITH_READLOCK +insert into t2 (a) values (1); +unlock tables; +insert into t1 (a) values (1); +insert into t2 (a) values (1); +flush table t1, t2 with read lock; +select * from t1; +select * from t2; +--error ER_TABLE_NOT_LOCKED +select * from t3; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t1 (a) values (2); +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t2 (a) values (2); +--error ER_TABLE_NOT_LOCKED +insert into t3 (a) values (2); +--error ER_NO_SUCH_TABLE +lock table no_such_table read; +insert into t3 (a) values (2); + + +--echo # +--echo # III. Concurrent tests. +--echo # +connect (con1,localhost,root,,); +--echo # --> connection default +--echo # +--echo # Check that flush tables with read lock +--echo # does not affect non-locked tables. +connection default; +--echo # +flush tables t1 with read lock; +--echo # --> connection con1; +connection con1; +select * from t1; +select * from t2; +insert into t2 (a) values (3); +--echo # --> connection default; +connection default; +unlock tables; +--echo # --> connection con1 +connection con1; +disconnect con1; +--source include/wait_until_disconnected.inc +connection default; +drop table t1, t2, t3; diff --git a/mysys/thr_rwlock.c b/mysys/thr_rwlock.c index 0aa4d3fc3c4..2ac4a00695e 100644 --- a/mysys/thr_rwlock.c +++ b/mysys/thr_rwlock.c @@ -16,7 +16,8 @@ /* Synchronization - readers / writer thread locks */ #include "mysys_priv.h" -#if defined(THREAD) && !defined(HAVE_PTHREAD_RWLOCK_RDLOCK) && !defined(HAVE_RWLOCK_INIT) +#if defined(THREAD) +#if defined(NEED_MY_RW_LOCK) #include /* @@ -58,7 +59,7 @@ * Mountain View, California 94043 */ -int my_rwlock_init(rw_lock_t *rwp, void *arg __attribute__((unused))) +int my_rw_init(my_rw_lock_t *rwp, my_bool *prefer_readers_attr) { pthread_condattr_t cond_attr; @@ -70,12 +71,14 @@ int my_rwlock_init(rw_lock_t *rwp, void *arg __attribute__((unused))) rwp->state = 0; rwp->waiters = 0; + /* If attribute argument is NULL use default value - prefer writers. */ + rwp->prefer_readers= prefer_readers_attr ? *prefer_readers_attr : FALSE; return(0); } -int my_rwlock_destroy(rw_lock_t *rwp) +int my_rw_destroy(my_rw_lock_t *rwp) { pthread_mutex_destroy( &rwp->lock ); pthread_cond_destroy( &rwp->readers ); @@ -84,12 +87,13 @@ int my_rwlock_destroy(rw_lock_t *rwp) } -int my_rw_rdlock(rw_lock_t *rwp) +int my_rw_rdlock(my_rw_lock_t *rwp) { pthread_mutex_lock(&rwp->lock); /* active or queued writers */ - while (( rwp->state < 0 ) || rwp->waiters) + while (( rwp->state < 0 ) || + (rwp->waiters && ! rwp->prefer_readers)) pthread_cond_wait( &rwp->readers, &rwp->lock); rwp->state++; @@ -97,11 +101,12 @@ int my_rw_rdlock(rw_lock_t *rwp) return(0); } -int my_rw_tryrdlock(rw_lock_t *rwp) +int my_rw_tryrdlock(my_rw_lock_t *rwp) { int res; pthread_mutex_lock(&rwp->lock); - if ((rwp->state < 0 ) || rwp->waiters) + if ((rwp->state < 0 ) || + (rwp->waiters && ! rwp->prefer_readers)) res= EBUSY; /* Can't get lock */ else { @@ -113,7 +118,7 @@ int my_rw_tryrdlock(rw_lock_t *rwp) } -int my_rw_wrlock(rw_lock_t *rwp) +int my_rw_wrlock(my_rw_lock_t *rwp) { pthread_mutex_lock(&rwp->lock); rwp->waiters++; /* another writer queued */ @@ -127,7 +132,7 @@ int my_rw_wrlock(rw_lock_t *rwp) } -int my_rw_trywrlock(rw_lock_t *rwp) +int my_rw_trywrlock(my_rw_lock_t *rwp) { int res; pthread_mutex_lock(&rwp->lock); @@ -143,7 +148,7 @@ int my_rw_trywrlock(rw_lock_t *rwp) } -int my_rw_unlock(rw_lock_t *rwp) +int my_rw_unlock(my_rw_lock_t *rwp) { DBUG_PRINT("rw_unlock", ("state: %d waiters: %d", rwp->state, rwp->waiters)); @@ -160,7 +165,8 @@ int my_rw_unlock(rw_lock_t *rwp) } else { - if ( --rwp->state == 0 ) /* no more readers */ + if ( --rwp->state == 0 && /* no more readers */ + rwp->waiters) pthread_cond_signal( &rwp->writers ); } @@ -168,4 +174,30 @@ int my_rw_unlock(rw_lock_t *rwp) return(0); } -#endif + +int rw_pr_init(struct st_my_rw_lock_t *rwlock) +{ + my_bool prefer_readers_attr= TRUE; + return my_rw_init(rwlock, &prefer_readers_attr); +} + +#else + +/* + We are on system which has native read/write locks which support + preferring of readers. +*/ + +int rw_pr_init(rw_pr_lock_t *rwlock) +{ + pthread_rwlockattr_t rwlock_attr; + + pthread_rwlockattr_init(&rwlock_attr); + pthread_rwlockattr_setkind_np(&rwlock_attr, PTHREAD_RWLOCK_PREFER_READER_NP); + pthread_rwlock_init(rwlock, NULL); + pthread_rwlockattr_destroy(&rwlock_attr); + return 0; +} + +#endif /* defined(NEED_MY_RW_LOCK) */ +#endif /* defined(THREAD) */ diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index 21fca0c0848..90f759739d0 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -56,6 +56,8 @@ Usage: $0 db_name[./table_regex/] [new_db_name | directory] -h, --host=# hostname for local server when connecting over TCP/IP -P, --port=# port to use when connecting to local server with TCP/IP -S, --socket=# socket to use when connecting to local server + --old_server connect to old MySQL-server (before v5.5) which + doesn't have FLUSH TABLES WITH READ LOCK fully implemented. --allowold don\'t abort if target dir already exists (rename it _old) --addtodest don\'t rename target dir if it exists, just add files to it @@ -103,6 +105,7 @@ GetOptions( \%opt, "password|p=s", "port|P=s", "socket|S=s", + "old_server", "allowold!", "keepold!", "addtodest!", @@ -441,21 +444,37 @@ if ( $opt{checkpoint} || $opt{record_log_pos} ) { my $hc_started = time; # count from time lock is granted if ( $opt{dryrun} ) { - print "LOCK TABLES $hc_locks\n"; - print "FLUSH TABLES /*!32323 $hc_tables */\n"; + if ( $opt{old_server} ) { + print "LOCK TABLES $hc_locks\n"; + print "FLUSH TABLES /*!32323 $hc_tables */\n"; + } + else { + print "FLUSH TABLES $hc_tables WITH READ LOCK\n"; + } + print "FLUSH LOGS\n" if ( $opt{flushlog} ); print "RESET MASTER\n" if ( $opt{resetmaster} ); print "RESET SLAVE\n" if ( $opt{resetslave} ); } else { my $start = time; - $dbh->do("LOCK TABLES $hc_locks"); - printf "Locked $num_tables tables in %d seconds.\n", time-$start unless $opt{quiet}; - $hc_started = time; # count from time lock is granted + if ( $opt{old_server} ) { + $dbh->do("LOCK TABLES $hc_locks"); + printf "Locked $num_tables tables in %d seconds.\n", time-$start unless $opt{quiet}; + $hc_started = time; # count from time lock is granted - # flush tables to make on-disk copy up to date - $start = time; - $dbh->do("FLUSH TABLES /*!32323 $hc_tables */"); + # flush tables to make on-disk copy up to date + $start = time; + $dbh->do("FLUSH TABLES /*!32323 $hc_tables */"); + } + else { + $dbh->do("FLUSH TABLES $hc_tables WITH READ LOCK"); + printf "Locked $num_tables tables in %d seconds.\n", time-$start unless $opt{quiet}; + $hc_started = time; # count from time lock is granted + + # flush tables to make on-disk copy up to date + $start = time; + } printf "Flushed tables ($hc_tables) in %d seconds.\n", time-$start unless $opt{quiet}; $dbh->do( "FLUSH LOGS" ) if ( $opt{flushlog} ); $dbh->do( "RESET MASTER" ) if ( $opt{resetmaster} ); @@ -975,6 +994,10 @@ when using the --host option. UNIX domain socket to use when connecting to local server. +=item --old_server + +Use old server (pre v5.5) commands. + =item --noindices Don\'t include index files in copy. Only up to the first 2048 bytes diff --git a/sql/mdl.cc b/sql/mdl.cc index 28cff420e0d..f9a4e10aade 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -148,10 +148,37 @@ public: /** Read-write lock protecting this lock context. - TODO/FIXME: Replace with RW-lock which will prefer readers - on all platforms and not only on Linux. + @note The fact that we use read-write lock prefers readers here is + important as deadlock detector won't work correctly otherwise. + + For example, imagine that we have following waiters graph: + + ctxA -> obj1 -> ctxB -> obj1 -| + ^ | + |----------------------------| + + and both ctxA and ctxB start deadlock detection process: + + ctxA read-locks obj1 ctxB read-locks obj2 + ctxA goes deeper ctxB goes deeper + + Now ctxC comes in who wants to start waiting on obj1, also + ctxD comes in who wants to start waiting on obj2. + + ctxC tries to write-lock obj1 ctxD tries to write-lock obj2 + ctxC is blocked ctxD is blocked + + Now ctxA and ctxB resume their search: + + ctxA tries to read-lock obj2 ctxB tries to read-lock obj1 + + If m_rwlock prefers writes (or fair) both ctxA and ctxB would be + blocked because of pending write locks from ctxD and ctxC + correspondingly. Thus we will get a deadlock in deadlock detector. + If m_wrlock prefers readers (actually ignoring pending writers is + enough) ctxA and ctxB will continue and no deadlock will occur. */ - rw_lock_t m_rwlock; + rw_pr_lock_t m_rwlock; bool is_empty() const { @@ -213,12 +240,12 @@ public: m_ref_release(0), m_is_destroyed(FALSE) { - my_rwlock_init(&m_rwlock, NULL); + rw_pr_init(&m_rwlock); } virtual ~MDL_lock() { - rwlock_destroy(&m_rwlock); + rw_pr_destroy(&m_rwlock); } inline static void destroy(MDL_lock *lock); public: @@ -480,7 +507,7 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock) lock->m_ref_usage++; mysql_mutex_unlock(&m_mutex); - rw_wrlock(&lock->m_rwlock); + rw_pr_wrlock(&lock->m_rwlock); lock->m_ref_release++; if (unlikely(lock->m_is_destroyed)) { @@ -495,7 +522,7 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock) */ uint ref_usage= lock->m_ref_usage; uint ref_release= lock->m_ref_release; - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); if (ref_usage == ref_release) MDL_lock::destroy(lock); return TRUE; @@ -538,7 +565,7 @@ void MDL_map::remove(MDL_lock *lock) lock->m_is_destroyed= TRUE; ref_usage= lock->m_ref_usage; ref_release= lock->m_ref_release; - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); mysql_mutex_unlock(&m_mutex); if (ref_usage == ref_release) MDL_lock::destroy(lock); @@ -559,7 +586,7 @@ MDL_context::MDL_context() m_deadlock_weight(0), m_signal(NO_WAKE_UP) { - my_rwlock_init(&m_waiting_for_lock, NULL); + rw_pr_init(&m_waiting_for_lock); mysql_mutex_init(NULL /* pfs key */, &m_signal_lock, NULL); mysql_cond_init(NULL /* pfs key */, &m_signal_cond, NULL); } @@ -581,7 +608,7 @@ void MDL_context::destroy() { DBUG_ASSERT(m_tickets.is_empty()); - rwlock_destroy(&m_waiting_for_lock); + rw_pr_destroy(&m_waiting_for_lock); mysql_mutex_destroy(&m_signal_lock); mysql_cond_destroy(&m_signal_cond); } @@ -1071,7 +1098,7 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg, void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket) { - rw_wrlock(&m_rwlock); + rw_pr_wrlock(&m_rwlock); (this->*list).remove_ticket(ticket); if (is_empty()) mdl_locks.remove(this); @@ -1082,7 +1109,7 @@ void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket) which now might be able to do it. Wake them up! */ wake_up_waiters(); - rw_unlock(&m_rwlock); + rw_pr_unlock(&m_rwlock); } } @@ -1102,9 +1129,9 @@ bool MDL_lock::has_pending_conflicting_lock(enum_mdl_type type) mysql_mutex_assert_not_owner(&LOCK_open); - rw_rdlock(&m_rwlock); + rw_pr_rdlock(&m_rwlock); result= (m_waiting.bitmap() & incompatible_granted_types_bitmap()[type]); - rw_unlock(&m_rwlock); + rw_pr_unlock(&m_rwlock); return result; } @@ -1298,7 +1325,7 @@ MDL_context::try_acquire_lock(MDL_request *mdl_request) { ticket->m_lock= lock; lock->m_granted.add_ticket(ticket); - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); m_tickets.push_front(ticket); @@ -1308,7 +1335,7 @@ MDL_context::try_acquire_lock(MDL_request *mdl_request) { /* We can't get here if we allocated a new lock. */ DBUG_ASSERT(! lock->is_empty()); - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); MDL_ticket::destroy(ticket); } @@ -1349,9 +1376,9 @@ MDL_context::clone_ticket(MDL_request *mdl_request) ticket->m_lock= mdl_request->ticket->m_lock; mdl_request->ticket= ticket; - rw_wrlock(&ticket->m_lock->m_rwlock); + rw_pr_wrlock(&ticket->m_lock->m_rwlock); ticket->m_lock->m_granted.add_ticket(ticket); - rw_unlock(&ticket->m_lock->m_rwlock); + rw_pr_unlock(&ticket->m_lock->m_rwlock); m_tickets.push_front(ticket); @@ -1457,7 +1484,7 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request, if (ticket->is_upgradable_or_exclusive()) lock->notify_shared_locks(this); - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); set_deadlock_weight(mdl_request->get_deadlock_weight()); will_wait_for(ticket); @@ -1492,7 +1519,7 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request, my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); return TRUE; } - rw_wrlock(&lock->m_rwlock); + rw_pr_wrlock(&lock->m_rwlock); } lock->m_waiting.remove_ticket(ticket); @@ -1502,7 +1529,7 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request, (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= NULL; - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); m_tickets.push_front(ticket); @@ -1647,7 +1674,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket); /* Merge the acquired and the original lock. @todo: move to a method. */ - rw_wrlock(&mdl_ticket->m_lock->m_rwlock); + rw_pr_wrlock(&mdl_ticket->m_lock->m_rwlock); if (is_new_ticket) mdl_ticket->m_lock->m_granted.remove_ticket(mdl_xlock_request.ticket); /* @@ -1659,7 +1686,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, mdl_ticket->m_type= MDL_EXCLUSIVE; mdl_ticket->m_lock->m_granted.add_ticket(mdl_ticket); - rw_unlock(&mdl_ticket->m_lock->m_rwlock); + rw_pr_unlock(&mdl_ticket->m_lock->m_rwlock); if (is_new_ticket) { @@ -1677,7 +1704,7 @@ bool MDL_lock::find_deadlock(MDL_ticket *waiting_ticket, MDL_ticket *ticket; bool result= FALSE; - rw_rdlock(&m_rwlock); + rw_pr_rdlock(&m_rwlock); Ticket_iterator granted_it(m_granted); Ticket_iterator waiting_it(m_waiting); @@ -1729,7 +1756,7 @@ bool MDL_lock::find_deadlock(MDL_ticket *waiting_ticket, } end: - rw_unlock(&m_rwlock); + rw_pr_unlock(&m_rwlock); return result; } @@ -1738,7 +1765,7 @@ bool MDL_context::find_deadlock(Deadlock_detection_context *deadlock_ctx) { bool result= FALSE; - rw_rdlock(&m_waiting_for_lock); + rw_pr_rdlock(&m_waiting_for_lock); if (m_waiting_for) { @@ -1767,14 +1794,14 @@ bool MDL_context::find_deadlock(Deadlock_detection_context *deadlock_ctx) deadlock_ctx->victim= this; else if (deadlock_ctx->victim->m_deadlock_weight >= m_deadlock_weight) { - rw_unlock(&deadlock_ctx->victim->m_waiting_for_lock); + rw_pr_unlock(&deadlock_ctx->victim->m_waiting_for_lock); deadlock_ctx->victim= this; } else - rw_unlock(&m_waiting_for_lock); + rw_pr_unlock(&m_waiting_for_lock); } else - rw_unlock(&m_waiting_for_lock); + rw_pr_unlock(&m_waiting_for_lock); return result; } @@ -1800,7 +1827,7 @@ bool MDL_context::find_deadlock() if (deadlock_ctx.victim != this) { deadlock_ctx.victim->awake(VICTIM_WAKE_UP); - rw_unlock(&deadlock_ctx.victim->m_waiting_for_lock); + rw_pr_unlock(&deadlock_ctx.victim->m_waiting_for_lock); /* After adding new arc to waiting graph we found that it participates in some loop (i.e. there is a deadlock). We decided to destroy this @@ -1813,7 +1840,7 @@ bool MDL_context::find_deadlock() else { DBUG_ASSERT(&deadlock_ctx.victim->m_waiting_for_lock == &m_waiting_for_lock); - rw_unlock(&deadlock_ctx.victim->m_waiting_for_lock); + rw_pr_unlock(&deadlock_ctx.victim->m_waiting_for_lock); return TRUE; } } @@ -1870,14 +1897,14 @@ MDL_context::wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout) if (lock->can_grant_lock(mdl_request->type, this)) { - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); return FALSE; } MDL_ticket *pending_ticket; if (! (pending_ticket= MDL_ticket::create(this, mdl_request->type))) { - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); return TRUE; } @@ -1886,7 +1913,7 @@ MDL_context::wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout) lock->m_waiting.add_ticket(pending_ticket); wait_reset(); - rw_unlock(&lock->m_rwlock); + rw_pr_unlock(&lock->m_rwlock); set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML); will_wait_for(pending_ticket); @@ -2037,7 +2064,7 @@ void MDL_ticket::downgrade_exclusive_lock(enum_mdl_type type) if (m_type != MDL_EXCLUSIVE) return; - rw_wrlock(&m_lock->m_rwlock); + rw_pr_wrlock(&m_lock->m_rwlock); /* To update state of MDL_lock object correctly we need to temporarily exclude ticket from the granted queue and then include it back. @@ -2046,7 +2073,7 @@ void MDL_ticket::downgrade_exclusive_lock(enum_mdl_type type) m_type= type; m_lock->m_granted.add_ticket(this); m_lock->wake_up_waiters(); - rw_unlock(&m_lock->m_rwlock); + rw_pr_unlock(&m_lock->m_rwlock); } diff --git a/sql/mdl.h b/sql/mdl.h index 59bc1f64762..42461f6ac2f 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -624,10 +624,11 @@ private: /** Read-write lock protecting m_waiting_for member. - TODO/FIXME: Replace with RW-lock which will prefer readers - on all platforms and not only on Linux. + @note The fact that this read-write lock prefers readers is + important as deadlock detector won't work correctly + otherwise. @sa Comment for MDL_lock::m_rwlock. */ - rw_lock_t m_waiting_for_lock; + rw_pr_lock_t m_waiting_for_lock; MDL_ticket *m_waiting_for; uint m_deadlock_weight; /** @@ -651,9 +652,9 @@ private: void will_wait_for(MDL_ticket *pending_ticket) { - rw_wrlock(&m_waiting_for_lock); + rw_pr_wrlock(&m_waiting_for_lock); m_waiting_for= pending_ticket; - rw_unlock(&m_waiting_for_lock); + rw_pr_unlock(&m_waiting_for_lock); } void set_deadlock_weight(uint weight) @@ -669,9 +670,9 @@ private: void stop_waiting() { - rw_wrlock(&m_waiting_for_lock); + rw_pr_wrlock(&m_waiting_for_lock); m_waiting_for= NULL; - rw_unlock(&m_waiting_for_lock); + rw_pr_unlock(&m_waiting_for_lock); } void wait_reset() diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 20720d50194..d5df0ab5930 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1965,7 +1965,8 @@ extern ulong slow_launch_threads, slow_launch_time; extern ulong table_cache_size, table_def_size; extern MYSQL_PLUGIN_IMPORT ulong max_connections; extern ulong max_connect_errors, connect_timeout; -extern ulong slave_net_timeout, slave_trans_retries; +extern ulong slave_trans_retries; +extern uint slave_net_timeout; extern ulong what_to_log,flush_time; extern ulong query_buff_size; extern ulong max_prepared_stmt_count, prepared_stmt_count; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6bd133660b3..41563db4abb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -464,7 +464,8 @@ ulong table_cache_size, table_def_size; ulong what_to_log; ulong query_buff_size, slow_launch_time, slave_open_temp_tables; ulong open_files_limit, max_binlog_size, max_relay_log_size; -ulong slave_net_timeout, slave_trans_retries; +ulong slave_trans_retries; +uint slave_net_timeout; uint slave_exec_mode_options; ulonglong slave_type_conversions_options; ulong thread_cache_size=0, thread_pool_size= 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e66f2756c54..7a13c46e4f6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1587,6 +1587,113 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, } +/** + Implementation of FLUSH TABLES WITH READ LOCK. + + In brief: take exclusive locks, expel tables from the table + cache, reopen the tables, enter the 'LOCKED TABLES' mode, + downgrade the locks. + + Required privileges + ------------------- + Since the statement implicitly enters LOCK TABLES mode, + it requires LOCK TABLES privilege on every table. + But since the rest of FLUSH commands require + the global RELOAD_ACL, it also requires RELOAD_ACL. + + Compatibility with the global read lock + --------------------------------------- + We don't wait for the GRL, since neither the + 5.1 combination that this new statement is intended to + replace (LOCK TABLE WRITE; FLUSH TABLES;), + nor FLUSH TABLES WITH READ LOCK do. + @todo: this is not implemented, Dmitry disagrees. + Currently we wait for GRL in another connection, + but are compatible with a GRL in our own connection. + + Behaviour under LOCK TABLES + --------------------------- + Bail out: i.e. don't perform an implicit UNLOCK TABLES. + This is not consistent with LOCK TABLES statement, but is + in line with behaviour of FLUSH TABLES WITH READ LOCK, and we + try to not introduce any new statements with implicit + semantics. + + Compatibility with parallel updates + ----------------------------------- + As a result, we will wait for all open transactions + against the tables to complete. After the lock downgrade, + new transactions will be able to read the tables, but not + write to them. + + Differences from FLUSH TABLES + ------------------------------------- + - you can't flush WITH READ LOCK a non-existent table + - you can't flush WITH READ LOCK under LOCK TABLES + - currently incompatible with the GRL (@todo: fix) +*/ + +static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) +{ + Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; + TABLE_LIST *table_list; + + /* + This is called from SQLCOM_FLUSH, the transaction has + been committed implicitly. + */ + + /* RELOAD_ACL is checked by the caller. Check table-level privileges. */ + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, + FALSE, UINT_MAX, FALSE)) + goto error; + + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + goto error; + } + + /* + @todo: Since lock_table_names() acquires a global IX + lock, this actually waits for a GRL in another connection. + We are thus introducing an incompatibility. + Do nothing for now, since not taking a global IX violates + current internal MDL asserts, fix after discussing with + Dmitry. + */ + if (lock_table_names(thd, all_tables)) + goto error; + + if (open_and_lock_tables(thd, all_tables, FALSE, + MYSQL_OPEN_HAS_MDL_LOCK, + &lock_tables_prelocking_strategy) || + thd->locked_tables_list.init_locked_tables(thd)) + { + close_thread_tables(thd); + goto error; + } + + /* + Downgrade the exclusive locks. + Use MDL_SHARED_NO_WRITE as the intended + post effect of this call is identical + to LOCK TABLES <...> READ, and we didn't use + thd->in_lock_talbes and thd->sql_command= SQLCOM_LOCK_TABLES + hacks to enter the LTM. + @todo: release the global IX lock here!!! + */ + for (table_list= all_tables; table_list; + table_list= table_list->next_global) + table_list->mdl_request.ticket->downgrade_exclusive_lock(MDL_SHARED_NO_WRITE); + + return FALSE; + +error: + return TRUE; +} + + /** Read query from packet and store in thd->query. Used in COM_QUERY and COM_STMT_PREPARE. @@ -3728,9 +3835,18 @@ end_with_restore_list: case SQLCOM_FLUSH: { bool write_to_binlog; + if (check_global_access(thd,RELOAD_ACL)) goto error; + if (first_table && lex->type & REFRESH_READ_LOCK) + { + if (flush_tables_with_read_lock(thd, all_tables)) + goto error; + my_ok(thd); + break; + } + /* reload_acl_and_cache() will tell us if we are allowed to write to the binlog or not. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f03694cb359..5648e3cbdb5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -767,10 +767,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 169 shift/reduce conflicts. + Currently there are 168 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 169 +%expect 168 /* Comments for TOKENS. @@ -1554,6 +1554,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_column_list grant_privileges grant_ident grant_list grant_option object_privilege object_privilege_list user_list rename_list clear_privileges flush_options flush_option + opt_with_read_lock flush_options_list equal optional_braces opt_mi_check_type opt_to mi_check_types normal_join table_to_table_list table_to_table opt_table_list opt_as @@ -11095,17 +11096,27 @@ flush: ; flush_options: - flush_options ',' flush_option - | flush_option - ; - -flush_option: table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list {} - | TABLES WITH READ_SYM LOCK_SYM - { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } - | ERROR_SYM LOGS_SYM + opt_with_read_lock {} + | flush_options_list + ; + +opt_with_read_lock: + /* empty */ {} + | WITH READ_SYM LOCK_SYM + { Lex->type|= REFRESH_READ_LOCK; } + ; + +flush_options_list: + flush_options_list ',' flush_option + | flush_option + {} + ; + +flush_option: + ERROR_SYM LOGS_SYM { Lex->type|= REFRESH_ERROR_LOG; } | ENGINE_SYM LOGS_SYM { Lex->type|= REFRESH_ENGINE_LOG; } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ba970925fc6..80eee89c8a9 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1305,6 +1305,17 @@ static Sys_var_ulong Sys_optimizer_prune_level( SESSION_VAR(optimizer_prune_level), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1), DEFAULT(1), BLOCK_SIZE(1)); +/** Warns about deprecated value 63 */ +static bool fix_optimizer_search_depth(sys_var *self, THD *thd, + enum_var_type type) +{ + SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables; + if (sv->optimizer_search_depth == MAX_TABLES+2) + WARN_DEPRECATED(thd, 6, 0, "optimizer-search-depth=63", + "a search depth less than 63"); + return false; +} + static Sys_var_ulong Sys_optimizer_search_depth( "optimizer_search_depth", "Maximum depth of search performed by the query optimizer. Values " @@ -1313,10 +1324,12 @@ static Sys_var_ulong Sys_optimizer_search_depth( "than the number of tables in a relation result in faster " "optimization, but may produce very bad query plans. If set to 0, " "the system will automatically pick a reasonable value; if set to " - "63, the optimizer will switch to the original find_best search" - "(used for testing/comparison)", + "63, the optimizer will switch to the original find_best search. " + "NOTE: The value 63 and its associated behaviour is deprecated", SESSION_VAR(optimizer_search_depth), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(0, MAX_TABLES+2), DEFAULT(MAX_TABLES+1), BLOCK_SIZE(1)); + VALID_RANGE(0, MAX_TABLES+2), DEFAULT(MAX_TABLES+1), BLOCK_SIZE(1), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(fix_optimizer_search_depth)); static const char *optimizer_switch_names[]= { @@ -2795,7 +2808,7 @@ static bool fix_slave_net_timeout(sys_var *self, THD *thd, enum_var_type type) mysql_mutex_unlock(&LOCK_active_mi); return false; } -static Sys_var_ulong Sys_slave_net_timeout( +static Sys_var_uint Sys_slave_net_timeout( "slave_net_timeout", "Number of seconds to wait for more data " "from a master/slave connection before aborting the read", GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG), diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc index 2ce3a844290..28b54cc6979 100644 --- a/storage/perfschema/pfs_instr.cc +++ b/storage/perfschema/pfs_instr.cc @@ -66,7 +66,7 @@ ulong events_waits_history_per_thread; /** Number of instruments class per thread. */ ulong instr_class_per_thread; /** Number of locker lost. @sa LOCKER_STACK_SIZE. */ -ulong locker_lost; +ulong locker_lost= 0; /** Mutex instrumentation instances array. @@ -746,8 +746,23 @@ find_or_create_file(PFS_thread *thread, PFS_file_class *klass, } } - if (len >= sizeof(pfs->m_filename)) - len= sizeof(pfs->m_filename) - 1; + /* + Normalize the file name to avoid duplicates when using aliases: + - absolute or relative paths + - symbolic links + */ + char buffer[FN_REFLEN]; + const char *normalized_filename; + int normalized_length; + + /* + Ignore errors, the file may not exist. + my_realpath always provide a best effort result in buffer. + */ + (void) my_realpath(buffer, filename, MYF(0)); + + normalized_filename= buffer; + normalized_length= strlen(normalized_filename); PFS_file **entry; uint retry_count= 0; @@ -755,7 +770,7 @@ find_or_create_file(PFS_thread *thread, PFS_file_class *klass, search: entry= reinterpret_cast (lf_hash_search(&filename_hash, thread->m_filename_hash_pins, - filename, len)); + normalized_filename, normalized_length)); if (entry && (entry != MY_ERRPTR)) { pfs= *entry; @@ -783,9 +798,9 @@ search: if (pfs->m_lock.free_to_dirty()) { pfs->m_class= klass; - strncpy(pfs->m_filename, filename, len); - pfs->m_filename[len]= '\0'; - pfs->m_filename_length= len; + strncpy(pfs->m_filename, normalized_filename, normalized_length); + pfs->m_filename[normalized_length]= '\0'; + pfs->m_filename_length= normalized_length; pfs->m_file_stat.m_open_count= 1; pfs->m_wait_stat.m_control_flag= &flag_events_waits_summary_by_instance;