List:Commits« Previous MessageNext Message »
From:Dao-Gang.Qu Date:April 20 2010 3:04am
Subject:bzr commit into mysql-trunk-bugfixing branch (Dao-Gang.Qu:3008) Bug#51839
View as plain text  
#At file:///home/daogangqu/mysql/bzrwork/bug51839/mysql-trunk-bugfixing/ based on revid:alik@stripped

 3008 Dao-Gang.Qu@stripped	2010-04-20
      Bug #51839   mixup of DDL causes slave to stop
      
      Stored routine DDL statements use statement-based replication
      regardless of the current binlog format. The problem here was
      that if a DDL statement failed during metadata lock acquisition
      or opening of mysql.proc, the binlog format would not be reset
      before returning. So the following DDL or DML statements are 
      binlogged with a wrong binlog format, which causes the slave 
      to stop.
      
      The problem can be resolved by grabbing an exclusive MDL lock firstly
      instead of clearing the current binlog format. So that the binlog
      format will not be affected when the lock grab returns directly with
      an error. The same way is taken to open a proc table for update.
     @ mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
        Test Result for bug#51839
     @ mysql-test/suite/rpl/t/rpl_tmp_table_and_DDL.test
        Added test to make sure the binlog format is not changed
        after a execution of DDL with a table locked.
     @ sql/sp.cc
        Grab an exclusive MDL lock and open a proc table for update firstly
        instead of clearing the current binlog format.

    modified:
      mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
      mysql-test/suite/rpl/t/rpl_tmp_table_and_DDL.test
      sql/sp.cc
=== modified file 'mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result'
--- a/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result	2010-01-22 09:38:21 +0000
+++ b/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result	2010-04-20 03:04:11 +0000
@@ -94,3 +94,100 @@ INSERT INTO t1 VALUES (1);
 DROP TABLE t2;
 INSERT INTO t1 VALUES (1);
 DROP TEMPORARY TABLE t1;
+CREATE TABLE t1 (a CHAR(30));
+CREATE TEMPORARY TABLE t2 (b CHAR(60));
+LOCK TABLE t1 WRITE;
+CREATE FUNCTION f1 () RETURNS TINYINT RETURN 13;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("CREATE FUNCTION f1 with table locked");
+UNLOCK TABLE;
+CREATE FUNCTION f2 () RETURNS TINYINT RETURN 13;
+LOCK TABLE t1 WRITE;
+ALTER FUNCTION f2 SQL SECURITY INVOKER;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("ALTER FUNCTION f2 with table locked");
+LOCK TABLE t1 WRITE;
+DROP FUNCTION f2;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("DROP FUNCTION f2 with table locked");
+CREATE PROCEDURE p1() SELECT 1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("CREATE PROCEDURE p1 with table locked");
+UNLOCK TABLE;
+CREATE PROCEDURE p2() SELECT 1;
+LOCK TABLE t1 WRITE;
+ALTER PROCEDURE p2 SQL SECURITY INVOKER;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("ALTER PROCEDURE P2 with table locked");
+DROP PROCEDURE p2;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("DROP PROCEDURE p2 with table locked");
+CREATE EVENT e1 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("CREATE EVENT e1 with table locked");
+UNLOCK TABLE;
+CREATE EVENT e2 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
+LOCK TABLE t1 WRITE;
+ALTER EVENT e2 ON SCHEDULE EVERY 20 HOUR DO SELECT 1;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("ALTER EVENT e2 with table locked");
+DROP EVENT e2;
+ERROR HY000: Table 'event' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("DROP EVENT e2 with table locked");
+CREATE DATABASE mysqltest1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("CREATE DATABASE mysqltest1 with table locked");
+UNLOCK TABLE;
+CREATE DATABASE mysqltest2;
+LOCK TABLE t1 WRITE;
+DROP DATABASE mysqltest2;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
+INSERT INTO t2 VALUES ("DROP DATABASE mysqltest2 with table locked");
+UNLOCK TABLE;
+DROP DATABASE mysqltest2;
+LOCK TABLE t1 WRITE;
+CREATE USER test_1@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("CREATE USER test_1@localhost with table locked");
+UNLOCK TABLE;
+CREATE USER test_2@localhost;
+LOCK TABLE t1 WRITE;
+GRANT SELECT ON t1 TO test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("GRANT select on table to user with table locked");
+GRANT ALL ON f2 TO test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("GRANT ALL ON f2 TO test_2 with table locked");
+GRANT ALL ON p2 TO test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("GRANT ALL ON p2 TO test_2 with table locked");
+GRANT USAGE ON *.* TO test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("GRANT USAGE ON *.* TO test_2 with table locked");
+REVOKE ALL PRIVILEGES ON f2 FROM test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on function to user with table locked");
+REVOKE ALL PRIVILEGES ON p2 FROM test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on procedure to user with table locked");
+REVOKE ALL PRIVILEGES ON t1 FROM test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on table to user with table locked");
+REVOKE USAGE ON *.* FROM test_2@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("REVOKE USAGE ON *.* TO test_2 with table locked");
+RENAME USER test_2@localhost TO test_3@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("RENAME USER test_2 TO test_3 with table locked");
+UNLOCK TABLE;
+RENAME USER test_2@localhost TO test_3@localhost;
+LOCK TABLE t1 WRITE;
+DROP USER test_3@localhost;
+ERROR HY000: Table 'user' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES ("DROP USER test_3@localhost with table locked");
+UNLOCK TABLE;
+DROP USER test_3@localhost;
+DROP FUNCTION f2;
+DROP PROCEDURE p2;
+DROP EVENT e2;
+DROP TABLE t1, t2;

