mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
Fixed bug in LOCK TABLE + DROP TABLE when other thread was waiting for a table that was locked bug not droped
This commit is contained in:
parent
065bcf92fb
commit
2a7dfa172c
11 changed files with 144 additions and 15 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
||||
{
|
||||
|
|
19
sql/lock.cc
19
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue