List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:December 10 2007 5:45pm
Subject:bk commit into 5.1 tree (istruewing:1.2624)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 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, 2007-12-10 18:45:51+01:00, istruewing@stripped +11 -0
  Test Synchronization Facility
  
  This is a proposal for a facility to synchronize threads in the
  test suite to make race conditions repeatable and get rid of sleeps.
  
  The facility is controlled by user variables.
  
  The patch includes functions for convenient access to
  user variable values.
  
  The patch does also include a changed merge.test and its result.
  It is an example how to use the facility.
  
  The synchronization points required in the example tests are also
  included.
  
  See item_func.cc for the main part of the facility and for
  documentation. I added it here because item_func.cc contains
  user variable handling.

  mysql-test/r/merge.result@stripped, 2007-12-10 18:45:48+01:00, istruewing@stripped +170 -87
    Test Synchronization Facility
    Fixed test results from example changes.

  mysql-test/t/merge.test@stripped, 2007-12-10 18:45:48+01:00, istruewing@stripped +304 -63
    Test Synchronization Facility
    Example test changes to make them repeatable and avoid sleeps.

  mysys/thr_lock.c@stripped, 2007-12-10 18:45:48+01:00, istruewing@stripped +17 -0
    Test Synchronization Facility
    Added a synchronization point in wait_for_lock().
    Since this file does not include mysql_priv.h,
    we need to trick with a function pointer.

  sql/item_func.cc@stripped, 2007-12-10 18:45:48+01:00, istruewing@stripped +314 -0
    Test Synchronization Facility
    Added a synchronization point in Item_func_acos::val_real().
    Defined functions for convenient reading of user variables.
    Defined variables and functions for the test synchronization
    facility.

  sql/lock.cc@stripped, 2007-12-10 18:45:48+01:00, istruewing@stripped +71 -0
    Test Synchronization Facility
    Added a check for infinite loop in mysql_lock_tables(). This was
    possible with MERGE tables before 5.1.23.
    Added a synchronization point in wait_for_locked_table_names().

  sql/mysql_priv.h@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +24 -0
    Test Synchronization Facility
    Added declarations for the test synchronization facility.

  sql/mysqld.cc@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +9 -0
    Test Synchronization Facility
    Added calls for initialization and cleanup.

  sql/sql_base.cc@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +12 -0
    Test Synchronization Facility
    Added synchronization points.

  sql/sql_parse.cc@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +4 -0
    Test Synchronization Facility
    Added synchronization points.

  sql/sql_table.cc@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +2 -0
    Test Synchronization Facility
    Added a synchronization point.

  storage/myisammrg/ha_myisammrg.cc@stripped, 2007-12-10 18:45:49+01:00, istruewing@stripped +4 -0
    Test Synchronization Facility
    Added synchronization points.

diff -Nrup a/mysql-test/r/merge.result b/mysql-test/r/merge.result
--- a/mysql-test/r/merge.result	2007-11-18 20:28:35 +01:00
+++ b/mysql-test/r/merge.result	2007-12-10 18:45:48 +01:00
@@ -1034,28 +1034,67 @@ 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;
+connection con1
+SET @mysql_test_sync_after_admin_flush= 'admin_flush:end_repair';
 REPAIR TABLE t1;
+connection default;
+SET @mysql_test_sync_after_use_db= ':admin_flush';
+USE test;
+SET @mysql_test_sync_lock_retry_limit= 1000;
+SET @mysql_test_sync_before_open_table_wait_refresh= 'end_repair';
 INSERT INTO t2 VALUES (1);
+SET @mysql_test_sync_lock_retry_limit= NULL;
+SET @mysql_test_sync_before_open_table_wait_refresh= NULL;
+SET @mysql_test_sync_after_use_db= 'end_repair';
+USE test;
+connection con1
 Table	Op	Msg_type	Msg_text
 test.t1	repair	status	OK
+connection default;
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 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;
+connection con1
+SET @mysql_test_sync_lock_retry_limit= 1000;
+SET @mysql_test_sync_after_insert= 'end_repair';
+SET @mysql_test_sync_before_open_table_wait_refresh= 'end_repair';
+INSERT INTO t2 VALUES (1);
+connection default;
+SET @mysql_test_sync_after_use_db= ':end_repair';
+USE test;
+UNLOCK TABLES;
+connection con1
+connection default;
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 DROP TABLE t1, t2;
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 LOCK TABLE t1 WRITE;
+connection con1
+SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+SET @mysql_test_sync_wait_for_lock= '*locked';
+SET @mysql_test_sync_after_insert= 'locked';
 INSERT INTO t1 VALUES (1);
+connection default
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
 FLUSH TABLES;
-FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
 SELECT * FROM t1;
 c1
 UNLOCK TABLES;
+connection con1
+connection default
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 DROP TABLE t1;
 #
 # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
