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-04-29 11:22:04+02: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-04-29 11:16:50+02:00, istruewing@stripped +0 -0
Rename: mysql-test/r/merge-big.result -> BitKeeper/deleted/.del-merge-big.result
BitKeeper/deleted/.del-merge-big.test@stripped, 2008-04-29 11:16:52+02:00, istruewing@stripped +0 -0
Rename: mysql-test/t/merge-big.test -> BitKeeper/deleted/.del-merge-big.test
BitKeeper/etc/ignore@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Added libmysqld/debug_sync.cc
configure.in@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +17 -0
WL#4259 - Debug Sync Facility
Added configure option --enable-debug-sync.
libmysqld/CMakeLists.txt@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Include debug_sync.cc
libmysqld/Makefile.am@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Include debug_sync.cc
mysql-test/include/have_debug_sync.inc@stripped, 2008-04-29 11:22:02+02: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-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/mysql-test-run.pl@stripped, 2008-04-29 11:22:01+02: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-04-29 11:22:02+02:00, istruewing@stripped +239 -0
WL#4259 - Debug Sync Facility
New file. The new test result.
mysql-test/r/debug_sync.result@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/r/have_debug_sync.require@stripped, 2008-04-29 11:22:02+02: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-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/r/merge-sync.result@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +151 -0
WL#4259 - Debug Sync Facility
New example test result.
mysql-test/r/merge-sync.result@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/r/merge.result@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +4 -111
WL#4259 - Debug Sync Facility
Fixed the example test result.
mysql-test/t/debug_sync.test@stripped, 2008-04-29 11:22:02+02: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-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/t/merge-sync.test@stripped, 2008-04-29 11:22:02+02: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-04-29 11:22:02+02:00, istruewing@stripped +0 -0
mysql-test/t/merge.test@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +6 -156
WL#4259 - Debug Sync Facility
Removed tests that need the the new facility.
mysys/thr_lock.c@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +17 -0
WL#4259 - Debug Sync Facility
Added a synchronization point hook.
sql/CMakeLists.txt@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Include debug_sync.cc
sql/Makefile.am@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Include debug_sync.cc
sql/debug_sync.cc@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +1698 -0
WL#4259 - Debug Sync Facility
New file. Implementation of the facility.
sql/debug_sync.cc@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +0 -0
sql/item_func.cc@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +2 -0
WL#4259 - Debug Sync Facility
Added a synchronization point.
sql/lock.cc@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +2 -0
WL#4259 - Debug Sync Facility
Added synchronization points.
sql/mysql_priv.h@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +21 -0
WL#4259 - Debug Sync Facility
Added declarations for the facility.
sql/mysqld.cc@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +44 -1
WL#4259 - Debug Sync Facility
Added initialization/termination of the facility for the server.
Added option "debug-sync-timeout".
sql/set_var.cc@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +6 -0
WL#4259 - Debug Sync Facility
Added definition of the 'debug_sync' system variable.
sql/set_var.h@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +15 -0
WL#4259 - Debug Sync Facility
Added declaration for the 'debug_sync' system variable.
sql/share/errmsg.txt@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +8 -0
WL#4259 - Debug Sync Facility
Added new error messages.
sql/sql_base.cc@stripped, 2008-04-29 11:22:01+02: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-04-29 11:22:01+02:00, istruewing@stripped +14 -0
WL#4259 - Debug Sync Facility
Added initialization/termination of the facility for THD.
sql/sql_class.h@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +5 -0
WL#4259 - Debug Sync Facility
Added the new THD element debug_sync_control.
sql/sql_parse.cc@stripped, 2008-04-29 11:22:01+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Added a synchronization point.
sql/sql_table.cc@stripped, 2008-04-29 11:22:02+02:00, istruewing@stripped +1 -0
WL#4259 - Debug Sync Facility
Added a synchronization point.
storage/myisammrg/ha_myisammrg.cc@stripped, 2008-04-29 11:22:02+02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02: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: Variable 'debug_sync' is a SESSION variable and can't be used with SET GLOBAL
+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-04-29 11:22:02 +02: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-04-29 11:22:02 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02: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_LOCAL_VARIABLE
+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-04-29 11:22:02 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02:00
@@ -0,0 +1,1698 @@
+/* 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 just as
+ if it had been hit:
+
+ SET DEBUG_SYNC= 'name TEST';
+
+
+ ==== Formal Syntax ====
+
+ The string to "assign" to the DEBUG_SYNC variable can contain:
+
+ {RESET |
+ <sync point name> TEST |
+ <sync point name> CLEAR |
+ <sync point name> {{SIGNAL <signal name> |
+ WAIT_FOR <signal name> [TIMEOUT <seconds>]}
+ [EXECUTE <count>] &| HIT_LIMIT <count>}
+
+ Here '&|' means 'and/or'. This means that one of the sections
+ separated by '&|' must be present or both of them.
+
+
+ ==== 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.
+
+
+ ==== Further reading ====
+
+ For a discussion of other methods to synchronize threads see
+ http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
+
+ For complete syntax tests, functional tests, and examples see the test
+ case debug_sync.test.
+
+ See also worklog entry WL#4259 - Test Synchronization Facility
+*/
+
+#include "mysql_priv.h"
+
+#if defined(ENABLED_DEBUG_SYNC)
+
+/*
+ Action to perform at a synchronization point.
+ NOTE: This structure is moved around in memory by realloc(), qsort(),
+ and memmove(). Do not add objects with non-trivial constuctors
+ or destructors, which might prevent moving of this structure
+ with these functions.
+*/
+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 */
+ 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.
+
+ We cannot place a sync point directly in thr_lock.c. It is C code
+ and does not include mysql_priv.h. So it does not know the macro
+ DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
+ Hence it cannot be used in files outside of the sql/ directory.
+
+ The workaround is to call back simple functions like this one from
+ non-sql/ files.
+
+ We want to allow modules like thr_lock to be used without sql/ and
+ especially without Debug Sync. So we cannot just do a simple call
+ of the callback function. Instead we provide a global pointer in
+ the other file, which is to be set to the callback by Debug Sync.
+ If the pointer is not set, no call back will be done. If Debug
+ Sync sets the pointer to a callback function like this one, it will
+ be called. That way thr_lock.c does not have an undefined reference
+ to Debug Sync and can be used without it. Debug Sync, in contrast,
+ has an undefined reference to that pointer and thus requires
+ thr_lock to be linked too. But this is not a problem as it is part
+ of the MySQL server anyway.
+
+ @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.
+
+ In detail it works so: When thr_lock() tries to acquire a table
+ lock, it locks the lock->mutex, checks if it can have the lock, and
+ if not, it calls wait_for_lock(). Here it unlocks the table lock
+ while waiting on a condition. The sync point is located before this
+ wait for condition. If we have a waiting action here, we hold the
+ the table locks mutex all the time. Any attempt to look at the table
+ lock by another thread blocks it immediately on lock->mutex. This
+ can easily become an unexpected and unobvious blockage. So be
+ warned: Do not request a WAIT_FOR action for the 'wait_for_lock'
+ sync point unless you really know what you do.
+
+ @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)
+ {
+ int rc;
+
+ /* Initialize the global variables. */
+ debug_sync_global.ds_signal.length(0);
+ if ((rc= pthread_cond_init(&debug_sync_global.ds_cond, NULL)) ||
+ (rc= pthread_mutex_init(&debug_sync_global.ds_mutex,
+ MY_MUTEX_INIT_FAST)))
+ DBUG_RETURN(rc);
+
+ /* 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.
+
+ @note
+ Do not end the facility here because the global variables can
+ be in use by other threads.
+*/
+
+static void debug_sync_emergency_disable(void)
+{
+ 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");
+ DBUG_ASSERT(thd);
+
+ 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(); /* purecov: tested */
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ 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");
+ DBUG_ASSERT(thd);
+
+ if (thd->debug_sync_control)
+ {
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+
+ if (ds_control->ds_action)
+ {
+ st_debug_sync_action *action= ds_control->ds_action;
+ st_debug_sync_action *action_end= action + ds_control->ds_allocated;
+ for (; action < action_end; action++)
+ {
+ action->signal.free();
+ action->wait_for.free();
+ action->sync_point.free();
+ }
+ 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)
+{
+ DBUG_ASSERT(to);
+ DBUG_ASSERT(to_end);
+ DBUG_ASSERT(!length || from);
+ 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_ASSERT(result);
+ DBUG_ASSERT(action);
+
+ /* 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';
+}
+
+
+/**
+ 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");
+ DBUG_ASSERT(thd);
+
+ if (!ds_control)
+ return;
+
+ for (idx= 0; idx < ds_control->ds_active; idx++)
+ {
+ 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;
+ DBUG_ASSERT(action1);
+ DBUG_ASSERT(action2);
+
+ 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 ;
+ DBUG_ASSERT(actionarr);
+ DBUG_ASSERT(dsp_name);
+ DBUG_ASSERT(name_len);
+
+ low= 0;
+ high= quantity;
+
+ while (low < high)
+ {
+ mid= (low + high) / 2;
+ action= actionarr + mid;
+ 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");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(ds_control);
+
+ /* Remove all actions of this thread. */
+ ds_control->ds_active= 0;
+
+ /* Clear the global signal. */
+ debug_sync_global.ds_signal.length(0);
+
+ 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)
+{
+ uint dsp_idx= action - ds_control->ds_action;
+ DBUG_ENTER("debug_sync_remove_action");
+ DBUG_ASSERT(ds_control);
+ DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
+ DBUG_ASSERT(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)
+ {
+ /*
+ Do not make save_action an object of class st_debug_sync_action.
+ Its destructor would tamper with the String pointers.
+ */
+ uchar save_action[sizeof(st_debug_sync_action)];
+
+ /*
+ Copy the to-be-removed action object to temporary storage before
+ the shift copies the string pointers over. Do not use assignment
+ because it would use assignment operator methods for the Strings.
+ This would copy the strings. The shift below overwrite the string
+ pointers without freeing them first. By using memmove() we save
+ the pointers, which are overwritten by the shift.
+ */
+ memmove(save_action, action, sizeof(st_debug_sync_action));
+
+ /* 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));
+
+ /*
+ Copy back the saved action object to the now free array slot. This
+ replaces the double references of String pointers that have been
+ produced by the shift. Again do not use an assignment operator to
+ avoid string allocation/copy.
+ */
+ memmove(ds_control->ds_action + ds_control->ds_active, save_action,
+ sizeof(st_debug_sync_action));
+ }
+
+ 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_ASSERT(thd);
+ DBUG_ASSERT(dsp_name);
+ DBUG_ASSERT(name_len);
+ DBUG_ASSERT(ds_control);
+ DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
+ DBUG_PRINT("debug_sync", ("active: %lu allocated: %lu",
+ ds_control->ds_active, ds_control->ds_allocated));
+
+ /* 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));
+ 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", ("action: 0x%lx array: 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. It arms the action for
+ the requested sync point. If the action parsed into an empty action,
+ it is removed instead.
+
+ Setting an action for a sync point means to make the sync point
+ active. When it is hit it will execute this action.
+
+ Before parsing, we "get" an action object. This is placed at the
+ end of the thread's action array unless the requested sync point
+ has an action already.
+
+ Then the parser fills the action object from the request string.
+
+ Finally the action is "set" for the sync point. If it was parsed
+ to be empty, it is removed from the array. If it did belong to a
+ sync point before, the sync point becomes inactive. If the action
+ became non-empty and it did not belong to a sync point before (it
+ was added at the end of the action array), the action array needs
+ to be sorted by sync point.
+
+ If the sync point name is "now", it is executed immediately.
+*/
+
+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(thd);
+ DBUG_ASSERT(action);
+ DBUG_ASSERT(ds_control);
+
+ 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 for sync point "now" was 1, the execution of the sync
+ point decremented it to 0. In this case the following happened:
+
+ - an error message was reported with my_error() and
+ - the statement was killed with thd->killed= THD::KILL_QUERY.
+
+ If a statement reports an error, it must not call send_ok().
+ The calling functions will not call send_ok(), if we return TRUE
+ from this function.
+
+ To detect, if an error message has been reported, we can check one
+ of the above conditions. That is, either examine the error message
+ stack for an entry, or check thd->killed. We do the latter below.
+ */
+ if (thd->killed)
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ 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)
+{
+ DBUG_ASSERT(token_p);
+ DBUG_ASSERT(token_length_p);
+ DBUG_ASSERT(ptr);
+
+ /* 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] actstrptr current pointer in action string
+
+ @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 *actstrptr)
+{
+ char *ptr;
+ char *ept;
+ char *token;
+ uint token_length;
+ DBUG_ASSERT(number_p);
+ DBUG_ASSERT(actstrptr);
+
+ /* Get token from string. */
+ if (!(ptr= debug_sync_token(&token, &token_length, actstrptr)))
+ 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.
+
+ Before parsing, we "get" an action object. This is placed at the
+ end of the thread's action array unless the requested sync point
+ has an action already.
+
+ Then the parser fills the action object from the request string.
+
+ Finally the action is "set" for the sync point. This means that the
+ sync point becomes active or inactive, depending on the action
+ values.
+
+ @note
+ The input string needs to be ASCII NUL ('\0') terminated. We split
+ nul-terminated tokens in it without copy.
+
+ @see the function comment of debug_sync_token() for more constraints
+ for the string.
+*/
+
+static bool debug_sync_eval_action(THD *thd, char *action_str)
+{
+ st_debug_sync_action *action= NULL;
+ const char *errmsg;
+ char *ptr;
+ char *token;
+ uint token_length;
+ DBUG_ENTER("debug_sync_eval_action");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(action_str);
+
+ /*
+ Get debug sync point name. Or a special command.
+ */
+ if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
+ {
+ errmsg= "Missing synchronization point name";
+ 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);
+
+ /*
+ 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);
+ }
+ if (action)
+ debug_sync_remove_action(thd->debug_sync_control, action);
+ 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");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(var);
+
+ /*
+ Variable can be set for the session only.
+
+ This could be changed later. Then we need to have a global array of
+ actions in addition to the thread local ones. SET GLOBAL would
+ manage the global array, SET [SESSION] the local array. A sync point
+ would need to look for a local and a global action. Setting and
+ executing of global actions need to be protected by a mutex.
+
+ The purpose of global actions could be to allow synchronizing with
+ connectionless threads that cannot execute SET statements.
+ */
+ if (var->type == OPT_GLOBAL)
+ {
+ my_error(ER_LOCAL_VARIABLE, MYF(0), name);
+ 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_ASSERT(thd);
+
+ 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");
+ DBUG_ASSERT(thd);
+
+ 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_ASSERT(thd);
+ DBUG_ASSERT(action);
+ 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);
+ action->activation_count--;
+
+ if (action->execute)
+ {
+ 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(); /* 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);
+
+ } /* end if (action->signal.length()) */
+
+ 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);
+
+ } /* end if (action->wait_for.length()) */
+
+ } /* end if (action->execute) */
+
+ /* 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()));
+ }
+
+ 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_ASSERT(thd);
+ DBUG_ASSERT(sync_point_name);
+ DBUG_ASSERT(name_len);
+ DBUG_ASSERT(ds_control);
+ 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)
+ {
+ /* Sync point is active (action exists). */
+ debug_sync_execute(thd, action);
+
+ /* Statistics. */
+ ds_control->dsp_executed++;
+
+ /* If action became inactive, remove it to shrink the search array. */
+ if (!action->activation_count)
+ debug_sync_remove_action(ds_control, action);
+ }
+
+ 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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-timeout. See mysqld.cc. */
+extern uint opt_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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02: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"
+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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:01 +02: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-04-29 11:22:02 +02: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-04-29 11:22:02 +02: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
| Thread |
|---|
| • bk commit into 6.0 tree (istruewing:1.2569) WL#4259 | Ingo Struewing | 29 Apr |