summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysqltest.c1
-rw-r--r--include/thr_lock.h1
-rw-r--r--mysql-test/mysql-test-run.sh3
-rw-r--r--mysql-test/r/lock_multi.result9
-rw-r--r--mysql-test/t/lock_multi.test24
-rw-r--r--mysys/thr_lock.c48
-rw-r--r--sql/handler.cc3
-rw-r--r--sql/lock.cc19
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/mysqld.cc4
-rw-r--r--sql/sql_base.cc46
11 files changed, 144 insertions, 15 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c
index cad13fe1349..9d5d9f9cd35 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -2484,6 +2484,7 @@ int main(int argc, char** argv)
}
case Q_COMMENT: /* Ignore row */
case Q_COMMENT_WITH_COMMAND:
+ break;
case Q_PING:
(void) mysql_ping(&cur_con->mysql);
break;
diff --git a/include/thr_lock.h b/include/thr_lock.h
index 6650d25e145..f1bda0ce6b4 100644
--- a/include/thr_lock.h
+++ b/include/thr_lock.h
@@ -107,6 +107,7 @@ void thr_unlock(THR_LOCK_DATA *data);
int thr_multi_lock(THR_LOCK_DATA **data,uint count);
void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
void thr_abort_locks(THR_LOCK *lock);
+void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread);
void thr_print_locks(void); /* For debugging */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data);
my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data);
diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh
index 48bb94ea0e4..b12ed2b3739 100644
--- a/mysql-test/mysql-test-run.sh
+++ b/mysql-test/mysql-test-run.sh
@@ -349,7 +349,8 @@ while test $# -gt 0; do
--debug=d:t:i:A,$MYSQL_TEST_DIR/var/log/master.trace"
EXTRA_SLAVE_MYSQLD_OPT="$EXTRA_SLAVE_MYSQLD_OPT \
--debug=d:t:i:A,$MYSQL_TEST_DIR/var/log/slave.trace"
- EXTRA_MYSQL_TEST_OPT="$EXTRA_MYSQL_TEST_OPT --debug"
+ EXTRA_MYSQL_TEST_OPT="$EXTRA_MYSQL_TEST_OPT \
+ --debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqltest.trace"
;;
--fast)
FAST_START=1
diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result
index 20bc9b9572f..b808fca0acf 100644
--- a/mysql-test/r/lock_multi.result
+++ b/mysql-test/r/lock_multi.result
@@ -1,4 +1,4 @@
-drop table if exists t1;
+drop table if exists t1,t2;
create table t1(n int);
insert into t1 values (1);
lock tables t1 write;
@@ -17,3 +17,10 @@ unlock tables;
n
1
drop table t1;
+create table t1 (a int);
+create table t2 (a int);
+lock table t1 write, t2 write;
+ insert t1 select * from t2;
+drop table t2;
+Table 'test.t2' doesn't exist
+drop table t1;
diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test
index 792ae956e2a..0295fca29e7 100644
--- a/mysql-test/t/lock_multi.test
+++ b/mysql-test/t/lock_multi.test
@@ -6,8 +6,9 @@
#
-- source include/not_embedded.inc
-drop table if exists t1;
-#test to see if select will get the lock ahead of low priority update
+drop table if exists t1,t2;
+
+# test to see if select will get the lock ahead of low priority update
connect (locker,localhost,root,,);
connect (reader,localhost,root,,);
@@ -48,3 +49,22 @@ reap;
connection reader;
reap;
drop table t1;
+
+#
+# Test problem when using locks on many tables and droping a table that
+# is to-be-locked by another thread
+#
+
+connection locker;
+create table t1 (a int);
+create table t2 (a int);
+lock table t1 write, t2 write;
+connection reader;
+send insert t1 select * from t2;
+connection locker;
+drop table t2;
+connection reader;
+--error 1146
+reap;
+connection locker;
+drop table t1;
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index 0288c7c1cbe..61616a4cf2b 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -945,6 +945,54 @@ void thr_abort_locks(THR_LOCK *lock)
}
+/*
+ Abort all locks for specific table/thread combination
+
+ This is used to abort all locks for a specific thread
+*/
+
+void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
+{
+ THR_LOCK_DATA *data;
+ DBUG_ENTER("thr_abort_locks_for_thread");
+
+ pthread_mutex_lock(&lock->mutex);
+ for (data= lock->read_wait.data; data ; data= data->next)
+ {
+ if (pthread_equal(thread, data->thread))
+ {
+ DBUG_PRINT("info",("Aborting read-wait lock"));
+ data->type= TL_UNLOCK; /* Mark killed */
+ pthread_cond_signal(data->cond);
+ data->cond= 0; /* Removed from list */
+
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->read_wait.last= data->prev;
+ }
+ }
+ for (data= lock->write_wait.data; data ; data= data->next)
+ {
+ if (pthread_equal(thread, data->thread))
+ {
+ DBUG_PRINT("info",("Aborting write-wait lock"));
+ data->type= TL_UNLOCK;
+ pthread_cond_signal(data->cond);
+ data->cond= 0;
+
+ if (((*data->prev)= data->next))
+ data->next->prev= data->prev;
+ else
+ lock->write_wait.last= data->prev;
+ }
+ }
+ pthread_mutex_unlock(&lock->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data)
diff --git a/sql/handler.cc b/sql/handler.cc
index fb33888e91e..52ec910dd01 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -473,7 +473,8 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
int error;
DBUG_ENTER("handler::open");
DBUG_PRINT("enter",("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d",
- name, table->db_type, table->db_stat, mode, test_if_locked));
+ name, table->db_type, table->db_stat, mode,
+ test_if_locked));
if ((error=open(name,mode,test_if_locked)))
{
diff --git a/sql/lock.cc b/sql/lock.cc
index 4c84bbb6e69..64456e6ec36 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -313,6 +313,25 @@ void mysql_lock_abort(THD *thd, TABLE *table)
}
+/* Abort one thread / table combination */
+
+void mysql_lock_abort_for_thread(THD *thd, TABLE *table)
+{
+ MYSQL_LOCK *locked;
+ TABLE *write_lock_used;
+ DBUG_ENTER("mysql_lock_abort_for_thread");
+
+ if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
+ {
+ for (uint i=0; i < locked->lock_count; i++)
+ thr_abort_locks_for_thread(locked->locks[i]->lock,
+ table->in_use->real_id);
+ my_free((gptr) locked,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
{
MYSQL_LOCK *sql_lock;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 83b41359192..7d830859fb2 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -711,6 +711,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
void mysql_lock_abort(THD *thd, TABLE *table);
+void mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 2e47312f588..a5241d33132 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -4416,8 +4416,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case (int) OPT_SAFE:
opt_specialflag|= SPECIAL_SAFE_MODE;
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
- myisam_recover_options= HA_RECOVER_NONE; // To be changed
- ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE);
+ myisam_recover_options= HA_RECOVER_DEFAULT;
+ ha_open_options&= ~(HA_OPEN_DELAY_KEY_WRITE);
break;
case (int) OPT_SKIP_PRIOR:
opt_specialflag|= SPECIAL_NO_PRIOR;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index b53c05c0357..4d1e57f0c1e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1244,25 +1244,44 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
}
-/* lock table to force abort of any threads trying to use table */
+/*
+ If we have the table open, which only happens when a LOCK TABLE has been
+ done on the table, change the lock type to a lock that will abort all
+ other threads trying to get the lock.
+*/
void abort_locked_tables(THD *thd,const char *db, const char *table_name)
{
TABLE *table;
- for (table=thd->open_tables; table ; table=table->next)
+ for (table= thd->open_tables; table ; table= table->next)
{
if (!strcmp(table->real_name,table_name) &&
!strcmp(table->table_cache_key,db))
+ {
mysql_lock_abort(thd,table);
+ break;
+ }
}
}
-/****************************************************************************
-** open_unireg_entry
-** Purpose : Load a table definition from file and open unireg table
-** Args : entry with DB and table given
-** Returns : 0 if ok
-** Note that the extra argument for open is taken from thd->open_options
+
+/*
+ Load a table definition from file and open unireg table
+
+ SYNOPSIS
+ open_unireg_entry()
+ thd Thread handle
+ entry Store open table definition here
+ db Database name
+ name Table name
+ alias Alias name
+
+ NOTES
+ Extra argument for open is taken from thd->open_options
+
+ RETURN
+ 0 ok
+ # Error
*/
static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
@@ -2277,6 +2296,17 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
}
pthread_mutex_unlock(&in_use->mysys_var->mutex);
}
+ /*
+ Now we must abort all tables locks used by this thread
+ as the thread may be waiting to get a lock for another table
+ */
+ for (TABLE *thd_table= in_use->open_tables;
+ thd_table ;
+ thd_table= thd_table->next)
+ {
+ if (thd_table->db_stat) // If table is open
+ mysql_lock_abort_for_thread(thd, thd_table);
+ }
}
else
result= result || return_if_owned_by_thd;