List:Commits« Previous MessageNext Message »
From:Alfranio Correia Date:March 16 2009 4:56pm
Subject:bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)
Bug#42861
View as plain text  
#At file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-42861/mysql-5.1-bugteam/ based on revid:joro@stripped

 2832 Alfranio Correia	2009-03-16
      BUG#42861 Assigning invalid directories to --slave-load-tmpdir crashes the slave
            
      Compiling with debug and assigning an invalid directory to --slave-load-tmpdir
      was crashing the slave due to the following assertion DBUG_ASSERT(! is_set() ||
      can_overwrite_status).
            
      This assertion assumes that a thread can changing its state once (i.e. ok,
      error, etc) before aborting, cleaning/resuming or completing its execution
      unless the overwrite flag (i.e. can_overwrite_status) is true.
            
      The cleanup function however was being failing and an error thrown by
      my_dir and the replication SQL thread was not aborting its execution.
      Furthermore, the Append_block_log_event::do_apply_event was not reseting 
      internal states before starting processing.
            
      To fix the problem we check the directory before starting the replication
      and reset internal states before starting processing.
added:
  mysql-test/suite/rpl/r/rpl_slave_fail_load_in_start.result
  mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm-slave.opt
  mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm.test
  mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start-slave.opt
  mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start.test
renamed:
  mysql-test/suite/rpl/r/rpl_slave_load_in.result => mysql-test/suite/rpl/r/rpl_slave_pass_load_in.result
  mysql-test/suite/rpl/t/rpl_slave_load_in.test => mysql-test/suite/rpl/t/rpl_slave_pass_load_in.test
modified:
  sql/log_event.cc
  sql/rpl_rli.cc
  mysql-test/suite/rpl/r/rpl_slave_pass_load_in.result
  mysql-test/suite/rpl/t/rpl_slave_pass_load_in.test

per-file messages:
  sql/log_event.cc
    Three changes:
    
    1 - Any error in the cleanup procedure is ignore. Eventually,
    any problem will be caught later.
    
    2 - Resets internal states before creating a temporary file.
    
    3 - Introduces fault injection to simulate that a problem happend with the temporary file.
  sql/rpl_rli.cc
    Checks if temporary directory used by commands like
    LOAD DATA INFILE is valid.
=== added file 'mysql-test/suite/rpl/r/rpl_slave_fail_load_in_start.result'
--- a/mysql-test/suite/rpl/r/rpl_slave_fail_load_in_start.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_slave_fail_load_in_start.result	2009-03-16 16:55:56 +0000
@@ -0,0 +1,2 @@
+START SLAVE;
+ERROR HY000: Can't read dir of '../../../error' (Errcode: 2)

=== renamed file 'mysql-test/suite/rpl/r/rpl_slave_load_in.result' => 'mysql-test/suite/rpl/r/rpl_slave_pass_load_in.result'
--- a/mysql-test/suite/rpl/r/rpl_slave_load_in.result	2009-02-21 09:36:07 +0000
+++ b/mysql-test/suite/rpl/r/rpl_slave_pass_load_in.result	2009-03-16 16:55:56 +0000
@@ -5,6 +5,19 @@ reset slave;
 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
 start slave;
 create table t1(a int not null auto_increment, b int, primary key(a));
+create table t2(a int not null auto_increment, b int, primary key(a)) engine=innodb;
+create table t3(a int not null auto_increment, b int, primary key(a)) engine=innodb;
 load data infile '../../std_data/rpl_loaddata.dat' into table t1;
+start transaction;
+insert into t2(b) values (1);
+insert into t2(b) values (2);
+insert into t3(b) values (1);
+insert into t3(b) values (2);
+load data infile '../../std_data/rpl_loaddata.dat' into table t2;
+load data infile '../../std_data/rpl_loaddata.dat' into table t2;
+commit;
 Comparing tables master:test.t1 and slave:test.t1
+Comparing tables master:test.t2 and slave:test.t2
 drop table t1;
+drop table t2;
+drop table t3;

