List:Commits« Previous MessageNext Message »
From:Luis Soares Date:June 29 2010 3:10pm
Subject:bzr commit into mysql-5.1-bugteam branch (luis.soares:3451) Bug#54866
View as plain text  
#At file:///home/lsoares/Workspace/bzr/work/bugfixing/54866/mysql-5.1-bugteam/ based on revid:davi.arnaut@stripped

 3451 Luis Soares	2010-06-29
      BUG#54866: Partially failed REVOKE not binlogged, causes
      inconsistency or replication abort
      
      The server was skipping binary logging for some half complete
      GRANT and REVOKE operations.
      
      
      We fix this by writing to the binary log GRANT/REVOKE operations,
      those that were successful (nothing new here) and those that may
      have changed master state, even if they ended up in
      error. Additionally, for failed GRANT/REVOKE statements we log
      the error code the master got from the execution. This way we
      give the slave a chance to replay the statement, get the same
      error, and the same side effects from the unsuccessful execution
      as the master did.

    modified:
      mysql-test/suite/rpl/r/rpl_grant.result
      mysql-test/suite/rpl/t/rpl_grant.test
      sql/sql_acl.cc
=== modified file 'mysql-test/suite/rpl/r/rpl_grant.result'
--- a/mysql-test/suite/rpl/r/rpl_grant.result	2010-05-24 13:54:08 +0000
+++ b/mysql-test/suite/rpl/r/rpl_grant.result	2010-06-29 15:10:32 +0000
@@ -41,3 +41,65 @@ user	host
 SELECT COUNT(*) FROM mysql.user WHERE user like 'dummy%';
 COUNT(*)
 0
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+CREATE DATABASE b54866 ;
+use b54866;
+CREATE TABLE t1 ( c1 INT, c2 INT, c3 INT );
+CREATE USER 'b54866_user'@'localhost';
+GRANT ALL ON b54866.* TO 'b54866_user'@'localhost';
+REVOKE ALL ON b54866.* FROM 'b54866_user'@'localhost', 'b54866_fake_user';
+ERROR 42000: There is no such grant defined for user 'b54866_fake_user' on host '%'
+FLUSH PRIVILEGES;
+Last_IO_Errno	0
+****** Checking grants on the master for user: b54866_user ******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+*************************************************************
+****** Checking grants on the slave for user: b54866_user *******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+*************************************************************
+CREATE TABLE test.t2 ( i INT );
+CREATE TRIGGER test.tr AFTER INSERT ON test.t2 FOR EACH ROW INSERT INTO b54866.t1 VALUES (new.i);
+INSERT INTO test.t2 VALUES (1);
+ERROR 42000: INSERT command denied to user 'b54866_user'@'localhost' for table 't1'
+GRANT ALTER,CREATE,DROP ON TABLE b54866.t1 TO 'b54866_user'@'localhost';
+FLUSH PRIVILEGES;
+****** Checking grants on the master for user: b54866_user ******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+GRANT CREATE, DROP, ALTER ON `b54866`.`t1` TO 'b54866_user'@'localhost'
+*************************************************************
+****** Checking grants on the slave for user: b54866_user *******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+GRANT CREATE, DROP, ALTER ON `b54866`.`t1` TO 'b54866_user'@'localhost'
+*************************************************************
+REVOKE ALTER ON TABLE b54866.t1 FROM 'b54866_user'@'localhost', 'b54866_fake_user';
+ERROR 42000: There is no such grant defined for user 'b54866_fake_user' on host '%'
+FLUSH PRIVILEGES;
+****** Checking grants on the master for user: b54866_user ******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+GRANT CREATE, DROP ON `b54866`.`t1` TO 'b54866_user'@'localhost'
+*************************************************************
+****** Checking grants on the slave for user: b54866_user *******
+SHOW GRANTS FOR 'b54866_user'@'localhost';
+Grants for b54866_user@localhost
+GRANT USAGE ON *.* TO 'b54866_user'@'localhost'
+GRANT CREATE, DROP ON `b54866`.`t1` TO 'b54866_user'@'localhost'
+*************************************************************
+Last_IO_Errno	0
+DROP TABLE test.t2;
+DROP USER 'b54866_user'@'localhost';
+DROP DATABASE b54866;

