#------------------------------------------- # Test case #------------------------------------------- # Note: # By default, the test checks how UPDATE works in this scenario. # If you want to see the behavior of EXPLAIN UPDATE instead, run the test as # EXPLAIN=1 perl ./mtr if ($EXPLAIN) { --let $EXPLAIN= EXPLAIN EXTENDED } --source include/have_innodb.inc --source include/have_debug_sync.inc --enable_connect_log set @stats_backup= @@innodb_stats_persistent; set @dbug_backup= @@debug; set global innodb_stats_persistent=ON; --let $con_id= `SELECT CONNECTION_ID()` CREATE TABLE t1 ( pk bigint(20) unsigned NOT NULL, key1 int, col1 int, filler1 varchar(255), filler2 text, PRIMARY KEY (pk), KEY (key1) ) ENGINE=InnoDB; create table ten(a int primary key); insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); insert into t1 (pk, key1) select A.a+10*B.a+100*C.a+1000*D.a, A.a+10*B.a+100*C.a+1000*D.a from ten A, ten B, ten C, ten D; select count(*) from t1; --echo # --echo # Normal plan for UPDATE: --echo # explain extended UPDATE t1 SET col1 = 1234 WHERE pk IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20); --echo # --echo # Check that it lives up to the plan: --echo # flush status; UPDATE t1 SET col1 = 1234 WHERE pk IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20); show status like 'Handler_read%'; --echo # --echo # Now let's trigger the race condition. --echo # --echo # First, we need to activate the sleep in dict_stats_update: --echo # set global debug= '+d,innodb_wait_with_empty_stats'; set debug= '+d,innodb_wait_with_empty_stats'; --echo # --echo # The sync point is only needed if we want to reproduce the printout from the patch, --echo # where Records == Records2 == 1 and Records3 is the real number. --echo # Without the printing patch the sync point won't make any difference. set debug_sync= 'opt_range_wait_for_innodb_stats WAIT_FOR go'; --echo # flush status; --connect (con1,localhost,root,,) --echo # --echo # Get the table evicted from the cache; --echo # flush tables with read lock; unlock tables; --echo # --echo # Insert something into the table to trigger a call to dict_stats_update, --echo # which will in turn trigger a call to dict_stats_empty_table and then will repopulate statistics. --echo # Only we have a big delay between the call to dict_stats_empty_table and repopulating statistics, so it will wait there. --echo # We are sending the INSERT now... --send insert into t1 (pk,key1,col1) select pk+100000, 345,345 from t1 limit 2000 --connection default --echo # --echo # Wait a bit to make sure the insert reached the injected sleep... --sleep 8 --echo # --echo # Now, run our $EXPLAIN UPDATE. The table right now has "empty" statistics, which will give us 1 row, and full scan will be preferred. --echo # Since we might have enabled the sync point, the UPDATE will wait for a signal after it has done the quick check. --echo # We are sending the UPDATE now... --echo # --send_eval $EXPLAIN UPDATE t1 SET col1 = 1234 WHERE pk IN (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20) --connection con1 --echo # --echo # Now wait for INSERT to finish, and send the signal to UPDATE to proceed (in case it's waiting on the sync point) --echo # --reap set debug_sync= 'now SIGNAL go'; --connection default --echo # Now the $EXPLAIN UPDATE is finished too. This is how it ran: --reap --echo # if (!$EXPLAIN) { show status like 'Handler_read%'; } --echo # --echo # Cleanup --echo # --disconnect con1 --connection default drop table t1; drop table ten; set global innodb_stats_persistent= @stats_backup; set global debug= @dbug_backup; set debug_sync= 'RESET';