@@ -1574,6 +1613,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;
@@ -1756,9 +1796,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;
@@ -1770,88 +1810,26 @@ 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;
+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 @mysql_test_sync_before_acos_function= 'select:truncated';
+SELECT ACOS(c1) FROM t3;
+connection default
+SET @mysql_test_sync_after_use_db= ':select';
+USE test;
+SET @mysql_test_sync_before_wait_locked_tname= 'truncated';
 TRUNCATE TABLE t1;
-SELECT COUNT(*) FROM t1;
-COUNT(*)
-0
-SELECT COUNT(*) FROM t2;
-COUNT(*)
-80
-SELECT COUNT(*) FROM t3;
-COUNT(*)
-80
+SET @mysql_test_sync_after_use_db= 'truncated';
+USE test;
+connection con1
+connection default
+SELECT ACOS(c1) FROM t3;
+ACOS(c1)
+1.3694384060046
 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;
@@ -1947,3 +1925,108 @@ test.t1	optimize	status	OK
 FLUSH TABLES m1, t1;
 UNLOCK TABLES;
 DROP TABLE t1, m1;
+CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
+CREATE TABLE m1 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
+connection con1
+SET @mysql_test_sync_before_myisammrg_attach= 'attach:flushed';
+INSERT INTO m1 VALUES (2);
+connection default;
+SET @mysql_test_sync_before_flush_tables= ':attach';
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLE m1;
+connection con1
+connection default;
+SELECT * FROM m1;
+c1
+2
+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 @mysql_test_sync_before_myisammrg_attach= 'attach:store_lock1';
+SET @mysql_test_sync_before_myisammrg_store_lock= 'store_lock2:flushed';
+INSERT INTO m1 VALUES (2);
+connection default;
+SET @mysql_test_sync_before_flush_tables= ':attach';
+SET @mysql_test_sync_before_myisammrg_store_lock= 'store_lock1:store_lock2';
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLE m1;
+connection con1
+connection default;
+SELECT * FROM m1;
+c1
+2
+DROP TABLE m1, t1;
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+connection con1
+SET @mysql_test_sync_lock_retry_limit= NULL;
+SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+SET @mysql_test_sync_wait_for_lock= '*locked';
+SET @mysql_test_sync_after_insert= 'locked';
+INSERT INTO t1 VALUES (1);
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+connection con1
+connection default;
+SET @mysql_test_sync_after_use_db= '';
+USE test;
+DROP TABLE t1;
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+connection con1
+SET @mysql_test_sync_lock_retry_limit= 2;
+SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+SET @mysql_test_sync_wait_for_lock= '*locked';
+SET @mysql_test_sync_after_insert= 'locked';
+INSERT INTO t1 VALUES (1);
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+LOCK TABLE t1 WRITE;
+connection con1
+SET @mysql_test_sync_lock_retry_limit= 2;
+SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+SET @mysql_test_sync_wait_for_lock= '*locked';
+SET @mysql_test_sync_after_insert= 'locked';
+INSERT INTO t1 VALUES (1);
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+LOCK TABLE t1 WRITE;
+connection con1
+SET @mysql_test_sync_lock_retry_limit= 2;
+SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+SET @mysql_test_sync_wait_for_lock= '*locked';
+SET @mysql_test_sync_after_insert= 'locked';
+INSERT INTO t1 VALUES (1);
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+connection con1
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+connection default;
+SET @mysql_test_sync_after_use_db= '';
+USE test;
+DROP TABLE t1;
diff -Nrup a/mysql-test/t/merge.test b/mysql-test/t/merge.test
--- a/mysql-test/t/merge.test	2007-11-18 20:28:35 +01:00
+++ b/mysql-test/t/merge.test	2007-12-10 18:45:48 +01:00
@@ -671,12 +671,6 @@ 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
@@ -698,12 +692,46 @@ connection default;
 #
 CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
 CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
-send REPAIR TABLE t1;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # When reaching repair code, signal admin_flush and wait for end_repair.
+    SET @mysql_test_sync_after_admin_flush= 'admin_flush:end_repair';
+    send REPAIR TABLE t1;
+#
+--echo connection default;
+connection default;
+# Wait that the other thread reaches repair.
+SET @mysql_test_sync_after_use_db= ':admin_flush';
+USE test;
+#
+# If the bug exists, INSERT will loop infinitely in getting its lock.
+# Bail out if lock retry is done 1000 fold in a second.
+SET @mysql_test_sync_lock_retry_limit= 1000;
+# 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 @mysql_test_sync_before_open_table_wait_refresh= 'end_repair';
+# Succeeds with bug fixed.
+INSERT INTO t2 VALUES (1);
+#
+# Clear user variables.
+SET @mysql_test_sync_lock_retry_limit= NULL;
+SET @mysql_test_sync_before_open_table_wait_refresh= NULL;
+#
+# Resume the other thread. (non-bug fixed case)
+SET @mysql_test_sync_after_use_db= 'end_repair';
+USE test;
+#
+    --echo connection con1
     connection con1;
-    sleep 1; # let repair run into its sleep
-    INSERT INTO t2 VALUES (1);
+    reap;
+    disconnect con1;
+#
+--echo connection default;
 connection default;