=== modified file 'mysql-test/suite/rpl/t/rpl_tmp_table_and_DDL.test'
--- a/mysql-test/suite/rpl/t/rpl_tmp_table_and_DDL.test	2010-02-10 12:12:55 +0000
+++ b/mysql-test/suite/rpl/t/rpl_tmp_table_and_DDL.test	2010-04-20 03:04:11 +0000
@@ -12,3 +12,148 @@ LET $ENGINE_TYPE= MyISAM;
 source extra/rpl_tests/rpl_tmp_table_and_DDL.test;
 sync_slave_with_master;
 
+#
+# BUG #51839
+# The test makes sure the binlog format is not changed 
+# after a execution of DDL with a table locked, so that
+# the following DML statements will not cause the slave 
+# to stop.
+#
+
+--disable_abort_on_error
+
+CREATE TABLE t1 (a CHAR(30));
+CREATE TEMPORARY TABLE t2 (b CHAR(60));
+
+# CREATE FUNCTION when a table is locked.
+LOCK TABLE t1 WRITE;
+CREATE FUNCTION f1 () RETURNS TINYINT RETURN 13;
+INSERT INTO t2 VALUES ("CREATE FUNCTION f1 with table locked");
+
+UNLOCK TABLE;
+CREATE FUNCTION f2 () RETURNS TINYINT RETURN 13;
+
+# ALTER FUNCTION when a table is locked.
+LOCK TABLE t1 WRITE;
+ALTER FUNCTION f2 SQL SECURITY INVOKER;
+INSERT INTO t2 VALUES ("ALTER FUNCTION f2 with table locked");
+
+# DROP FUNCTION when a table is locked.
+LOCK TABLE t1 WRITE;
+DROP FUNCTION f2;
+INSERT INTO t2 VALUES ("DROP FUNCTION f2 with table locked");
+
+
+# CREATE PROCEDURE when a table is locked.
+CREATE PROCEDURE p1() SELECT 1;
+INSERT INTO t2 VALUES ("CREATE PROCEDURE p1 with table locked");
+
+UNLOCK TABLE;
+CREATE PROCEDURE p2() SELECT 1;
+
+# ALTER PROCEDURE when a table is locked.
+LOCK TABLE t1 WRITE;
+ALTER PROCEDURE p2 SQL SECURITY INVOKER;
+INSERT INTO t2 VALUES ("ALTER PROCEDURE P2 with table locked");
+
+# DROP PROCEDURE when a table is locked.
+DROP PROCEDURE p2;
+INSERT INTO t2 VALUES ("DROP PROCEDURE p2 with table locked");
+
+
+# CREATE EVENT when a table is locked.
+CREATE EVENT e1 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
+INSERT INTO t2 VALUES ("CREATE EVENT e1 with table locked");
+
+UNLOCK TABLE;
+CREATE EVENT e2 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
+
+# ALTER EVENT when a table is locked.
+LOCK TABLE t1 WRITE;
+ALTER EVENT e2 ON SCHEDULE EVERY 20 HOUR DO SELECT 1;
+INSERT INTO t2 VALUES ("ALTER EVENT e2 with table locked");
+
+# DROP EVENT when a table is locked.
+DROP EVENT e2;
+INSERT INTO t2 VALUES ("DROP EVENT e2 with table locked");
+
+
+# CREATE DATABASE when a table is locked.
+CREATE DATABASE mysqltest1;
+INSERT INTO t2 VALUES ("CREATE DATABASE mysqltest1 with table locked");
+
+UNLOCK TABLE;
+CREATE DATABASE mysqltest2;
+
+# DROP DATABASE when a table is locked.
+LOCK TABLE t1 WRITE;
+DROP DATABASE mysqltest2;
+INSERT INTO t2 VALUES ("DROP DATABASE mysqltest2 with table locked");
+
+UNLOCK TABLE;
+DROP DATABASE mysqltest2;
+
+# CREATE USER when a table is locked.
+LOCK TABLE t1 WRITE;
+CREATE USER test_1@localhost;
+INSERT INTO t2 VALUES ("CREATE USER test_1@localhost with table locked");
+
+UNLOCK TABLE;
+CREATE USER test_2@localhost;
+
+# GRANT select on table to user when a table is locked.
+LOCK TABLE t1 WRITE;
+GRANT SELECT ON t1 TO test_2@localhost;
+INSERT INTO t2 VALUES ("GRANT select on table to user with table locked");
+
+# GRANT all on function to user when a table is locked.
+GRANT ALL ON f2 TO test_2@localhost;
+INSERT INTO t2 VALUES ("GRANT ALL ON f2 TO test_2 with table locked");
+
+# GRANT all on procdure to user when a table is locked.
+GRANT ALL ON p2 TO test_2@localhost;
+INSERT INTO t2 VALUES ("GRANT ALL ON p2 TO test_2 with table locked");
+
+# GRANT USAGE ON *.* to user when a table is locked.
+GRANT USAGE ON *.* TO test_2@localhost;
+INSERT INTO t2 VALUES ("GRANT USAGE ON *.* TO test_2 with table locked");
+
+
+# REVOKE ALL PRIVILEGES on function to user when a table is locked.
+REVOKE ALL PRIVILEGES ON f2 FROM test_2@localhost;
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on function to user with table locked");
+
+# REVOKE ALL PRIVILEGES on procedure to user when a table is locked.
+REVOKE ALL PRIVILEGES ON p2 FROM test_2@localhost;
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on procedure to user with table locked");
+
+# REVOKE ALL PRIVILEGES on table to user when a table is locked.
+REVOKE ALL PRIVILEGES ON t1 FROM test_2@localhost;
+INSERT INTO t2 VALUES ("REVOKE ALL PRIVILEGES on table to user with table locked");
+
+# REVOKE USAGE ON *.* to user when a table is locked.
+REVOKE USAGE ON *.* FROM test_2@localhost;
+INSERT INTO t2 VALUES ("REVOKE USAGE ON *.* TO test_2 with table locked");
+
+
+# RENAME USER when a table is locked.
+RENAME USER test_2@localhost TO test_3@localhost;
+INSERT INTO t2 VALUES ("RENAME USER test_2 TO test_3 with table locked");
+
+UNLOCK TABLE;
+RENAME USER test_2@localhost TO test_3@localhost;
+
+# DROP USER when a table is locked.
+LOCK TABLE t1 WRITE;
+DROP USER test_3@localhost;
+INSERT INTO t2 VALUES ("DROP USER test_3@localhost with table locked");
+
+UNLOCK TABLE;
+DROP USER test_3@localhost;
+DROP FUNCTION f2;
+DROP PROCEDURE p2;
+DROP EVENT e2;
+DROP TABLE t1, t2;
+
+--source include/master-slave-end.inc
+