=== added file 'mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm-slave.opt'
--- a/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm-slave.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm-slave.opt	2009-03-16 16:55:56 +0000
@@ -0,0 +1 @@
+--loose-debug=d,LOAD_DATA_INFILE_has_no_file

=== added file 'mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm.test'
--- a/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_rm.test	2009-03-16 16:55:56 +0000
@@ -0,0 +1,46 @@
+##########################################################################
+# This test verifies if the slave fails gracefully when the temporary 
+# file used to load data is removed while it is about to be used it. 
+# Similar errors are caught if the temporary directory is removed.
+#
+# Steps:
+#    1 - Creates a table and populates it through "LOAD DATA INFILE".
+#    2 - Catches error.
+##########################################################################
+--source include/have_binlog_format_mixed_or_statement.inc
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/master-slave.inc
+
+##########################################################################
+#                            Loading data
+##########################################################################
+connection master;
+
+create table t1(a int not null auto_increment, b int, primary key(a)) engine=innodb;
+
+start transaction;
+  insert into t1(b) values (1);
+  insert into t1(b) values (2);
+  load data infile '../../std_data/rpl_loaddata.dat' into table t1;
+commit;
+
+##########################################################################
+#                            Catch Error
+##########################################################################
+connection slave;
+source include/wait_for_slave_sql_to_stop.inc;
+
+let $error=query_get_value("show slave status", Last_SQL_Error, 1);
+echo $error;
+
+##########################################################################
+#                             Clean up
+##########################################################################
+connection master;
+
+drop table t1;
+
+connection slave;
+
+drop table t1;

=== added file 'mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start-slave.opt'
--- a/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start-slave.opt	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start-slave.opt	2009-03-16 16:55:56 +0000
@@ -0,0 +1 @@
+--slave-load-tmpdir=../../../error

=== added file 'mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start.test'
--- a/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_slave_fail_load_in_start.test	2009-03-16 16:55:56 +0000
@@ -0,0 +1,13 @@
+##########################################################################
+# This test verifies if the start slave fails gracefuly when an 
+# invalid directory is used to set --slave-load-tmpdir.
+##########################################################################
+connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,);
+connect (master1,127.0.0.1,root,,test,$MASTER_MYPORT,);
+connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,);
+connect (slave1,127.0.0.1,root,,test,$SLAVE_MYPORT,);
+
+connection slave;
+
+--error 12
+START SLAVE;

=== renamed file 'mysql-test/suite/rpl/t/rpl_slave_load_in.test' => 'mysql-test/suite/rpl/t/rpl_slave_pass_load_in.test'
--- a/mysql-test/suite/rpl/t/rpl_slave_load_in.test	2009-02-21 09:36:07 +0000
+++ b/mysql-test/suite/rpl/t/rpl_slave_pass_load_in.test	2009-03-16 16:55:56 +0000
@@ -3,10 +3,11 @@
 # event while the "--secure-file-priv" option is set.
 # 
 # The test is divided in two steps:
-#    1 - Creates a table and populates it through "LOAD DATA INFILE".
+#    1 - Creates tables and populates them through "LOAD DATA INFILE".
 #    2 - Compares the master and slave.
 ##########################################################################
-source include/master-slave.inc;
+--source include/have_innodb.inc
+--source include/master-slave.inc
 
 ##########################################################################
 #                            Loading data
@@ -14,8 +15,20 @@ source include/master-slave.inc;
 connection master;
 
 create table t1(a int not null auto_increment, b int, primary key(a));
+create table t2(a int not null auto_increment, b int, primary key(a)) engine=innodb;
+create table t3(a int not null auto_increment, b int, primary key(a)) engine=innodb;
+
 load data infile '../../std_data/rpl_loaddata.dat' into table t1;
 
+start transaction;
+  insert into t2(b) values (1);
+  insert into t2(b) values (2);
+  insert into t3(b) values (1);
+  insert into t3(b) values (2);
+  load data infile '../../std_data/rpl_loaddata.dat' into table t2;
+  load data infile '../../std_data/rpl_loaddata.dat' into table t2;
+commit;
+
 ##########################################################################
 #                       Checking Consistency
 ##########################################################################
