From: Jon Olav Hauglid Date: November 16 2011 4:21pm Subject: bzr push into mysql-trunk-wl5534 branch (jon.hauglid:3426 to 3427) List-Archive: http://lists.mysql.com/commits/141998 Message-Id: <201111161622.pAGGM3Hg013520@acsmt358.oracle.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3427 Jon Olav Hauglid 2011-11-16 [merge] Merge from mysql-trunk to mysql-trunk-wl5534 No conflicts removed: mysql-test/extra/rpl_tests/rpl_parallel_benchmark_load.test mysql-test/suite/rpl/t/rpl_parallel_benchmark-master.opt mysql-test/suite/rpl/t/rpl_parallel_benchmark-slave.opt mysql-test/suite/rpl/t/rpl_parallel_benchmark.test added: mysql-test/suite/rpl/r/rpl_row_find_row_debug.result mysql-test/suite/rpl/t/rpl_row_find_row_debug.test mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test modified: client/mysql_upgrade.c mysql-test/extra/rpl_tests/rpl_parallel_load.test mysql-test/r/mysql_upgrade.result mysql-test/r/mysqld--help-notwin.result mysql-test/r/mysqld--help-win.result mysql-test/t/mysql_upgrade.test sql/item_func.cc sql/log_event.cc sql/log_event.h sql/mdl.cc sql/mdl.h sql/rpl_rli.cc sql/rpl_rli.h sql/sql_class.h sql/sql_partition.cc sql/sql_plist.h sql/sys_vars.cc 3426 Dmitry Lenev 2011-11-16 WL#5534 Online ALTER, Phase 1. Patch #50: Review fix. Move check for potentially broken .FRMs to is_inplace_alter_impossible(). modified: sql/sql_table.cc === modified file 'client/mysql_upgrade.c' --- a/client/mysql_upgrade.c 2011-08-29 12:08:58 +0000 +++ b/client/mysql_upgrade.c 2011-11-15 12:34:23 +0000 @@ -286,6 +286,8 @@ get_one_option(int optid, const struct m case 'v': /* --verbose */ case 'f': /* --force */ + case 's': /* --upgrade-system-tables */ + case OPT_WRITE_BINLOG: /* --write-binlog */ add_option= FALSE; break; === removed file 'mysql-test/extra/rpl_tests/rpl_parallel_benchmark_load.test' --- a/mysql-test/extra/rpl_tests/rpl_parallel_benchmark_load.test 2011-08-19 13:04:28 +0000 +++ b/mysql-test/extra/rpl_tests/rpl_parallel_benchmark_load.test 1970-01-01 00:00:00 +0000 @@ -1,308 +0,0 @@ -# -# This is a load generator to call from rpl_parallel and rpl_sequential tests -# - -# -# load volume parameter -# - -let $iter= 02; -let $tables= 2; -let $wk_i_queries= 2; -let $wk_m_queries= 0; -let $nk_i_queries= 0; -let $nk_m_queries= 0; -let $pre_inserted_rows= 50; - -connection slave; - -call mtr.add_suppression('Slave: Error dropping database'); ## todo: fix - -source include/stop_slave.inc; -start slave; - -connection master; - ---disable_query_log ---disable_result_log - -use test; -delimiter |; -create procedure one_session(k int) -begin - while k > 0 do - insert into tm_nk values(k, 0); - insert into tm_wk values(null, 0); - insert into ti_nk values(k, 0); - insert into ti_wk values(null, 0); - set k = k - 1; - end while; -end| -delimiter ;| - -## let $i = $workers + 1; -##eval -# delimiter |; -# create procedure p1(i int) -# begin -# while i > 0 -# ##while ($i) -# ##{ -# ## let $i1=$i; -# ## dec $i1; -# ## use test$i1; -# ## call on_session(); -# ## dec $i; -# ##} -# use test0; -# call one_session(); -# use test1; -# call one_session(); -# use test2; -# call one_session(); -# use test3; -# call one_session(); -# i= i-1; -# end while; -# end| -# delimiter ;| - - -let $i = $databases; -while($i) -{ - eval create database test$i; - - let $m= $tables; - while ($m) - { - eval create table test$i.tm_nk_$m (a int, b int, c text) engine=myisam; - eval create table test$i.tm_wk_$m (a int auto_increment primary key, b int, c text) engine=myisam; - eval create table test$i.ti_nk_$m (a int, b int, c text) engine=innodb; - eval create table test$i.ti_wk_$m (a int auto_increment primary key, b int, c text) engine=innodb; - let $k= $pre_inserted_rows; - while ($k) - { - eval insert into test$i.ti_wk_$m values(null, $i, uuid()); - eval insert into test$i.ti_nk_$m values(null, $i, uuid()); - eval insert into test$i.tm_wk_$m values(null, $i, uuid()); - eval insert into test$i.tm_nk_$m values(null, $i, uuid()); - - dec $k; - } - dec $m; - } - - - # this table is special - just for timing. It's more special on test1 db - # where it contains master timing of the load as well. - eval create table test$i.benchmark (state text) engine=myisam; # timestamp keep on the slave side - - dec $i; -} - ---enable_result_log ---enable_query_log - - -sync_slave_with_master; -#connection slave; - ---disable_query_log ---disable_result_log - -let $i = $databases; -while($i) -{ - eval alter table test$i.benchmark add ts timestamp not null default current_timestamp; - - dec $i; -} ---enable_result_log ---enable_query_log - - -# not gather events into relay log w/o executing yet -stop slave sql_thread; - -##call p1(1); - -connection master; - ---disable_query_log ---disable_result_log - -# -# Load producer -# - -# initial timestamp to record - -# the extra ts col on slave is effective only with the STMT format (todo: bug-report) -set @save.binlog_format= @@session.binlog_format; -set @@session.binlog_format=STATEMENT; -let $i = $databases; -while($i) -{ - eval insert into test$i.benchmark set state='slave takes on load'; - - dec $i; -} -set @@session.binlog_format= @save.binlog_format; - -connection slave; - -insert into test1.benchmark set state='master started load'; - - -connection master; - -while ($iter) -{ - let $i = $databases; - - while ($i) - { - - begin; - ###eval insert into tm_nk values($iter, $i1, repeat('a', round(rand()*10))); - - let $q= $wk_m_queries; - while ($q) - { - let $m= `select 1 + floor(rand() * $tables)`; - eval update test$i.tm_wk_$m set c= uuid(); - eval insert into test$i.tm_wk_$m values(null, $i, uuid()); - - dec $q; - } - - let $q= $wk_i_queries; - while ($q) - { - let $m= `select 1 + floor(rand() * $tables)`; - eval update test$i.ti_wk_$m set c= uuid(); - - eval insert into test$i.ti_wk_$m values(null, $i, uuid()); - - dec $q; - } - - # NK - - let $q= $nk_m_queries; - while ($q) - { - let $m= `select 1 + floor(rand() * $tables)`; - eval update test$i.tm_nk_$m set c= uuid(); - eval insert into test$i.tm_nk_$m values(null, $i, uuid()); - - dec $q; - } - - let $q= $nk_i_queries; - while ($q) - { - let $m= `select 1 + floor(rand() * $tables)`; - eval update test$i.ti_nk_$m set c= uuid(); - eval insert into test$i.ti_nk_$m values(null, $i, uuid()); - - dec $q; - } - commit; - - dec $i; - } - - dec $iter; -} - -connection slave; - -##use test1; -insert into test1.benchmark set state='master ends load'; - -connection master; - -# terminal timestamp to record - -let $i = $databases; -set @save.binlog_format= @@session.binlog_format; -set @@session.binlog_format=STATEMENT; -while($i) -{ - eval insert into test$i.benchmark set state='slave ends load'; - - dec $i; -} -set @@session.binlog_format= @save.binlog_format; - ---enable_result_log ---enable_query_log - -connection slave; - -## todo: record start and end time of appying to compare times of -# parallel and sequential execution. - ---disable_query_log ---disable_result_log - -insert into test1.benchmark set state='slave is processing load'; - -# To force filling timestamp cols with the slave local clock values -# to implement benchmarking. - -# TODO: replace with another alg -#set @save.mts_exp_slave_local_timestamp=@@global.mts_exp_slave_local_timestamp; -#set @@global.mts_exp_slave_local_timestamp=1; - ---sleep 1 - -start slave sql_thread; - -let $wait_timeout= 600; -let $wait_condition= SELECT count(*)+sleep(1) = 5 FROM test1.benchmark; -source include/wait_condition.inc; - -# cleanup for files that could not be removed in the end of previous invocation. -let $MYSQLD_DATADIR= `select @@datadir`; ---remove_files_wildcard $MYSQLD_DATADIR *.out - -use test; -let $benchmark_file= `select replace(concat("benchmark_",uuid(),".out"),"-","_")`; ---replace_regex /benchmark_.*.out/benchmark.out/ -eval select * from test1.benchmark into outfile '$benchmark_file'; -select ts from test1.benchmark where state like 'master started load' into @m_0; -select ts from test1.benchmark where state like 'master ends load' into @m_1; -select ts from test1.benchmark where state like 'slave takes on load' into @s_0; -select ts from test1.benchmark where state like 'slave ends load' into @s_1; -let $delta_file= `select replace(concat("delta_",uuid(),".out"),"-","_")`; ---replace_regex /delta_.*.out/delta.out/ -eval select time_to_sec(@m_1) - time_to_sec(@m_0) as 'delta_m', - time_to_sec(@s_1) - time_to_sec(@s_0) as 'delta_s' into outfile '$delta_file'; - ---enable_result_log ---enable_query_log - - -connection master; - ---disable_query_log ---disable_result_log - -let $i = $databases; -while($i) -{ - eval drop database test$i; - - dec $i; -} - -use test; -drop procedure one_session; - ---enable_result_log ---enable_query_log - -sync_slave_with_master; === modified file 'mysql-test/extra/rpl_tests/rpl_parallel_load.test' --- a/mysql-test/extra/rpl_tests/rpl_parallel_load.test 2011-10-31 13:52:32 +0000 +++ b/mysql-test/extra/rpl_tests/rpl_parallel_load.test 2011-11-14 15:44:42 +0000 @@ -6,16 +6,9 @@ # load volume parameter # -let $iter = 50; - -# windows run on PB2 is too slow to time out -disable_query_log; -if (`select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Windows") as "TRUE"`) -{ - let $iter = 30; -} -enable_query_log; - +# The value of iteration is chosen to be rather low to satisfy +# slow PB environments that are not just Win. +let $iter = 20; let $databases = 16; connection slave; === modified file 'mysql-test/r/mysql_upgrade.result' --- a/mysql-test/r/mysql_upgrade.result 2011-08-19 13:04:28 +0000 +++ b/mysql-test/r/mysql_upgrade.result 2011-11-15 12:34:23 +0000 @@ -224,3 +224,41 @@ GRANT ALL PRIVILEGES ON `roelt`.`test2` DROP USER 'user3'@'%'; End of 5.1 tests The --upgrade-system-tables option was used, databases won't be touched. +# +# Bug#11827359 60223: MYSQL_UPGRADE PROBLEM WITH OPTION +# SKIP-WRITE-BINLOG +# +# Droping the previously created mysql_upgrade_info file.. +# Running mysql_upgrade with --skip-write-binlog.. +mtr.global_suppressions OK +mtr.test_suppressions OK +mysql.columns_priv OK +mysql.db OK +mysql.event OK +mysql.func OK +mysql.general_log OK +mysql.help_category OK +mysql.help_keyword OK +mysql.help_relation OK +mysql.help_topic OK +mysql.host OK +mysql.innodb_index_stats OK +mysql.innodb_table_stats OK +mysql.ndb_binlog_index OK +mysql.plugin OK +mysql.proc OK +mysql.procs_priv OK +mysql.proxies_priv OK +mysql.servers OK +mysql.slave_master_info OK +mysql.slave_relay_log_info OK +mysql.slave_worker_info OK +mysql.slow_log OK +mysql.tables_priv OK +mysql.time_zone OK +mysql.time_zone_leap_second OK +mysql.time_zone_name OK +mysql.time_zone_transition OK +mysql.time_zone_transition_type OK +mysql.user OK +End of tests === modified file 'mysql-test/r/mysqld--help-notwin.result' --- a/mysql-test/r/mysqld--help-notwin.result 2011-11-11 12:36:01 +0000 +++ b/mysql-test/r/mysqld--help-notwin.result 2011-11-15 18:51:44 +0000 @@ -353,6 +353,8 @@ The following options may be given as th After this many write locks, allow some read locks to run in between --memlock Lock mysqld in memory. + --metadata-locks-cache-size=# + Size of unused metadata locks cache --min-examined-row-limit=# Don't write queries to slow log that examine fewer rows than that @@ -929,6 +931,7 @@ max-tmp-tables 32 max-user-connections 0 max-write-lock-count 18446744073709551615 memlock FALSE +metadata-locks-cache-size 1024 min-examined-row-limit 0 multi-range-count 256 myisam-block-size 1024 === modified file 'mysql-test/r/mysqld--help-win.result' --- a/mysql-test/r/mysqld--help-win.result 2011-11-11 12:36:01 +0000 +++ b/mysql-test/r/mysqld--help-win.result 2011-11-15 18:51:44 +0000 @@ -352,6 +352,8 @@ The following options may be given as th After this many write locks, allow some read locks to run in between --memlock Lock mysqld in memory. + --metadata-locks-cache-size=# + Size of unused metadata locks cache --min-examined-row-limit=# Don't write queries to slow log that examine fewer rows than that @@ -932,6 +934,7 @@ max-tmp-tables 32 max-user-connections 0 max-write-lock-count 18446744073709551615 memlock FALSE +metadata-locks-cache-size 1024 min-examined-row-limit 0 multi-range-count 256 myisam-block-size 1024 === added file 'mysql-test/suite/rpl/r/rpl_row_find_row_debug.result' --- a/mysql-test/suite/rpl/r/rpl_row_find_row_debug.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/rpl/r/rpl_row_find_row_debug.result 2011-11-11 17:26:56 +0000 @@ -0,0 +1,18 @@ +include/master-slave.inc +[connection master] +include/stop_slave.inc +SET GLOBAL log_warnings = 2; +SET GLOBAL debug="d,inject_long_find_row_note"; +include/start_slave.inc +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 VALUES (1), (2); +UPDATE t1 SET c1= 1000 WHERE c1=2; +DELETE FROM t1; +DROP TABLE t1; +# Check if any note related to long DELETE_ROWS and UPDATE_ROWS appears in the error log +Occurrences: update=1, delete=1 +include/stop_slave.inc +SET GLOBAL debug = ''; +SET GLOBAL log_warnings = 1; +include/start_slave.inc +include/rpl_end.inc === removed file 'mysql-test/suite/rpl/t/rpl_parallel_benchmark-master.opt' --- a/mysql-test/suite/rpl/t/rpl_parallel_benchmark-master.opt 2011-08-19 13:04:28 +0000 +++ b/mysql-test/suite/rpl/t/rpl_parallel_benchmark-master.opt 1970-01-01 00:00:00 +0000 @@ -1 +0,0 @@ ---log-warnings=0 === removed file 'mysql-test/suite/rpl/t/rpl_parallel_benchmark-slave.opt' --- a/mysql-test/suite/rpl/t/rpl_parallel_benchmark-slave.opt 2011-08-19 13:04:28 +0000 +++ b/mysql-test/suite/rpl/t/rpl_parallel_benchmark-slave.opt 1970-01-01 00:00:00 +0000 @@ -1 +0,0 @@ ---log-warnings=0 --slave-transaction-retries=0 === removed file 'mysql-test/suite/rpl/t/rpl_parallel_benchmark.test' --- a/mysql-test/suite/rpl/t/rpl_parallel_benchmark.test 2011-08-19 13:04:28 +0000 +++ b/mysql-test/suite/rpl/t/rpl_parallel_benchmark.test 1970-01-01 00:00:00 +0000 @@ -1,29 +0,0 @@ -# -# WL#5569 MTS -# -# The test is similar to rpl_parallel but focuses more to -# provide benchmarking data. -# See rpl_parallel.test comments and rpl_parallel_benchmark_load for -# how to vary running configuration. -# - ---source include/master-slave.inc - -connection slave; -set @save.slave_parallel_workers= @@global.slave_parallel_workers; -###select @@global.slave_parallel_workers as 'non-zero means parallel'; - -let $workers = `select @@global.slave_parallel_workers`; -###let $databases= $workers; -# workers vary db:s do not -let $databases= 16; - -connection master; -source extra/rpl_tests/rpl_parallel_benchmark_load.test; - -connection slave; ---disable_query_log -set @@global.slave_parallel_workers= @save.slave_parallel_workers; ---enable_result_log - ---source include/rpl_end.inc === added file 'mysql-test/suite/rpl/t/rpl_row_find_row_debug.test' --- a/mysql-test/suite/rpl/t/rpl_row_find_row_debug.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/rpl/t/rpl_row_find_row_debug.test 2011-11-15 13:04:11 +0000 @@ -0,0 +1,64 @@ +# +# Bug#11760927: 53375: RBR + NO PK => HIGH LOAD ON SLAVE (TABLE SCAN/CPU) => SLAVE FAILURE +# +--disable_warnings +--source include/master-slave.inc +--enable_warnings +--source include/have_binlog_format_row.inc +--source include/have_debug.inc + +# SETUP +# - setup log_warnings and debug +--connection slave +--source include/stop_slave.inc +--let $debug_save= `SELECT @@GLOBAL.debug` +--let $log_warnings_save= `SELECT @@GLOBAL.log_warnings` + +SET GLOBAL log_warnings = 2; + +let $log_error_= `SELECT @@GLOBAL.log_error`; +if(!$log_error_) +{ + # MySQL Server on windows is started with --console and thus + # does not know the location of its .err log, use default location + let $log_error_ = $MYSQLTEST_VARDIR/log/mysqld.2.err; +} + +# Assign env variable LOG_ERROR +let LOG_ERROR=$log_error_; + +# force printing the notes to the error log +SET GLOBAL debug="d,inject_long_find_row_note"; +--source include/start_slave.inc + +# test +--connection master +CREATE TABLE t1 (c1 INT); +--sync_slave_with_master +--connection master + +INSERT INTO t1 VALUES (1), (2); +UPDATE t1 SET c1= 1000 WHERE c1=2; +DELETE FROM t1; +DROP TABLE t1; +--sync_slave_with_master + +--echo # Check if any note related to long DELETE_ROWS and UPDATE_ROWS appears in the error log +perl; + use strict; + my $log_error= $ENV{'LOG_ERROR'} or die "LOG_ERROR not set"; + open(FILE, "$log_error") or die("Unable to open $log_error: $!\n"); + my $upd_count = () = grep(/The slave is applying a ROW event on behalf of an UPDATE statement on table t1 and is currently taking a considerable amount/g,); + seek(FILE, 0, 0) or die "Can't seek to beginning of file: $!"; + my $del_count = () = grep(/The slave is applying a ROW event on behalf of a DELETE statement on table t1 and is currently taking a considerable amount/g,); + print "Occurrences: update=$upd_count, delete=$del_count\n"; + close(FILE); +EOF + +# cleanup +--source include/stop_slave.inc +--eval SET GLOBAL debug = '$debug_save' +--eval SET GLOBAL log_warnings = $log_warnings_save +--source include/start_slave.inc + +--source include/rpl_end.inc === added file 'mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result' --- a/mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result 2011-11-15 18:00:14 +0000 @@ -0,0 +1,21 @@ +# +# Check that the paremeter is correctly set by start-up +# option (.opt file sets it to 256 while default is 1024). +select @@global.metadata_locks_cache_size = 256; +@@global.metadata_locks_cache_size = 256 +1 +# +# Check that variable is read only +# +set @@global.metadata_locks_cache_size= 1024; +ERROR HY000: Variable 'metadata_locks_cache_size' is a read only variable +select @@global.metadata_locks_cache_size = 256; +@@global.metadata_locks_cache_size = 256 +1 +# +# And only GLOBAL +# +select @@session.metadata_locks_cache_size; +ERROR HY000: Variable 'metadata_locks_cache_size' is a GLOBAL variable +set @@session.metadata_locks_cache_size= 1024; +ERROR HY000: Variable 'metadata_locks_cache_size' is a read only variable === added file 'mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt' --- a/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt 2011-11-15 18:00:14 +0000 @@ -0,0 +1 @@ +--metadata-locks-cache-size=256 === added file 'mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test' --- a/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test 2011-11-15 18:00:14 +0000 @@ -0,0 +1,25 @@ +# +# Basic test coverage for --metadata-locks-cache-size startup +# parameter and corresponding read-only global @@metadata_locks_cache_size +# variable. +# + +--echo # +--echo # Check that the paremeter is correctly set by start-up +--echo # option (.opt file sets it to 256 while default is 1024). +select @@global.metadata_locks_cache_size = 256; + +--echo # +--echo # Check that variable is read only +--echo # +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set @@global.metadata_locks_cache_size= 1024; +select @@global.metadata_locks_cache_size = 256; + +--echo # +--echo # And only GLOBAL +--echo # +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.metadata_locks_cache_size; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set @@session.metadata_locks_cache_size= 1024; === modified file 'mysql-test/t/mysql_upgrade.test' --- a/mysql-test/t/mysql_upgrade.test 2010-08-11 17:56:56 +0000 +++ b/mysql-test/t/mysql_upgrade.test 2011-11-15 12:18:42 +0000 @@ -102,5 +102,24 @@ DROP USER 'user3'@'%'; # Test the --upgrade-system-tables option # --replace_result $MYSQLTEST_VARDIR var ---exec $MYSQL_UPGRADE --skip-verbose --upgrade-system-tables +--exec $MYSQL_UPGRADE --skip-verbose --force --upgrade-system-tables +--echo # +--echo # Bug#11827359 60223: MYSQL_UPGRADE PROBLEM WITH OPTION +--echo # SKIP-WRITE-BINLOG +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; + +--echo # Droping the previously created mysql_upgrade_info file.. +--remove_file $MYSQLD_DATADIR/mysql_upgrade_info + +--echo # Running mysql_upgrade with --skip-write-binlog.. +--replace_result $MYSQLTEST_VARDIR var +--exec $MYSQL_UPGRADE --skip-verbose --skip-write-binlog + +# mysql_upgrade must have created mysql_upgrade_info file, +# so the following command should never fail. +--remove_file $MYSQLD_DATADIR/mysql_upgrade_info + +--echo End of tests === modified file 'sql/item_func.cc' --- a/sql/item_func.cc 2011-10-13 07:26:28 +0000 +++ b/sql/item_func.cc 2011-11-15 09:24:13 +0000 @@ -2352,25 +2352,31 @@ double my_double_round(double value, lon /* tmp2 is here to avoid return the value with 80 bit precision This will fix that the test round(0.1,1) = round(0.1,1) is true + Tagging with volatile is no guarantee, it may still be optimized away... */ volatile double tmp2; tmp=(abs_dec < array_elements(log_10) ? log_10[abs_dec] : pow(10.0,(double) abs_dec)); + // Pre-compute these, to avoid optimizing away e.g. 'floor(v/tmp) * tmp'. + volatile double value_div_tmp= value / tmp; + volatile double value_mul_tmp= value * tmp; + if (dec_negative && my_isinf(tmp)) - tmp2= 0; - else if (!dec_negative && my_isinf(value * tmp)) + tmp2= 0.0; + else if (!dec_negative && my_isinf(value_mul_tmp)) tmp2= value; else if (truncate) { - if (value >= 0) - tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp; + if (value >= 0.0) + tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp; else - tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp; + tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp; } else - tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp; + tmp2=dec < 0 ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp; + return tmp2; } === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2011-11-10 06:53:27 +0000 +++ b/sql/log_event.cc 2011-11-15 14:40:04 +0000 @@ -8872,6 +8872,12 @@ int Rows_log_event::do_apply_event(Relay // row processing loop + /* + set the initial time of this ROWS statement if it was not done + before in some other ROWS event. + */ + const_cast(rli)->set_row_stmt_start_timestamp(); + const uchar *saved_m_curr_row= m_curr_row; while (error == 0) { @@ -10610,6 +10616,50 @@ search_key_in_table(TABLE *table, MY_BIT return res; } +/* + Check if we are already spending too much time on this statement. + if we are, warn user that it might be because table does not have + a PK, but only if the warning was not printed before for this STMT. + + @param type The event type code. + @param table_name The name of the table that the slave is + operating. + @param is_index_scan States whether the slave is doing an index scan + or not. + @param rli The relay metadata info. +*/ +static inline +void issue_long_find_row_warning(Log_event_type type, + const char *table_name, + bool is_index_scan, + const Relay_log_info *rli) +{ + if ((log_warnings > 1 && + !const_cast(rli)->is_long_find_row_note_printed())) + { + time_t now= my_time(0); + time_t stmt_ts= const_cast(rli)->get_row_stmt_start_timestamp(); + + DBUG_EXECUTE_IF("inject_long_find_row_note", + stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2);); + + long delta= (long) (now - stmt_ts); + + if (delta > LONG_FIND_ROW_THRESHOLD) + { + const_cast(rli)->set_long_find_row_note_printed(); + const char* evt_type= type == DELETE_ROWS_EVENT ? " DELETE" : "n UPDATE"; + const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table"; + + sql_print_information("The slave is applying a ROW event on behalf of a%s statement " + "on table %s and is currently taking a considerable amount " + "of time (%ld seconds). This is due to the fact that it is %s " + "while looking up records to be processed. Consider adding a " + "primary key (or unique key) to the table to improve " + "performance.", evt_type, table_name, delta, scan_type); + } + } +} /** Locate the current row in event's table. @@ -10649,6 +10699,7 @@ int Rows_log_event::find_row(const Relay int error= 0; KEY *keyinfo; uint key; + bool is_table_scan= false, is_index_scan= false; /* rpl_row_tabledefs.test specifies that @@ -10838,6 +10889,8 @@ INDEX_SCAN: } } + is_index_scan=true; + /* In case key is not unique, we still have to iterate over records found and find the one which is identical to the row given. A copy of the @@ -10898,6 +10951,8 @@ TABLE_SCAN: goto err; } + is_table_scan= true; + /* Continue until we find the right record or have made a full loop */ do { @@ -10951,10 +11006,18 @@ TABLE_SCAN: goto err; } ok: + if (is_table_scan || is_index_scan) + issue_long_find_row_warning(get_type_code(), m_table->alias, + is_index_scan, rli); + table->default_column_bitmaps(); DBUG_RETURN(0); err: + if (is_table_scan || is_index_scan) + issue_long_find_row_warning(get_type_code(), m_table->alias, + is_index_scan, rli); + table->default_column_bitmaps(); DBUG_RETURN(error); } === modified file 'sql/log_event.h' --- a/sql/log_event.h 2011-08-29 12:08:58 +0000 +++ b/sql/log_event.h 2011-11-15 13:04:11 +0000 @@ -51,6 +51,7 @@ typedef ulonglong sql_mode_t; typedef struct st_db_worker_hash_entry db_worker_hash_entry; #define PREFIX_SQL_LOAD "SQL_LOAD-" +#define LONG_FIND_ROW_THRESHOLD 60 /* seconds */ /** Either assert or return an error. === modified file 'sql/mdl.cc' --- a/sql/mdl.cc 2011-11-14 15:41:08 +0000 +++ b/sql/mdl.cc 2011-11-16 16:21:20 +0000 @@ -107,6 +107,10 @@ void MDL_key::init_psi_keys() static bool mdl_initialized= 0; +class MDL_object_lock; +class MDL_object_lock_cache_adapter; + + /** A collection of all MDL locks. A singleton, there is only one instance of the map in the server. @@ -127,6 +131,25 @@ private: HASH m_locks; /* Protects access to m_locks hash. */ mysql_mutex_t m_mutex; + /** + Cache of (unused) MDL_lock objects available for re-use. + + On some systems (e.g. Windows XP) constructing/destructing + MDL_lock objects can be fairly expensive. We use this cache + to avoid these costs in scenarios in which they can have + significant negative effect on performance. For example, when + there is only one thread constantly executing statements in + auto-commit mode and thus constantly causing creation/ + destruction of MDL_lock objects for the tables it uses. + + Note that this cache contains only MDL_object_lock objects. + + Protected by m_mutex mutex. + */ + typedef I_P_List + Lock_cache; + Lock_cache m_unused_locks_cache; /** Pre-allocated MDL_lock object for GLOBAL namespace. */ MDL_lock *m_global_lock; /** Pre-allocated MDL_lock object for COMMIT namespace. */ @@ -395,7 +418,8 @@ public: : key(key_arg), m_ref_usage(0), m_ref_release(0), - m_is_destroyed(FALSE) + m_is_destroyed(FALSE), + m_version(0) { mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock); } @@ -430,6 +454,22 @@ public: uint m_ref_usage; uint m_ref_release; bool m_is_destroyed; + /** + We use the same idea and an additional version counter to support + caching of unused MDL_lock object for further re-use. + This counter is incremented while holding both MDL_map::m_mutex and + MDL_lock::m_rwlock locks each time when a MDL_lock is moved from + the hash to the unused objects list (or destroyed). + A thread, which has found a MDL_lock object for the key in the hash + and then released the MDL_map::m_mutex before acquiring the + MDL_lock::m_rwlock, can determine that this object was moved to the + unused objects list (or destroyed) while it held no locks by comparing + the version value which it read while holding the MDL_map::m_mutex + with the value read after acquiring the MDL_lock::m_rwlock. + Note that since it takes several years to overflow this counter such + theoretically possible overflows should not have any practical effects. + */ + ulonglong m_version; }; @@ -478,6 +518,26 @@ public: : MDL_lock(key_arg) { } + /** + Reset unused MDL_object_lock object to represent the lock context for a + different object. + */ + void reset(const MDL_key *new_key) + { + /* We need to change only object's key. */ + key.mdl_key_init(new_key); + /* m_granted and m_waiting should be already in the empty/initial state. */ + DBUG_ASSERT(is_empty()); + /* Object should not be marked as destroyed. */ + DBUG_ASSERT(! m_is_destroyed); + /* + Values of the rest of the fields should be preserved between old and + new versions of the object. E.g., m_version and m_ref_usage/release + should be kept intact to properly handle possible remaining references + to the old version of the object. + */ + } + virtual const bitmap_t *incompatible_granted_types_bitmap() const { return m_granted_incompatible; @@ -495,10 +555,29 @@ public: private: static const bitmap_t m_granted_incompatible[MDL_TYPE_END]; static const bitmap_t m_waiting_incompatible[MDL_TYPE_END]; + +public: + /** Members for linking the object into the list of unused objects. */ + MDL_object_lock *next_in_cache, **prev_in_cache; +}; + + +/** + Helper class for linking MDL_object_lock objects into the unused objects list. +*/ +class MDL_object_lock_cache_adapter : + public I_P_List_adapter +{ }; static MDL_map mdl_locks; +/** + Start-up parameter for the maximum size of the unused MDL_lock objects cache. +*/ +ulong mdl_locks_cache_size; + extern "C" { @@ -581,6 +660,10 @@ void MDL_map::destroy() my_hash_free(&m_locks); MDL_lock::destroy(m_global_lock); MDL_lock::destroy(m_commit_lock); + + MDL_object_lock *lock; + while ((lock= m_unused_locks_cache.pop_front())) + MDL_lock::destroy(lock); } @@ -630,11 +713,49 @@ retry: mdl_key->ptr(), mdl_key->length()))) { - lock= MDL_lock::create(mdl_key); + MDL_object_lock *unused_lock= NULL; + + /* + No lock object found so we need to create a new one + or reuse an existing unused object. + */ + if (mdl_key->mdl_namespace() != MDL_key::SCHEMA && + m_unused_locks_cache.elements()) + { + /* + We need a MDL_object_lock type of object and the unused objects + cache has some. Get the first object from the cache and set a new + key for it. + */ + DBUG_ASSERT(mdl_key->mdl_namespace() != MDL_key::GLOBAL && + mdl_key->mdl_namespace() != MDL_key::COMMIT); + + unused_lock= m_unused_locks_cache.pop_front(); + unused_lock->reset(mdl_key); + + lock= unused_lock; + } + else + { + lock= MDL_lock::create(mdl_key); + } + if (!lock || my_hash_insert(&m_locks, (uchar*)lock)) { + if (unused_lock) + { + /* + Note that we can't easily destroy an object from cache here as it + still might be referenced by other threads. So we simply put it + back into the cache. + */ + m_unused_locks_cache.push_front(unused_lock); + } + else + { + MDL_lock::destroy(lock); + } mysql_mutex_unlock(&m_mutex); - MDL_lock::destroy(lock); return NULL; } } @@ -649,7 +770,7 @@ retry: /** Release mdl_locks.m_mutex mutex and lock MDL_lock::m_rwlock for lock object from the hash. Handle situation when object was released - while the held no mutex. + while we held no locks. @retval FALSE - Success. @retval TRUE - Object was released while we held no mutex, caller @@ -658,6 +779,8 @@ retry: bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock) { + ulonglong version; + DBUG_ASSERT(! lock->m_is_destroyed); mysql_mutex_assert_owner(&m_mutex); @@ -667,26 +790,50 @@ bool MDL_map::move_from_hash_to_lock_mut m_is_destroyed is FALSE. */ lock->m_ref_usage++; + /* Read value of the version counter under protection of m_mutex lock. */ + version= lock->m_version; mysql_mutex_unlock(&m_mutex); mysql_prlock_wrlock(&lock->m_rwlock); lock->m_ref_release++; - if (unlikely(lock->m_is_destroyed)) + + if (unlikely(lock->m_version != version)) { /* - Object was released while we held no mutex, we need to - release it if no others hold references to it, while our own - reference count ensured that the object as such haven't got - its memory released yet. We can also safely compare - m_ref_usage and m_ref_release since the object is no longer - present in the hash so no one will be able to find it and - increment m_ref_usage anymore. + If the current value of version differs from one that was read while + we held m_mutex mutex, this MDL_lock object was moved to the unused + objects list or destroyed while we held no locks. + We should retry our search. But first we should destroy the MDL_lock + object if necessary. */ - uint ref_usage= lock->m_ref_usage; - uint ref_release= lock->m_ref_release; - mysql_prlock_unlock(&lock->m_rwlock); - if (ref_usage == ref_release) - MDL_lock::destroy(lock); + if (unlikely(lock->m_is_destroyed)) + { + /* + Object was released while we held no locks, we need to + release it if no others hold references to it, while our own + reference count ensured that the object as such haven't got + its memory released yet. We can also safely compare + m_ref_usage and m_ref_release since the object is no longer + present in the hash (or unused objects list) so no one will + be able to find it and increment m_ref_usage anymore. + */ + uint ref_usage= lock->m_ref_usage; + uint ref_release= lock->m_ref_release; + mysql_prlock_unlock(&lock->m_rwlock); + if (ref_usage == ref_release) + MDL_lock::destroy(lock); + } + else + { + /* + Object was not destroyed but its version has changed. + This means that it was moved to the unused objects list + (and even might be already re-used). So now it might + correspond to a different key, therefore we should simply + retry our search. + */ + mysql_prlock_unlock(&lock->m_rwlock); + } return TRUE; } return FALSE; @@ -701,8 +848,6 @@ bool MDL_map::move_from_hash_to_lock_mut void MDL_map::remove(MDL_lock *lock) { - uint ref_usage, ref_release; - if (lock->key.mdl_namespace() == MDL_key::GLOBAL || lock->key.mdl_namespace() == MDL_key::COMMIT) { @@ -714,31 +859,65 @@ void MDL_map::remove(MDL_lock *lock) return; } - /* - Destroy the MDL_lock object, but ensure that anyone that is - holding a reference to the object is not remaining, if so he - has the responsibility to release it. - - Setting of m_is_destroyed to TRUE while holding _both_ - mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the - protection of m_ref_usage from mdl_locks.m_mutex to - MDL_lock::m_rwlock while removal of object from the hash makes - it read-only. Therefore whoever acquires MDL_lock::m_rwlock next - will see most up to date version of m_ref_usage. - - This means that when m_is_destroyed is TRUE and we hold the - MDL_lock::m_rwlock we can safely read the m_ref_usage - member. - */ mysql_mutex_lock(&m_mutex); my_hash_delete(&m_locks, (uchar*) lock); - lock->m_is_destroyed= TRUE; - ref_usage= lock->m_ref_usage; - ref_release= lock->m_ref_release; - mysql_prlock_unlock(&lock->m_rwlock); - mysql_mutex_unlock(&m_mutex); - if (ref_usage == ref_release) - MDL_lock::destroy(lock); + /* + To let threads holding references to the MDL_lock object know that it was + moved to the list of unused objects or destroyed, we increment the version + counter under protection of both MDL_map::m_mutex and MDL_lock::m_rwlock + locks. This allows us to read the version value while having either one + of those locks. + */ + lock->m_version++; + + if ((lock->key.mdl_namespace() != MDL_key::SCHEMA) && + (m_unused_locks_cache.elements() < mdl_locks_cache_size)) + { + /* + This is an object of MDL_object_lock type and the cache of unused + objects has not reached its maximum size yet. So instead of destroying + object we move it to the list of unused objects to allow its later + re-use with possibly different key. Any threads holding references to + this object (owning MDL_map::m_mutex or MDL_lock::m_rwlock) will notice + this thanks to the fact that we have changed the MDL_lock::m_version + counter. + */ + DBUG_ASSERT(lock->key.mdl_namespace() != MDL_key::GLOBAL && + lock->key.mdl_namespace() != MDL_key::COMMIT); + + m_unused_locks_cache.push_front((MDL_object_lock*)lock); + mysql_mutex_unlock(&m_mutex); + mysql_prlock_unlock(&lock->m_rwlock); + } + else + { + /* + Destroy the MDL_lock object, but ensure that anyone that is + holding a reference to the object is not remaining, if so he + has the responsibility to release it. + + Setting of m_is_destroyed to TRUE while holding _both_ + mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the + protection of m_ref_usage from mdl_locks.m_mutex to + MDL_lock::m_rwlock while removal of the object from the hash + (and cache of unused objects) makes it read-only. Therefore + whoever acquires MDL_lock::m_rwlock next will see the most up + to date version of m_ref_usage. + + This means that when m_is_destroyed is TRUE and we hold the + MDL_lock::m_rwlock we can safely read the m_ref_usage + member. + */ + uint ref_usage, ref_release; + + lock->m_is_destroyed= TRUE; + ref_usage= lock->m_ref_usage; + ref_release= lock->m_ref_release; + mysql_mutex_unlock(&m_mutex); + mysql_prlock_unlock(&lock->m_rwlock); + if (ref_usage == ref_release) + MDL_lock::destroy(lock); + } } @@ -837,9 +1016,6 @@ void MDL_request::init(const MDL_key *ke Auxiliary functions needed for creation/destruction of MDL_lock objects. @note Also chooses an MDL_lock descendant appropriate for object namespace. - - @todo This naive implementation should be replaced with one that saves - on memory allocation by reusing released objects. */ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key) === modified file 'sql/mdl.h' --- a/sql/mdl.h 2011-11-14 15:41:08 +0000 +++ b/sql/mdl.h 2011-11-16 16:21:20 +0000 @@ -941,4 +941,12 @@ void mdl_destroy(); extern mysql_mutex_t LOCK_open; #endif + +/* + Start-up parameter for the maximum size of the unused MDL_lock objects cache + and a constant for its default value. +*/ +extern ulong mdl_locks_cache_size; +static const ulong MDL_LOCKS_CACHE_SIZE_DEFAULT = 1024; + #endif === modified file 'sql/rpl_rli.cc' --- a/sql/rpl_rli.cc 2011-10-28 12:45:35 +0000 +++ b/sql/rpl_rli.cc 2011-11-16 01:19:40 +0000 @@ -85,7 +85,8 @@ Relay_log_info::Relay_log_info(bool is_s checkpoint_group(mts_checkpoint_group), mts_recovery_group_cnt(0), mts_recovery_index(0), mts_recovery_group_seen_begin(0), mts_group_status(MTS_NOT_IN_GROUP), reported_unsafe_warning(false), - sql_delay(0), sql_delay_end(0), m_flags(0) + sql_delay(0), sql_delay_end(0), m_flags(0), row_stmt_start_timestamp(0), + long_find_row_note_printed(false) { DBUG_ENTER("Relay_log_info::Relay_log_info"); @@ -1142,6 +1143,15 @@ void Relay_log_info::cleanup_context(THD */ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS; thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS; + + /* + Reset state related to long_find_row notes in the error log: + - timestamp + - flag that decides whether the slave prints or not + */ + reset_row_stmt_start_timestamp(); + unset_long_find_row_note_printed(); + DBUG_VOID_RETURN; } === modified file 'sql/rpl_rli.h' --- a/sql/rpl_rli.h 2011-10-13 14:01:50 +0000 +++ b/sql/rpl_rli.h 2011-11-15 13:04:11 +0000 @@ -758,6 +758,40 @@ public: with several warning messages. */ bool reported_unsafe_warning; + + time_t get_row_stmt_start_timestamp() + { + return row_stmt_start_timestamp; + } + + time_t set_row_stmt_start_timestamp() + { + if (row_stmt_start_timestamp == 0) + row_stmt_start_timestamp= my_time(0); + + return row_stmt_start_timestamp; + } + + void reset_row_stmt_start_timestamp() + { + row_stmt_start_timestamp= 0; + } + + void set_long_find_row_note_printed() + { + long_find_row_note_printed= true; + } + + void unset_long_find_row_note_printed() + { + long_find_row_note_printed= false; + } + + bool is_long_find_row_note_printed() + { + return long_find_row_note_printed; + } + private: /** Delay slave SQL thread by this amount, compared to master (in @@ -799,6 +833,13 @@ private: Relay_log_info(const Relay_log_info& info); Relay_log_info& operator=(const Relay_log_info& info); + + /* + Runtime state for printing a note when slave is taking + too long while processing a row event. + */ + time_t row_stmt_start_timestamp; + bool long_find_row_note_printed; }; bool mysql_show_relaylog_events(THD* thd); === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2011-11-14 15:41:08 +0000 +++ b/sql/sql_class.h 2011-11-16 16:21:20 +0000 @@ -2361,16 +2361,16 @@ public: /** Current statement instrumentation. */ PSI_statement_locker *m_statement_psi; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_PSI_STATEMENT_INTERFACE /** Current statement instrumentation state. */ PSI_statement_locker_state m_statement_state; -#endif /* EMBEDDED_LIBRARY */ +#endif /* HAVE_PSI_STATEMENT_INTERFACE */ /** Idle instrumentation. */ PSI_idle_locker *m_idle_psi; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_PSI_IDLE_INTERFACE /** Idle instrumentation state. */ PSI_idle_locker_state m_idle_state; -#endif /* EMBEDDED_LIBRARY */ +#endif /* HAVE_PSI_IDLE_INTERFACE */ /** True if the server code is IDLE for this connection. */ bool m_server_idle; === modified file 'sql/sql_partition.cc' --- a/sql/sql_partition.cc 2011-11-16 12:27:09 +0000 +++ b/sql/sql_partition.cc 2011-11-16 16:21:20 +0000 @@ -6470,8 +6470,24 @@ static void alter_partition_lock_handlin lpt->table= 0; lpt->old_table= 0; lpt->table_list->table= 0; - if (thd->locked_tables_list.reopen_tables(thd)) - sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + if (thd->locked_tables_mode) + { + Diagnostics_area *stmt_da= NULL; + Diagnostics_area tmp_stmt_da; + + if (thd->is_error()) + { + /* reopen might fail if we have a previous error, use a temporary da. */ + stmt_da= thd->get_stmt_da(); + thd->set_stmt_da(&tmp_stmt_da); + } + + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + + if (stmt_da) + thd->set_stmt_da(stmt_da); + } } === modified file 'sql/sql_plist.h' --- a/sql/sql_plist.h 2011-07-03 23:56:47 +0000 +++ b/sql/sql_plist.h 2011-11-15 18:51:44 +0000 @@ -128,6 +128,15 @@ public: } inline T* front() { return m_first; } inline const T *front() const { return m_first; } + inline T* pop_front() + { + T *result= front(); + + if (result) + remove(result); + + return result; + } void swap(I_P_List &rhs) { swap_variables(T *, m_first, rhs.m_first); === modified file 'sql/sys_vars.cc' --- a/sql/sys_vars.cc 2011-11-10 06:53:27 +0000 +++ b/sql/sys_vars.cc 2011-11-15 18:51:44 +0000 @@ -1425,6 +1425,12 @@ static Sys_var_ulonglong Sys_max_heap_ta VALID_RANGE(16384, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024), BLOCK_SIZE(1024)); +static Sys_var_ulong Sys_metadata_locks_cache_size( + "metadata_locks_cache_size", "Size of unused metadata locks cache", + READ_ONLY GLOBAL_VAR(mdl_locks_cache_size), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(1, 1024*1024), DEFAULT(MDL_LOCKS_CACHE_SIZE_DEFAULT), + BLOCK_SIZE(1)); + static Sys_var_ulong Sys_pseudo_thread_id( "pseudo_thread_id", "This variable is for internal server use", No bundle (reason: useless for push emails).