-reap;
+# Clear test_sync signal.
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 DROP TABLE t1, t2;
 #
 # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
@@ -718,20 +746,41 @@ DROP TABLE t1, t2;
 # 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;
+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 1000 fold in a second.
+    SET @mysql_test_sync_lock_retry_limit= 1000;
+    # If the bug exists, resume repair thread after reaching the retry limit.
+    SET @mysql_test_sync_after_insert= '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 @mysql_test_sync_before_open_table_wait_refresh= 'end_repair';
     send INSERT INTO t2 VALUES (1);
+#
+--echo connection default;
 connection default;
-sleep 1; # Let INSERT go into thr_multi_lock().
-REPAIR TABLE t1;
-sleep 2; # con1 performs busy waiting during this sleep.
+# Wait for signal from insert. Would be infinite with bug and no retry limit.
+SET @mysql_test_sync_after_use_db= ':end_repair';
+USE test;
 UNLOCK TABLES;
+#
+    --echo connection con1
     connection con1;
+    # Succeeds with bug fixed.
     reap;
+    disconnect con1;
+#
+--echo connection default;
 connection default;
+# Clear test_sync signal.
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 DROP TABLE t1, t2;
 #
 # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
@@ -753,28 +802,54 @@ DROP TABLE t1, t2;
 # 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;
-    connection con1;
+#
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # After open, wait for flush.
+    SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+    # If bug is fixed, INSERT will go into wait_for_lock.
+    # Retain variable value after use. Double used by general_log.
+    SET @mysql_test_sync_wait_for_lock= '*locked';
+    # If bug is not fixed, INSERT will succeed. Pretend locked.
+    SET @mysql_test_sync_after_insert= 'locked';
     send INSERT INTO t1 VALUES (1);
+#
+--echo connection default
 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.
+# Wait until INSERT opened the table.
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+#
+# Let INSERT exploit the gap when flush waits wthout lock
+# for other threads to close the tables.
+SET @mysql_test_sync_after_flush_tables= 'flushed';
 FLUSH TABLES;
-# This should give no result. But it will with sleep(2) at the right place.
+#
+# Wait until INSERT is locked (bug fixed) or finished (bug not fixed).
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+#
+# 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 test_sync signal.
+SET @mysql_test_sync_after_use_db= '';
+USE test;
 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
@@ -1100,6 +1175,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;
@@ -1203,11 +1279,12 @@ 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;
@@ -1216,49 +1293,44 @@ 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;
+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 @mysql_test_sync_before_acos_function= 'select:truncated';
+    send SELECT ACOS(c1) FROM t3;
+#
+--echo connection default
 connection default;
-sleep 1;
+# Wait for con1 to reach acos().
+SET @mysql_test_sync_after_use_db= ':select';
+USE test;
+#
+# With bug fix present, TRUNCATE runs into wait_for_locked_table_names().
+SET @mysql_test_sync_before_wait_locked_tname= 'truncated';
 TRUNCATE TABLE t1;
+#
+# With bug fix not present, we need to signal after TRUNCATE.
+SET @mysql_test_sync_after_use_db= 'truncated';
+USE test;
+#
+    --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 COUNT(*) FROM t1;
-SELECT COUNT(*) FROM t2;
-SELECT COUNT(*) FROM t3;
+SELECT ACOS(c1) FROM t3;
 DROP TABLE t1, t2, t3;
 
 #
@@ -1358,4 +1430,173 @@ OPTIMIZE TABLE t1;
 FLUSH TABLES m1, t1;
 UNLOCK TABLES;
 DROP TABLE t1, m1;
