From: Date: April 5 2006 9:05pm Subject: bk commit into 4.1 tree (ingo:1.2488) BUG#18544 List-Archive: http://lists.mysql.com/commits/4515 X-Bug: 18544 Message-Id: Below is the list of changes that have just been committed into a local 4.1 repository of mydev. When mydev 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 1.2488 06/04/05 21:05:37 ingo@stripped +5 -0 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption After a lock wait timeout the open table(s) were not fully cleaned up for reuse. But they were put into the open table cache even before the lock was tried. The next statement reused the table(s) with a wrong lock type set up. This tricked MyISAM into believing that it don't need to update the table statistics. Hence CHECK TABLE reported a record count and table size mismatch. Fortunately nothing worse has been detected yet. The effect of the test case was that the insert worked on a read locked table. (!) I added a new function that clears the lock type from all tables that were prepared for a lock. I call this function when a lock failes. In an unrelated change I added the configuration variable 'innodb_lock_wait_timeout' to the list of server variables. I needed it to to shorten the lock wait timeout from 50 seconds to 1 second in the test case. Without this change the test case would have added 49 additional seconds to the test suite. sql/set_var.cc 1.179 06/04/05 21:04:29 ingo@stripped +4 -1 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption Added 'innodb_lock_wait_timeout' to the list of system variables. sql/lock.cc 1.65 06/04/05 21:04:29 ingo@stripped +36 -0 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption Resetting the lock type in the open table(s) lock data after a failed lock_external(). sql/ha_innodb.h 1.81 06/04/05 21:04:29 ingo@stripped +1 -0 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption Added 'innodb_lock_wait_timeout' to the list of system variables. mysql-test/t/innodb.test 1.80 06/04/05 21:04:29 ingo@stripped +35 -0 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption The test case. mysql-test/r/innodb.result 1.109 06/04/05 21:04:29 ingo@stripped +16 -0 Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption The test result. # This is a BitKeeper patch. What follows are the unified diffs for the # set of deltas contained in the patch. The rest of the patch, the part # that BitKeeper cares about, is below these diffs. # User: ingo # Host: chilla.local # Root: /home/mydev/mysql-4.1-bug18544 --- 1.64/sql/lock.cc 2006-01-04 15:35:17 +01:00 +++ 1.65/sql/lock.cc 2006-04-05 21:04:29 +02:00 @@ -77,6 +77,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, bool unlock, TABLE **write_locked); +static void reset_lock_data(MYSQL_LOCK *sql_lock); static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error); @@ -133,6 +134,15 @@ thd->proc_info="System lock"; if (lock_external(thd, tables, count)) { + /* + At this point we want to quit the locking of the table(s). + But we have already set the lock type into the lock data of the + open table(s). If the table(s) are in the open table cache, they + could be reused with the non-zero lock type set. This could lead + to ignoring a different lock type with the next lock. (Bug #18544) + Clear the lock type of all lock data. + */ + reset_lock_data(sql_lock); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; thd->proc_info=0; @@ -559,6 +569,32 @@ (*org_locks)->debug_print_param= (void *) table; } DBUG_RETURN(sql_lock); +} + + +/* + Reset lock type in lock data. + + SYNOPSIS + reset_lock_data() + sql_lock The MySQL lock. + + RETURN + void +*/ + +static void reset_lock_data(MYSQL_LOCK *sql_lock) +{ + THR_LOCK_DATA **ldata; + THR_LOCK_DATA **ldata_end; + + for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count; + ldata < ldata_end; + ldata++) + { + /* Reset lock type. */ + (*ldata)->type= TL_UNLOCK; + } } --- 1.108/mysql-test/r/innodb.result 2006-01-30 13:17:30 +01:00 +++ 1.109/mysql-test/r/innodb.result 2006-04-05 21:04:29 +02:00 @@ -1807,3 +1807,19 @@ KEY `t2_ibfk_0` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 DROP TABLE t2,t1; +create table t1 (c1 varchar(255), primary key(c1)) engine=innodb; +create table t2 (c1 varchar(255), primary key(c1)) engine=myisam; +set autocommit=0; +insert into t1 values ('anything'); +insert into t2 values ('anything'); +set autocommit=0; +set @old_innodb_lock_wait_timeout= @@global.innodb_lock_wait_timeout; +set @@global.innodb_lock_wait_timeout= 1; +lock tables t1 read, t2 read; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +set @@global.innodb_lock_wait_timeout= @old_innodb_lock_wait_timeout; +insert into t2 values ('anything else'); +check table t2; +Table Op Msg_type Msg_text +test.t2 check status OK +drop table t1, t2; --- 1.79/mysql-test/t/innodb.test 2006-01-30 13:17:30 +01:00 +++ 1.80/mysql-test/t/innodb.test 2006-04-05 21:04:29 +02:00 @@ -1378,4 +1378,39 @@ SHOW CREATE TABLE t2; DROP TABLE t2,t1; +# +# Bug#18544 - LOCK TABLES timeout causes MyISAM table corruption +# This is not really an InnoDB test, but it needs InnoDB to trigger the bug. +# +create table t1 (c1 varchar(255), primary key(c1)) engine=innodb; +create table t2 (c1 varchar(255), primary key(c1)) engine=myisam; +# +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +# +connection con1; +set autocommit=0; +insert into t1 values ('anything'); +insert into t2 values ('anything'); +# +connection con2; +set autocommit=0; +set @old_innodb_lock_wait_timeout= @@global.innodb_lock_wait_timeout; +set @@global.innodb_lock_wait_timeout= 1; +# The next statement waits because of the innodb lock taken by connection 1. +# It waits until the lock times out. +--error 1205 +lock tables t1 read, t2 read; +set @@global.innodb_lock_wait_timeout= @old_innodb_lock_wait_timeout; +# +connection con1; +insert into t2 values ('anything else'); +# The following shows table corruption if the bug is present. +check table t2; +# +connection default; +disconnect con1; +disconnect con2; +drop table t1, t2; + # End of 4.1 tests --- 1.80/sql/ha_innodb.h 2005-05-04 15:05:53 +02:00 +++ 1.81/sql/ha_innodb.h 2006-04-05 21:04:29 +02:00 @@ -203,6 +203,7 @@ extern ulong srv_max_buf_pool_modified_pct; extern ulong srv_max_purge_lag; extern ulong srv_auto_extend_increment; +extern ulong srv_lock_wait_timeout; } extern TYPELIB innobase_lock_typelib; --- 1.178/sql/set_var.cc 2005-09-21 00:18:26 +02:00 +++ 1.179/sql/set_var.cc 2006-04-05 21:04:29 +02:00 @@ -374,6 +374,8 @@ &SV::innodb_table_locks); sys_var_long_ptr sys_innodb_autoextend_increment("innodb_autoextend_increment", &srv_auto_extend_increment); +sys_var_long_ptr sys_innodb_lock_wait_timeout("innodb_lock_wait_timeout", + &srv_lock_wait_timeout); #endif #ifdef HAVE_NDBCLUSTER_DB @@ -653,6 +655,7 @@ &sys_innodb_table_locks, &sys_innodb_max_purge_lag, &sys_innodb_autoextend_increment, + &sys_innodb_lock_wait_timeout, #endif #ifdef HAVE_NDBCLUSTER_DB &sys_ndb_autoincrement_prefetch_sz, @@ -744,7 +747,7 @@ {"innodb_flush_log_at_trx_commit", (char*) &innobase_flush_log_at_trx_commit, SHOW_INT}, {"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR}, {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG }, - {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG }, + {"innodb_lock_wait_timeout", (char*) &srv_lock_wait_timeout, SHOW_LONG }, {"innodb_locks_unsafe_for_binlog", (char*) &innobase_locks_unsafe_for_binlog, SHOW_MY_BOOL}, {"innodb_log_arch_dir", (char*) &innobase_log_arch_dir, SHOW_CHAR_PTR}, {"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL},