Hi Ingo,
I have read your patch. In general it looks OK but see my comments and questions
below.
Rafal
Ingo Struewing wrote:
> Below is the list of changes that have just been committed into a local
> 6.0 repository of istruewing. When istruewing does a push these changes
> will be propagated to the main repository and, within 24 hours after the
> push, to the public repository.
> For information on how to access the public repository
> see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
>
> ChangeSet@stripped, 2008-03-11 18:54:28+01:00, istruewing@stripped +32 -0
> WL#4259 - Debug Sync Facility
>
> The Debug Sync Facility allows to place synchronization points
> in the code:
>
> open_tables(...)
>
> DEBUG_SYNC(thd, "after_open_tables");
>
> lock_tables(...)
>
> When activated, a sync point can
>
> - Send a signal and/or
> - Wait for a signal
>
> Nomenclature:
>
> - signal: A value of a global variable that persists
> until overwritten by a new signal. The global
> variable can also be seen as a "signal post"
> or "flag mast". Then the signal is what is
> attached to the "signal post" or "flag mast".
>
> - send a signal: Assign the value (the signal) to the global
> variable ("set a flag") and broadcast a
> global condition to wake those waiting for
> a signal.
>
> - wait for a signal: Loop over waiting for the global condition until
> the global value matches the wait-for signal.
>
> Please find more information in the top comment in debug_sync.cc
> or in the worklog entry.
>
> Included are modified MERGE table tests. They can be seen as
> examples for the use of the facility. The merge-big test case is
> removed. The contained test is moved to the new merge-sync test.
>
> BitKeeper/deleted/.del-merge-big.result@stripped, 2008-03-07 19:56:25+01:00,
> istruewing@stripped +0 -0
> Delete: mysql-test/r/merge-big.result
>
> BitKeeper/deleted/.del-merge-big.test@stripped, 2008-03-07 19:56:33+01:00,
> istruewing@stripped +0 -0
> Delete: mysql-test/t/merge-big.test
>
> BitKeeper/etc/ignore@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +1
> -0
> WL#4259 - Debug Sync Facility
> Added libmysqld/debug_sync.cc
>
> configure.in@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +17 -0
> WL#4259 - Debug Sync Facility
> Added configure option --enable-debug-sync.
>
> libmysqld/CMakeLists.txt@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped
> +1 -0
> WL#4259 - Debug Sync Facility
> Include debug_sync.cc
>
> libmysqld/Makefile.am@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +1
> -0
> WL#4259 - Debug Sync Facility
> Include debug_sync.cc
>
> mysql-test/include/have_debug_sync.inc@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +5 -0
> WL#4259 - Debug Sync Facility
> New file. Test for "have_debug_sync".
>
>
> mysql-test/include/have_debug_sync.inc@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/mysql-test-run.pl@stripped, 2008-03-11 18:54:23+01:00,
> istruewing@stripped +8 -0
> WL#4259 - Debug Sync Facility
> Added code for enabling/disabling of the facility.
>
> mysql-test/r/debug_sync.result@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +239 -0
> WL#4259 - Debug Sync Facility
> New file. The new test result.
>
>
> mysql-test/r/debug_sync.result@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/r/have_debug_sync.require@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +2 -0
> WL#4259 - Debug Sync Facility
> New file. Result for "have_debug_sync".
>
>
> mysql-test/r/have_debug_sync.require@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/r/merge-sync.result@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +151 -0
> WL#4259 - Debug Sync Facility
> New example test result.
>
> mysql-test/r/merge-sync.result@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/r/merge.result@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped
> +4 -111
> WL#4259 - Debug Sync Facility
> Fixed the example test result.
>
> mysql-test/t/debug_sync.test@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +361 -0
> WL#4259 - Debug Sync Facility
> New file. A new test case to test the facility itself.
>
>
> mysql-test/t/debug_sync.test@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/t/merge-sync.test@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +384 -0
> WL#4259 - Debug Sync Facility
> New file. Tests that utilize the new facility.
>
> mysql-test/t/merge-sync.test@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +0 -0
>
> mysql-test/t/merge.test@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +6
> -156
> WL#4259 - Debug Sync Facility
> Removed tests that need the the new facility.
>
> mysys/thr_lock.c@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +17 -0
> WL#4259 - Debug Sync Facility
> Added a synchronization point hook.
>
> sql/CMakeLists.txt@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +1 -0
> WL#4259 - Debug Sync Facility
> Include debug_sync.cc
>
> sql/Makefile.am@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +1 -0
> WL#4259 - Debug Sync Facility
> Include debug_sync.cc
>
> sql/debug_sync.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +1527 -0
> WL#4259 - Debug Sync Facility
> New file. Implementation of the facility.
>
>
> sql/debug_sync.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +0 -0
>
> sql/item_func.cc@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +2 -0
> WL#4259 - Debug Sync Facility
> Added a synchronization point.
>
> sql/lock.cc@stripped, 2008-03-11 18:54:23+01:00, istruewing@stripped +2 -0
> WL#4259 - Debug Sync Facility
> Added synchronization points.
>
> sql/mysql_priv.h@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +21 -0
> WL#4259 - Debug Sync Facility
> Added declarations for the facility.
>
> sql/mysqld.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +44 -1
> WL#4259 - Debug Sync Facility
> Added initialization/termination of the facility for the server.
> Added option "debug-sync".
> Added system variable "have_debug_sync".
>
> sql/set_var.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +6 -0
> WL#4259 - Debug Sync Facility
> Added definition of the 'debug_sync' system variable.
> Added system variable "have_debug_sync".
>
> sql/set_var.h@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +15 -0
> WL#4259 - Debug Sync Facility
> Added declaration for the 'debug_sync' system variable.
>
> sql/share/errmsg.txt@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +8
> -0
> WL#4259 - Debug Sync Facility
> Added new error messages.
>
> sql/sql_base.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +6 -7
> WL#4259 - Debug Sync Facility
> Added synchronization points.
> Removed old "Debug Sleep" synchronization point, which was used
> in merge-big.test only.
>
> sql/sql_class.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +14 -0
> WL#4259 - Debug Sync Facility
> Added initialization/termination of the facility for THD.
>
> sql/sql_class.h@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +5 -0
> WL#4259 - Debug Sync Facility
> Added the new THD element debug_sync_control.
>
> sql/sql_parse.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +1 -0
> WL#4259 - Debug Sync Facility
> Added a synchronization point.
>
> sql/sql_table.cc@stripped, 2008-03-11 18:54:24+01:00, istruewing@stripped +1 -0
> WL#4259 - Debug Sync Facility
> Added a synchronization point.
>
> storage/myisammrg/ha_myisammrg.cc@stripped, 2008-03-11 18:54:24+01:00,
> istruewing@stripped +4 -0
> WL#4259 - Debug Sync Facility
> Added synchronization points.
>
> diff -Nrup a/BitKeeper/etc/ignore b/BitKeeper/etc/ignore
> --- a/BitKeeper/etc/ignore 2008-01-25 18:40:41 +01:00
> +++ b/BitKeeper/etc/ignore 2008-03-11 18:54:23 +01:00
> @@ -3027,3 +3027,4 @@ win/vs8cache.txt
> ylwrap
> zlib/*.ds?
> zlib/*.vcproj
> +libmysqld/debug_sync.cc
====================================================================================
> diff -Nrup a/configure.in b/configure.in
> --- a/configure.in 2008-02-15 22:27:06 +01:00
> +++ b/configure.in 2008-03-11 18:54:23 +01:00
> @@ -1701,6 +1701,23 @@ else
> CXXFLAGS="$OPTIMIZE_CXXFLAGS $CXXFLAGS"
> fi
>
> +# Debug Sync Facility. NOTE: depends on 'with_debug'. Must be behind it.
> +AC_MSG_CHECKING(if Debug Sync Facility should be enabled.)
> +AC_ARG_ENABLE(debug_sync,
> + AS_HELP_STRING([--enable-debug-sync],
> + [Build a version with Debug Sync Facility]),
> + [ enable_debug_sync=$enableval ],
> + [ enable_debug_sync=$with_debug ])
> +
> +if test "$enable_debug_sync" != "no"
> +then
> + AC_DEFINE([ENABLED_DEBUG_SYNC], [1],
> + [If Debug Sync Facility should be enabled])
> + AC_MSG_RESULT([yes])
> +else
> + AC_MSG_RESULT([no])
> +fi
> +
> # If we should allow error injection tests
> AC_ARG_WITH(error-inject,
> AC_HELP_STRING([--with-error-inject],[Enable error injection in MySQL Server]),
====================================================================================
> diff -Nrup a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
> --- a/libmysqld/CMakeLists.txt 2007-12-13 13:47:11 +01:00
> +++ b/libmysqld/CMakeLists.txt 2008-03-11 18:54:23 +01:00
> @@ -178,6 +178,7 @@ SET(LIBMYSQLD_SOURCES emb_qcache.cc libm
> ../sql/sql_list.cc ../sql/sql_load.cc ../sql/sql_locale.cc
> ../sql/sql_binlog.cc ../sql/sql_manager.cc ../sql/sql_map.cc
> ../sql/sql_parse.cc ../sql/sql_partition.cc ../sql/sql_plugin.cc
> + ../sql/debug_sync.cc
> ../sql/sql_prepare.cc ../sql/sql_rename.cc ../sql/sql_repl.cc
> ../sql/sql_select.cc ../sql/sql_servers.cc
> ../sql/sql_show.cc ../sql/sql_state.c ../sql/sql_string.cc
====================================================================================
> diff -Nrup a/libmysqld/Makefile.am b/libmysqld/Makefile.am
> --- a/libmysqld/Makefile.am 2007-12-19 14:34:58 +01:00
> +++ b/libmysqld/Makefile.am 2008-03-11 18:54:23 +01:00
> @@ -78,6 +78,7 @@ sqlsources = derror.cc field.cc field_co
> event_scheduler.cc events.cc event_data_objects.cc \
> event_queue.cc event_db_repository.cc \
> rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
> + debug_sync.cc \
> sql_tablespace.cc \
> rpl_injector.cc my_user.c partition_info.cc \
> sql_servers.cc
====================================================================================
> diff -Nrup a/mysql-test/include/have_debug_sync.inc
> b/mysql-test/include/have_debug_sync.inc
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/include/have_debug_sync.inc 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,5 @@
> +--require r/have_debug_sync.require
> +disable_query_log;
> +let $value= query_get_value(SHOW VARIABLES LIKE 'debug_sync', Value, 1);
> +eval SELECT ('$value' LIKE 'ON %') AS debug_sync;
> +enable_query_log;
====================================================================================
> diff -Nrup a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
> --- a/mysql-test/mysql-test-run.pl 2008-02-11 18:19:05 +01:00
> +++ b/mysql-test/mysql-test-run.pl 2008-03-11 18:54:23 +01:00
> @@ -268,6 +268,7 @@ my @default_valgrind_args= ("--show-reac
> my @valgrind_args;
> my $opt_valgrind_path;
> my $opt_callgrind;
> +my $opt_debug_sync_timeout= 300; # Default timeout for WAIT_FOR actions.
>
> our $opt_stress= "";
> our $opt_stress_suite= "main";
> @@ -617,6 +618,7 @@ sub command_line_setup () {
> 'valgrind-option=s' => \@valgrind_args,
> 'valgrind-path=s' => \$opt_valgrind_path,
> 'callgrind' => \$opt_callgrind,
> + 'debug-sync-timeout=i' => \$opt_debug_sync_timeout,
>
> # Stress testing
> 'stress' => \$opt_stress,
> @@ -3590,6 +3592,10 @@ sub mysqld_arguments ($$$$) {
> # see BUG#28359
> mtr_add_arg($args, "%s--connect-timeout=60", $prefix);
>
> + # Enable the debug sync facility, set default wait timeout.
> + # Facility stays disabled if timeout value is zero.
> + mtr_add_arg($args, "%s--loose-debug-sync-timeout=%s", $prefix,
> + $opt_debug_sync_timeout);
>
> # When mysqld is run by a root user(euid is 0), it will fail
> # to start unless we specify what user to run as. If not running
> @@ -5024,6 +5030,8 @@ Options for coverage, profiling etc
> can be specified more then once
> valgrind-path=[EXE] Path to the valgrind executable
> callgrind Instruct valgrind to use callgrind
> + debug-sync-timeout=NUM Set default timeout for WAIT_FOR debug sync
> + actions. Disable facility with NUM=0.
>
> Misc options
>
====================================================================================
> diff -Nrup a/mysql-test/r/debug_sync.result b/mysql-test/r/debug_sync.result
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/r/debug_sync.result 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,239 @@
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE IF EXISTS t1;
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: ''
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 EXECUTE 2';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2';
> +SET DEBUG_SYNC='p0 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 CLEAR';
> +SET DEBUG_SYNC='p0 TEST';
> +SET DEBUG_SYNC='RESET';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 execute 2';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6';
> +set debug_sync='p0 signal s1 wait_for s2 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 execute 2';
> +set debug_sync='p0 signal s1 wait_for s2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2';
> +set debug_sync='p0 signal s1 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 execute 2';
> +set debug_sync='p0 signal s1 hit_limit 3';
> +set debug_sync='p0 signal s1';
> +set debug_sync='p0 wait_for s2 timeout 6 execute 2 hit_limit 3';
> +set debug_sync='p0 wait_for s2 timeout 6 execute 2';
> +set debug_sync='p0 wait_for s2 timeout 6 hit_limit 3';
> +set debug_sync='p0 wait_for s2 timeout 6';
> +set debug_sync='p0 wait_for s2 execute 2 hit_limit 3';
> +set debug_sync='p0 wait_for s2 execute 2';
> +set debug_sync='p0 wait_for s2 hit_limit 3';
> +set debug_sync='p0 wait_for s2';
> +set debug_sync='p0 hit_limit 3';
> +set debug_sync='p0 clear';
> +set debug_sync='p0 test';
> +set debug_sync='reset';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6
> + EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0';
> +ERROR 42000: Missing action after synchronization point name 'p0'
> +SET DEBUG_SYNC='p0 EXECUTE 2';
> +ERROR 42000: Missing action before EXECUTE
> +SET DEBUG_SYNC='p0 TIMEOUT 6 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 TIMEOUT 6';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAL'
> +SET DEBUG_SYNC='p0 TIMEOUT 6 WAIT_FOR s2 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 TIMEOUT 6 WAIT_FOR s2';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 SIGNAL s1 TIMEOUT 6';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 EXECUTE 2 SIGNAL s1 TIMEOUT 6';
> +ERROR 42000: Missing action before EXECUTE
> +SET DEBUG_SYNC='p0 TIMEOUT 6 SIGNAL s1';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUT'
> +SET DEBUG_SYNC='p0 EXECUTE 2 TIMEOUT 6 SIGNAL s1';
> +ERROR 42000: Missing action before EXECUTE
> +SET DEBUG_SYNC='p0 CLEAR HIT_LIMIT 3';
> +ERROR 42000: Nothing must follow action CLEAR
> +SET DEBUG_SYNC='CLEAR';
> +ERROR 42000: Missing action after synchronization point name 'CLEAR'
> +SET DEBUG_SYNC='p0 CLEAR p0';
> +ERROR 42000: Nothing must follow action CLEAR
> +SET DEBUG_SYNC='TEST';
> +ERROR 42000: Missing action after synchronization point name 'TEST'
> +SET DEBUG_SYNC='p0 TEST p0';
> +ERROR 42000: Nothing must follow action TEST
> +SET DEBUG_SYNC='p0 RESET';
> +ERROR 42000: Illegal or out of order stuff: 'RESET'
> +SET DEBUG_SYNC='RESET p0';
> +ERROR 42000: Illegal or out of order stuff: 'p0'
> +SET DEBUG_SYNC='p0 RESET p0';
> +ERROR 42000: Illegal or out of order stuff: 'RESET'
> +SET DEBUG_SYNCx='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +ERROR HY000: Unknown system variable 'DEBUG_SYNCx'
> +SET DEBUG_SYNC='p0 SIGNAx s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +ERROR 42000: Illegal or out of order stuff: 'SIGNAx'
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOx s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +ERROR 42000: Illegal or out of order stuff: 'WAIT_FOx'
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUx 0 EXECUTE 2 HIT_LIMIT 3';
> +ERROR 42000: Illegal or out of order stuff: 'TIMEOUx'
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTx 2 HIT_LIMIT 3';
> +ERROR 42000: Illegal or out of order stuff: 'EXECUTx'
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIx 3';
> +ERROR 42000: Illegal or out of order stuff: 'HIT_LIMIx'
> +SET DEBUG_SYNC='p0 CLEARx';
> +ERROR 42000: Illegal or out of order stuff: 'CLEARx'
> +SET DEBUG_SYNC='p0 TESTx';
> +ERROR 42000: Illegal or out of order stuff: 'TESTx'
> +SET DEBUG_SYNC='RESETx';
> +ERROR 42000: Missing action after synchronization point name 'RESETx'
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 0x6 EXECUTE 2 HIT_LIMIT 3';
> +ERROR 42000: Missing valid number after TIMEOUT
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 0x2 HIT_LIMIT 3';
> +ERROR 42000: Missing valid number after EXECUTE
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 7 EXECUTE 2 HIT_LIMIT 0x3';
> +ERROR 42000: Missing valid number after HIT_LIMIT
> +SET DEBUG_SYNC= 7;
> +ERROR 42000: Incorrect argument type to variable 'debug_sync'
> +SET GLOBAL DEBUG_SYNC= 'p0 CLEAR';
> +ERROR HY000: 'SESSION.debug_sync' is not global variable
> +SET DEBUG_SYNC= 'now SIGNAL something';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 'something'
> +SET DEBUG_SYNC= 'now WAIT_FOR nothing TIMEOUT 0';
> +Warnings:
> +Warning 1662 debug sync point wait timed out
> +SET DEBUG_SYNC= 'now SIGNAL nothing';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 'nothing'
> +SET DEBUG_SYNC= 'now WAIT_FOR nothing TIMEOUT 0';
> +SET DEBUG_SYNC= 'now SIGNAL something EXECUTE 0';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 'nothing'
> +SET DEBUG_SYNC= 'now WAIT_FOR anotherthing TIMEOUT 0 EXECUTE 0';
> +SET DEBUG_SYNC= 'now HIT_LIMIT 1';
> +ERROR HY000: debug sync point hit limit reached
> +SET DEBUG_SYNC= 'RESET';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: ''
> +SET DEBUG_SYNC= 'p1abcd SIGNAL s1 EXECUTE 2';
> +SET DEBUG_SYNC= 'p2abc SIGNAL s2 EXECUTE 2';
> +SET DEBUG_SYNC= 'p9abcdef SIGNAL s9 EXECUTE 2';
> +SET DEBUG_SYNC= 'p4a SIGNAL s4 EXECUTE 2';
> +SET DEBUG_SYNC= 'p5abcde SIGNAL s5 EXECUTE 2';
> +SET DEBUG_SYNC= 'p6ab SIGNAL s6 EXECUTE 2';
> +SET DEBUG_SYNC= 'p7 SIGNAL s7 EXECUTE 2';
> +SET DEBUG_SYNC= 'p8abcdef SIGNAL s8 EXECUTE 2';
> +SET DEBUG_SYNC= 'p3abcdef SIGNAL s3 EXECUTE 2';
> +SET DEBUG_SYNC= 'p4a TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's4'
> +SET DEBUG_SYNC= 'p1abcd TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's1'
> +SET DEBUG_SYNC= 'p7 TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's7'
> +SET DEBUG_SYNC= 'p9abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's9'
> +SET DEBUG_SYNC= 'p3abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's3'
> +SET DEBUG_SYNC= 'p1abcd CLEAR';
> +SET DEBUG_SYNC= 'p2abc CLEAR';
> +SET DEBUG_SYNC= 'p5abcde CLEAR';
> +SET DEBUG_SYNC= 'p6ab CLEAR';
> +SET DEBUG_SYNC= 'p8abcdef CLEAR';
> +SET DEBUG_SYNC= 'p9abcdef CLEAR';
> +SET DEBUG_SYNC= 'p3abcdef CLEAR';
> +SET DEBUG_SYNC= 'p4a CLEAR';
> +SET DEBUG_SYNC= 'p7 CLEAR';
> +SET DEBUG_SYNC= 'p1abcd TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's3'
> +SET DEBUG_SYNC= 'p7 TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's3'
> +SET DEBUG_SYNC= 'p9abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: 's3'
> +SET DEBUG_SYNC= 'RESET';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +Variable_name Value
> +debug_sync ON - current signal: ''
> +CREATE USER mysqltest_1@localhost;
> +GRANT SUPER ON *.* TO mysqltest_1@localhost;
> +connection con1, mysqltest_1
> +SET DEBUG_SYNC= 'RESET';
> +connection default
> +DROP USER mysqltest_1@localhost;
> +CREATE USER mysqltest_2@localhost;
> +GRANT ALL ON *.* TO mysqltest_2@localhost;
> +REVOKE SUPER ON *.* FROM mysqltest_2@localhost;
> +connection con1, mysqltest_2
> +SET DEBUG_SYNC= 'RESET';
> +ERROR 42000: Access denied; you need the SUPER privilege for this operation
> +connection default
> +DROP USER mysqltest_2@localhost;
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE IF EXISTS t1;
> +CREATE TABLE t1 (c1 INT);
> +connection con1
> +SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> +INSERT INTO t1 VALUES(1);
> +connection default
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE t1;
> +connection con1
> +connection default
> +DROP TABLE t1;
> +SET DEBUG_SYNC= 'RESET';
====================================================================================
> diff -Nrup a/mysql-test/r/have_debug_sync.require
> b/mysql-test/r/have_debug_sync.require
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/r/have_debug_sync.require 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,2 @@
> +debug_sync
> +1
====================================================================================
> diff -Nrup a/mysql-test/r/merge-big.result b/mysql-test/r/merge-big.result
> --- a/mysql-test/r/merge-big.result 2008-02-07 12:04:18 +01:00
> +++ /dev/null Wed Dec 31 16:00:00 196900
> @@ -1,26 +0,0 @@
> -drop table if exists t1,t2,t3,t4,t5,t6;
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
> -# corrupts a MERGE table
> -# Problem #3
> -#
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -LOCK TABLE t1 WRITE;
> -# connection con1
> -SET SESSION debug="+d,sleep_open_and_lock_after_open";
> -INSERT INTO t1 VALUES (1);
> -# connection default
> -# Let INSERT go into thr_multi_lock().
> -# Kick INSERT out of thr_multi_lock().
> -FLUSH TABLES;
> -# Let INSERT go through open_tables() where it sleeps.
> -# Unlock and close table and wait for con1 to close too.
> -FLUSH TABLES;
> -# This should give no result.
> -SELECT * FROM t1;
> -c1
> -UNLOCK TABLES;
> -# connection con1
> -SET SESSION debug="-d,sleep_open_and_lock_after_open";
> -# connection default
> -DROP TABLE t1;
====================================================================================
> diff -Nrup a/mysql-test/r/merge-sync.result b/mysql-test/r/merge-sync.result
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/r/merge-sync.result 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,151 @@
> +SET DEBUG_SYNC= 'RESET';
> +drop table if exists t1,t2,t3,t4,t5,t6;
> +drop database if exists mysqltest;
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> +connection con1
> +SET DEBUG_SYNC= 'after_admin_flush
> + SIGNAL admin_flush WAIT_FOR end_repair';
> +REPAIR TABLE t1;
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR admin_flush';
> +SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 3';
> +SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL end_repair';
> +INSERT INTO t2 VALUES (1);
> +SET DEBUG_SYNC= 'now SIGNAL end_repair';
> +connection con1
> +Table Op Msg_type Msg_text
> +test.t1 repair status OK
> +connection default;
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2;
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> +LOCK TABLE t1 WRITE;
> +REPAIR TABLE t1;
> +Table Op Msg_type Msg_text
> +test.t1 repair status OK
> +connection con1
> +SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 3';
> +SET DEBUG_SYNC= 'after_insert SIGNAL end_repair';
> +SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL end_repair';
> +INSERT INTO t2 VALUES (1);
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR end_repair';
> +UNLOCK TABLES;
> +connection con1
> +connection default;
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2;
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +connection con1
> +SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> +SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
> +SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL locked';
> +SET DEBUG_SYNC= 'after_insert SIGNAL locked';
> +INSERT INTO t1 VALUES (1);
> +connection default
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLES;
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +SELECT * FROM t1;
> +c1
> +UNLOCK TABLES;
> +connection con1
> +connection default
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
> +CREATE TABLE t1 (c1 REAL) ENGINE=MyISAM;
> +CREATE TABLE t2 (c1 REAL) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES(0.1);
> +INSERT INTO t2 VALUES(0.2);
> +CREATE TABLE t3 (c1 REAL) ENGINE=MRG_MYISAM UNION=(t1,t2);
> +connection con1
> +SET DEBUG_SYNC= 'before_acos_function
> + SIGNAL select WAIT_FOR truncated';
> +SELECT ACOS(c1) FROM t3;
> +connection default
> +SET DEBUG_SYNC= 'now WAIT_FOR select';
> +SET DEBUG_SYNC= 'before_wait_locked_tname SIGNAL truncated';
> +TRUNCATE TABLE t1;
> +SET DEBUG_SYNC= 'now SIGNAL truncated';
> +connection con1
> +connection default
> +SELECT ACOS(c1) FROM t3;
> +ACOS(c1)
> +1.369438406004566
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2, t3;
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
> +connection con1
> +SET DEBUG_SYNC= 'before_myisammrg_attach
> + SIGNAL attach WAIT_FOR flushed';
> +INSERT INTO m1 VALUES (2);
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR attach';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE m1;
> +connection con1
> +connection default;
> +SELECT * FROM m1;
> +c1
> +2
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE m1, t1;
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
> +connection con1
> +SET DEBUG_SYNC= 'before_myisammrg_attach
> + SIGNAL attach WAIT_FOR store_lock1';
> +SET DEBUG_SYNC= 'before_myisammrg_store_lock
> + SIGNAL store_lock2 WAIT_FOR flushed';
> +INSERT INTO m1 VALUES (2);
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR attach';
> +SET DEBUG_SYNC= 'before_myisammrg_store_lock
> + SIGNAL store_lock1 WAIT_FOR store_lock2';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE m1;
> +connection con1
> +connection default;
> +SELECT * FROM m1;
> +c1
> +2
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE m1, t1;
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +connection con1
> +SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 1';
> +SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> +SET DEBUG_SYNC= 'after_insert SIGNAL locked';
> +SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
> +SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL locked';
> +INSERT INTO t1 VALUES (1);
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed EXECUTE 2';
> +FLUSH TABLES;
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +UNLOCK TABLES;
> +connection con1
> +connection default;
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +connection con1
> +SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked';
> +INSERT INTO t1 VALUES (1);
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +UNLOCK TABLES;
> +connection con1
> +connection default;
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
====================================================================================
> diff -Nrup a/mysql-test/r/merge.result b/mysql-test/r/merge.result
> --- a/mysql-test/r/merge.result 2007-12-13 13:56:17 +01:00
> +++ b/mysql-test/r/merge.result 2008-03-11 18:54:23 +01:00
> @@ -1069,31 +1069,6 @@ c1
> 2
> UNLOCK TABLES;
> DROP TABLE t1, t2, t3, t4;
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> -REPAIR TABLE t1;
> -INSERT INTO t2 VALUES (1);
> -Table Op Msg_type Msg_text
> -test.t1 repair status OK
> -DROP TABLE t1, t2;
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> -LOCK TABLE t1 WRITE;
> -INSERT INTO t2 VALUES (1);
> -REPAIR TABLE t1;
> -Table Op Msg_type Msg_text
> -test.t1 repair status OK
> -UNLOCK TABLES;
> -DROP TABLE t1, t2;
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -LOCK TABLE t1 WRITE;
> -INSERT INTO t1 VALUES (1);
> -FLUSH TABLES;
> -FLUSH TABLES;
> -SELECT * FROM t1;
> -c1
> -UNLOCK TABLES;
> -DROP TABLE t1;
> #
> # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
> # REPAIR TABLE corrupts a MERGE table
> @@ -1611,6 +1586,7 @@ c1
> 33
> DELETE FROM t4 WHERE c1 = 33;
> DROP TRIGGER t3_ai;
> +UNLOCK TABLES;
> #
> # Trigger with table use on child
> DELETE FROM t4 WHERE c1 = 4;
> @@ -1793,9 +1769,9 @@ ALTER TABLE t2 UNION=(t3,t1);
> SELECT * FROM t2;
> ERROR HY000: Table 't3' is differently defined or of non-MyISAM type or doesn't
> exist
> DROP TABLE t1, t2, t3;
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2);
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
> INSERT INTO t1 VALUES (1);
> INSERT INTO t2 VALUES (2);
> SELECT * FROM t3;
> @@ -1806,89 +1782,6 @@ TRUNCATE TABLE t1;
> SELECT * FROM t3;
> c1
> 2
> -DROP TABLE t1, t2, t3;
> -CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER);
> -SET @rnd_max= 2147483647;
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -SET @rnd= RAND();
> -SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> -SET @id_rev= @rnd_max - @id;
> -SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> -INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> -set @@read_buffer_size=2*1024*1024;
> -CREATE TABLE t2 SELECT * FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER)
> -ENGINE= MRG_MYISAM UNION= (t1, t2);
> -SELECT COUNT(*) FROM t1;
> -COUNT(*)
> -130
> -SELECT COUNT(*) FROM t2;
> -COUNT(*)
> -80
> -SELECT COUNT(*) FROM t3;
> -COUNT(*)
> -210
> -SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2
> -WHERE a1.id = a2.id GROUP BY a2.grp;
> -TRUNCATE TABLE t1;
> -SELECT COUNT(*) FROM t1;
> -COUNT(*)
> -0
> -SELECT COUNT(*) FROM t2;
> -COUNT(*)
> -80
> -SELECT COUNT(*) FROM t3;
> -COUNT(*)
> -80
> DROP TABLE t1, t2, t3;
> CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
====================================================================================
> diff -Nrup a/mysql-test/t/debug_sync.test b/mysql-test/t/debug_sync.test
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/t/debug_sync.test 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,361 @@
> +###################### t/debug_sync.test ###############################
> +# #
> +# Testing of the Debug Sync Facility. #
> +# #
> +# There is important documentation within sql/debug_sync.cc #
> +# #
> +# Used objects in this test case: #
> +# p0 - synchronization point 0. Non-existent dummy sync point. #
> +# s1 - signal 1. #
> +# s2 - signal 2. #
> +# #
> +# Creation: #
> +# 2008-02-18 istruewing #
> +# #
> +########################################################################
> +
> +#
> +# We need the Debug Sync Facility.
> +#
> +--source include/have_debug_sync.inc
> +
> +#
> +# We are checking privileges, which the embedded server cannot do.
> +#
> +--source include/not_embedded.inc
> +
> +#
> +# Preparative cleanup.
> +#
> +--disable_warnings
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE IF EXISTS t1;
> +--enable_warnings
> +
> +#
> +# Show the special system variable.
> +# It shows ON or OFF depending on the command line option --debug-sync.
> +# The test case assumes it is ON (command line option present).
> +#
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +
> +#
> +# Syntax. Valid forms.
> +#
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1 EXECUTE 2';
> +SET DEBUG_SYNC='p0 SIGNAL s1 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 SIGNAL s1';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 EXECUTE 2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 EXECUTE 2';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 WAIT_FOR s2';
> +SET DEBUG_SYNC='p0 HIT_LIMIT 3';
> +SET DEBUG_SYNC='p0 CLEAR';
> +SET DEBUG_SYNC='p0 TEST';
> +SET DEBUG_SYNC='RESET';
> +
> +#
> +# Syntax. Valid forms. Lower case.
> +#
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 execute 2';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 timeout 6';
> +set debug_sync='p0 signal s1 wait_for s2 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2 execute 2';
> +set debug_sync='p0 signal s1 wait_for s2 hit_limit 3';
> +set debug_sync='p0 signal s1 wait_for s2';
> +set debug_sync='p0 signal s1 execute 2 hit_limit 3';
> +set debug_sync='p0 signal s1 execute 2';
> +set debug_sync='p0 signal s1 hit_limit 3';
> +set debug_sync='p0 signal s1';
> +set debug_sync='p0 wait_for s2 timeout 6 execute 2 hit_limit 3';
> +set debug_sync='p0 wait_for s2 timeout 6 execute 2';
> +set debug_sync='p0 wait_for s2 timeout 6 hit_limit 3';
> +set debug_sync='p0 wait_for s2 timeout 6';
> +set debug_sync='p0 wait_for s2 execute 2 hit_limit 3';
> +set debug_sync='p0 wait_for s2 execute 2';
> +set debug_sync='p0 wait_for s2 hit_limit 3';
> +set debug_sync='p0 wait_for s2';
> +set debug_sync='p0 hit_limit 3';
> +set debug_sync='p0 clear';
> +set debug_sync='p0 test';
> +set debug_sync='reset';
> +
> +#
> +# Syntax. Valid forms. Line wrap.
> +#
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6
> + EXECUTE 2 HIT_LIMIT 3';
> +
> +#
> +# Syntax. Invalid forms.
> +#
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TIMEOUT 6 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TIMEOUT 6';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 SIGNAL s1 TIMEOUT 6';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 SIGNAL s1';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TIMEOUT 6 WAIT_FOR s2 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TIMEOUT 6 WAIT_FOR s2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 TIMEOUT 6 EXECUTE 2';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 TIMEOUT 6';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 EXECUTE 2 SIGNAL s1 TIMEOUT 6';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TIMEOUT 6 SIGNAL s1';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 EXECUTE 2 TIMEOUT 6 SIGNAL s1';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 CLEAR HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='CLEAR';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 CLEAR p0';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='TEST';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TEST p0';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 RESET';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='RESET p0';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 RESET p0';
> +
> +#
> +# Syntax. Invalid keywords used.
> +#
> +--error ER_UNKNOWN_SYSTEM_VARIABLE
> +SET DEBUG_SYNCx='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAx s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOx s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUx 0 EXECUTE 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTx 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 SIGNAL s1 WAIT_FOR s2 TIMEOUT 6 EXECUTE 2 HIT_LIMIx 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 CLEARx';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 TESTx';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='RESETx';
> +
> +#
> +# Syntax. Invalid numbers. Decimal only.
> +#
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 0x6 EXECUTE 2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 6 EXECUTE 0x2 HIT_LIMIT 3';
> +--error ER_PARSE_ERROR
> +SET DEBUG_SYNC='p0 WAIT_FOR s2 TIMEOUT 7 EXECUTE 2 HIT_LIMIT 0x3';
> +
> +#
> +# Syntax. Invalid value type.
> +#
> +--error ER_WRONG_TYPE_FOR_VAR
> +SET DEBUG_SYNC= 7;
> +
> +#
> +# Syntax. DEBUG_SYNC is a SESSION-only variable.
> +#
> +--error ER_WRONG_OBJECT
> +SET GLOBAL DEBUG_SYNC= 'p0 CLEAR';
> +
> +#
> +# Functional tests.
> +#
> +# NOTE: There is the special synchronization point 'now'. It is placed
> +# immediately after setting of the DEBUG_SYNC variable.
> +# So it is executed before the SET statement ends.
> +#
> +# NOTE: There is only on global signal (say "signal post" or "flag mast").
> +# A SIGNAL action writes its signal into it ("sets a flag").
> +# The signal persists until explicitly overwritten.
> +# To avoid confusion for later tests, it is recommended to clear
> +# the signal by signalling "empty" ("setting the 'empty' flag"):
> +# SET DEBUG_SYNC= 'now SIGNAL empty';
> +# Preferably you can reset the whole facility with:
> +# SET DEBUG_SYNC= 'RESET';
> +# The signal is then '' (really empty) which connot be done otherwise.
> +#
> +
> +#
> +# Time out immediately. This gives just a warning.
> +#
> +SET DEBUG_SYNC= 'now SIGNAL something';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'now WAIT_FOR nothing TIMEOUT 0';
> +#
> +# If signal is present already, TIMEOUT 0 does not give a warning.
> +#
> +SET DEBUG_SYNC= 'now SIGNAL nothing';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'now WAIT_FOR nothing TIMEOUT 0';
> +
> +#
> +# EXECUTE 0 is effectively a no-op.
> +#
> +SET DEBUG_SYNC= 'now SIGNAL something EXECUTE 0';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'now WAIT_FOR anotherthing TIMEOUT 0 EXECUTE 0';
> +
> +#
> +# Run into HIT_LIMIT. This gives an error.
> +#
> +--error ER_DEBUG_SYNC_HIT_LIMIT
> +SET DEBUG_SYNC= 'now HIT_LIMIT 1';
> +
> +#
> +# Many actions. Watch the array growing and shrinking in the debug trace:
> +# egrep 'query:|debug_sync_action:' mysql-test/var/log/master.trace
> +#
> +SET DEBUG_SYNC= 'RESET';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p1abcd SIGNAL s1 EXECUTE 2';
> +SET DEBUG_SYNC= 'p2abc SIGNAL s2 EXECUTE 2';
> +SET DEBUG_SYNC= 'p9abcdef SIGNAL s9 EXECUTE 2';
> +SET DEBUG_SYNC= 'p4a SIGNAL s4 EXECUTE 2';
> +SET DEBUG_SYNC= 'p5abcde SIGNAL s5 EXECUTE 2';
> +SET DEBUG_SYNC= 'p6ab SIGNAL s6 EXECUTE 2';
> +SET DEBUG_SYNC= 'p7 SIGNAL s7 EXECUTE 2';
> +SET DEBUG_SYNC= 'p8abcdef SIGNAL s8 EXECUTE 2';
> +SET DEBUG_SYNC= 'p3abcdef SIGNAL s3 EXECUTE 2';
> +#
> +# Execute some actions to show they exist. Each sets a distinct signal.
> +#
> +SET DEBUG_SYNC= 'p4a TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p1abcd TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p7 TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p9abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p3abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +#
> +# Clear the actions.
> +#
> +SET DEBUG_SYNC= 'p1abcd CLEAR';
> +SET DEBUG_SYNC= 'p2abc CLEAR';
> +SET DEBUG_SYNC= 'p5abcde CLEAR';
> +SET DEBUG_SYNC= 'p6ab CLEAR';
> +SET DEBUG_SYNC= 'p8abcdef CLEAR';
> +SET DEBUG_SYNC= 'p9abcdef CLEAR';
> +SET DEBUG_SYNC= 'p3abcdef CLEAR';
> +SET DEBUG_SYNC= 'p4a CLEAR';
> +SET DEBUG_SYNC= 'p7 CLEAR';
> +#
> +# Execute some actions to show they have gone.
> +#
> +SET DEBUG_SYNC= 'p1abcd TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p7 TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +SET DEBUG_SYNC= 'p9abcdef TEST';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +#
> +# Now cleanup. Actions are clear already, but signal needs to be cleared.
> +#
> +SET DEBUG_SYNC= 'RESET';
> +SHOW VARIABLES LIKE 'DEBUG_SYNC';
> +
> +#
> +# Facility requires SUPER privilege.
> +#
> +CREATE USER mysqltest_1@localhost;
> +GRANT SUPER ON *.* TO mysqltest_1@localhost;
> +--echo connection con1, mysqltest_1
> +connect (con1,localhost,mysqltest_1,,);
> +SET DEBUG_SYNC= 'RESET';
> +disconnect con1;
> +--echo connection default
> +connection default;
> +DROP USER mysqltest_1@localhost;
> +#
> +CREATE USER mysqltest_2@localhost;
> +GRANT ALL ON *.* TO mysqltest_2@localhost;
> +REVOKE SUPER ON *.* FROM mysqltest_2@localhost;
> +--echo connection con1, mysqltest_2
> +connect (con1,localhost,mysqltest_2,,);
> +--error ER_SPECIFIC_ACCESS_DENIED_ERROR
> +SET DEBUG_SYNC= 'RESET';
> +disconnect con1;
> +--echo connection default
> +connection default;
> +DROP USER mysqltest_2@localhost;
> +
> +#
> +# Example.
> +#
> +# Preparative cleanup.
> +--disable_warnings
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE IF EXISTS t1;
> +--enable_warnings
> +#
> +# Test.
> +CREATE TABLE t1 (c1 INT);
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> + send INSERT INTO t1 VALUES(1);
> +--echo connection default
> +connection default;
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE t1;
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +--echo connection default
> +connection default;
> +DROP TABLE t1;
> +
> +#
> +# Cleanup after test case.
> +# Otherwise signal would contain 'flushed' here,
> +# which could confuse the next test.
> +#
> +SET DEBUG_SYNC= 'RESET';
> +
====================================================================================
> diff -Nrup a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test
> --- a/mysql-test/t/merge-big.test 2008-02-07 12:04:18 +01:00
> +++ /dev/null Wed Dec 31 16:00:00 196900
> @@ -1,82 +0,0 @@
> -#
> -# Test of MERGE tables with multisession and many waits.
> -#
> -# This test takes rather long time so let us run it only in --big-test mode
> ---source include/big_test.inc
> -# We use some debug-only features in this test
> ---source include/have_debug.inc
> -# We use INFORMATION_SCHEMA.PROCESSLIST in this test
> ---source include/not_embedded.inc
> -
> ---disable_warnings
> -drop table if exists t1,t2,t3,t4,t5,t6;
> ---enable_warnings
> -
> ---echo #
> ---echo # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
> ---echo # corrupts a MERGE table
> ---echo # Problem #3
> ---echo #
> -# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
> -# This did *not* require a MERGE table.
> -#
> -# To increase reproducibility it was necessary to enter a sleep of 2
> -# seconds at the end of wait_for_tables() after unlock of LOCK_open. In
> -# 5.0 and 5.1 the sleep must be inserted in open_and_lock_tables() after
> -# open_tables() instead. wait_for_tables() is not used in this case. The
> -# problem was that FLUSH TABLES releases LOCK_open while having unlocked
> -# and closed all tables. When this happened while a thread was in the
> -# loop in mysql_lock_tables() right after wait_for_tables()
> -# (open_tables()) and before retrying to lock, the thread got the lock.
> -# And it did not notice that the table needed a refresh after the
> -# [re-]open. So it executed its statement on the table.
> -#
> -# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and
> -# let it wait in wait_for_tables() (open_table()). The second FLUSH
> -# TABLES must happen while the INSERT was on its way from
> -# wait_for_tables() (open_table()) to the next call of thr_multi_lock().
> -# This needed to be supported by a sleep to make it repeatable.
> -#
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -LOCK TABLE t1 WRITE;
> -#SELECT NOW();
> - --echo # connection con1
> - connect (con1,localhost,root,,);
> - let $con1_id= `SELECT CONNECTION_ID()`;
> - SET SESSION debug="+d,sleep_open_and_lock_after_open";
> - send INSERT INTO t1 VALUES (1);
> ---echo # connection default
> -connection default;
> ---echo # Let INSERT go into thr_multi_lock().
> -#--sleep 8
> -#SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST;
> -let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
> - WHERE ID = $con1_id AND STATE = 'Locked';
> ---source include/wait_condition.inc
> -#SELECT NOW();
> ---echo # Kick INSERT out of thr_multi_lock().
> -FLUSH TABLES;
> -#SELECT NOW();
> ---echo # Let INSERT go through open_tables() where it sleeps.
> -#--sleep 8
> -#SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST;
> -let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
> - WHERE ID = $con1_id AND STATE = 'Waiting for table';
> ---source include/wait_condition.inc
> -#SELECT NOW();
> ---echo # Unlock and close table and wait for con1 to close too.
> -FLUSH TABLES;
> -#SELECT NOW();
> ---echo # This should give no result.
> -SELECT * FROM t1;
> -#SELECT NOW();
> -UNLOCK TABLES;
> - --echo # connection con1
> - connection con1;
> - reap;
> - SET SESSION debug="-d,sleep_open_and_lock_after_open";
> - disconnect con1;
> ---echo # connection default
> -connection default;
> -DROP TABLE t1;
> -
====================================================================================
> diff -Nrup a/mysql-test/t/merge-sync.test b/mysql-test/t/merge-sync.test
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/mysql-test/t/merge-sync.test 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,384 @@
> +#
> +# Test of MERGE TABLES
> +#
> +
> +#
> +# We need the Debug Sync Facility.
> +#
> +--source include/have_debug_sync.inc
> +
> +# Clean up resources used in this test case.
> +--disable_warnings
> +SET DEBUG_SYNC= 'RESET';
> +drop table if exists t1,t2,t3,t4,t5,t6;
> +drop database if exists mysqltest;
> +--enable_warnings
> +
> +#
> +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> +# Problem #1
> +# A thread trying to lock a MERGE table performed busy waiting while
> +# REPAIR TABLE or a similar table administration task was ongoing on one or
> +# more of its MyISAM tables.
> +# To allow for observability it was necessary to enter a multi-second sleep
> +# in mysql_admin_table() after remove_table_from_cache(), which comes after
> +# mysql_abort_lock(). The sleep faked a long running operation. One could
> +# watch a high CPU load during the sleep time.
> +# The problem was that mysql_abort_lock() upgrades the write lock to
> +# TL_WRITE_ONLY. This lock type persisted until the final unlock at the end
> +# of the administration task. The effect of TL_WRITE_ONLY is to reject any
> +# attempt to lock the table. The trying thread must close the table and wait
> +# until it is no longer used. Unfortunately there is no way to detect that
> +# one of the MyISAM tables of a MERGE table is in use. When trying to lock
> +# the MERGE table, all MyISAM tables are locked. If one fails on
> +# TL_WRITE_ONLY, all locks are aborted and wait_for_tables() is entered.
> +# But this doesn't see the MERGE table as used, so it seems appropriate to
> +# retry a lock...
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # When reaching repair code, signal admin_flush and wait for end_repair.
> + SET DEBUG_SYNC= 'after_admin_flush
> + SIGNAL admin_flush WAIT_FOR end_repair';
> + send REPAIR TABLE t1;
> +#
> +--echo connection default;
> +connection default;
> +# Wait that the other thread reaches repair.
> +SET DEBUG_SYNC= 'now WAIT_FOR admin_flush';
> +#
> +# If the bug exists, INSERT will loop infinitely in getting its lock.
> +# Bail out if lock retry is done 3 times.
> +SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 3';
> +# When the bug is fixed, we wait for refresh of repaired table.
> +# In this case resume repair thread so that we do not deadlock.
> +SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL end_repair';
> +# Succeeds with bug fixed.
> +INSERT INTO t2 VALUES (1);
> +#
> +# Resume the other thread. (non-bug fixed case)
> +SET DEBUG_SYNC= 'now SIGNAL end_repair';
> +#
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +#
> +--echo connection default;
> +connection default;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2;
> +#
> +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> +# Problem #2
> +# A thread trying to lock a MERGE table performed busy waiting until all
> +# threads that did REPAIR TABLE or similar table administration tasks on
> +# one or more of its MyISAM tables in LOCK TABLES segments did
> +# UNLOCK TABLES.
> +# The difference against problem #1 is that the busy waiting took place
> +# *after* the administration task. It was terminated by UNLOCK TABLES only.
> +#
> +# This is the same test case as for
> +# Bug#26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> +LOCK TABLE t1 WRITE;
> +REPAIR TABLE t1;
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # If the bug exists, the insert will loop infinitely in getting its lock.
> + # Bail out if lock retry is done 3 times.
> + SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 3';
> + # If the bug exists, resume repair thread after reaching the retry limit.
> + SET DEBUG_SYNC= 'after_insert SIGNAL end_repair';
> + # If the bug is fixed, we wait for refresh of repaired table.
> + # In this case resume repair thread so that we do not deadlock.
> + SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL end_repair';
> + send INSERT INTO t2 VALUES (1);
> +#
> +--echo connection default;
> +connection default;
> +# Wait for signal from insert. Would be infinite with bug and no retry limit.
> +SET DEBUG_SYNC= 'now WAIT_FOR end_repair';
> +UNLOCK TABLES;
> +#
> + --echo connection con1
> + connection con1;
> + # Succeeds with bug fixed.
> + reap;
> + disconnect con1;
> +#
> +--echo connection default;
> +connection default;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2;
> +#
> +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> +# Problem #3
> +# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
> +# This did *not* require a MERGE table.
> +# To increase reproducibility it was necessary to enter a sleep of 2 seconds
> +# at the end of wait_for_tables() after unlock of LOCK_open. In 5.0 and 5.1
> +# the sleep must be inserted in open_and_lock_tables() after open_tables()
> +# instead. wait_for_tables() is not used in this case.
> +# The problem was that FLUSH TABLES releases LOCK_open while having unlocked
> +# and closed all tables. When this happened while a thread was in the loop in
> +# mysql_lock_tables() right after wait_for_tables() and before retrying to
> +# lock, the thread got the lock. (Translate to similar code places in 5.0
> +# and 5.1). And it did not notice that the table needed a refresh. So it
> +# executed its statement on the table.
> +# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and let
> +# it wait in wait_for_tables(). (open_table() in 5.0 and 5.1). The second
> +# FLUSH TABLES must happen while the INSERT was on its way from
> +# wait_for_tables() to the next call of thr_multi_lock(). This needed to be
> +# supported by a sleep to make it repeatable.
> +# In >= 5.0, when waiting after open_tables() one FLUSH is sufficient
> +# to allow the INSERT to step in.
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # After open, wait for flush.
> + SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> + # If bug is fixed, INSERT will go into wait_for_lock.
> + # Retain action after use. First used by general_log.
> + SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
> + # Alternatively INSERT can wait for refresh in open_table().
> + SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL locked';
> + # If bug is not fixed, INSERT will succeed. Pretend locked.
> + SET DEBUG_SYNC= 'after_insert SIGNAL locked';
> + send INSERT INTO t1 VALUES (1);
> +#
> +--echo connection default
> +connection default;
> +# Wait until INSERT opened the table.
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +#
> +# Let INSERT exploit the gap when flush waits wthout lock
> +# for other threads to close the tables.
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLES;
> +#
> +# Wait until INSERT is locked (bug fixed) or finished (bug not fixed).
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +#
> +# This should give no result. But it will if the bug exists.
> +SELECT * FROM t1;
> +UNLOCK TABLES;
> +#
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +#
> +--echo connection default
> +connection default;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
> +#
> +# Show that truncate of child table waits while parent table is used.
> +CREATE TABLE t1 (c1 REAL) ENGINE=MyISAM;
> +CREATE TABLE t2 (c1 REAL) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES(0.1);
> +INSERT INTO t2 VALUES(0.2);
> +CREATE TABLE t3 (c1 REAL) ENGINE=MRG_MYISAM UNION=(t1,t2);
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # When reaching acos(), send 'select' and wait for truncated.
> + SET DEBUG_SYNC= 'before_acos_function
> + SIGNAL select WAIT_FOR truncated';
> + send SELECT ACOS(c1) FROM t3;
> +#
> +--echo connection default
> +connection default;
> +# Wait for con1 to reach acos().
> +SET DEBUG_SYNC= 'now WAIT_FOR select';
> +#
> +# With bug fix present, TRUNCATE runs into wait_for_locked_table_names().
> +SET DEBUG_SYNC= 'before_wait_locked_tname SIGNAL truncated';
> +TRUNCATE TABLE t1;
> +#
> +# With bug fix not present, we need to signal after TRUNCATE.
> +SET DEBUG_SYNC= 'now SIGNAL truncated';
> +#
> + --echo connection con1
> + connection con1;
> + # In non-debug server, the order of select and truncate is undetermined.
> + # So we may have one or two rows here.
> + --disable_result_log
> + reap;
> + --enable_result_log
> + disconnect con1;
> +#
> +--echo connection default
> +connection default;
> +SELECT ACOS(c1) FROM t3;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1, t2, t3;
> +
> +#
> +# In-depth test.
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # Wait for flush before attaching children.
> + SET DEBUG_SYNC= 'before_myisammrg_attach
> + SIGNAL attach WAIT_FOR flushed';
> + send INSERT INTO m1 VALUES (2);
> +--echo connection default;
> +connection default;
> +#
> +# Wait for con1 to reach attach_merge_children(), then flush and signal.
> +SET DEBUG_SYNC= 'now WAIT_FOR attach';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE m1;
> +#
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +--echo connection default;
> +connection default;
> +SELECT * FROM m1;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE m1, t1;
> +#
> +# Test derived from test program for
> +# Bug#30273 - merge tables: Can't lock file (errno: 155)
> +# Second test try to step in between lock_count() and store_lock().
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + # When reaching attach_merge_children(), signal attach and
> + # wait for store_lock1 before attaching children.
> + # Then run through attach of children until store_lock().
> + # Signal store_lock2 and wait for flushed.
> + SET DEBUG_SYNC= 'before_myisammrg_attach
> + SIGNAL attach WAIT_FOR store_lock1';
> + SET DEBUG_SYNC= 'before_myisammrg_store_lock
> + SIGNAL store_lock2 WAIT_FOR flushed';
> + send INSERT INTO m1 VALUES (2);
> +--echo connection default;
> +connection default;
> +#
> +# Wait for con1 to reach attach.
> +SET DEBUG_SYNC= 'now WAIT_FOR attach';
> +# Run until myisammrg store_lock(),
> +# then signal store_lock1 and
> +# wait for con1 to go through attach until store_lock() (store_lock2),
> +# then flush and signal flushed.
> +SET DEBUG_SYNC= 'before_myisammrg_store_lock
> + SIGNAL store_lock1 WAIT_FOR store_lock2';
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed';
> +FLUSH TABLE m1;
> +#
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +--echo connection default;
> +connection default;
> +SELECT * FROM m1;
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE m1, t1;
> +
> +#
> +# Coverage test for mysql_lock_retry hit limit.
> +# Similar test as for Bug#26379, Problem #3.
> +# But mysql_lock_retry hit limit set.
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + SET DEBUG_SYNC= 'mysql_lock_retry HIT_LIMIT 1';
> + # After open, wait for flush.
> + SET DEBUG_SYNC= 'before_lock_tables_takes_lock
> + SIGNAL opened WAIT_FOR flushed';
> + # If bug is not fixed, INSERT will succeed. Pretend locked.
> + SET DEBUG_SYNC= 'after_insert SIGNAL locked';
> + # If bug is fixed, INSERT will go into wait_for_lock.
> + # Retain action after use. First used by general_log.
> + SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
> + # Alternatively INSERT can wait for refresh in open_table().
> + SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL locked';
> + send INSERT INTO t1 VALUES (1);
> +#
> +--echo connection default;
> +connection default;
> +#
> +# Wait until INSERT opened the table.
> +SET DEBUG_SYNC= 'now WAIT_FOR opened';
> +#
> +# Let INSERT exploit the gap when flush waits wthout lock
> +# for other threads to close the tables.
> +SET DEBUG_SYNC= 'after_flush_unlock SIGNAL flushed EXECUTE 2';
> +FLUSH TABLES;
> +#
> +# Wait until INSERT is locked (bug fixed) or finished (bug not fixed).
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +UNLOCK TABLES;
> +#
> + --echo connection con1
> + connection con1;
> + # Succeeds if DEBUG_SYNC is disabled
> + --error 0, ER_DEBUG_SYNC_HIT_LIMIT
> + reap;
> + disconnect con1;
> +#
> +--echo connection default;
> +connection default;
> +#
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
> +
> +#
> +# Coverage test for wait_for_lock.
> +#
> +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> +LOCK TABLE t1 WRITE;
> +#
> + --echo connection con1
> + connect (con1,localhost,root,,);
> + SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked';
> + send INSERT INTO t1 VALUES (1);
> +#
> +--echo connection default;
> +connection default;
> +#
> +# Wait until INSERT is locked.
> +SET DEBUG_SYNC= 'now WAIT_FOR locked';
> +UNLOCK TABLES;
> +#
> + --echo connection con1
> + connection con1;
> + reap;
> + disconnect con1;
> +#
> +--echo connection default;
> +connection default;
> +#
> +# Clear debug_sync signal.
> +SET DEBUG_SYNC= 'RESET';
> +DROP TABLE t1;
> +
====================================================================================
> diff -Nrup a/mysql-test/t/merge.test b/mysql-test/t/merge.test
> --- a/mysql-test/t/merge.test 2007-12-13 13:56:18 +01:00
> +++ b/mysql-test/t/merge.test 2008-03-11 18:54:23 +01:00
> @@ -2,6 +2,7 @@
> # Test of MERGE TABLES
> #
>
> +# Clean up resources used in this test case.
> --disable_warnings
> drop table if exists t1,t2,t3,t4,t5,t6;
> drop database if exists mysqltest;
> @@ -691,113 +692,6 @@ SELECT * FROM t3;
> UNLOCK TABLES;
> DROP TABLE t1, t2, t3, t4;
>
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> -# Preparation
> -connect (con1,localhost,root,,);
> -connect (con2,localhost,root,,);
> -connection default;
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> -# Problem #1
> -# A thread trying to lock a MERGE table performed busy waiting while
> -# REPAIR TABLE or a similar table administration task was ongoing on one or
> -# more of its MyISAM tables.
> -# To allow for observability it was necessary to enter a multi-second sleep
> -# in mysql_admin_table() after remove_table_from_cache(), which comes after
> -# mysql_abort_lock(). The sleep faked a long running operation. One could
> -# watch a high CPU load during the sleep time.
> -# The problem was that mysql_abort_lock() upgrades the write lock to
> -# TL_WRITE_ONLY. This lock type persisted until the final unlock at the end
> -# of the administration task. The effect of TL_WRITE_ONLY is to reject any
> -# attempt to lock the table. The trying thread must close the table and wait
> -# until it is no longer used. Unfortunately there is no way to detect that
> -# one of the MyISAM tables of a MERGE table is in use. When trying to lock
> -# the MERGE table, all MyISAM tables are locked. If one fails on
> -# TL_WRITE_ONLY, all locks are aborted and wait_for_tables() is entered.
> -# But this doesn't see the MERGE table as used, so it seems appropriate to
> -# retry a lock...
> -#
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> -send REPAIR TABLE t1;
> - connection con1;
> - sleep 1; # let repair run into its sleep
> - INSERT INTO t2 VALUES (1);
> -connection default;
> -reap;
> -DROP TABLE t1, t2;
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> -# Problem #2
> -# A thread trying to lock a MERGE table performed busy waiting until all
> -# threads that did REPAIR TABLE or similar table administration tasks on
> -# one or more of its MyISAM tables in LOCK TABLES segments did
> -# UNLOCK TABLES.
> -# The difference against problem #1 is that the busy waiting took place
> -# *after* the administration task. It was terminated by UNLOCK TABLES only.
> -#
> -# This is the same test case as for
> -# Bug#26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging
> -#
> -#
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
> -LOCK TABLE t1 WRITE;
> - connection con1;
> - send INSERT INTO t2 VALUES (1);
> -connection default;
> -sleep 1; # Let INSERT go into thr_multi_lock().
> -REPAIR TABLE t1;
> -sleep 2; # con1 performs busy waiting during this sleep.
> -UNLOCK TABLES;
> - connection con1;
> - reap;
> -connection default;
> -DROP TABLE t1, t2;
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> -# Problem #3
> -# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
> -# This did *not* require a MERGE table.
> -# To increase reproducibility it was necessary to enter a sleep of 2 seconds
> -# at the end of wait_for_tables() after unlock of LOCK_open. In 5.0 and 5.1
> -# the sleep must be inserted in open_and_lock_tables() after open_tables()
> -# instead. wait_for_tables() is not used in this case.
> -# The problem was that FLUSH TABLES releases LOCK_open while having unlocked
> -# and closed all tables. When this happened while a thread was in the loop in
> -# mysql_lock_tables() right after wait_for_tables() and before retrying to
> -# lock, the thread got the lock. (Translate to similar code places in 5.0
> -# and 5.1). And it did not notice that the table needed a refresh. So it
> -# executed its statement on the table.
> -# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and let
> -# it wait in wait_for_tables(). (open_table() in 5.0 and 5.1). The second
> -# FLUSH TABLES must happen while the INSERT was on its way from
> -# wait_for_tables() to the next call of thr_multi_lock(). This needed to be
> -# supported by a sleep to make it repeatable.
> -#
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -LOCK TABLE t1 WRITE;
> - connection con1;
> - send INSERT INTO t1 VALUES (1);
> -connection default;
> -sleep 1; # Let INSERT go into thr_multi_lock().
> -FLUSH TABLES;
> -sleep 1; # Let INSERT go through wait_for_tables() where it sleeps.
> -FLUSH TABLES;
> -# This should give no result. But it will with sleep(2) at the right place.
> -SELECT * FROM t1;
> -UNLOCK TABLES;
> - connection con1;
> - reap;
> -connection default;
> -DROP TABLE t1;
> -#
> -# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
> -# Cleanup
> -disconnect con1;
> -disconnect con2;
> -#
> --echo #
> --echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
> --echo # REPAIR TABLE corrupts a MERGE table
> @@ -1122,6 +1016,7 @@ SELECT @a;
> SELECT * FROM t4 ORDER BY c1;
> DELETE FROM t4 WHERE c1 = 33;
> DROP TRIGGER t3_ai;
> +UNLOCK TABLES;
> --echo #
> --echo # Trigger with table use on child
> DELETE FROM t4 WHERE c1 = 4;
> @@ -1225,62 +1120,17 @@ DROP TABLE t1, t2, t3;
>
> #
> # Bug#25038 - Waiting TRUNCATE
> +# Truncate failed with error message when table was in use by MERGE.
> #
> # Show that truncate of child table after use of parent table works.
> -CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t2 (c1 INT) ENGINE= MyISAM;
> -CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2);
> +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE t2 (c1 INT) ENGINE=MyISAM;
> +CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2);
> INSERT INTO t1 VALUES (1);
> INSERT INTO t2 VALUES (2);
> SELECT * FROM t3;
> TRUNCATE TABLE t1;
> SELECT * FROM t3;
> -DROP TABLE t1, t2, t3;
> -#
> -# Show that truncate of child table waits while parent table is used.
> -# (test partly borrowed from count_distinct3.)
> -CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER);
> -SET @rnd_max= 2147483647;
> -let $1 = 10;
> -while ($1)
> -{
> - SET @rnd= RAND();
> - SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
> - SET @id_rev= @rnd_max - @id;
> - SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
> - INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
> - dec $1;
> -}
> -set @@read_buffer_size=2*1024*1024;
> -CREATE TABLE t2 SELECT * FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
> -INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
> -CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER)
> - ENGINE= MRG_MYISAM UNION= (t1, t2);
> -SELECT COUNT(*) FROM t1;
> -SELECT COUNT(*) FROM t2;
> -SELECT COUNT(*) FROM t3;
> -connect (con1,localhost,root,,);
> - # As t3 contains random numbers, results are different from test to test.
> - # That's okay, because we test only that select doesn't yield an
> - # error. Note, that --disable_result_log doesn't suppress error output.
> - --disable_result_log
> - send SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2
> - WHERE a1.id = a2.id GROUP BY a2.grp;
> -connection default;
> -sleep 1;
> -TRUNCATE TABLE t1;
> - connection con1;
> - reap;
> - --enable_result_log
> - disconnect con1;
> -connection default;
> -SELECT COUNT(*) FROM t1;
> -SELECT COUNT(*) FROM t2;
> -SELECT COUNT(*) FROM t3;
> DROP TABLE t1, t2, t3;
>
> #
====================================================================================
> diff -Nrup a/mysys/thr_lock.c b/mysys/thr_lock.c
> --- a/mysys/thr_lock.c 2008-01-28 13:52:34 +01:00
> +++ b/mysys/thr_lock.c 2008-03-11 18:54:23 +01:00
> @@ -386,6 +386,14 @@ static inline my_bool have_specific_lock
>
> static void wake_up_waiters(THR_LOCK *lock);
>
> +#if defined(ENABLED_DEBUG_SYNC)
> +/**
> + Global pointer to be set if callback function is defined
> + (e.g. in mysqld). See debug_sync.cc.
> +*/
> +void (*debug_sync_wait_for_lock_callback_ptr)(void);
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
>
> static enum enum_thr_lock_result
> wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
> @@ -397,6 +405,15 @@ wait_for_lock(struct st_lock_list *wait,
> enum enum_thr_lock_result result= THR_LOCK_ABORTED;
> my_bool can_deadlock= test(data->owner->info->n_cursors);
> DBUG_ENTER("wait_for_lock");
> +
> +#if defined(ENABLED_DEBUG_SYNC)
> + /*
> + One can use this to signal when a thread is going to wait for a lock.
> + See debug_sync.cc.
> + */
> + if (debug_sync_wait_for_lock_callback_ptr)
> + (*debug_sync_wait_for_lock_callback_ptr)();
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
>
> if (!in_wait_list)
> {
====================================================================================
> diff -Nrup a/sql/CMakeLists.txt b/sql/CMakeLists.txt
> --- a/sql/CMakeLists.txt 2007-12-19 14:34:59 +01:00
> +++ b/sql/CMakeLists.txt 2008-03-11 18:54:23 +01:00
> @@ -65,6 +65,7 @@ ADD_EXECUTABLE(mysqld
> sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc
> sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc
> sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
> + debug_sync.cc
> sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
> sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
> sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
====================================================================================
> diff -Nrup a/sql/Makefile.am b/sql/Makefile.am
> --- a/sql/Makefile.am 2007-12-21 20:27:42 +01:00
> +++ b/sql/Makefile.am 2008-03-11 18:54:23 +01:00
> @@ -112,6 +112,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
> discover.cc time.cc opt_range.cc opt_sum.cc \
> records.cc filesort.cc handler.cc \
> ha_partition.cc \
> + debug_sync.cc \
> sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
> sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
> sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
====================================================================================
> diff -Nrup a/sql/debug_sync.cc b/sql/debug_sync.cc
> --- /dev/null Wed Dec 31 16:00:00 196900
> +++ b/sql/debug_sync.cc 2008-03-11 18:54:24 +01:00
> @@ -0,0 +1,1527 @@
> +/* Copyright (C) 2008 MySQL AB
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
> +
> +/*
> + Debug Sync Facility.
> +
> + The Debug Sync Facility allows to place synchronization points in the
> + code:
> +
> + open_tables(...)
> +
> + DEBUG_SYNC(thd, "after_open_tables");
> +
> + lock_tables(...)
> +
> + When activated, a sync point can
> +
> + - Send a signal and/or
> + - Wait for a signal
> +
> + Nomenclature:
> +
> + - signal: A value of a global variable that persists
> + until overwritten by a new signal. The global
> + variable can also be seen as a "signal post"
> + or "flag mast". Then the signal is what is
> + attached to the "signal post" or "flag mast".
> +
> + - send a signal: Assign the value (the signal) to the global
> + variable ("set a flag") and broadcast a
> + global condition to wake those waiting for
> + a signal.
> +
> + - wait for a signal: Loop over waiting for the global condition until
> + the global value matches the wait-for signal.
> +
> + By default, all sync points are inactive. They do nothing (except of
> + burning a couple of CPU cycles for checking if they are active).
> +
> + A sync point becomes active when an action is requested for it:
> +
> + SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
> +
> + This activates the sync point 'after_open_tables'. It requests it to
> + send the signal 'opened' and wait for another thread to send the signal
> + 'flushed' when the threads execution runs through the sync point.
> +
> + For every sync point there can be one action per thread only. Every
> + thread can request multiple actions, but only one per sync point. In
> + other words, a thread can activate multiple sync points.
> +
> + Here is an example how to activate and use the sync points:
> +
> + --connection conn1
> + SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
> + send INSERT INTO t1 VALUES(1);
> + --connection conn2
> + SET DEBUG_SYNC= 'now WAIT_FOR opened';
> + SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
> + FLUSH TABLE t1;
> +
> + When conn1 runs through the INSERT statement, it hits the sync point
> + 'after_open_tables'. It notices that it is active and executes its
> + action. It sends the signal 'opened' and waits for another thread to
> + send the signal 'flushed'.
> +
> + conn2 waits immediately at the special sync point 'now' for another
> + thread to send the 'opened' signal.
> +
> + A signal remains in effect until it is overwritten. If conn1 signals
> + 'opened' before conn2 reaches 'now', it will still find the 'opened'
> + signal. It does not wait in this case.
> +
> + When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
> + conn1 awake.
> +
> + Normally the activation of a sync point is cleared when it has been
> + executed. Sometimes it is necessary to keep the sync point active for
> + another execution. You can add an execute count to the action:
> +
> + SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
> +
> + This will set an activation counter to 3. Each execution decrements the
> + counter. After the third execution the sync point becomes inactive in
> + this example.
> +
> + One of the primary goals of this facility is to eliminate sleeps from
> + the test suite. In most cases it should be possible to rewrite test
> + cases so that they do not need to sleep. (But this facility cannot
> + synchronize multiple processes.) However to support developing of the
> + tests, and as a last resort, sync point waiting times out. There is a
> + default timeout, but it can be overridden:
> +
> + SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
> +
> + TIMEOUT 0 is special: If the signal is not present, the wait times out
> + immediately.
> +
> + When a wait timed out (even on TIMEOUT 0), a warning is generated so
> + that it shows up in the test result.
> +
> + You can throw an error message and kill the query when a synchronization
> + point is hit a certain number of times:
> +
> + SET DEBUG_SYNC= 'name HIT_LIMIT 3';
> +
> + Or combine it with signal and/or wait:
> +
> + SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
> +
> + Here the first two hits send the signal, the third hit returns the error
> + message and kills the query.
> +
> + For cases where you are not sure that an action is taken and thus
> + cleared in any case, you can force to clear (deactivate) a sync point:
> +
> + SET DEBUG_SYNC= 'name CLEAR';
> +
> + If you want to clear all actions and clear the global signal, use:
> +
> + SET DEBUG_SYNC= 'RESET';
> +
> + This is the only way to reset the global signal to an empty string.
> +
> + For testing of the facility itself you can test a sync point:
> +
> + SET DEBUG_SYNC= 'name TEST';
> +
> +
> + ==== Activation/Deactivation ====
> +
> + The facility is an optional part of the MySQL server.
> +
> + ./configure --enable-debug-sync
> +
> + The Debug Sync Facility, when compiled in, is disabled by default. It
> + can be enabled by a mysqld command line option:
> +
> + --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
> +
> + 'default_wait_timeout_value_in_seconds' is the default timeout for the
> + WAIT_FOR action. If set to zero, the facility stays disabled.
> +
> + The facility is enabled by default in the test suite, but can be
> + disabled with:
> +
> + mysql-test-run.pl ... --debug-sync-timeout=0 ...
> +
> + Likewise the default wait timeout can be set:
> +
> + mysql-test-run.pl ... --debug-sync-timeout=10 ...
> +
> +
> + ==== Implementation ====
> +
> + Pseudo code for a sync point:
> +
> + #define DEBUG_SYNC(thd, sync_point_name)
> + if (unlikely(opt_debug_sync_timeout))
> + debug_sync(thd, STRING_WITH_LEN(sync_point_name))
> +
> + The sync point performs a binary search in a sorted array of actions
> + for this thread.
> +
> + The SET DEBUG_SYNC statement adds a requested action to the array or
> + overwrites an existing action for the same sync point. When it adds a
> + new action, the array is sorted again.
> +*/
> +
> +#include "mysql_priv.h"
> +
> +#if defined(ENABLED_DEBUG_SYNC)
> +
> +/* Action to perform at a synchronization point. */
> +struct st_debug_sync_action
> +{
> + ulong activation_count; /* max(hit_limit, execute) */
> + ulong hit_limit; /* hits before kill query */
> + ulong execute; /* executes before self-clear */
> + ulong timeout; /* wait_for timeout */
> + String signal; /* signal to send */
> + String wait_for; /* signal to wait for */
> + String sync_point; /* sync point name */
> + bool need_sort; /* if new action, array needs sort */
> +};
> +
> +/* Debug sync control. Referenced by THD. */
> +struct st_debug_sync_control
> +{
> + st_debug_sync_action *ds_action; /* array of actions */
Wouldn't it be nicer to declare it as "st_debug_sync_action ds_action[]"?
Another thing - why not use dynamic array here and reuse the code from mysys
library?
> + uint ds_active; /* # active actions */
> + uint ds_allocated; /* # allocated actions */
> + ulonglong dsp_hits; /* statistics */
> + ulonglong dsp_executed; /* statistics */
> + ulonglong dsp_max_active; /* statistics */
> +};
> +
> +
> +/**
> + Definitions for the debug sync facility.
> + 1. Global string variable to hold a "signal" ("signal post", "flag mast").
> + 2. Global condition variable for signalling and waiting.
> + 3. Global mutex to synchronize access to the above.
> +*/
> +struct st_debug_sync_globals
> +{
> + String ds_signal; /* signal variable */
> + pthread_cond_t ds_cond; /* condition variable */
> + pthread_mutex_t ds_mutex; /* mutex variable */
> + ulonglong dsp_hits; /* statistics */
> + ulonglong dsp_executed; /* statistics */
> + ulonglong dsp_max_active; /* statistics */
> +};
> +static st_debug_sync_globals debug_sync_global; /* All globals in one object */
> +
> +/**
> + Callback pointer from thr_lock.cc
> +*/
> +extern void (*debug_sync_wait_for_lock_callback_ptr)(void);
> +
> +
> +/**
> + Callback from wait_for_lock() for debug sync. See thr_lock.c.
> +
> + @description
> + One can use this to signal when a thread is going to wait for a lock.
I was bit confused looking at this callback function. Perhaps in the comment you
could elaborate more on why you define it here and how it is used.
> +
> + @note
> + Beware of waiting for a signal here. The lock has aquired its mutex.
> + While waiting on a signal here, the locking thread could not aquire
> + the mutex to release the lock. One could lock up the table
> + completely.
Hmm, I don't understand this note. But perhaps it's only me...
> +
> + @note
> + The callback pointer in thr_lock.c is set only if debug sync is
> + initialized. And this is done only if opt_debug_sync_timeout is set.
> +*/
> +
> +static void debug_sync_wait_for_lock_callback(void)
> +{
> + DEBUG_SYNC(current_thd, "wait_for_lock");
> +}
> +
> +
> +/**
> + Initialize the debug sync facility at server start.
> +
> + @return status
> + @retval 0 ok
> + @retval != 0 error
> +*/
> +
> +int debug_sync_init(void)
> +{
> + DBUG_ENTER("debug_sync_init");
> +
> + if (opt_debug_sync_timeout)
> + {
> + /* Initialize the global variables. */
> + debug_sync_global.ds_signal.length(0);
> + (void) pthread_cond_init(&debug_sync_global.ds_cond, NULL);
> + (void) pthread_mutex_init(&debug_sync_global.ds_mutex, MY_MUTEX_INIT_FAST);
> +
> + /* Set the call back pointer in thr_lock.c. */
> + debug_sync_wait_for_lock_callback_ptr=
> + debug_sync_wait_for_lock_callback;
> + }
> +
> + DBUG_RETURN(0);
> +}
> +
> +
> +/**
> + End the debug sync facility.
> +
> + @description
> + This is called at server shutdown or after a thread initialization error.
> +*/
> +
> +void debug_sync_end(void)
> +{
> + DBUG_ENTER("debug_sync_end");
> +
> + /* End the facility only if it had been initialized. */
> + if (debug_sync_wait_for_lock_callback_ptr)
> + {
> + /* Clear the call back pointer in thr_lock.c. */
> + debug_sync_wait_for_lock_callback_ptr= NULL;
> +
> + /* Destroy the global variables. */
> + debug_sync_global.ds_signal.free();
> + (void) pthread_cond_destroy(&debug_sync_global.ds_cond);
> + (void) pthread_mutex_destroy(&debug_sync_global.ds_mutex);
> +
> + /* Print statistics. */
> + {
> + char llbuff[22];
> + sql_print_information("Debug sync points hit: %22s",
> + llstr(debug_sync_global.dsp_hits, llbuff));
> + sql_print_information("Debug sync points executed: %22s",
> + llstr(debug_sync_global.dsp_executed, llbuff));
> + sql_print_information("Debug sync points max active per thread: %22s",
> + llstr(debug_sync_global.dsp_max_active, llbuff));
> + }
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/* purecov: begin tested */
> +
> +/**
> + Disable the facility after lack of memory if no error can be returned.
> +
> + @param[in] thd thread handle
> +
> + @note
> + Do not end the facility here because the global variables can
> + be in use by other threads.
> +*/
> +
> +static void debug_sync_emergency_disable(THD *thd)
> +{
> + DBUG_ENTER("debug_sync_emergency_disable");
> +
> + opt_debug_sync_timeout= 0;
> +
> + DBUG_PRINT("debug_sync",
> + ("Debug Sync Facility disabled due to lack of memory."));
> + sql_print_error("Debug Sync Facility disabled due to lack of memory.");
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +/* purecov: end */
> +
> +
> +/**
> + Initialize the debug sync facility at thread start.
> +
> + @param[in] thd thread handle
> +*/
> +
> +void debug_sync_init_thread(THD *thd)
> +{
> + DBUG_ENTER("debug_sync_init_thread");
> +
> + if (opt_debug_sync_timeout)
> + {
> + thd->debug_sync_control= (st_debug_sync_control*)
> + my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
> + if (!thd->debug_sync_control)
> + {
> + /*
> + Error is reported by my_malloc().
> + We must disable the facility. We have no way to return an error.
> + */
> + debug_sync_emergency_disable(thd); /* purecov: tested */
> + }
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Free sync point actions.
> +
> + @param[in] action first action
> + @param[in] count number of actions to free
> +*/
> +
> +static void debug_sync_free_actions(st_debug_sync_action *action, uint count)
> +{
> + st_debug_sync_action *action_end= action + count;
> + DBUG_ENTER("debug_sync_free_actions");
> +
> + for (; action < action_end; action++)
> + {
> + action->signal.free();
> + action->wait_for.free();
> + action->sync_point.free();
> + }
> + DBUG_VOID_RETURN;
> +}
> +
It is just a suggestion, but adding constructor/destructor to
st_debug_sync_action structure would mke the code look nicer and easier to
maintain IMO.
> +
> +/**
> + End the debug sync facility at thread end.
> +
> + @param[in] thd thread handle
> +*/
> +
> +void debug_sync_end_thread(THD *thd)
> +{
> + DBUG_ENTER("debug_sync_end_thread");
> +
> + if (thd->debug_sync_control)
> + {
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> +
> + if (ds_control->ds_action)
> + {
> + debug_sync_free_actions(ds_control->ds_action,
> ds_control->ds_allocated);
> + my_free(ds_control->ds_action, MYF(0));
> + }
> +
> + /* Statistics. */
> + pthread_mutex_lock(&debug_sync_global.ds_mutex);
> + debug_sync_global.dsp_hits+= ds_control->dsp_hits;
> + debug_sync_global.dsp_executed+= ds_control->dsp_executed;
> + if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
> + debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
> + pthread_mutex_unlock(&debug_sync_global.ds_mutex);
> +
> + my_free(ds_control, MYF(0));
> + thd->debug_sync_control= NULL;
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Move a string by length.
> +
> + @param[out] to buffer for the resulting string
> + @param[in] to_end end of buffer
> + @param[in] from source string
> + @param[in] length number of bytes to copy
> +
> + @return pointer to end of copied string
> +*/
> +
> +static char *debug_sync_bmove_len(char *to, char *to_end,
> + const char *from, size_t length)
> +{
> + set_if_smaller(length, (size_t) (to_end - to));
> + memcpy(to, from, length);
> + return (to + length);
> +}
> +
> +
> +#if !defined(DBUG_OFF)
> +
> +/**
> + Create a string that describes an action.
> +
> + @param[out] result buffer for the resulting string
> + @param[in] size size of result buffer
> + @param[in] action action to describe
> +*/
> +
> +static void debug_sync_action_string(char *result, uint size,
> + st_debug_sync_action *action)
> +{
> + char *wtxt= result;
> + char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
> + DBUG_ENTER("debug_sync_action_string");
> +
> + /* There must not be inactive actions in the array. */
> + DBUG_ASSERT(action->activation_count);
> +
> + /* If an execute count is present, signal or wait_for are needed too. */
> + DBUG_ASSERT(!action->execute ||
> + action->signal.length() || action->wait_for.length());
> +
> + if (action->execute)
> + {
> + if (action->signal.length())
> + {
> + wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
> + wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
> + action->signal.length());
> + }
> + if (action->wait_for.length())
> + {
> + if ((wtxt == result) && (wtxt < wend))
> + *(wtxt++)= ' ';
> + wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("WAIT_FOR "));
> + wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
> + action->wait_for.length());
> +
> + if (action->timeout != opt_debug_sync_timeout)
> + {
> + wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
> + }
> + }
> + if (action->execute != 1)
> + {
> + wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
> + }
> + }
> + if (action->hit_limit)
> + {
> + wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
> + (wtxt == result) ? "" : " ", action->hit_limit);
> + }
> +
> + /*
> + If (wtxt == wend) string may not be terminated.
> + There is one byte left for an emergency termination.
> + */
> + *wtxt= '\0';
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Print actions.
> +
> + @param[in] thd thread handle
> +*/
> +
> +static void debug_sync_print_actions(THD *thd)
> +{
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> + uint idx;
> + DBUG_ENTER("debug_sync_print_actions");
> +
> + if (!ds_control)
> + return;
> +
> + for (idx= 0; idx < ds_control->ds_active; idx++)
> + {
> + if (ds_control->ds_action[idx].activation_count)
> + {
> + char action_string[256];
> +
> + debug_sync_action_string(action_string, sizeof(action_string),
> + ds_control->ds_action + idx);
> + DBUG_PRINT("debug_sync_list",
> + ("%s %s", ds_control->ds_action[idx].sync_point.c_ptr(),
> + action_string));
> + }
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +#endif /* !defined(DBUG_OFF) */
> +
> +
> +/**
> + Compare two actions by sync point name length, string.
> +
> + @param[in] arg1 reference to action1
> + @param[in] arg2 reference to action2
> +
> + @return difference
> + @retval == 0 length1/string1 is same as length2/string2
> + @retval < 0 length1/string1 is smaller
> + @retval > 0 length1/string1 is bigger
> +*/
> +
> +static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
> +{
> + st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
> + st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
> + int diff;
> +
> + if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
> + diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
> + action1->sync_point.length());
> +
> + return diff;
> +}
> +
> +
> +/**
> + Find a debug sync action.
> +
> + @param[in] actionarr array of debug sync actions
> + @param[in] quantity number of actions in array
> + @param[in] dsp_name name of debug sync point to find
> + @param[in] name_len length of name of debug sync point
> +
> + @return action
> + @retval != NULL found sync point in array
> + @retval NULL not found
> +
> + @description
> + Binary search. Array needs to be sorted by length, sync point name.
> +*/
> +
> +static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
> + int quantity,
> + const char* dsp_name,
> + uint name_len)
> +{
> + st_debug_sync_action *action;
> + int low ;
> + int high ;
> + int mid ;
> + int diff ;
> +
> + low= 0;
> + high= quantity;
> +
> + while (low < high)
> + {
> + mid= (low + high) / 2;
> + action= actionarr + mid;
What if actionarr is NULL?
> + if (!(diff= name_len - action->sync_point.length()) &&
> + !(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
> + return action;
> + if (diff > 0)
> + low= mid + 1;
> + else
> + high= mid - 1;
> + }
> +
> + if (low < quantity)
> + {
> + action= actionarr + low;
> + if ((name_len == action->sync_point.length()) &&
> + !memcmp(dsp_name, action->sync_point.ptr(), name_len))
> + return action;
> + }
> +
> + return NULL;
> +}
> +
> +
> +/**
> + Reset the debug sync facility.
> +
> + @param[in] thd thread handle
> +
> + @description
> + Remove all actions of this thread.
> + Clear the global signal.
> +*/
> +
> +static void debug_sync_reset(THD *thd)
> +{
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> + DBUG_ENTER("debug_sync_reset");
> +
> + /* Remove all actions of this thread. */
> + ds_control->ds_active= 0;
What if ds_control is NULL?
> +
> + /* Clear the global signal. */
> + debug_sync_global.ds_signal.length(0);
> +
Don't we need to call String destructors for all stored actions?
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Remove a debug sync action.
> +
> + @param[in] ds_control control object
> + @param[in] action action to be removed
> +
> + @description
> + Removing an action mainly means to decrement the ds_active counter.
> + But if the action is between other active action in the array, then
> + the array needs to be shrinked. The active actions above the one to
> + be removed have to be moved down by one slot.
> +*/
> +
> +static void debug_sync_remove_action(st_debug_sync_control *ds_control,
> + st_debug_sync_action *action)
> +{
DBUG_ASSERT(ds_control) ?
DBUG_ASSERT(action) ?
> + uint dsp_idx= action - ds_control->ds_action;
This code will break if we store pointer to an action, then add new actions to
ds_control->ds_action so that it is re-allocated to a new location and then try
to remove stored action... Should be documented as precondition for this
function or... a safer implementation used.
> + DBUG_ENTER("debug_sync_remove_action");
> + DBUG_ASSERT(dsp_idx < ds_control->ds_active);
> +
> + /* Decrement the number of currently active actions. */
> + ds_control->ds_active--;
> +
> + /*
> + If this was not the last active action in the array, we need to
> + shift remaining active actions down to keep the array gap-free.
> + Otherwise binary search might fail or take longer than necessary at
> + least. Also new actions are always put to the end of the array.
> + */
> + if (ds_control->ds_active > dsp_idx)
> + {
> + /* Free strings before copying them over. */
> + debug_sync_free_actions(action, 1);
> +
> + /* Move actions down. */
> + memmove(ds_control->ds_action + dsp_idx,
> + ds_control->ds_action + dsp_idx + 1,
> + (ds_control->ds_active - dsp_idx) *
> + sizeof(st_debug_sync_action));
> +
> + /* Remove double references of String pointers. */
> + bzero(ds_control->ds_action +
> + ds_control->ds_active, sizeof(st_debug_sync_action));
To my taste it is too dangerous to move constructed objects like this. My
objections are:
- I'm not sure if C++ compilers guarantee (and will guarantee that in the
future) that if x is an object of type X and you move sizeof(X) bytes from &x to
a different addres b then you will get a valid instance of X object at b
- In the future one can change either st_debug_sync_action or String class. A
possible change is to add member with non-trivial copy constructor (e.g. with
reference couts). Then above code will break things.
Btw. Have you considered using some pointer structure (binary search tree or
even a simple linked list) for storing actions. Then there will be no need to
move objects around.
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Get a debug sync action.
> +
> + @param[in] thd thread handle
> + @param[in] dsp_name debug sync point name
> + @param[in] name_len length of sync point name
> +
> + @return action
> + @retval != NULL ok
> + @retval NULL error
> +
> + @description
> + Find the debug sync action for a debug sync point or make a new one.
> +*/
> +
> +static st_debug_sync_action *debug_sync_get_action(THD *thd,
> + const char *dsp_name,
> + uint name_len)
> +{
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> + st_debug_sync_action *action;
> + DBUG_ENTER("debug_sync_get_action");
> + DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
> +
DBUG_ASSERT(ds_control) ?
> + /* There cannot be more active actions than allocated. */
> + DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
> + /* If there are active actions, the action array must be present. */
> + DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
> +
> + /* Try to reuse existing action if there is one for this sync point. */
> + if (ds_control->ds_active &&
> + (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
> + dsp_name, name_len)))
> + {
> + /* Reuse an already active sync point action. */
> + DBUG_ASSERT((action - ds_control->ds_action) < ds_control->ds_active);
> + DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
> + action - ds_control->ds_action));
> + }
> + else
> + {
> + /* Create a new action. */
> + int dsp_idx= ds_control->ds_active++;
> + set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
> + if (ds_control->ds_active > ds_control->ds_allocated)
> + {
> + uint new_alloc= ds_control->ds_active + 3;
> + void *new_action= my_realloc(ds_control->ds_action,
> + new_alloc * sizeof(st_debug_sync_action),
> + MYF(MY_WME | MY_ALLOW_ZERO_PTR));
Is it the case that if my_realloc moves the memory to a different location it
copies all the bytes from the old location?
> + if (!new_action)
> + {
> + /* Error is reported by my_malloc(). */
> + goto err; /* purecov: tested */
> + }
> + ds_control->ds_action= (st_debug_sync_action*) new_action;
> + ds_control->ds_allocated= new_alloc;
> + /* Clear memory as we do not run string constructors here. */
> + bzero((uchar*) (ds_control->ds_action + dsp_idx),
> + (new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
> + }
> + DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
> + action= ds_control->ds_action + dsp_idx;
> + if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
> + {
> + /* Error is reported by my_malloc(). */
> + goto err; /* purecov: tested */
> + }
> + action->need_sort= TRUE;
> + }
> + DBUG_ASSERT(action >= ds_control->ds_action);
> + DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
> + DBUG_PRINT("debug_sync", ("que action: 0x%lx beg: 0x%lx count: %u",
> + (long) action, (long) ds_control->ds_action,
> + ds_control->ds_active));
> +
> + DBUG_RETURN(action);
> +
> + err:
> + DBUG_RETURN(NULL);
> +}
> +
> +
> +/**
> + Set a debug sync action.
> +
> + @param[in] thd thread handle
> + @param[in] action synchronization action
> +
> + @return status
> + @retval FALSE ok
> + @retval TRUE error
> +
> + @description
> + This is called from the debug sync parser.
Perhaps elaborate more what does that mean to "set an action".
> +*/
> +
> +static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
> +{
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> + bool is_dsp_now= FALSE;
> + DBUG_ENTER("debug_sync_set_action");
> + DBUG_ASSERT(ds_control);
You also assume action != NULL.
> +
> + action->activation_count= max(action->hit_limit, action->execute);
> + if (!action->activation_count)
> + {
> + debug_sync_remove_action(ds_control, action);
> + DBUG_PRINT("debug_sync", ("action cleared"));
> + }
> + else
> + {
> + DBUG_PRINT("debug_sync",
> + ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
> + "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
> + action->sync_point.c_ptr(), action->activation_count,
> + action->hit_limit, action->execute, action->timeout,
> + action->signal.c_ptr(), action->wait_for.c_ptr()));
> +
> + /* Check this before sorting the array. action may move. */
> + is_dsp_now= !my_strcasecmp(system_charset_info,
> + action->sync_point.c_ptr(), "now");
> +
> + if (action->need_sort)
> + {
> + action->need_sort= FALSE;
> + /* Sort actions by (name_len, name). */
> + my_qsort(ds_control->ds_action, ds_control->ds_active,
> + sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
> + }
> + }
> + DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
> +
> + /* Execute the special sync point 'now' if activated above. */
> + if (is_dsp_now)
> + {
> + DEBUG_SYNC(thd, "now");
> + /* If HIT_LIMIT is 1, return error to suppress send_ok(). */
I don't understand the above comment...
> + if (thd->killed)
> + goto err;
> + }
> +
> + DBUG_RETURN(FALSE);
> +
> + err:
> + DBUG_RETURN(TRUE);
> +}
> +
> +
> +/**
> + Extract a token from a string.
> +
> + @param[out] token_p returns start of token
> + @param[out] token_length_p returns length of token
> + @param[in] ptr current string pointer
> +
> + @return string pointer or NULL
> + @retval != NULL ptr behind token terminator or at string end
> + @retval NULL no token found in remainder of string
> +
> + @note
> + This function assumes that the string is in system_charset_info,
> + that this charset is single byte for ASCII NUL ('\0'), that no
> + character except of ASCII NUL ('\0') contains a byte with value 0,
> + and that ASCII NUL ('\0') is used as the string terminator.
> +
> + This function needs to return tokens that are terminated with ASCII
> + NUL ('\0'). The tokens are used in my_strcasecmp(). Unfortunately
> + there is no my_strncasecmp().
> +
> + To return the last token without copying it, we require the input
> + string to be nul terminated.
> +
> + @description
> + This function skips space characters at string begin.
> +
> + It returns a pointer to the first non-space character in *token_p.
> +
> + If no non-space character is found before the string terminator
> + ASCII NUL ('\0'), the function returns NULL. *token_p and
> + *token_length_p remain unchanged in this case (they are not set).
> +
> + The function takes a space character or an ASCII NUL ('\0') as a
> + terminator of the token. The space character could be multi-byte.
> +
> + It returns the length of the token in bytes, excluding the
> + terminator, in *token_length_p.
> +
> + If the terminator of the token is ASCII NUL ('\0'), it returns a
> + pointer to the terminator (string end).
> +
> + If the terminator is a space character, it replaces the the first
> + byte of the terminator character by ASCII NUL ('\0'), skips the (now
> + corrupted) terminator character, and skips all following space
> + characters. It returns a pointer to the next non-space character or
> + to the string terminator ASCII NUL ('\0').
> +*/
> +
> +static char *debug_sync_token(char **token_p, uint *token_length_p, char *ptr)
> +{
How about asserting here that token_p, token_length_p and ptr are not NULL? The
code below uses that assumption.
> + /* Skip leading space */
> + while (my_isspace(system_charset_info, *ptr))
> + ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
> +
> + if (!*ptr)
> + {
> + ptr= NULL;
> + goto end;
> + }
> +
> + /* Get token start. */
> + *token_p= ptr;
> +
> + /* Find token end. */
> + while (*ptr && !my_isspace(system_charset_info, *ptr))
> + ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
> +
> + /* Get token length. */
> + *token_length_p= ptr - *token_p;
> +
> + /* If necessary, terminate token. */
> + if (*ptr)
> + {
> + /* Get terminator character length. */
> + uint mbspacelen= my_mbcharlen(system_charset_info, (uchar) *ptr);
> +
> + /* Terminate token. */
> + *ptr= '\0';
> +
> + /* Skip the terminator. */
> + ptr+= mbspacelen;
> +
> + /* Skip trailing space */
> + while (my_isspace(system_charset_info, *ptr))
> + ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
> + }
> +
> + end:
> + return ptr;
> +}
> +
> +
> +/**
> + Extract a number from a string.
> +
> + @param[out] number_p returns number
> + @param[in] ptr current string pointer
> +
> + @return string pointer or NULL
> + @retval != NULL ptr behind token terminator or at string end
> + @retval NULL no token found or token is not valid number
> +
> + @note
> + The same assumptions about charset apply as for debug_sync_token().
> +
> + @description
> + This function fetches a token from the string and converts it
> + into a number.
> +
> + If there is no token left in the string, or the token is not a valid
> + decimal number, NULL is returned. The result in *number_p is
> + undefined in this case.
> +*/
> +
> +static char *debug_sync_number(ulong *number_p, char *ptr)
> +{
> + char *ept;
> + char *token;
> + uint token_length;
> +
> + /* Get token from string. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto end;
> +
> + *number_p= strtoul(token, &ept, 10);
> + if (*ept)
> + ptr= NULL;
> +
> + end:
> + return ptr;
> +}
> +
> +
> +/**
> + Evaluate a debug sync action string.
> +
> + @param[in] thd thread handle
> + @param[in,out] action_str action string to receive '\0' terminators
> +
> + @return status
> + @retval FALSE ok
> + @retval TRUE error
> +
> + @description
> + This is called when the DEBUG_SYNC system variable is set.
> + Parse action string, build a debug sync action, activate it.
Mention that the result of parsing is stored inside thd's action table.
> +
> + @note
> + The input string needs to be ASCII NUL ('\0') terminated. We split
> + nul-terminated tokens in it without copy.
Perhaps add here all the other assumptions about action_str. For example, that
it must be in system character set.
> +*/
> +
This is the grammar for command strings which I deduced from
reverse-engenieering the code (there are additional constraints, for example
EXECUTE must be preceeded by SIGNAL or WAIT_FOR:
RESET
<point name> TEST
<point name> CLEAR
<point name> [SIGNAL <signal name>] [WAIT_FOR <signal name> [TIMEOUT
<seconds>]]
[EXECUTE <count>] [HIT_LIMIT <count>]
Would be good if this grammar is documented somewhere. In particular, the order
of actions is fixed which is not clearly documented in the existing documentation.
> +static bool debug_sync_eval_action(THD *thd, char *action_str)
How about calling it debug_sync_parse_action instead?
> +{
> + st_debug_sync_action *action= NULL;
> + const char *errmsg;
> + char *ptr;
> + char *token;
> + uint token_length;
> + DBUG_ENTER("debug_sync_eval_action");
> +
> + /*
> + Get debug sync point name. Or a special command.
> + */
> + if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
> + {
> + errmsg= "Missing synchronization point name";
Why error messages are not internationalized?
> + goto err;
> + }
> +
> + /*
> + If there is a second token, the first one is the sync point name.
> + */
> + if (*ptr)
> + {
> + /* Get an action object to collect the requested action parameters. */
> + action= debug_sync_get_action(thd, token, token_length);
> + if (!action)
> + {
> + /* Error message is sent. */
> + DBUG_RETURN(TRUE);
> + }
> + }
> +
> + /*
> + Get kind of action to be taken at sync point.
> + */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + {
> + /* No action present. Try special commands. Token unchanged. */
> +
> + /*
> + Try RESET.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "RESET"))
> + {
> + /* It is RESET. Reset all actions and global signal. */
> + debug_sync_reset(thd);
> + goto end;
> + }
> +
> + /* Token unchanged. It still contains sync point name. */
> + errmsg= "Missing action after synchronization point name '%.*s'";
> + goto err;
> + }
> +
> + /*
> + Check for pseudo actions first. Start with actions that work on
> + an existing action.
> + */
> + DBUG_ASSERT(action);
> +
> + /*
> + Try TEST.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "TEST"))
> + {
> + /* It is TEST. Nothing must follow it. */
> + if (*ptr)
> + {
> + errmsg= "Nothing must follow action TEST";
> + goto err;
> + }
> +
> + /* Execute sync point. */
> + debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
> + /* Fix statistics. This was not a real hit of the sync point. */
> + thd->debug_sync_control->dsp_hits--;
> + goto end;
> + }
> +
> + /*
> + Now check for actions that define a new action.
> + Initialize action. Do not use bzero(). Strings may have malloced.
> + */
> + action->activation_count= 0;
> + action->hit_limit= 0;
> + action->execute= 0;
> + action->timeout= 0;
> + action->signal.length(0);
> + action->wait_for.length(0);
> +
Use of constructor would be nice...
> + /*
> + Try CLEAR.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
> + {
> + /* It is CLEAR. Nothing must follow it. */
> + if (*ptr)
> + {
> + errmsg= "Nothing must follow action CLEAR";
> + goto err;
> + }
> +
> + /* Set (clear/remove) action. */
> + goto set_action;
> + }
> +
> + /*
> + Now check for real sync point actions.
> + */
> +
> + /*
> + Try SIGNAL.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
> + {
> + /* It is SIGNAL. Signal name must follow. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + {
> + errmsg= "Missing signal name after action SIGNAL";
> + goto err;
> + }
> + if (action->signal.copy(token, token_length, system_charset_info))
> + {
> + /* Error is reported by my_malloc(). */
> + errmsg= NULL;
> + goto err; /* purecov: tested */
> + }
> +
> + /* Set default for EXECUTE option. */
> + action->execute= 1;
> +
> + /* Get next token. If none follows, set action. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto set_action;
> + }
> +
> + /*
> + Try WAIT_FOR.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
> + {
> + /* It is WAIT_FOR. Wait_for signal name must follow. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + {
> + errmsg= "Missing signal name after action WAIT_FOR";
> + goto err;
> + }
> + if (action->wait_for.copy(token, token_length, system_charset_info))
> + {
> + /* Error is reported by my_malloc(). */
> + errmsg= NULL;
> + goto err; /* purecov: tested */
> + }
> +
> + /* Set default for EXECUTE and TIMEOUT options. */
> + action->execute= 1;
> + action->timeout= opt_debug_sync_timeout;
> +
> + /* Get next token. If none follows, set action. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto set_action;
> +
> + /*
> + Try TIMEOUT.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
> + {
> + /* It is TIMEOUT. Number must follow. */
> + if (!(ptr= debug_sync_number(&action->timeout, ptr)))
> + {
> + errmsg= "Missing valid number after TIMEOUT";
> + goto err;
> + }
> +
> + /* Get next token. If none follows, set action. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto set_action;
> + }
> + }
> +
> + /*
> + Try EXECUTE.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
> + {
> + /*
> + EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
> + In this case action->execute has been preset to 1.
> + */
> + if (!action->execute)
> + {
> + errmsg= "Missing action before EXECUTE";
> + goto err;
> + }
> +
> + /* Number must follow. */
> + if (!(ptr= debug_sync_number(&action->execute, ptr)))
> + {
> + errmsg= "Missing valid number after EXECUTE";
> + goto err;
> + }
> +
> + /* Get next token. If none follows, set action. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto set_action;
> + }
> +
> + /*
> + Try HIT_LIMIT.
> + */
> + if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
> + {
> + /* Number must follow. */
> + if (!(ptr= debug_sync_number(&action->hit_limit, ptr)))
> + {
> + errmsg= "Missing valid number after HIT_LIMIT";
> + goto err;
> + }
> +
> + /* Get next token. If none follows, set action. */
> + if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
> + goto set_action;
> + }
> +
> + errmsg= "Illegal or out of order stuff: '%.*s'";
> +
> + err:
> + if (errmsg)
> + {
> + /*
> + NOTE: errmsg must either have %.*s or none % at all.
> + It can be NULL if an error message is already reported
> + (e.g. by my_malloc()).
> + */
> + set_if_smaller(token_length, 64); /* Limit error message length. */
> + my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
> + }
> + DBUG_RETURN(TRUE);
> +
> + set_action:
> + DBUG_RETURN(debug_sync_set_action(thd, action));
> +
> + end:
> + DBUG_RETURN(FALSE);
> +}
> +
> +
> +/**
> + Check if the system variable 'debug_sync' can be set.
> +
> + @param[in] thd thread handle
> + @param[in] var set variable request
> +
> + @return status
> + @retval FALSE ok, variable can be set
> + @retval TRUE error, variable cannot be set
> +*/
> +
> +bool sys_var_debug_sync::check(THD *thd, set_var *var)
> +{
> + DBUG_ENTER("sys_var_debug_sync::check");
> +
> + /* Variable can be set for the session only. */
> + if (var->type == OPT_GLOBAL)
> + {
> + my_error(ER_WRONG_OBJECT, MYF(0), "SESSION", name, "global variable");
> + DBUG_RETURN(TRUE);
> + }
> +
> + /*
> + Do not check for disabled facility. Test result should not
> + unnecessarily differ from enabled facility.
> + */
> +
> + /*
> + Facility requires SUPER privilege. Sync points could be inside
> + global mutexes (e.g. LOCK_open). Waiting there forever would
> + stall the whole server.
> + */
> + DBUG_RETURN(check_global_access(thd, SUPER_ACL));
> +}
> +
> +
> +/**
> + Set the system variable 'debug_sync'.
> +
> + @param[in] thd thread handle
> + @param[in] var set variable request
> +
> + @return status
> + @retval FALSE ok, variable is set
> + @retval TRUE error, variable could not be set
> +
> + @note
> + "Setting" of the system variable 'debug_sync' does not mean to
> + assign a value to it as usual. Instead a debug sync action is parsed
> + from the input string and stored apart from the variable value.
> +*/
> +
> +bool sys_var_debug_sync::update(THD *thd, set_var *var)
> +{
> + char empty= '\0';
> + char *val_str= var ? var->value->str_value.c_ptr() : ∅
> + DBUG_ENTER("sys_var_debug_sync::update");
> +
> + DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
> +
> + DBUG_RETURN(opt_debug_sync_timeout ?
> + debug_sync_eval_action(thd, val_str) :
> + FALSE);
> +}
> +
> +
> +/**
> + Retrieve the value of the system variable 'debug_sync'.
> +
> + @param[in] thd thread handle
> + @param[in] type variable type, unused
> + @param[in] base variable base, unused
> +
> + @return string
> + @retval != NULL ok, string pointer
> + @retval NULL memory allocation error
> +
> + @note
> + The value of the system variable 'debug_sync' reflects if
> + the facility is enabled ("ON") or disabled (default, "OFF").
> +
> + When "ON", the current signal is added.
> +*/
> +
> +uchar *sys_var_debug_sync::value_ptr(THD *thd,
> + enum_var_type type __attribute__((unused)),
> + LEX_STRING *base __attribute__((unused)))
> +{
> + char *value;
> + DBUG_ENTER("sys_var_debug_sync::value_ptr");
> +
> + if (opt_debug_sync_timeout)
> + {
> + static char on[]= "ON - current signal: '";
> + size_t lgt= (sizeof(on) /* includes '\0' */ +
> + debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
> + char *vend;
> + char *vptr;
> +
> + if ((value= (char*) alloc_root(thd->mem_root, lgt)))
> + {
> + vend= value + lgt - 1; /* reserve space for '\0'. */
> + vptr= debug_sync_bmove_len(value, vend, STRING_WITH_LEN(on));
> + vptr= debug_sync_bmove_len(vptr, vend, debug_sync_global.ds_signal.ptr(),
> + debug_sync_global.ds_signal.length());
> + if (vptr < vend)
> + *(vptr++)= '\'';
> + *vptr= '\0'; /* We have one byte reserved for the worst case. */
> + }
> + }
> + else
> + value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF"));
> +
> + DBUG_RETURN((uchar*) value);
> +}
> +
> +
> +/**
> + Execute requested action at a synchronization point.
> +
> + @param[in] thd thread handle
> + @param[in] action action to be executed
> +
> + @note
> + This is to be called only if activation count > 0.
> +*/
> +
> +static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
> +{
> + DBUG_ENTER("debug_sync_execute");
> + DBUG_PRINT("debug_sync", ("sync_point: '%s' activation_count: %lu "
> + "hit_limit: %lu execute: %lu timeout: %lu "
> + "signal: '%s' wait_for: '%s'",
> + action->sync_point.c_ptr(),
> + action->activation_count,
> + action->hit_limit, action->execute,
> action->timeout,
> + action->signal.c_ptr(),
> action->wait_for.c_ptr()));
> + DBUG_ASSERT(action->activation_count);
> +
> + if (action->execute)
> + {
> + if (action->signal.length())
> + {
> + /*
> + Protect copying of the signal string to the global string
> + to avoid race conditions during test case development.
> + After approaching a clean test case it should not happen
> + that two threads try to signal at the same time.
> + */
> + pthread_mutex_lock(&debug_sync_global.ds_mutex);
> + /* Copy the signal to the global variable. */
> + if (debug_sync_global.ds_signal.copy(action->signal))
> + {
> + /*
> + Error is reported by my_malloc().
> + We must disable the facility. We have no way to return an error.
> + */
> + debug_sync_emergency_disable(thd); /* purecov: tested */
> + }
> + /* Wake threads waiting in a sync point. */
> + pthread_cond_broadcast(&debug_sync_global.ds_cond);
> + DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
> + action->signal.c_ptr(),
> + action->sync_point.c_ptr()));
> + pthread_mutex_unlock(&debug_sync_global.ds_mutex);
> + }
> +
> + if (action->wait_for.length())
> + {
> + const char *old_proc_info;
> + int error= 0;
> + struct timespec abstime;
> +
> + pthread_mutex_lock(&debug_sync_global.ds_mutex);
> + old_proc_info= thd->enter_cond(&debug_sync_global.ds_cond,
> + &debug_sync_global.ds_mutex,
> + action->sync_point.c_ptr());
> +
> + set_timespec(abstime, action->timeout);
> + DBUG_PRINT("debug_sync_exec", ("wait for '%s' at: '%s' curr: '%s'",
> + action->wait_for.c_ptr(),
> + action->sync_point.c_ptr(),
> + debug_sync_global.ds_signal.c_ptr()));
> +
> + /*
> + Wait until global signal string matches the wait_for string.
> + Interrupt when thread or query is killed or facility disabled.
> + The facility can become disabled when some thread cannot get
> + the required dynamic memory allocated.
> + */
> + while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for)
> &&
> + !thd->killed && opt_debug_sync_timeout)
> + {
> + error= pthread_cond_timedwait(&debug_sync_global.ds_cond,
> + &debug_sync_global.ds_mutex,
> + &abstime);
> + if (error == ETIMEDOUT || error == ETIME)
> + {
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
> + break;
> + }
> + error= 0;
> + }
> + DBUG_PRINT("debug_sync_exec", ("%s from '%s' at: '%s'",
> + error ? "timeout" : "resume",
> + action->wait_for.c_ptr(),
> + action->sync_point.c_ptr()));
> +
> + thd->exit_cond(old_proc_info);
> + }
> +
> + action->execute--;
Wouldn't it be better to decrease the counter at the very beginning? This way we
don't have to worry about early returns and such...
> + }
> +
> + /* hit_limit is zero for infinite. Don't decrement unconditionally. */
> + if (action->hit_limit)
> + {
> + if (!--action->hit_limit)
> + {
> + thd->killed= THD::KILL_QUERY;
> + my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
> + }
> + DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
> + action->hit_limit,
> + action->sync_point.c_ptr()));
> + }
> +
> + action->activation_count--;
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/**
> + Execute requested action at a synchronization point.
> +
> + @param[in] thd thread handle
> + @param[in] sync_point_name name of synchronization point
> + @param[in] name_len length of sync point name
> +*/
> +
> +void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
> +{
> + st_debug_sync_control *ds_control= thd->debug_sync_control;
> + st_debug_sync_action *action;
> + DBUG_ENTER("debug_sync");
> + DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
> +
> + /* Statistics. */
> + ds_control->dsp_hits++;
> +
> + if (ds_control->ds_active &&
> + (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
> + sync_point_name, name_len)) &&
> + action->activation_count)
> + {
> + debug_sync_execute(thd, action);
> +
> + /* Statistics. */
> + ds_control->dsp_executed++;
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
====================================================================================
> diff -Nrup a/sql/item_func.cc b/sql/item_func.cc
> --- a/sql/item_func.cc 2008-01-25 21:27:10 +01:00
> +++ b/sql/item_func.cc 2008-03-11 18:54:23 +01:00
> @@ -1689,6 +1689,8 @@ double Item_func_pow::val_real()
> double Item_func_acos::val_real()
> {
> DBUG_ASSERT(fixed == 1);
> + /* One can use this to defer SELECT processing. */
> + DEBUG_SYNC(current_thd, "before_acos_function");
> // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
> volatile double value= args[0]->val_real();
> if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
====================================================================================
> diff -Nrup a/sql/lock.cc b/sql/lock.cc
> --- a/sql/lock.cc 2007-12-21 20:27:43 +01:00
> +++ b/sql/lock.cc 2008-03-11 18:54:23 +01:00
> @@ -351,6 +351,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
> */
> reset_lock_data_and_free(&sql_lock);
> retry:
> + DEBUG_SYNC(thd, "mysql_lock_retry");
> if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
> {
> *need_reopen= TRUE;
> @@ -1112,6 +1113,7 @@ bool wait_for_locked_table_names(THD *th
> result=1;
> break;
> }
> + DEBUG_SYNC(thd, "before_wait_locked_tname");
> wait_for_condition(thd, &LOCK_open, &COND_refresh);
> pthread_mutex_lock(&LOCK_open);
> }
====================================================================================
> diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
> --- a/sql/mysql_priv.h 2008-02-14 18:35:22 +01:00
> +++ b/sql/mysql_priv.h 2008-03-11 18:54:24 +01:00
> @@ -589,6 +589,27 @@ void debug_sync_point(const char* lock_n
> #define DBUG_SYNC_POINT(lock_name,lock_timeout)
> #endif /* EXTRA_DEBUG */
>
> +/* Debug Sync Facility. */
> +#if defined(ENABLED_DEBUG_SYNC)
> +/* Macro to be put in the code at synchronization points. */
> +#define DEBUG_SYNC(_thd_, _sync_point_name_) \
> + do { if (unlikely(opt_debug_sync_timeout)) \
> + debug_sync(_thd_, STRING_WITH_LEN(_sync_point_name_)); \
> + } while (0)
> +/* Command line option --debug-sync. See mysqld.cc. */
> +extern uint opt_debug_sync_timeout;
Comment should say "--debug-sync-timeout".
> +/* Default WAIT_FOR timeout if command line option is given without argument. */
> +#define DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT 300
> +/* Debug Sync prototypes. See debug_sync.cc. */
> +extern int debug_sync_init(void);
> +extern void debug_sync_end(void);
> +extern void debug_sync_init_thread(THD *thd);
> +extern void debug_sync_end_thread(THD *thd);
> +extern void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
> +#else /* defined(ENABLED_DEBUG_SYNC) */
> +#define DEBUG_SYNC(_thd_, _sync_point_name_) /* disabled DEBUG_SYNC */
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> /* BINLOG_DUMP options */
>
> #define BINLOG_DUMP_NON_BLOCK 1
====================================================================================
> diff -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
> --- a/sql/mysqld.cc 2008-02-14 10:00:48 +01:00
> +++ b/sql/mysqld.cc 2008-03-11 18:54:24 +01:00
> @@ -468,6 +468,9 @@ my_bool lower_case_file_system= 0;
> my_bool opt_large_pages= 0;
> my_bool opt_myisam_use_mmap= 0;
> uint opt_large_page_size= 0;
> +#if defined(ENABLED_DEBUG_SYNC)
> +uint opt_debug_sync_timeout= 0;
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
> /*
> True if there is at least one per-hour limit for some user, so we should
> @@ -1332,6 +1335,10 @@ void clean_up(bool print_message)
> #ifdef USE_REGEX
> my_regex_end();
> #endif
> +#if defined(ENABLED_DEBUG_SYNC)
> + /* End the debug sync facility. See debug_sync.cc. */
> + debug_sync_end();
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
>
> #if !defined(EMBEDDED_LIBRARY)
> if (!opt_bootstrap)
> @@ -3298,6 +3305,12 @@ static int init_common_variables(const c
> sys_var_slow_log_path.value= my_strdup(s, MYF(0));
> sys_var_slow_log_path.value_length= strlen(s);
>
> +#if defined(ENABLED_DEBUG_SYNC)
> + /* Initialize the debug sync facility. See debug_sync.cc. */
> + if (debug_sync_init())
> + return 1; /* purecov: tested */
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
> return 1;
> if (my_database_names_init())
> @@ -4617,7 +4630,7 @@ void create_thread_to_handle_connection(
> handle_one_connection,
> (void*) thd)))
> {
> - /* purify: begin inspected */
> + /* purecov: begin inspected */
> DBUG_PRINT("error",
> ("Can't create thread to handle request (error %d)",
> error));
> @@ -5404,6 +5417,9 @@ enum options_mysqld
> OPT_SECURE_FILE_PRIV,
> OPT_MIN_EXAMINED_ROW_LIMIT,
> OPT_LOG_SLOW_SLAVE_STATEMENTS,
> +#if defined(ENABLED_DEBUG_SYNC)
> + OPT_DEBUG_SYNC_TIMEOUT,
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> OPT_OLD_MODE,
> #if HAVE_POOL_OF_THREADS == 1
> OPT_POOL_OF_THREADS,
> @@ -6151,6 +6167,14 @@ log and this option does nothing anymore
> "Decision to use in heuristic recover process. Possible values are COMMIT or
> ROLLBACK.",
> (uchar**) &opt_tc_heuristic_recover, (uchar**)
> &opt_tc_heuristic_recover,
> 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
> +#if defined(ENABLED_DEBUG_SYNC)
> + {"debug-sync-timeout", OPT_DEBUG_SYNC_TIMEOUT,
> + "Enable the debug sync facility "
> + "and optionally specify a default wait timeout in seconds. "
> + "A zero value keeps the facility disabled.",
> + (uchar**) &opt_debug_sync_timeout, 0,
> + 0, GET_UINT, OPT_ARG, 0, 0, UINT_MAX, 0, 0, 0},
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> {"temp-pool", OPT_TEMP_POOL,
> "Using this option will cause most temporary files created to use a small set of
> names, rather than a unique name for each new file.",
> (uchar**) &use_temp_pool, (uchar**) &use_temp_pool, 0, GET_BOOL, NO_ARG,
> 1,
> @@ -7343,6 +7367,9 @@ static void mysql_init_variables(void)
> bzero((uchar*) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
> bzero((char *) &global_status_var, sizeof(global_status_var));
> opt_large_pages= 0;
> +#if defined(ENABLED_DEBUG_SYNC)
> + opt_debug_sync_timeout= 0;
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> key_map_full.set_all();
>
> /* Character sets */
> @@ -8001,6 +8028,22 @@ mysqld_get_one_option(int optid,
> lower_case_table_names= argument ? atoi(argument) : 1;
> lower_case_table_names_used= 1;
> break;
> +#if defined(ENABLED_DEBUG_SYNC)
> + case OPT_DEBUG_SYNC_TIMEOUT:
> + /*
> + Debug Sync Facility. See debug_sync.cc.
> + Default timeout for WAIT_FOR action.
> + Default value is zero (facility disabled).
> + If option is given without an argument, supply a non-zero value.
> + */
> + if (!argument)
> + {
> + /* purecov: begin tested */
> + opt_debug_sync_timeout= DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT;
> + /* purecov: end */
> + }
> + break;
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> }
> return 0;
> }
====================================================================================
> diff -Nrup a/sql/set_var.cc b/sql/set_var.cc
> --- a/sql/set_var.cc 2008-02-08 17:54:40 +01:00
> +++ b/sql/set_var.cc 2008-03-11 18:54:24 +01:00
> @@ -481,6 +481,12 @@ static sys_var_long_ptr sys_table_cache_
> &table_cache_size);
> static sys_var_long_ptr sys_table_lock_wait_timeout(&vars,
> "table_lock_wait_timeout",
> &table_lock_wait_timeout);
> +
> +#if defined(ENABLED_DEBUG_SYNC)
> +/* Debug Sync Facility. Implemented in debug_sync.cc. */
> +static sys_var_debug_sync sys_debug_sync(&vars, "debug_sync");
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> static sys_var_long_ptr sys_thread_cache_size(&vars, "thread_cache_size",
> &thread_cache_size);
> #if HAVE_POOL_OF_THREADS == 1
====================================================================================
> diff -Nrup a/sql/set_var.h b/sql/set_var.h
> --- a/sql/set_var.h 2008-02-08 17:02:58 +01:00
> +++ b/sql/set_var.h 2008-03-11 18:54:24 +01:00
> @@ -567,6 +567,21 @@ public:
> uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *b);
> };
>
> +#if defined(ENABLED_DEBUG_SYNC)
> +/* Debug Sync Facility. Implemented in debug_sync.cc. */
> +class sys_var_debug_sync :public sys_var_thd
> +{
> +public:
> + sys_var_debug_sync(sys_var_chain *chain, const char *name_arg)
> + :sys_var_thd(name_arg)
> + { chain_sys_var(chain); }
> + bool check(THD *thd, set_var *var);
> + bool update(THD *thd, set_var *var);
> + SHOW_TYPE show_type() { return SHOW_CHAR; }
> + bool check_update_type(Item_result type) { return type != STRING_RESULT; }
> + uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
> +};
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
>
>
> /* some variables that require special handling */
====================================================================================
> diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
> --- a/sql/share/errmsg.txt 2008-02-05 15:30:25 +01:00
> +++ b/sql/share/errmsg.txt 2008-03-11 18:54:24 +01:00
> @@ -6234,3 +6234,11 @@ ER_SLAVE_HEARTBEAT_FAILURE
> eng "Unexpected master's heartbeat data: %s"
> ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE
> eng "The requested value for the heartbeat period %s %s"
> +
> +ER_DEBUG_SYNC_TIMEOUT
> + eng "debug sync point wait timed out"
> + ger "Debug Sync Point Wartezeit �berschritten"
> +ER_DEBUG_SYNC_HIT_LIMIT
> + eng "debug sync point hit limit reached"
> + ger "Debug Sync Point Hit Limit erreicht"
> +
> diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
> --- a/sql/sql_base.cc 2008-02-02 13:19:47 +01:00
> +++ b/sql/sql_base.cc 2008-03-11 18:54:24 +01:00
> @@ -1021,6 +1021,8 @@ bool close_cached_tables(THD *thd, TABLE
> close_old_data_files(thd,thd->open_tables,1,1);
> mysql_ha_flush(thd);
>
> + DEBUG_SYNC(thd, "after_flush_unlock");
> +
> bool found=1;
> /* Wait until all threads has closed all the tables we had locked */
> DBUG_PRINT("info",
> @@ -2898,6 +2900,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *
> */
> if (table->in_use != thd)
> {
> + DEBUG_SYNC(thd, "before_open_table_wait_refresh");
> +
> /* wait_for_conditionwill unlock LOCK_open for us */
> wait_for_condition(thd, &LOCK_open, &COND_refresh);
> }
> @@ -4870,13 +4874,6 @@ bool open_and_lock_tables_derived(THD *t
> {
> if (open_tables(thd, &tables, &counter, 0))
> DBUG_RETURN(-1);
> -
> - DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
> - const char *old_proc_info= thd->proc_info;
> - thd->proc_info= "DBUG sleep";
> - my_sleep(6000000);
> - thd->proc_info= old_proc_info;});
> -
> if (!lock_tables(thd, tables, counter, &need_reopen))
> break;
> if (!need_reopen)
> @@ -5170,6 +5167,8 @@ int lock_tables(THD *thd, TABLE_LIST *ta
> thd->set_current_stmt_binlog_row_based_if_mixed();
> }
> }
> +
> + DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
>
> if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
> lock_flag, need_reopen)))
====================================================================================
> diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
> --- a/sql/sql_class.cc 2008-02-12 18:29:06 +01:00
> +++ b/sql/sql_class.cc 2008-03-11 18:54:24 +01:00
> @@ -501,6 +501,9 @@ THD::THD()
> derived_tables_processing(FALSE),
> spcont(NULL),
> m_lip(NULL)
> +#if defined(ENABLED_DEBUG_SYNC)
> + ,debug_sync_control(0)
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> {
> ulong tmp;
>
> @@ -727,6 +730,11 @@ void THD::init(void)
> update_charset();
> reset_current_stmt_binlog_row_based();
> bzero((char *) &status_var, sizeof(status_var));
> +
> +#if defined(ENABLED_DEBUG_SYNC)
> + /* Initialize the Debug Sync Facility. See debug_sync.cc. */
> + debug_sync_init_thread(this);
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> }
>
>
> @@ -802,6 +810,12 @@ void THD::cleanup(void)
> lock=locked_tables; locked_tables=0;
> close_thread_tables(this);
> }
> +
> +#if defined(ENABLED_DEBUG_SYNC)
> + /* End the Debug Sync Facility. See debug_sync.cc. */
> + debug_sync_end_thread(this);
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> mysql_ha_cleanup(this);
> delete_dynamic(&user_var_events);
> hash_free(&user_vars);
====================================================================================
> diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
> --- a/sql/sql_class.h 2008-02-01 15:08:44 +01:00
> +++ b/sql/sql_class.h 2008-03-11 18:54:24 +01:00
> @@ -1740,6 +1740,11 @@ public:
> partition_info *work_part_info;
> #endif
>
> +#if defined(ENABLED_DEBUG_SYNC)
> + /* Debug Sync facility. See debug_sync.cc. */
> + struct st_debug_sync_control *debug_sync_control;
> +#endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> THD();
> ~THD();
>
====================================================================================
> diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
> --- a/sql/sql_parse.cc 2008-02-15 22:25:57 +01:00
> +++ b/sql/sql_parse.cc 2008-03-11 18:54:24 +01:00
> @@ -2789,6 +2789,7 @@ end_with_restore_list:
> thd->first_successful_insert_id_in_cur_stmt=
> thd->first_successful_insert_id_in_prev_stmt;
>
> + DEBUG_SYNC(thd, "after_insert");
> break;
> }
> case SQLCOM_REPLACE_SELECT:
====================================================================================
> diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
> --- a/sql/sql_table.cc 2008-02-14 18:35:22 +01:00
> +++ b/sql/sql_table.cc 2008-03-11 18:54:24 +01:00
> @@ -4184,6 +4184,7 @@ static bool mysql_admin_table(THD* thd,
> RTFC_WAIT_OTHER_THREAD_FLAG |
> RTFC_CHECK_KILLED_FLAG);
> thd->exit_cond(old_message);
> + DEBUG_SYNC(thd, "after_admin_flush");
> DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
> if (thd->killed)
> goto err;
====================================================================================
> diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
> --- a/storage/myisammrg/ha_myisammrg.cc 2007-12-21 20:27:49 +01:00
> +++ b/storage/myisammrg/ha_myisammrg.cc 2008-03-11 18:54:24 +01:00
> @@ -456,6 +456,8 @@ int ha_myisammrg::attach_children(void)
> DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
> DBUG_ASSERT(!this->file->children_attached);
>
> + DEBUG_SYNC(current_thd, "before_myisammrg_attach");
> +
> /*
> Initialize variables that are used, modified, and/or set by
> myisammrg_attach_children_callback().
> @@ -925,6 +927,8 @@ THR_LOCK_DATA **ha_myisammrg::store_lock
> enum thr_lock_type lock_type)
> {
> MYRG_TABLE *open_table;
> +
> + DEBUG_SYNC(thd, "before_myisammrg_store_lock");
>
> /*
> This method can be called while another thread is attaching the
>