+#
+# 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 @mysql_test_sync_before_myisammrg_attach= 'attach: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 @mysql_test_sync_before_flush_tables= ':attach';
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLE m1;
+#
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+SELECT * FROM m1;
+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 @mysql_test_sync_before_myisammrg_attach= 'attach:store_lock1';
+    SET @mysql_test_sync_before_myisammrg_store_lock= 'store_lock2:flushed';
+    send INSERT INTO m1 VALUES (2);
+--echo connection default;
+connection default;
+#
+# Wen reaching flush tables (close_cached_tables()),
+# wait for con1 to reach attach,
+# then 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 @mysql_test_sync_before_flush_tables= ':attach';
+SET @mysql_test_sync_before_myisammrg_store_lock= 'store_lock1:store_lock2';
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLE m1;
+#
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+SELECT * FROM m1;
+DROP TABLE m1, t1;
+
+#
+# Coverage test for get_user_var().
+# Same test as for Bug#26379, Problem #3.
+# But mysql_test_sync_lock_retry_limit set to null.
+#
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    # Set a null integer value.
+    SET @mysql_test_sync_lock_retry_limit= NULL;
+    SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+    SET @mysql_test_sync_wait_for_lock= '*locked';
+    SET @mysql_test_sync_after_insert= 'locked';
+    send INSERT INTO t1 VALUES (1);
+--echo connection default;
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+    --echo connection con1
+    connection con1;
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+# Clear test_sync signal.
+SET @mysql_test_sync_after_use_db= '';
+USE test;
+DROP TABLE t1;
+#
+# Coverage test for mysql_lock_retry_limit().
+# Similar test as for Bug#26379, Problem #3.
+# But mysql_test_sync_lock_retry_limit set to 2 and INSERT/FLUSH repeated
+# twice so that the last INSERT results in ER_LOCK_WAIT_TIMEOUT.
+#
+CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
+LOCK TABLE t1 WRITE;
+    --echo connection con1
+    connect (con1,localhost,root,,);
+    SET @mysql_test_sync_lock_retry_limit= 2;
+    SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+    SET @mysql_test_sync_wait_for_lock= '*locked';
+    SET @mysql_test_sync_after_insert= 'locked';
+    send INSERT INTO t1 VALUES (1);
+--echo connection default;
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+LOCK TABLE t1 WRITE;
+    --echo connection con1
+    connection con1;
+    reap;
+    SET @mysql_test_sync_lock_retry_limit= 2;
+    SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+    SET @mysql_test_sync_wait_for_lock= '*locked';
+    SET @mysql_test_sync_after_insert= 'locked';
+    send INSERT INTO t1 VALUES (1);
+--echo connection default;
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+LOCK TABLE t1 WRITE;
+    --echo connection con1
+    connection con1;
+    reap;
+    SET @mysql_test_sync_lock_retry_limit= 2;
+    SET @mysql_test_sync_before_lock_tables= 'opened:flushed';
+    SET @mysql_test_sync_wait_for_lock= '*locked';
+    SET @mysql_test_sync_after_insert= 'locked';
+    send INSERT INTO t1 VALUES (1);
+--echo connection default;
+connection default;
+SET @mysql_test_sync_after_use_db= ':opened';
+USE test;
+SET @mysql_test_sync_after_flush_tables= 'flushed';
+FLUSH TABLES;
+SET @mysql_test_sync_after_use_db= ':locked';
+USE test;
+UNLOCK TABLES;
+    --echo connection con1
+    connection con1;
+    --error ER_LOCK_WAIT_TIMEOUT
+    reap;
+    disconnect con1;
+--echo connection default;
+connection default;
+# Clear test_sync signal.
+SET @mysql_test_sync_after_use_db= '';
+USE test;
+DROP TABLE t1;
 
diff -Nrup a/mysys/thr_lock.c b/mysys/thr_lock.c
--- a/mysys/thr_lock.c	2007-11-15 20:25:40 +01:00
+++ b/mysys/thr_lock.c	2007-12-10 18:45:48 +01:00
@@ -386,6 +386,14 @@ static inline my_bool have_specific_lock
 
 static void wake_up_waiters(THR_LOCK *lock);
 
+#ifndef DBUG_OFF
+/*
+  Global pointer to be set if callback function is defined
+  (e.g. in mysqld). See item_func.cc.
+*/
+void (*mysql_test_sync_wait_for_lock_callback_ptr)(void);
+#endif
+
 
 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");
