List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:November 9 2007 10:25pm
Subject:bk commit into 5.1 tree (davi:1.2608) BUG#23713
View as plain text  
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, &not_used)))
+                                 flags, &not_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#23713Davi Arnaut9 Nov