Below is the list of changes that have just been committed into a local
5.1 repository of davi. When davi 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-11-09 20:25:31-02:00, davi@stripped +8 -0
Bug#23713 LOCK TABLES + CREATE TRIGGER + FLUSH TABLES WITH READ LOCK = deadlock
FOR INTERNAL REVIEW ONLY. PLEASE DISREGARD.
mysql-test/r/not_embedded_server.result@stripped, 2007-11-09 20:25:28-02:00, davi@stripped +13 -0
Add test case result for Bug#23713
mysql-test/r/trigger.result@stripped, 2007-11-09 20:25:28-02:00, davi@stripped +25 -0
Add test case result for Bug#23713
mysql-test/t/not_embedded_server.test@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +29 -0
Add test case for Bug#23713
mysql-test/t/trigger.test@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +38 -0
Add test case for Bug#23713
sql/mysql_priv.h@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +3 -2
Export wait_while_table_is_used function and fix prototype
for reopen_tables.
sql/sql_base.cc@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +8 -8
Add flags argument to reopen_tables and fix callers.
sql/sql_table.cc@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +5 -3
Export wait_while_Table_is_used function and fix reopen_tables
caller.
sql/sql_trigger.cc@stripped, 2007-11-09 20:25:29-02:00, davi@stripped +74 -11
If under lock tables, only proceed if the table is write locked.
diff -Nrup a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result
--- a/mysql-test/r/not_embedded_server.result 2006-09-06 09:32:43 -03:00
+++ b/mysql-test/r/not_embedded_server.result 2007-11-09 20:25:28 -02:00
@@ -3,3 +3,16 @@ execute stmt1;
ID USER HOST DB COMMAND TIME STATE INFO
number root localhost test Query time preparing SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND!='Daemon'
deallocate prepare stmt1;
+create table t1 (i int);
+connection: default
+lock tables t1 write;
+connection: flush
+flush tables with read lock;;
+connection: default
+create trigger t1_bi before insert on t1 for each row begin end;
+unlock tables;
+connection: flush
+unlock tables;
+select * from t1;
+i
+drop table t1;
diff -Nrup a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result
--- a/mysql-test/r/trigger.result 2007-07-19 12:40:42 -03:00
+++ b/mysql-test/r/trigger.result 2007-11-09 20:25:28 -02:00
@@ -1978,3 +1978,28 @@ a
1
drop table table_25411_a;
drop table table_25411_b;
+drop table if exists t1;
+create table t1 (i int);
+create trigger t1_bi before insert on t1 for each row begin end;
+create trigger t1_bi before insert on t1 for each row begin end;
+ERROR 42000: This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table'
+drop trigger t1_bi;
+drop trigger t1_bi;
+ERROR HY000: Trigger does not exist
+lock tables t1 read;
+create trigger t1_bi before insert on t1 for each row begin end;
+ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
+create trigger t1_bi before insert on t1 for each row begin end;
+ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
+drop trigger t1_bi;
+ERROR HY000: Trigger does not exist
+unlock tables;
+create trigger t1_bi before insert on t1 for each row begin end;
+lock tables t1 read;
+create trigger t1_bi before insert on t1 for each row begin end;
+ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
+drop trigger t1_bi;
+ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
+unlock tables;
+drop trigger t1_bi;
+drop table if exists t1;
diff -Nrup a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test
--- a/mysql-test/t/not_embedded_server.test 2006-08-15 08:10:07 -03:00
+++ b/mysql-test/t/not_embedded_server.test 2007-11-09 20:25:29 -02:00
@@ -20,4 +20,33 @@ prepare stmt1 from ' SELECT * FROM INFOR
execute stmt1;
deallocate prepare stmt1;
+#
+# Bug#23713 LOCK TABLES + CREATE TRIGGER + FLUSH TABLES WITH READ LOCK = deadlock
+#
+
+create table t1 (i int);
+connect (flush,localhost,root,,test,,);
+connection default;
+--echo connection: default
+lock tables t1 write;
+connection flush;
+--echo connection: flush
+--send flush tables with read lock;
+connection default;
+--echo connection: default
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Flushing tables";
+--source include/wait_condition.inc
+create trigger t1_bi before insert on t1 for each row begin end;
+unlock tables;
+connection flush;
+--echo connection: flush
+--reap
+unlock tables;
+connection default;
+select * from t1;
+drop table t1;
+disconnect flush;
+
# End of 5.1 tests
diff -Nrup a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test
--- a/mysql-test/t/trigger.test 2007-08-15 05:18:40 -03:00
+++ b/mysql-test/t/trigger.test 2007-11-09 20:25:29 -02:00
@@ -2246,3 +2246,41 @@ select * from table_25411_a;
drop table table_25411_a;
drop table table_25411_b;
+#
+# Bug#23713 LOCK TABLES + CREATE TRIGGER + FLUSH TABLES WITH READ LOCK = deadlock
+#
+# Test trigger under lock tables
+#
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (i int);
+
+create trigger t1_bi before insert on t1 for each row begin end;
+--error ER_NOT_SUPPORTED_YET
+create trigger t1_bi before insert on t1 for each row begin end;
+drop trigger t1_bi;
+--error ER_TRG_DOES_NOT_EXIST
+drop trigger t1_bi;
+
+lock tables t1 read;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create trigger t1_bi before insert on t1 for each row begin end;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create trigger t1_bi before insert on t1 for each row begin end;
+--error ER_TRG_DOES_NOT_EXIST
+drop trigger t1_bi;
+unlock tables;
+
+create trigger t1_bi before insert on t1 for each row begin end;
+lock tables t1 read;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+create trigger t1_bi before insert on t1 for each row begin end;
+--error ER_TABLE_NOT_LOCKED_FOR_WRITE
+drop trigger t1_bi;
+unlock tables;
+drop trigger t1_bi;
+
+drop table if exists t1;
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h 2007-11-01 18:52:51 -02:00
+++ b/sql/mysql_priv.h 2007-11-09 20:25:29 -02:00
@@ -979,7 +979,8 @@ bool check_dup(const char *db, const cha
bool compare_record(TABLE *table);
bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
-
+void wait_while_table_is_used(THD *thd, TABLE *table,
+ enum ha_extra_function function);
bool table_cache_init(void);
void table_cache_free(void);
bool table_def_init(void);
@@ -1149,7 +1150,7 @@ bool lock_table_name_if_not_cached(THD *
const char *table_name, TABLE **table);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
bool reopen_table(TABLE *table);
-bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
+bool reopen_tables(THD *thd, bool get_locks, bool in_refresh, uint flags);
void close_data_files_and_morph_locks(THD *thd, const char *db,
const char *table_name);
void close_handle_and_leave_table_as_lock(TABLE *table);
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc 2007-11-01 18:52:51 -02:00
+++ b/sql/sql_base.cc 2007-11-09 20:25:29 -02:00
@@ -970,7 +970,7 @@ bool close_cached_tables(THD *thd, bool
has removed the tables)
*/
thd->in_lock_tables=1;
- result=reopen_tables(thd,1,1);
+ result=reopen_tables(thd, 1, 1 , 0);
thd->in_lock_tables=0;
/* Set version for table */
for (TABLE *table=thd->open_tables; table ; table= table->next)
@@ -2920,8 +2920,8 @@ void close_data_files_and_morph_locks(TH
@param thd Thread context
@param get_locks Should we get locks after reopening tables ?
- @param in_refresh Are we in FLUSH TABLES ? TODO: It seems that
- we can remove this parameter.
+ @param in_refresh Are we in FLUSH TABLES ?
+ @param flags Flags for mysql_lock_tables
@note Since this function can't properly handle prelocking and
create placeholders it should be used in very special
@@ -2935,7 +2935,7 @@ void close_data_files_and_morph_locks(TH
@return FALSE in case of success, TRUE - otherwise.
*/
-bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
+bool reopen_tables(THD *thd, bool get_locks, bool in_refresh, uint flags)
{
TABLE *table,*next,**prev;
TABLE **tables,**tables_ptr; // For locks
@@ -2988,7 +2988,7 @@ bool reopen_tables(THD *thd,bool get_loc
/* We should always get these locks */
thd->some_tables_deleted=0;
if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
- 0, ¬_used)))
+ flags, ¬_used)))
{
thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
}
@@ -3019,8 +3019,8 @@ bool reopen_tables(THD *thd,bool get_loc
@param send_refresh Should we awake waiters even if we didn't close any tables?
*/
-void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
- bool send_refresh)
+static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
+ bool send_refresh)
{
bool found= send_refresh;
DBUG_ENTER("close_old_data_files");
@@ -3145,7 +3145,7 @@ bool wait_for_tables(THD *thd)
/* Now we can open all tables without any interference */
thd->proc_info="Reopen tables";
thd->version= refresh_version;
- result=reopen_tables(thd,0,0);
+ result=reopen_tables(thd, 0, 0, 0);
}
pthread_mutex_unlock(&LOCK_open);
thd->proc_info=0;
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc 2007-11-01 20:48:11 -02:00
+++ b/sql/sql_table.cc 2007-11-09 20:25:29 -02:00
@@ -3705,14 +3705,16 @@ mysql_rename_table(handlerton *base, con
Win32 clients must also have a WRITE LOCK on the table !
*/
-static void wait_while_table_is_used(THD *thd,TABLE *table,
- enum ha_extra_function function)
+void wait_while_table_is_used(THD *thd, TABLE *table,
+ enum ha_extra_function function)
{
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->version));
+ safe_mutex_assert_owner(&LOCK_open);
+
VOID(table->file->extra(function));
/* Mark all tables that are in use as 'old' */
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
@@ -6607,7 +6609,7 @@ view_err:
if (thd->locked_tables && new_name == table_name && new_db == db)
{
thd->in_lock_tables= 1;
- error= reopen_tables(thd, 1, 0);
+ error= reopen_tables(thd, 1, 0, 0);
thd->in_lock_tables= 0;
if (error)
goto err_with_placeholders;
diff -Nrup a/sql/sql_trigger.cc b/sql/sql_trigger.cc
--- a/sql/sql_trigger.cc 2007-09-04 19:50:03 -03:00
+++ b/sql/sql_trigger.cc 2007-11-09 20:25:29 -02:00
@@ -292,6 +292,57 @@ private:
};
+/**
+ Open exclusively a table that is possibly already locked by the thread.
+
+ @param thd current thread context
+ @param tables able list containing one table to open.
+
+ @return FALSE on success, TRUE otherwise.
+*/
+
+static bool open_table_name_locked_or_upgrade(THD *thd, TABLE_LIST *tables)
+{
+ DBUG_ENTER("open_table_name_locked_or_upgrade");
+
+ mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE, TRUE);
+
+ /* Under LOCK TABLES we must only accept write locked tables. */
+ if (thd->locked_tables)
+ {
+ tables->table= find_locked_table(thd, tables->db, tables->table_name);
+
+ if (!tables->table)
+ my_error(ER_TABLE_NOT_LOCKED, MYF(0), tables->alias);
+ else if (tables->table->reginfo.lock_type < TL_WRITE)
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->alias);
+ else
+ {
+ /*
+ Ensures that table is opened only by this thread and that no
+ other statement will open this table.
+ */
+ wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN);
+ DBUG_RETURN(FALSE);
+ }
+ }
+ else
+ {
+ /* Grab the name lock and insert the placeholder*/
+ if (lock_table_names(thd, tables))
+ DBUG_RETURN(TRUE);
+
+ /* Convert the placeholder to a real table */
+ if (!reopen_name_locked_table(thd, tables, TRUE))
+ DBUG_RETURN(FALSE);
+
+ unlock_table_name(thd, tables);
+ }
+
+ DBUG_RETURN(TRUE);
+}
+
+
/*
Create or drop trigger for table.
@@ -323,6 +374,7 @@ bool mysql_create_or_drop_trigger(THD *t
TABLE *table;
bool result= TRUE;
String stmt_query;
+ bool need_start_waiting= FALSE;
DBUG_ENTER("mysql_create_or_drop_trigger");
@@ -374,10 +426,12 @@ bool mysql_create_or_drop_trigger(THD *t
/*
We don't want perform our operations while global read lock is held
so we have to wait until its end and then prevent it from occurring
- again until we are done. (Acquiring LOCK_open is not enough because
- global read lock is held without holding LOCK_open).
+ again until we are done, unless we are under lock tables. (Acquiring
+ LOCK_open is not enough because global read lock is held without holding
+ LOCK_open).
*/
- if (wait_if_global_read_lock(thd, 0, 1))
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
DBUG_RETURN(TRUE);
VOID(pthread_mutex_lock(&LOCK_open));
@@ -433,17 +487,12 @@ bool mysql_create_or_drop_trigger(THD *t
goto end;
}
- if (lock_table_names(thd, tables))
- goto end;
-
/* We also don't allow creation of triggers on views. */
tables->required_type= FRMTYPE_TABLE;
- if (reopen_name_locked_table(thd, tables, TRUE))
- {
- unlock_table_name(thd, tables);
+ if (open_table_name_locked_or_upgrade(thd, tables))
goto end;
- }
+
table= tables->table;
if (!table->triggers)
@@ -462,6 +511,18 @@ bool mysql_create_or_drop_trigger(THD *t
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
+ /* Under LOCK TABLES we must reopen the table to activate the trigger. */
+ if (!result && thd->locked_tables)
+ {
+ /* Make table suitable for reopening (drop lock and placeholder). */
+ close_data_files_and_morph_locks(thd, tables->db, tables->table_name);
+ thd->in_lock_tables= 1;
+ /* XXX: trigger is created, but what if we can't reopen the table? */
+ reopen_tables(thd, 1, 1, MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_LOCK_IGNORE_FLUSH);
+ thd->in_lock_tables= 0;
+ }
+
end:
if (!result)
@@ -470,7 +531,9 @@ end:
}
VOID(pthread_mutex_unlock(&LOCK_open));
- start_waiting_global_read_lock(thd);
+
+ if (need_start_waiting)
+ start_waiting_global_read_lock(thd);
if (!result)
send_ok(thd);
| Thread |
|---|
| • bk commit into 5.1 tree (davi:1.2608) BUG#23713 | Davi Arnaut | 9 Nov |