+
+#ifndef DBUG_OFF
+  /*
+    One can use this to signal when a thread is going to wait for a lock.
+    See item_func.cc.
+  */
+  if (mysql_test_sync_wait_for_lock_callback_ptr)
+    (*mysql_test_sync_wait_for_lock_callback_ptr)();
+#endif
 
   if (!in_wait_list)
   {
diff -Nrup a/sql/item_func.cc b/sql/item_func.cc
--- a/sql/item_func.cc	2007-11-17 08:20:48 +01:00
+++ b/sql/item_func.cc	2007-12-10 18:45:48 +01:00
@@ -1681,6 +1681,8 @@ double Item_func_pow::val_real()
 
 double Item_func_acos::val_real()
 {
+  /* One can use this to defer SELECT processing. */
+  MYSQL_TEST_SYNC(current_thd, "mysql_test_sync_before_acos_function");
   DBUG_ASSERT(fixed == 1);
   // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
   volatile double value= args[0]->val_real();
@@ -5664,3 +5666,315 @@ longlong Item_func_uuid_short::val_int()
   pthread_mutex_unlock(&LOCK_uuid_generator);
   return (longlong) val;
 }
+
+
+#if !defined(DBUG_OFF)
+
+/**
+  Get the value of a user variable.
+
+  @param[in]    thd             thread handle
+  @param[in]    name            name of user variable
+  @param[in]    name_len        length of name of user variable
+  @param[out]   result          value of the user variable
+
+  @return       status
+    @retval     FALSE           ok, variable value is in *result
+    @retval     TRUE            error, no such variable or null
+
+  @note
+    This function comment applies to all get_user_var() functions.
+*/
+
+bool get_user_var(THD *thd, const char *name, size_t name_len, longlong *result)
+{
+  user_var_entry *user_var;
+  my_bool null_value;
+
+  if (!(user_var= (user_var_entry*) hash_search(&thd->user_vars,
+                                                (uchar*) name, name_len)))
+    return TRUE;
+  *result= user_var->val_int(&null_value);
+  return null_value;
+}
+
+#ifdef DEMO_IMPLEMENTATION
+bool get_user_var(THD *thd, const char *name, size_t name_len, double *result)
+{
+  user_var_entry *user_var;
+  my_bool null_value;
+
+  if (!(user_var= (user_var_entry*) hash_search(&thd->user_vars,
+                                                (uchar*) name, name_len)))
+    return TRUE;
+  *result= user_var->val_real(&null_value);
+  return null_value;
+}
+
+bool get_user_var(THD *thd, const char *name, size_t name_len, String *result)
+{
+  user_var_entry *user_var;
+  my_bool null_value;
+
+  if (!(user_var= (user_var_entry*) hash_search(&thd->user_vars,
+                                                (uchar*) name, name_len)))
+    return TRUE;
+  /* Requesting 6 decimals. No reason. Just an arbitrary value. */
+  if (!user_var->val_str(&null_value, result, 6))
+    return TRUE;
+  return null_value;
+}
+#endif
+
+
+/**
+  Definitions for the test synchronization facility.
+  1. Global string variable to hold a "signal".
+  2. Global condition variable for signalling and waiting.
+  3. Global mutex to synchronize access to the above.
+*/
+
+static String SIGNAL_mysql_test_sync;
+static pthread_cond_t COND_mysql_test_sync;
+static pthread_mutex_t LOCK_mysql_test_sync;
+
+
+/**
+  Callback from wait_for_lock() for test synchronization. See thr_lock.c.
+
+  One can use this to signal when a thread is going to wait for a lock.
+  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.
+*/
+
+static void mysql_test_sync_wait_for_lock_callback(void)
+{
+  mysql_test_sync(current_thd,
+                  STRING_WITH_LEN("mysql_test_sync_wait_for_lock"));
+}
+
+
+/**
+  Initialize the test synchronization facility.
+*/
+
+void init_mysql_test_sync(void)
+{
+  extern void (*mysql_test_sync_wait_for_lock_callback_ptr)(void);
+  DBUG_ENTER("init_mysql_test_sync");
+
+  SIGNAL_mysql_test_sync.length(0);
+  (void) pthread_cond_init(&COND_mysql_test_sync,NULL);
+  (void) pthread_mutex_init(&LOCK_mysql_test_sync, MY_MUTEX_INIT_FAST);
+  mysql_test_sync_wait_for_lock_callback_ptr=
+    mysql_test_sync_wait_for_lock_callback;
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  End the test synchronization facility.
+*/
+
+void end_mysql_test_sync(void)
+{
+  DBUG_ENTER("end_mysql_test_sync");
+  SIGNAL_mysql_test_sync.free();
+  (void) pthread_cond_destroy(&COND_mysql_test_sync);
+  (void) pthread_mutex_destroy(&LOCK_mysql_test_sync);
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Send a signal and optionally wait for a signal if a user variable is set.
+
+  @param[in]    thd             thread handle
+  @param[in]    user_var_name   name of user variable
+  @param[in]    name_len        length of name of user variable
+
+  @description
+    This function is used to synchronize threads so that they wait at
+    critical places without sleeping a fixed time.
+
+    Nomenclature: A "signal" is a string value that can be set in a
+    global variable. "Sending" a signal means assigning the string value
+    for this signal to the global variable and broadcast a pthread
+    condition. "Waiting" for a signal means to loop on a global pthread
+    condition variable until the global variable is assigned the signal
+    to wait for.
+
+    The global objects used for this facility are:
+
+    SIGNAL_mysql_test_sync     String variable to hold a "signal".
+    COND_mysql_test_sync       Condition variable for signalling and waiting.
+    LOCK_mysql_test_sync       Mutex to synchronize access to the above.
+
+    If the user variable 'user_var_name' contains a string like
+
+        [*][send_signal][:[wait_signal]]
+
+    then, if "send_signal" is present, the global variable
+    'SIGNAL_mysql_test_sync' is assigned "send_signal" and the
+    condition 'COND_mysql_test_sync' is broadcast. If a colon and
+    "wait_signal" is present, it waits on 'COND_mysql_test_sync' until
+    'SIGNAL_mysql_test_sync' becomes "wait_signal" assigned. If the
+    user variable value is empty, 'SIGNAL_mysql_test_sync' is cleared
+    and 'COND_mysql_test_sync' is broadcast. There are the following
+    options:
+
+        empty                      send empty signal, do not wait
+        send_signal                send send_signal, do not wait
+        send_signal:               send send_signal, do not wait
+        send_signal:wait_signal    send send_signal, wait for wait_signal
+        :wait_signal               do not send signal, wait for wait_signal
+        :                          do not send signal, do not wait (no-op)
+
+    An asterisk ('*') at string begin preserves the variable value
+    for another evaluation. Without it, the variable value is set to
+    null after it is evaluated.
+
+    Example:
+
+      test file:
+
+        connection con1;
+        SET @mysql_test_sync_after_open_table= 'open_table:flush_tables';
+        send INSERT INTO t1 VALUES (1);
+        connection con2;
+        SET @mysql_test_sync_before_flush_tables= ':open_table';
+        SET @mysql_test_sync_after_flush_tables= 'flush_tables';
+        FLUSH TABLE t1;
+
+      code:
+
+        in open_table():
+        ... open table ...
+        MYSQL_TEST_SYNC("mysql_test_sync_after_open_table");
+
+        in close_cached_tables():
+        MYSQL_TEST_SYNC("mysql_test_sync_before_flush_tables");
+        ... flush tables ...
+        MYSQL_TEST_SYNC("mysql_test_sync_after_flush_tables");
+
+    MYSQL_TEST_SYNC is a macro that has no code in a non-debug server.
+
+  @note
+    This function assumes that string type user variable values are
+    terminated with a '\0' byte.
+
+  @todo
+    If we want to support test cases that would otherwise fail in a
+    non-debug server, we either take the risk that users can want to
+    set user variables with names that conflict with the this facility,
+    or we find a way to set a thread specific variable by some SQL
+    statement, which enables (and perhaps disables) this facility.
+
+  @todo
+    One day we might find a test that cannot be synchronized with a
+    global signal. We could add a signal, condition, and mutex into THD
+    then. The user variable value could then be written as
+
+        [*][send_signal[,thread_id]][:[wait_signal[,THD]]]
+
+    Missing thread_id would use the global variable to be compatible
+    with the current facility. Waiting happens on the global signal and
+    condition or the own threads signal and condition, depending on the
+    presence of the literal ",THD".
+*/
+
+void mysql_test_sync(THD *thd, const char *user_var_name, size_t name_len)
+{
+  user_var_entry  *user_var;
+  char            *value_begin;
+  char            *value_end;
+  char            *colon_pos;
+  DBUG_ENTER("mysql_test_sync");
+  DBUG_PRINT("mysql_test_sync", ("user_var_name: '%s'", user_var_name));
+
+  /*
+    During bootstrap or in non-THD threads, thd can be NULL.
+    Get user variable, which must be a non-null string.
+  */
+  if (!thd ||
+      !(user_var= (user_var_entry*)
+        hash_search(&thd->user_vars, (uchar*) user_var_name, name_len)) ||
+      !user_var->value || (user_var->type != STRING_RESULT))
+    DBUG_VOID_RETURN;
+  DBUG_PRINT("mysql_test_sync", ("user_var_value: '%s'", user_var->value));
+
+  /* Check for asterisk and find colon in string. */
+  value_begin= user_var->value;
+  value_end= value_begin + user_var->length;
+  /* Skip asterisk, if present. */
+  if (*value_begin == '*')
+    value_begin++;
+  for (colon_pos= value_begin;
+       (colon_pos < value_end) && (*colon_pos != ':');
+       colon_pos++) {}
+
+  /* Send a signal, if colon is not the first character (behind asterisk). */
+  if (*value_begin != ':')
+  {
+    /* Colon present, send signal. */
+    DBUG_PRINT("mysql_test_sync", ("do broadcast '%.*s'", (int)
+                                   (colon_pos - value_begin), value_begin));
+    pthread_mutex_lock(&LOCK_mysql_test_sync);
+    SIGNAL_mysql_test_sync.copy(value_begin, colon_pos - value_begin,
+                                system_charset_info);
+    pthread_cond_broadcast(&COND_mysql_test_sync);
+    pthread_mutex_unlock(&LOCK_mysql_test_sync);
+  }
+
+  /* Skip colon, if present. */
+  if (colon_pos < value_end)
+    colon_pos++;
+
+  /* Wait for a signal, if rest of string is non-empty. */
+  if (colon_pos < value_end)
+  {
+    const char  *old_proc_info;
+    char        *old_query;
+    uint        old_query_length;
+    uint        siglen= value_end - colon_pos;
+
+    /* Save "process" information for INFORMATION_SCHEMA.PROCESSLIST. */
+    old_proc_info=      thd->proc_info;
+    old_query=          thd->query;
+    old_query_length=   thd->query_length;
+
+    /* Change "process" information for INFORMATION_SCHEMA.PROCESSLIST. */
+    thd->proc_info=     "MYSQL_TEST_SYNC";
+    thd->query=         (char*) user_var_name;
+    thd->query_length=  name_len;
+
+    DBUG_PRINT("mysql_test_sync", ("do wait for '%s'", colon_pos));
+    pthread_mutex_lock(&LOCK_mysql_test_sync);
+    while ((SIGNAL_mysql_test_sync.length() != siglen) ||
+           memcmp(SIGNAL_mysql_test_sync.ptr(), colon_pos, siglen))
+      pthread_cond_wait(&COND_mysql_test_sync, &LOCK_mysql_test_sync);
+    /* @todo Maybe we could do here: SIGNAL_mysql_test_sync.length(0); */
+    pthread_mutex_unlock(&LOCK_mysql_test_sync);
+    DBUG_PRINT("mysql_test_sync", ("do resume from '%s'  user_var_name: '%s'",
+                                   colon_pos, user_var_name));
+
+    /* Restore "process" information for INFORMATION_SCHEMA.PROCESSLIST. */
+    thd->proc_info= old_proc_info;
+    thd->query= old_query;
+    thd->query_length= old_query_length;
+  }
+
+  /*
+    If string does not begin with an asterisk, reset value to null.
+    In this case value_begin is equal to user_var->value.
+    Otherwise value_begin skipped over the asterisk.
+  */
+  if (value_begin == user_var->value)
+    update_hash(user_var, TRUE, 0, 0, STRING_RESULT, system_charset_info,
+                DERIVATION_IMPLICIT, 0 /* unsigned_arg */);
+
+  DBUG_VOID_RETURN;
+}
+
+#endif /*!DBUG_OFF*/
diff -Nrup a/sql/lock.cc b/sql/lock.cc
--- a/sql/lock.cc	2007-12-10 17:55:01 +01:00
+++ b/sql/lock.cc	2007-12-10 18:45:48 +01:00
@@ -92,6 +92,72 @@ static int lock_external(THD *thd, TABLE
 static int unlock_external(THD *thd, TABLE **table,uint count);
 static void print_lock_error(int error, const char *);
 
+
+#ifndef DBUG_OFF
+/**
+  Check if this is called too often in a second by the same thread.
+
+  @param[in]    thd             thread handle
+
+  @return if limit reached
+    @retval     FALSE           limit not reached
+    @retval     TRUE            limit reached
+
+  @description
+    If user variable "mysql_test_sync_lock_retry_limit" is set,
+    report lock wait timeout, if this is called too frequently.
+    The variable value is an integer which determines, how often
+    this can be called per second.
+
+  @note
+    If multiple statements run in the same second, the loop counter
+    could reach the limit, though a statement does not loop. When
+    setting @mysql_test_sync_lock_retry_limit to activate the limit,
+    please use statements from a second thread, to reset the counter
+    between statements.
+*/
+
+static bool mysql_lock_retry_limit(THD *thd)
+{
+  longlong limit;
+  DBUG_ENTER("mysql_lock_retry_limit");
+
+  if (!get_user_var(thd, STRING_WITH_LEN("mysql_test_sync_lock_retry_limit"),
+                    &limit))
+  {
+    static THD        *lastthd;
+    static longlong   loopcount;
+    static time_t     timestamp;
+    time_t            timest;
+
+    if (thd != lastthd)
+    {
+      timestamp= 0;
+      lastthd= thd;
+    }
+    if (timestamp == (timest= time((time_t*)0)))
+    {
+      if (++loopcount >= limit)
+      {
+        DBUG_PRINT("tlock", ("mysql_lock_retry_limit reached: %lu",
+                             (ulong) loopcount));
+        my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+        DBUG_RETURN(TRUE);
+      }
+      DBUG_PRINT("tlock", ("mysql_lock_retry_limit: %lu", (ulong) loopcount));
+    }
+    else
+    {
+      loopcount= 0;
+      timestamp= timest;
+      DBUG_PRINT("tlock", ("mysql_lock_retry_limit reset"));
+    }
+  }
+  DBUG_RETURN(FALSE);
+}
+#endif
+
+
 /*
   Lock tables.
 
@@ -318,6 +384,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, 
     thd->locked=0;
 retry:
     sql_lock=0;
+#ifndef DBUG_OFF
+    if (mysql_lock_retry_limit(thd))
+      break;
+#endif
     if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
     {
       *need_reopen= TRUE;
@@ -1109,6 +1179,7 @@ bool wait_for_locked_table_names(THD *th
       result=1;
       break;
     }
+    MYSQL_TEST_SYNC(thd, "mysql_test_sync_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	2007-11-15 20:25:41 +01:00
+++ b/sql/mysql_priv.h	2007-12-10 18:45:49 +01:00
@@ -2347,6 +2347,30 @@ bool load_collation(MEM_ROOT *mem_root,
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
+#if !defined(DBUG_OFF)
+/*
+  Functions to retrieve the value of user variables.
+*/
+extern bool get_user_var(THD *thd, const char *name, size_t name_len,
+                         longlong *result);
+extern bool get_user_var(THD *thd, const char *name, size_t name_len,
+                         String *result);
+/*
+  Declarations for the test synchronization facility.
+  Function to signal and/or wait based on the value of a user variable.
+  Macro to skip code generation in a non-debug server.
+  See item_func.cc for the code and more information.
+*/
+extern void init_mysql_test_sync(void);
+extern void end_mysql_test_sync(void);
+extern void mysql_test_sync(THD *thd, const char *user_var_name,
+                            size_t name_len);
+#define MYSQL_TEST_SYNC(_thd_, _user_var_name_)                 \
+        mysql_test_sync(_thd_, STRING_WITH_LEN(_user_var_name_))
+#else
+#define MYSQL_TEST_SYNC(_thd_, _user_var_name_) do{}while(0)
+#endif
+
 #endif /* MYSQL_SERVER */
 #endif /* MYSQL_CLIENT */
 
diff -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
--- a/sql/mysqld.cc	2007-10-31 22:10:56 +01:00
+++ b/sql/mysqld.cc	2007-12-10 18:45:49 +01:00
@@ -1244,6 +1244,10 @@ void clean_up(bool print_message)
 #ifdef USE_REGEX
   my_regex_end();
 #endif
+#if !defined(DBUG_OFF)
+  /* End the test synchronization facility. See item_func.cc */
+  end_mysql_test_sync();
+#endif
 
 #if !defined(EMBEDDED_LIBRARY)
   if (!opt_bootstrap)
@@ -2752,6 +2756,11 @@ static int init_common_variables(const c
   if (init_thread_environment())
     return 1;
   mysql_init_variables();
+
+#if !defined(DBUG_OFF)
+  /* Initialize the test synchronization facility. See item_func.cc */
+  init_mysql_test_sync();
+#endif
 
 #ifdef HAVE_TZNAME
   {
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-11-15 20:25:41 +01:00
+++ b/sql/sql_base.cc	2007-12-10 18:45:49 +01:00
@@ -897,6 +897,8 @@ bool close_cached_tables(THD *thd, bool 
   DBUG_ENTER("close_cached_tables");
   DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables));
 
+  MYSQL_TEST_SYNC(thd, "mysql_test_sync_before_flush_tables");
+
   if (!have_lock)
     VOID(pthread_mutex_lock(&LOCK_open));
   if (!tables)
@@ -995,6 +997,9 @@ bool close_cached_tables(THD *thd, bool 
     close_old_data_files(thd,thd->open_tables,1,1);
     mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
                    TRUE);
+
+    MYSQL_TEST_SYNC(thd, "mysql_test_sync_after_flush_tables");
+
     bool found=1;
     /* Wait until all threads has closed all the tables we had locked */
     DBUG_PRINT("info",
@@ -2821,6 +2826,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *
       */
       if (table->in_use != thd)
       {
+        MYSQL_TEST_SYNC(thd, "mysql_test_sync_before_open_table_wait_refresh");
+
         /* wait_for_conditionwill unlock LOCK_open for us */
         wait_for_condition(thd, &LOCK_open, &COND_refresh);
       }
@@ -5086,6 +5093,11 @@ int lock_tables(THD *thd, TABLE_LIST *ta
         thd->set_current_stmt_binlog_row_based_if_mixed();
       }
     }
+
+#ifndef DBUG_OFF
+    if (ptr - start)
+      MYSQL_TEST_SYNC(thd, "mysql_test_sync_before_lock_tables");
+#endif
 
     if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
                                         lock_flag, need_reopen)))
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2007-11-15 20:25:41 +01:00
+++ b/sql/sql_parse.cc	2007-12-10 18:45:49 +01:00
@@ -2823,6 +2823,7 @@ end_with_restore_list:
       thd->first_successful_insert_id_in_cur_stmt=
         thd->first_successful_insert_id_in_prev_stmt;
 
+    MYSQL_TEST_SYNC(thd, "mysql_test_sync_after_insert");
     break;
   }
   case SQLCOM_REPLACE_SELECT:
@@ -3066,6 +3067,9 @@ end_with_restore_list:
 
     if (!mysql_change_db(thd, &db_str, FALSE))
       send_ok(thd);
+
+    /* One can use this to signal and/or wait without side effects. */
+    MYSQL_TEST_SYNC(thd, "mysql_test_sync_after_use_db");
 
     break;
   }
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-11-15 20:25:41 +01:00
+++ b/sql/sql_table.cc	2007-12-10 18:45:49 +01:00
@@ -4171,6 +4171,8 @@ static bool mysql_admin_table(THD* thd, 
       /* Flush entries in the query cache involving this table. */
       query_cache_invalidate3(thd, table->table, 0);
       open_for_modify= 0;
+
+      MYSQL_TEST_SYNC(thd, "mysql_test_sync_after_admin_flush");
     }
 
     if (table->table->s->crashed && operator_func == &handler::ha_check)
diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc
--- a/storage/myisammrg/ha_myisammrg.cc	2007-12-10 17:55:01 +01:00
+++ b/storage/myisammrg/ha_myisammrg.cc	2007-12-10 18:45:49 +01:00
@@ -456,6 +456,8 @@ int ha_myisammrg::attach_children(void)
   DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
   DBUG_ASSERT(!this->file->children_attached);
 
+  MYSQL_TEST_SYNC(current_thd, "mysql_test_sync_before_myisammrg_attach");
+
   /*
     Initialize variables that are used, modified, and/or set by
     myisammrg_attach_children_callback().
@@ -926,6 +928,8 @@ THR_LOCK_DATA **ha_myisammrg::store_lock
 					 enum thr_lock_type lock_type)
 {
   MYRG_TABLE *open_table;
+
+  MYSQL_TEST_SYNC(thd, "mysql_test_sync_before_myisammrg_store_lock");
 
   /*
     This method can be called while another thread is attaching the
Thread
bk commit into 5.1 tree (istruewing:1.2624)Ingo Struewing10 Dec