=== modified file 'mysql-test/suite/rpl/t/rpl_grant.test'
--- a/mysql-test/suite/rpl/t/rpl_grant.test	2010-05-24 13:54:08 +0000
+++ b/mysql-test/suite/rpl/t/rpl_grant.test	2010-06-29 15:10:32 +0000
@@ -36,3 +36,108 @@ sync_slave_with_master;
 --echo **** On Slave ****
 SELECT user,host FROM mysql.user WHERE user like 'dummy%';
 SELECT COUNT(*) FROM mysql.user WHERE user like 'dummy%';
+
+
+######################################################
+
+#
+# BUG#54866: Partially failed REVOKE not binlogged, causes inconsistency or replication abort
+#
+
+## setup
+-- connection master
+-- source include/master-slave-reset.inc
+-- connection master
+
+--let $dbname= b54866
+--let $dbuser= b54866_user
+
+--eval CREATE DATABASE $dbname 
+--eval use $dbname
+CREATE TABLE t1 ( c1 INT, c2 INT, c3 INT );
+
+#### GRANT ALL
+
+## create user, grant and revoke (the last command fails partially)
+--eval CREATE USER '$dbuser'@'localhost'
+--eval GRANT ALL ON $dbname.* TO '$dbuser'@'localhost'
+--error ER_NONEXISTING_GRANT
+--eval REVOKE ALL ON $dbname.* FROM '$dbuser'@'localhost', 'b54866_fake_user'
+FLUSH PRIVILEGES;
+--sync_slave_with_master
+
+## assert that the slave did not fail: revoke grant event was logged
+## with the correct error # and when slave replays it, it runs into
+## the same error (so it is expected)
+--let $status_items= Last_IO_Errno
+-- source include/show_slave_status.inc
+
+## assert that grants are the same on master and on slave for $dbuser
+--connection master
+--echo ****** Checking grants on the master for user: $dbuser ******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+--sync_slave_with_master
+--echo ****** Checking grants on the slave for user: $dbuser *******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+## assert that query that fails on master because of revoked grant
+## will also fail on the slave (otherwise the slave would report
+## that the master logged the event with a failure #, but the it did
+## not run into the expected issue).
+##
+## This checks that partially revoked grants - due to master error - 
+## are revoked on the slave as well.
+--connect(con1,localhost,$dbuser,,)
+CREATE TABLE test.t2 ( i INT );
+--eval CREATE TRIGGER test.tr AFTER INSERT ON test.t2 FOR EACH ROW INSERT INTO $dbname.t1 VALUES (new.i)
+--error ER_TABLEACCESS_DENIED_ERROR
+INSERT INTO test.t2 VALUES (1);
+--disconnect con1
+
+#### TABLE GRANTS
+
+--connection master
+--eval GRANT ALTER,CREATE,DROP ON TABLE $dbname.t1 TO '$dbuser'@'localhost'
+FLUSH PRIVILEGES;
+--echo ****** Checking grants on the master for user: $dbuser ******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+--sync_slave_with_master
+--echo ****** Checking grants on the slave for user: $dbuser *******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+## revoke should now fail and partially succeed
+--connection master
+--error ER_NONEXISTING_GRANT
+## event must be logged with the error ER_NONEXISTING_GRANT
+--eval REVOKE ALTER ON TABLE $dbname.t1 FROM '$dbuser'@'localhost', 'b54866_fake_user'
+FLUSH PRIVILEGES;
+
+## assert that ALTER grant is revoked on slave and slave does not stop
+--connection master
+--echo ****** Checking grants on the master for user: $dbuser ******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+--sync_slave_with_master
+--echo ****** Checking grants on the slave for user: $dbuser *******
+--eval SHOW GRANTS FOR '$dbuser'@'localhost'
+--echo *************************************************************
+
+## assert that the slave did not fail: revoke grant event was logged
+## with the correct error # and when slave replays it, it runs into
+## the same error (so it is expected)
+--let $status_items= Last_IO_Errno
+-- source include/show_slave_status.inc
+
+## Cleanup
+--connection master
+DROP TABLE test.t2;
+--eval DROP USER '$dbuser'@'localhost'
+--eval DROP DATABASE $dbname
+--sync_slave_with_master

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2010-06-28 20:59:41 +0000
+++ b/sql/sql_acl.cc	2010-06-29 15:10:32 +0000
@@ -2979,6 +2979,7 @@ int mysql_table_grant(THD *thd, TABLE_LI
   bool create_new_users=0;
   char *db_name, *table_name;
   bool save_binlog_row_based;
+  bool should_write_to_binlog= FALSE;
   DBUG_ENTER("mysql_table_grant");
 
   if (!initialized)
@@ -3129,7 +3130,17 @@ int mysql_table_grant(THD *thd, TABLE_LI
     {
       result= TRUE;
       continue;
-    }  
+    } 
+
+    /*
+      Some operations below can fail and are not undone.
+      As such, we play it safe and log the statement with
+      an error to give a chance to the slave to replay this
+      statement and fail as well, hoping that it will also
+      get the same side effects.
+     */
+    should_write_to_binlog= TRUE;
+
     /* Create user if needed */
     error=replace_user_table(thd, tables[0].table, *Str,
 			     0, revoke_grant, create_new_users,
@@ -3223,11 +3234,9 @@ int mysql_table_grant(THD *thd, TABLE_LI
   thd->mem_root= old_root;
   pthread_mutex_unlock(&acl_cache->lock);
 
-  if (!result) /* success */
-  {
-    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
-  }
-
+  if (!result || should_write_to_binlog)
+    result= result |
+            write_bin_log(thd, FALSE, thd->query(), thd->query_length());
   rw_unlock(&LOCK_grant);
 
   if (!result) /* success */
@@ -3423,6 +3432,7 @@ bool mysql_grant(THD *thd, const char *d
   bool create_new_users=0;
   TABLE_LIST tables[2];
   bool save_binlog_row_based;
+  bool should_write_to_binlog= FALSE;
   DBUG_ENTER("mysql_grant");
   if (!initialized)
   {
@@ -3499,6 +3509,16 @@ bool mysql_grant(THD *thd, const char *d
       result= TRUE;
       continue;
     }
+
+    /*
+      Some operations below can fail and are not undone.
+      As such, we play it safe and log the statement with
+      an error to give a chance to the slave to replay this
+      statement and fail as well, hoping that it will also
+      get the same side effects.
+     */
+    should_write_to_binlog= TRUE;
+
     /*
       No User, but a password?
       They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
@@ -3529,10 +3549,9 @@ bool mysql_grant(THD *thd, const char *d
   }
   VOID(pthread_mutex_unlock(&acl_cache->lock));
 
-  if (!result)
-  {
-    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
-  }
+  if (!result || should_write_to_binlog)
+    result= result |
+            write_bin_log(thd, FALSE, thd->query(), thd->query_length());
 
   rw_unlock(&LOCK_grant);
   close_thread_tables(thd);


Attachment: [text/bzr-bundle] bzr/luis.soares@sun.com-20100629151032-w7g4v39g9gawdgx9.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (luis.soares:3451) Bug#54866Luis Soares29 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:3451)Bug#54866Libing Song4 Jul
    • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:3451) Bug#54866Luís Soares8 Jul
      • Re: bzr commit into mysql-5.1-bugteam branch (luis.soares:3451)Bug#54866Libing Song8 Jul