@@ -25,11 +38,17 @@ let $diff_table_1=master:test.t1;
 let $diff_table_2=slave:test.t1;
 source include/diff_tables.inc;
 
+let $diff_table_1=master:test.t2;
+let $diff_table_2=slave:test.t2;
+source include/diff_tables.inc;
+
 ##########################################################################
 #                             Clean up
 ##########################################################################
 connection master;
 
 drop table t1;
+drop table t2;
+drop table t3;
 
 sync_slave_with_master;

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2009-03-05 10:23:46 +0000
+++ b/sql/log_event.cc	2009-03-16 16:55:56 +0000
@@ -382,7 +382,7 @@ static void cleanup_load_tmpdir()
   uint i;
   char fname[FN_REFLEN], prefbuf[31], *p;
 
-  if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME))))
+  if (!(dirp=my_dir(slave_load_tmpdir,MYF(0))))
     return;
 
   /* 
@@ -6173,6 +6173,16 @@ int Append_block_log_event::do_apply_eve
   int error = 1;
   DBUG_ENTER("Append_block_log_event::do_apply_event");
 
+  /*
+    Usually lex_start() is called by mysql_parse(), but we need it here
+    as the present method does not call mysql_parse().
+  */
+  if (!thd->lock) 
+  {
+    lex_start(thd);
+    mysql_reset_thd_for_next_command(thd);
+  }
+
   fname= strmov(proc_info, "Making temp file ");
   slave_load_file_stem(fname, file_id, server_id, ".data");
   thd_proc_info(thd, proc_info);
@@ -6197,6 +6207,10 @@ int Append_block_log_event::do_apply_eve
                 get_type_str(), fname);
     goto err;
   }
+
+  DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_no_file", my_close(fd,MYF(0)); fd= -1; 
+                                                  my_delete(fname, MYF(0)););
+
   if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
   {
     rli->report(ERROR_LEVEL, my_errno,

=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2009-02-22 13:40:52 +0000
+++ b/sql/rpl_rli.cc	2009-03-16 16:55:56 +0000
@@ -80,6 +80,40 @@ Relay_log_info::~Relay_log_info()
   DBUG_VOID_RETURN;
 }
 
+/*
+  Check the temporary directory used by commands like
+  LOAD DATA INFILE.
+ */
+int check_temp_dir(char* tmp_dir, char *tmp_file)
+{
+  int fd;
+  MY_DIR *dirp;
+
+  DBUG_ENTER("check_temp_dir");
+
+  /*
+    Check if the directory exists.
+   */
+  if (!(dirp=my_dir(tmp_dir,MYF(MY_WME))))
+    DBUG_RETURN(1);
+  my_dirend(dirp);
+
+  /*
+    Check permissions to create a file.
+   */
+  if ((fd= my_create(tmp_file, CREATE_MODE,
+                     O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+                     MYF(MY_WME))) < 0)
+  DBUG_RETURN(1);
+
+  /*
+    Clean up.
+   */
+  my_close(fd, MYF(0));
+  my_delete(tmp_file, MYF(0));
+
+  DBUG_RETURN(0);
+}
 
 int init_relay_log_info(Relay_log_info* rli,
 			const char* info_fname)
@@ -109,6 +143,12 @@ int init_relay_log_info(Relay_log_info* 
             MY_RETURN_REAL_PATH);
   rli->slave_patternload_file_size= strlen(rli->slave_patternload_file);
 
+  if (check_temp_dir(slave_load_tmpdir, rli->slave_patternload_file))
+  {
+    pthread_mutex_unlock(&rli->data_lock);
+    DBUG_RETURN(1);
+  }
+
   /*
     The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE.
     Note that the I/O thread flushes it to disk after writing every

Thread
bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)Bug#42861Alfranio Correia16 Mar
  • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)Bug#42861He Zhenxing17 Mar
    • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)Bug#42861Alfranio Correia17 Mar
      • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)Bug#42861He Zhenxing17 Mar
        • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2832)Bug#42861Alfranio Correia17 Mar