=== modified file 'sql/sp.cc'
--- a/sql/sp.cc	2010-02-24 13:52:27 +0000
+++ b/sql/sp.cc	2010-04-20 03:04:11 +0000
@@ -920,6 +920,11 @@ sp_create_routine(THD *thd, int type, sp
   DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
               type == TYPE_ENUM_FUNCTION);
 
+  /* Grab an exclusive MDL lock. */
+  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
+                        sp->m_db.str, sp->m_name.str))
+    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
   /* Reset sql_mode during data dictionary operations. */
   thd->variables.sql_mode= 0;
 
@@ -931,11 +936,6 @@ sp_create_routine(THD *thd, int type, sp
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  /* Grab an exclusive MDL lock. */
-  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
-                        sp->m_db.str, sp->m_name.str))
-    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
-
   saved_count_cuted_fields= thd->count_cuted_fields;
   thd->count_cuted_fields= CHECK_FIELD_WARN;
 
@@ -1179,6 +1179,14 @@ sp_drop_routine(THD *thd, int type, sp_n
   DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
               type == TYPE_ENUM_FUNCTION);
 
+  /* Grab an exclusive MDL lock. */
+  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
+                        name->m_db.str, name->m_name.str))
+    DBUG_RETURN(SP_DELETE_ROW_FAILED);
+
+  if (!(table= open_proc_table_for_update(thd)))
+    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
   /*
     This statement will be replicated as a statement, even when using
     row-based replication.  The flag will be reset at the end of the
@@ -1187,13 +1195,6 @@ sp_drop_routine(THD *thd, int type, sp_n
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  /* Grab an exclusive MDL lock. */
-  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
-                        name->m_db.str, name->m_name.str))
-    DBUG_RETURN(SP_DELETE_ROW_FAILED);
-
-  if (!(table= open_proc_table_for_update(thd)))
-    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
   if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
   {
     if (table->file->ha_delete_row(table->record[0]))
@@ -1265,6 +1266,9 @@ sp_update_routine(THD *thd, int type, sp
                         name->m_db.str, name->m_name.str))
     DBUG_RETURN(SP_OPEN_TABLE_FAILED);
 
+  if (!(table= open_proc_table_for_update(thd)))
+    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
   /*
     This statement will be replicated as a statement, even when using
     row-based replication. The flag will be reset at the end of the
@@ -1273,8 +1277,6 @@ sp_update_routine(THD *thd, int type, sp
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  if (!(table= open_proc_table_for_update(thd)))
-    DBUG_RETURN(SP_OPEN_TABLE_FAILED);
   if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
   {
     if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&


Attachment: [text/bzr-bundle] bzr/dao-gang.qu@sun.com-20100420030411-spxl1y2dwao68ixh.bundle
Thread
bzr commit into mysql-trunk-bugfixing branch (Dao-Gang.Qu:3008) Bug#51839Dao-Gang.Qu20 Apr