List:Commits« Previous MessageNext Message »
From:Chuck Bell Date:July 31 2008 9:32pm
Subject:bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230
View as plain text  
#At file:///D:/source/bzr/mysql-6.0-bug-35230/

 2674 Chuck Bell	2008-07-31
      BUG#35230 Backup: no backupdir 
      
      This patch adds the dynamic variable --backupdir which specifies the path
      where the image files are placed (backup) or accessed (restore) if a path
      is not specified in the command.
added:
  mysql-test/r/backup_backupdir.result
  mysql-test/t/backup_backupdir.test
modified:
  mysql-test/r/backup_progress.result
  mysql-test/t/backup_progress.test
  sql/backup/backup_kernel.h
  sql/backup/kernel.cc
  sql/backup/stream.cc
  sql/backup/stream.h
  sql/mysqld.cc
  sql/set_var.cc
  sql/set_var.h
  sql/share/errmsg.txt
  sql/sql_parse.cc

per-file messages:
  mysql-test/r/backup_backupdir.result
    New result file.
  mysql-test/r/backup_progress.result
    New result file.
  mysql-test/t/backup_backupdir.test
    New test for backupdir variable settings.
  mysql-test/t/backup_progress.test
    Added a mask for backup location because this path will vary from 
    machine to machine.
  sql/backup/backup_kernel.h
    Changed methods to include original location from lex structure instead of
    traversing the THD object.
  sql/backup/kernel.cc
    Added code to process the backupdir IFF there is no path specified 
    in the command.
  sql/backup/stream.cc
    Moved path processing to Stream::Stream. Constructors now take backupdir plus
    original filename specified on command.
  sql/backup/stream.h
    Constructors now take backupdir plus original filename specified on command.
  sql/mysqld.cc
    Added code to support backupdir.
  sql/set_var.cc
    Added all of the methods needed to check, update, and set a default 
    for the backupdir variable.
  sql/set_var.h
    Added extern for backupdir variable named sys_var_backupdir.
  sql/share/errmsg.txt
    Added new error message for invalid backupdir paths.
  sql/sql_parse.cc
    Added processing of system variable for backupdir to remove dependency in
    backup code.
=== added file 'mysql-test/r/backup_backupdir.result'
--- a/mysql-test/r/backup_backupdir.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/backup_backupdir.result	2008-07-31 19:32:17 +0000
@@ -0,0 +1,49 @@
+Reset backupdir 
+SET @@global.backupdir = @@global.datadir;
+DROP DATABASE IF EXISTS bup_backupdir;
+Create a database
+CREATE DATABASE bup_backupdir;
+CREATE TABLE bup_backupdir.t1(a INT);
+INSERT INTO bup_backupdir.t1 VALUES (1), (2), (3);
+Create a directory for backup images
+Reset backupdir 
+SET @@global.backupdir = '../tmp/backup';
+Perform backup
+BACKUP DATABASE bup_backupdir TO 'bup_backupdir.bak';
+backup_id
+#
+Ensure backup image file went to the correct location
+/backup/bup_backupdir.bak
+Perform restore
+RESTORE FROM 'bup_backupdir.bak';
+backup_id
+#
+Now do the backup and restore by specifying a path.
+Perform backup
+BACKUP DATABASE bup_backupdir TO '../bup_backupdir.bak';
+backup_id
+#
+Ensure backup image file went to the correct location
+Perform restore
+RESTORE FROM '../bup_backupdir.bak';
+backup_id
+#
+Perform backup
+BACKUP DATABASE bup_backupdir TO '../../bup_backupdir.bak';
+backup_id
+#
+Ensure backup image file went to the correct location
+Try a backup to an invalid relative path.
+BACKUP DATABASE bup_backupdir TO
'../../../../../../../../../../../../../../../../../../bup_backupdir.bak';
+ERROR HY000: Can't write to backup location
'../../../../../../../../../../../../../../../../../../bup_backup' (file already exists?)
+Try a backup to an invalid hard path.
+BACKUP DATABASE bup_backupdir TO '/dev/not/there/either/bup_backupdir.bak';
+ERROR HY000: Can't write to backup location '/dev/not/there/either/bup_backupdir.bak'
(file already exists?)
+SET @@global.backupdir = 'This_is_really_stupid/not/there/at/all';
+Warnings:
+Warning	1725	The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: This_is_really_stupid/not/there/at/all
+Warning	1725	The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: This_is_really_stupid/not/there/at/all
+Cleanup
+Reset backupdir 
+SET @@global.backupdir = @@global.datadir;
+DROP DATABASE bup_backupdir;

=== modified file 'mysql-test/r/backup_progress.result'
--- a/mysql-test/r/backup_progress.result	2008-07-09 18:37:42 +0000
+++ b/mysql-test/r/backup_progress.result	2008-07-31 19:32:17 +0000
@@ -78,7 +78,7 @@ start_time	#
 stop_time	#
 host_or_server_name	localhost
 username	root
-backup_file	backup_progress_orig.bak
+backup_file	#
 user_comment	
 command	BACKUP DATABASE backup_progress to 'backup_progress_orig.bak'
 drivers	MyISAM, Default, Snapshot
@@ -135,7 +135,7 @@ start_time	#
 stop_time	#
 host_or_server_name	localhost
 username	root
-backup_file	backup_progress_orig.bak
+backup_file	#
 user_comment	
 command	RESTORE FROM 'backup_progress_orig.bak'
 drivers	MyISAM, Default, Snapshot

=== added file 'mysql-test/t/backup_backupdir.test'
--- a/mysql-test/t/backup_backupdir.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/backup_backupdir.test	2008-07-31 19:32:17 +0000
@@ -0,0 +1,96 @@
+#
+# This test is designed to test the new backupdir variable.
+#
+
+--source include/not_embedded.inc
+
+--echo Reset backupdir 
+SET @@global.backupdir = @@global.datadir;
+
+--disable_warnings
+DROP DATABASE IF EXISTS bup_backupdir;
+--enable_warnings
+
+--echo Create a database
+CREATE DATABASE bup_backupdir;
+CREATE TABLE bup_backupdir.t1(a INT);
+INSERT INTO bup_backupdir.t1 VALUES (1), (2), (3);
+
+#
+# The following tests the backupdir variable by changing it to
+# redirect the output of the backup command/input of the restore
+# command.
+#
+
+--error 0,1,2
+rmdir $MYSQLTEST_VARDIR/tmp/backup;
+
+--echo Create a directory for backup images
+mkdir $MYSQLTEST_VARDIR/tmp/backup;
+
+--echo Reset backupdir 
+SET @@global.backupdir = '../tmp/backup';
+
+--echo Perform backup
+--replace_column 1 #
+BACKUP DATABASE bup_backupdir TO 'bup_backupdir.bak';
+
+--echo Ensure backup image file went to the correct location
+--echo $MYSQLTEST_DIR/backup/bup_backupdir.bak
+--file_exists $MYSQLTEST_VARDIR/tmp/backup/bup_backupdir.bak
+
+--echo Perform restore
+--replace_column 1 #
+RESTORE FROM 'bup_backupdir.bak';
+
+--echo Now do the backup and restore by specifying a path.
+
+--echo Perform backup
+--replace_column 1 #
+BACKUP DATABASE bup_backupdir TO '../bup_backupdir.bak';
+
+--echo Ensure backup image file went to the correct location
+--file_exists $MYSQLTEST_VARDIR/tmp/bup_backupdir.bak
+
+--echo Perform restore
+--replace_column 1 #
+RESTORE FROM '../bup_backupdir.bak';
+
+--echo Perform backup
+--replace_column 1 #
+BACKUP DATABASE bup_backupdir TO '../../bup_backupdir.bak';
+
+--echo Ensure backup image file went to the correct location
+--file_exists $MYSQLTEST_VARDIR/bup_backupdir.bak
+
+--echo Try a backup to an invalid relative path.
+--error ER_BACKUP_WRITE_LOC
+BACKUP DATABASE bup_backupdir TO
'../../../../../../../../../../../../../../../../../../bup_backupdir.bak';
+
+--echo Try a backup to an invalid hard path.
+--error ER_BACKUP_WRITE_LOC
+BACKUP DATABASE bup_backupdir TO '/dev/not/there/either/bup_backupdir.bak';
+
+#
+# Attempt to set the backupdir to something invalid.
+#
+SET @@global.backupdir = 'This_is_really_stupid/not/there/at/all';
+
+--echo Cleanup
+
+--echo Reset backupdir 
+SET @@global.backupdir = @@global.datadir;
+
+DROP DATABASE bup_backupdir;
+
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/tmp/bup_backupdir.bak
+
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/tmp/backup/bup_backupdir.bak
+
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/bup_backupdir.bak
+
+--error 0,1
+rmdir $MYSQLTEST_VARDIR/tmp/backup;

=== modified file 'mysql-test/t/backup_progress.test'
--- a/mysql-test/t/backup_progress.test	2008-06-25 13:39:04 +0000
+++ b/mysql-test/t/backup_progress.test	2008-07-31 19:32:17 +0000
@@ -107,7 +107,7 @@ connection con2;
 reap;
 
 #Show results
---replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 #
+--replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # 15 #
 --query_vertical SELECT ob.* FROM mysql.online_backup AS ob JOIN backup_progress.t1_res
AS t1 ON ob.backup_id = t1.id;
 --replace_column 1 # 3 # 4 #
 SELECT obp.* FROM mysql.online_backup_progress AS obp JOIN backup_progress.t1_res AS t1
ON obp.backup_id = t1.id;
@@ -156,7 +156,7 @@ reap;
 DELETE FROM backup_progress.t1_res;
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.online_backup WHERE command LIKE "RESTORE
FROM%";
 INSERT INTO backup_progress.t1_res (id) VALUES (@bup_id);
---replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 #
+--replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # 15 #
 --query_vertical SELECT ob.* FROM mysql.online_backup AS ob JOIN backup_progress.t1_res
AS t1 ON ob.backup_id = t1.id;
 --replace_column 1 # 3 # 4 #
 SELECT obp.* FROM mysql.online_backup_progress AS obp JOIN backup_progress.t1_res AS t1
ON obp.backup_id = t1.id;

=== modified file 'sql/backup/backup_kernel.h'
--- a/sql/backup/backup_kernel.h	2008-07-07 12:51:56 +0000
+++ b/sql/backup/backup_kernel.h	2008-07-31 19:32:17 +0000
@@ -30,7 +30,7 @@ void backup_shutdown();
   Called from the big switch in mysql_execute_command() to execute
   backup related statement
 */
-int execute_backup_command(THD*, LEX*);
+int execute_backup_command(THD*, LEX*, String*);
 
 // forward declarations
 
@@ -66,8 +66,12 @@ class Backup_restore_ctx: public backup:
   bool is_valid() const;
   ulonglong op_id() const;
 
-  Backup_info*  prepare_for_backup(LEX_STRING location, const char*, bool);
-  Restore_info* prepare_for_restore(LEX_STRING location, const char*);  
+  Backup_info*  prepare_for_backup(String *location, 
+                                   LEX_STRING orig_loc, 
+                                   const char*, bool);
+  Restore_info* prepare_for_restore(String *location, 
+                                   LEX_STRING orig_loc,
+                                   const char*);  
 
   int do_backup();
   int do_restore();

=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc	2008-07-31 10:45:02 +0000
+++ b/sql/backup/kernel.cc	2008-07-31 19:32:17 +0000
@@ -19,7 +19,8 @@
   {
   
    Backup_restore_ctx context(thd); // create context instance
-   Backup_info *info= context.prepare_for_backup(location); // prepare for backup
+   Backup_info *info= context.prepare_for_backup(location, 
+                                                 orig_loc); // prepare for backup
   
    // select objects to backup
    info->add_all_dbs();
@@ -41,7 +42,8 @@
   {
   
    Backup_restore_ctx context(thd); // create context instance
-   Restore_info *info= context.prepare_for_restore(location); // prepare for restore
+   Restore_info *info= context.prepare_for_restore(location,
+                                                   orig_loc); // prepare for restore
   
    context.do_restore(); // perform restore
    
@@ -118,7 +120,9 @@ static int send_reply(Backup_restore_ctx
 /**
   Call backup kernel API to execute backup related SQL statement.
 
-  @param lex  results of parsing the statement.
+  @param[IN] thd        current thread object reference.
+  @param[IN] lex        results of parsing the statement.
+  @param[IN] backupdir  value of the backupdir variable from server.
 
   @note This function sends response to the client (ok, result set or error).
 
@@ -126,7 +130,7 @@ static int send_reply(Backup_restore_ctx
  */
 
 int
-execute_backup_command(THD *thd, LEX *lex)
+execute_backup_command(THD *thd, LEX *lex, String *backupdir)
 {
   int res= 0;
   
@@ -134,6 +138,7 @@ execute_backup_command(THD *thd, LEX *le
   DBUG_ASSERT(thd && lex);
   DEBUG_SYNC(thd, "before_backup_command");
 
+    
   using namespace backup;
 
   Backup_restore_ctx context(thd); // reports errors
@@ -141,13 +146,24 @@ execute_backup_command(THD *thd, LEX *le
   if (!context.is_valid())
     DBUG_RETURN(send_error(context, ER_BACKUP_CONTEXT_CREATE));
 
+  /*
+    Check backupdir for validity (needed since we cannot trust
+    that the path has become invalid since set).
+  */
+  if (backupdir->length() && my_access(backupdir->c_ptr(), (F_OK|W_OK)))
+  {
+    context.fatal_error(ER_BACKUP_BACKUPDIR, backupdir->c_ptr());
+    DBUG_RETURN(send_error(context, ER_BACKUP_BACKUPDIR, backupdir->c_ptr()));
+  }
+
   switch (lex->sql_command) {
 
   case SQLCOM_BACKUP:
   {
     // prepare for backup operation
     
-    Backup_info *info= context.prepare_for_backup(lex->backup_dir, thd->query,
+    Backup_info *info= context.prepare_for_backup(backupdir, lex->backup_dir, 
+                                                  thd->query,
                                                   lex->backup_compression);
                                                               // reports errors
 
@@ -192,7 +208,8 @@ execute_backup_command(THD *thd, LEX *le
 
   case SQLCOM_RESTORE:
   {
-    Restore_info *info= context.prepare_for_restore(lex->backup_dir, thd->query);
+    Restore_info *info= context.prepare_for_restore(backupdir, lex->backup_dir, 
+                                                    thd->query);
     
     if (!info || !info->is_valid())
       DBUG_RETURN(send_error(context, ER_BACKUP_RESTORE_PREPARE));
@@ -424,6 +441,11 @@ int Backup_restore_ctx::prepare(LEX_STRI
 
   // check if location is valid (we assume it is a file path)
 
+  /*
+    For this error to work correctly, we need to check original
+    file specified by the user rather than the path formed
+    using the backupdir.
+  */
   bool bad_filename= (location.length == 0);
   
   /*
@@ -433,7 +455,7 @@ int Backup_restore_ctx::prepare(LEX_STRI
 #if defined(__WIN__) || defined(__EMX__)  
 
   bad_filename = bad_filename || check_if_legal_filename(location.str);
-  
+
 #endif
 
   if (bad_filename)
@@ -471,7 +493,8 @@ int Backup_restore_ctx::prepare(LEX_STRI
 /**
   Prepare for backup operation.
   
-  @param[in] location   path to the file where backup image should be stored
+  @param[in] backupdir  path to the file where backup image should be stored
+  @param[in] orig_loc   path as specified on command line for backup image
   @param[in] query      BACKUP query starting the operation
   @param[in] with_compression  backup image compression switch
   
@@ -486,7 +509,9 @@ int Backup_restore_ctx::prepare(LEX_STRI
   is performed using @c do_backup() method.
  */ 
 Backup_info* 
-Backup_restore_ctx::prepare_for_backup(LEX_STRING location, const char *query,
+Backup_restore_ctx::prepare_for_backup(String *backupdir, 
+                                       LEX_STRING orig_loc, 
+                                       const char *query,
                                        bool with_compression)
 {
   using namespace backup;
@@ -494,7 +519,7 @@ Backup_restore_ctx::prepare_for_backup(L
   if (m_error)
     return NULL;
   
-  if (Logger::init(BACKUP, location, query))
+  if (Logger::init(BACKUP, orig_loc, query))
   {
     fatal_error(ER_BACKUP_LOGGER_INIT);
     return NULL;
@@ -507,16 +532,16 @@ Backup_restore_ctx::prepare_for_backup(L
     Do preparations common to backup and restore operations. After call
     to prepare() all meta-data changes are blocked.
    */ 
-  if (prepare(location))
+  if (prepare(orig_loc))
     return NULL;
 
-  backup::String path(location);
-  
   /*
     Open output stream.
    */
-
-  Output_stream *s= new Output_stream(*this, path, with_compression);
+  Output_stream *s= new Output_stream(*this, 
+                                      backupdir, 
+                                      orig_loc,
+                                      with_compression);
   m_stream= s;
   
   if (!s)
@@ -527,7 +552,11 @@ Backup_restore_ctx::prepare_for_backup(L
   
   if (!s->open())
   {
-    fatal_error(ER_BACKUP_WRITE_LOC, path.ptr());
+    /*
+      For this error, use the actual value returned instead of the
+      path complimented with backupdir.
+    */
+    fatal_error(ER_BACKUP_WRITE_LOC, orig_loc.str);
     return NULL;
   }
 
@@ -556,7 +585,8 @@ Backup_restore_ctx::prepare_for_backup(L
 /**
   Prepare for restore operation.
   
-  @param[in] location   path to the file where backup image is stored
+  @param[in] backupdir  path to the file where backup image is stored
+  @param[in] orig_loc   path as specified on command line for backup image
   @param[in] query      RESTORE query starting the operation
   
   @returns Pointer to a @c Restore_info instance containing catalogue of the
@@ -565,14 +595,16 @@ Backup_restore_ctx::prepare_for_backup(L
   @note This function reports errors.
  */ 
 Restore_info* 
-Backup_restore_ctx::prepare_for_restore(LEX_STRING location, const char *query)
+Backup_restore_ctx::prepare_for_restore(String *backupdir,
+                                        LEX_STRING orig_loc, 
+                                        const char *query)
 {
   using namespace backup;  
 
   if (m_error)
     return NULL;
   
-  if (Logger::init(RESTORE, location, query))
+  if (Logger::init(RESTORE, orig_loc, query))
   {
     fatal_error(ER_BACKUP_LOGGER_INIT);
     return NULL;
@@ -585,15 +617,14 @@ Backup_restore_ctx::prepare_for_restore(
     Do preparations common to backup and restore operations. After this call
     changes of meta-data are blocked.
    */ 
-  if (prepare(location))
+  if (prepare(orig_loc))
     return NULL;
   
   /*
     Open input stream.
    */
 
-  backup::String path(location);
-  Input_stream *s= new Input_stream(*this, path);
+  Input_stream *s= new Input_stream(*this, backupdir, orig_loc);
   m_stream= s;
   
   if (!s)
@@ -604,7 +635,11 @@ Backup_restore_ctx::prepare_for_restore(
   
   if (!s->open())
   {
-    fatal_error(ER_BACKUP_READ_LOC, path.ptr());
+    /*
+      For this error, use the actual value returned instead of the
+      path complimented with backupdir.
+    */
+    fatal_error(ER_BACKUP_READ_LOC, orig_loc.str);
     return NULL;
   }
 

=== modified file 'sql/backup/stream.cc'
--- a/sql/backup/stream.cc	2008-07-02 11:27:17 +0000
+++ b/sql/backup/stream.cc	2008-07-31 19:32:17 +0000
@@ -190,9 +190,11 @@ extern "C" int stream_read(void *instanc
 }
 
 
-Stream::Stream(Logger &log, const ::String &name, int flags)
-  :m_path(name), m_flags(flags), m_block_size(0), m_log(log)
+Stream::Stream(Logger &log, ::String *backupdir, 
+               LEX_STRING orig_loc, int flags)
+  :m_flags(flags), m_block_size(0), m_log(log)
 {
+  prepare_path(backupdir, orig_loc);
   bzero(&stream, sizeof(stream));
   bzero(&buf, sizeof(buf));
   bzero(&mem, sizeof(mem));
@@ -201,6 +203,149 @@ Stream::Stream(Logger &log, const ::Stri
   state= CLOSED;
 }
 
+/**
+  Make a relative path.
+
+  This method takes the backupdir and the path specified on the backup command
+  (orig_loc) and forms a combined path. It walks the backupdir from the right
+  and the orig_loc from the left to position the paths for concatenation. Output
+  is written to new_path.
+
+  @param[OUT] new_path   The newly combined path + file name.
+  @param[IN]  orig_loc   The path + file name specified in the backup command.
+  @param[IN]  backupdir  The backupdir system variable value.
+
+  For example, if backupdir = '/dev/tmp' and orig_log = '../backup.bak', the 
+  combined path is = '/dev/backup.bak'.
+
+  @returns 
+    0 if success
+    1 if cannot be combined. Note: m_path is set to '' when this occurs to
+      trigger error in call stack.
+*/
+int Stream::make_relative_path(char *new_path, 
+                               char *orig_loc, 
+                               ::String *backupdir)
+{
+  char fixed_base[FN_LEN];
+  char fixed_rel[FN_LEN];
+  cleanup_dirname(fixed_base, backupdir->c_ptr());
+  cleanup_dirname(fixed_rel, orig_loc);
+  char *rel;
+  char *base= fixed_base;
+  bool done= FALSE;
+  char *found= fixed_rel;
+  int j= backupdir->length();
+
+  /*
+    For each '../' in orig_loc, move the pointer to the right for rel.
+    For each '../' in orig_loc, move pointer to the left for base.
+  */
+  while (!done)
+  {
+    rel= found; // save last known position
+    // find location of next level in relative path
+    found= strstr(found, FN_PARENTDIR);
+    if (found)
+    {
+      found+= 2;   // move past '..\'
+      if (base[j] == FN_LIBCHAR)
+        j--;       // move past last '\'
+      if (j == 0)  // We are trying to move too far down the path
+        return 1;
+      /*
+        Look for the next level down.
+      */
+      while ((base[j] != FN_LIBCHAR) && j)
+        j--;       
+    }
+    else
+      done= TRUE;
+  }
+  strcpy (new_path, base);
+  strcpy (new_path + j, rel);
+  return 0;
+}
+
+/**
+  Prepare path for access.
+
+  This method takes the backupdir and the file name specified on the backup
+  command (orig_loc) and forms a combined path + file name as follows:
+
+    1. If orig_loc has a relative path, make it relative to backupdir
+    2. If orig_loc has a hard path, use it.
+    3. If orig_loc has no path, append to backupdir
+
+  @param[IN]  backupdir  The backupdir system variable value.
+  @param[IN]  orig_loc   The path + file name specified in the backup command.
+
+  @returns 0
+*/
+int Stream::prepare_path(::String *backupdir, 
+                         LEX_STRING orig_loc)
+{
+  int path_len= 0;
+
+  /*
+    Prepare the path using the backupdir iff no relative path
+    or no hard path included.
+
+    Relative paths are formed from the backupdir system variable.
+  */
+  if (is_prefix(orig_loc.str, FN_PARENTDIR))
+  {
+    /* 
+      Case 1: Backup image file name has relative path. 
+              Make relative to backupdir.
+
+      Example BACKUP DATATBASE ... TO '../monthly/dec.bak'
+              If backupdir = '/dev/daily/backup' then the
+              calculated path becomes
+              '/dev/monthly/backup/dec.bak'
+    */
+    char new_path[FN_LEN];
+    if (make_relative_path(new_path, orig_loc.str, backupdir))
+      m_path.length(0);
+    path_len= strlen(new_path) + 1;
+    m_path.alloc(path_len);
+    m_path.length(0);
+    m_path.append(new_path);
+  }
+  else if (!test_if_hard_path(orig_loc.str))
+  {
+    /* 
+      Case 2: Backup image file name has hard path. 
+
+      Example BACKUP DATATBASE ... TO '/dev/dec.bak'
+              If backupdir = '/dev/daily/backup' then the
+              calculated path becomes
+              '/dev/dec.bak'
+    */
+    path_len=backupdir->length() + orig_loc.length + 1;
+    m_path.alloc(path_len);
+    fn_format(m_path.c_ptr(), orig_loc.str, backupdir->c_ptr(), "",
+              MY_UNPACK_FILENAME);
+  }
+  else 
+  {
+    /* 
+      Case 3: Backup image file name has no path. 
+
+      Example BACKUP DATATBASE ... TO 'week2.bak'
+              If backupdir = '/dev/weekly/backup' then the
+              calculated path becomes
+              '/dev/weekly/week2.bak'
+    */
+    path_len= orig_loc.length + 1;
+    m_path.alloc(path_len);
+    m_path.length(0);
+    m_path.append(orig_loc.str);
+  }
+  m_path.length(path_len);
+  return 0;
+}
+
 bool Stream::open()
 {
   close();
@@ -228,9 +373,10 @@ bool Stream::rewind()
 }
 
 
-Output_stream::Output_stream(Logger &log, const ::String &name,
+Output_stream::Output_stream(Logger &log, ::String *backupdir,
+                             LEX_STRING orig_loc,
                              bool with_compression)
-  :Stream(log, name, 0)
+  :Stream(log, backupdir, orig_loc, 0)
 {
   m_with_compression= with_compression;
   stream.write= stream_write;
@@ -414,8 +560,9 @@ bool Output_stream::rewind()
 }
 
 
-Input_stream::Input_stream(Logger &log, const ::String &name)
-  :Stream(log, name, O_RDONLY)
+Input_stream::Input_stream(Logger &log, ::String *backupdir, 
+                           LEX_STRING orig_loc)
+  :Stream(log, backupdir, orig_loc, O_RDONLY)
 {
   m_with_compression= false;
   stream.read= stream_read;

=== modified file 'sql/backup/stream.h'
--- a/sql/backup/stream.h	2008-06-26 16:20:30 +0000
+++ b/sql/backup/stream.h	2008-07-31 19:32:17 +0000
@@ -92,7 +92,7 @@ class Stream: public fd_stream
 
  protected:
 
-  Stream(Logger&, const ::String&, int);
+  Stream(Logger&, ::String *, LEX_STRING, int);
 
   String  m_path;
   int     m_flags;  ///< flags used when opening the file
@@ -101,6 +101,15 @@ class Stream: public fd_stream
 
   friend int stream_write(void*, bstream_blob*, bstream_blob);
   friend int stream_read(void*, bstream_blob*, bstream_blob);
+
+private:
+
+  int make_relative_path(char *new_path, 
+                         char *orig_loc, 
+                         ::String *backupdir);
+  int prepare_path(::String *backupdir, 
+                   LEX_STRING orig_loc);
+
 };
 
 /// Used to write to backup stream.
@@ -109,7 +118,7 @@ class Output_stream:
 {
  public:
 
-  Output_stream(Logger&, const ::String&, bool);
+  Output_stream(Logger&, ::String *, LEX_STRING, bool);
 
   bool open();
   void close();
@@ -127,7 +136,7 @@ class Input_stream:
 {
  public:
 
-  Input_stream(Logger&, const ::String &name);
+  Input_stream(Logger&, ::String *, LEX_STRING);
 
   bool open();
   void close();

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2008-07-09 07:12:43 +0000
+++ b/sql/mysqld.cc	2008-07-31 19:32:17 +0000
@@ -1364,6 +1364,7 @@ void clean_up(bool print_message)
   my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR));
   my_free(sys_var_general_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
   my_free(sys_var_slow_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
+  my_free(sys_var_backupdir.value, MYF(MY_ALLOW_ZERO_PTR));
   free_tmpdir(&mysql_tmpdir_list);
 #ifdef HAVE_REPLICATION
   my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR));
@@ -5793,6 +5794,8 @@ struct my_option my_long_options[] =
    "Creating and dropping stored procedures alters ACLs. Disable with
--skip-automatic-sp-privileges.",
    (uchar**) &sp_automatic_privileges, (uchar**) &sp_automatic_privileges,
    0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+  {"backupdir", 'B', "Path used to store backup data.", (uchar**)
&sys_var_backupdir.value,
+   (uchar**) &sys_var_backupdir.value, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"basedir", 'b',
    "Path to installation directory. All paths are usually resolved relative to this.",
    (uchar**) &mysql_home_ptr, (uchar**) &mysql_home_ptr, 0, GET_STR,
REQUIRED_ARG,
@@ -7916,6 +7919,21 @@ mysqld_get_one_option(int optid,
   case 'l':
     opt_log=1;
     break;
+  case 'B':
+  {
+    /*
+      Check if directory exists and we have permission to create file and
+      write to file.
+    */
+    if (my_access(argument, (F_OK|W_OK)))
+    {
+      sql_print_warning("The path specified for the variable backupdir cannot"
+        " be accessed or is invalid. ref:'%s'\n", argument);
+    }
+    sys_var_backupdir.value= my_strdup(argument, MYF(0));
+    sys_var_backupdir.value_length= strlen(sys_var_backupdir.value);
+    break;
+  }
   case 'h':
     strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1);
     /* Correct pointer set by my_getopt (for embedded library) */
@@ -8625,6 +8643,16 @@ static void fix_paths(void)
   convert_dirname(language,language,NullS);
   (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir
   (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home);
+ 
+  /*
+    Set backupdir if datadir set on command line but not otherwise set.
+  */
+  if (!sys_var_backupdir.value)
+  {
+    sys_var_backupdir.value= my_strdup(mysql_real_data_home, MYF(0));
+    sys_var_backupdir.value_length= strlen(mysql_real_data_home);
+  }
+
   (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home);
   (void) my_load_path(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
                                       get_relative_path(PLUGINDIR), mysql_home);

=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc	2008-07-09 07:12:43 +0000
+++ b/sql/set_var.cc	2008-07-31 19:32:17 +0000
@@ -149,6 +149,9 @@ static bool sys_update_general_log_path(
 static void sys_default_general_log_path(THD *thd, enum_var_type type);
 static bool sys_update_slow_log_path(THD *thd, set_var * var);
 static void sys_default_slow_log_path(THD *thd, enum_var_type type);
+static int sys_check_backupdir(THD *thd, set_var *var);
+static bool sys_update_backupdir(THD *thd, set_var * var);
+static void sys_default_backupdir(THD *thd, enum_var_type type);
 
 /*
   Variable definition list
@@ -779,6 +782,15 @@ sys_var_str sys_var_slow_log_path(&vars,
 static sys_var_log_output sys_var_log_output_state(&vars, "log_output",
&log_output_options,
 					    &log_output_typelib, 0);
 
+/*
+  Create the backupdir dynamic variable.
+*/
+sys_var_str sys_var_backupdir(&vars, "backupdir",
+                              sys_check_backupdir,
+                              sys_update_backupdir,
+                              sys_default_backupdir,
+                               0);
+
 
 /*
   Additional variables (not derived from sys_var class, not accessible as
@@ -2611,6 +2623,140 @@ uchar *sys_var_log_output::value_ptr(THD
   if ((length= tmp.length()))
     length--;
   return (uchar*) thd->strmake(tmp.ptr(), length);
+}
+
+/*
+  Functions for backupdir variable.
+*/
+
+/**
+  Set the default for the backupdir
+
+  @param[IN] buff Buffer to use for making string.
+
+  @returns pointer to string (buff)
+*/
+char *make_default_backupdir(char *buff)
+{
+  strmake(buff, mysql_data_home, FN_REFLEN-5);
+  return buff;
+}
+
+/**
+  Check the backupdir value for validity.
+
+  This method ensures the users is specifying a valid path
+  for the backupdir. Validity in this case means the path
+  is accessible to the user.
+
+  @param[IN] thd   Current thread context.
+  @param[IN] var   The new value set in set_var structure.
+
+  @returns 0 valid, 1 invalid.
+*/
+static int sys_check_backupdir(THD *thd, set_var *var)
+{
+  char path[FN_REFLEN], buff[FN_REFLEN];
+  MY_STAT f_stat;
+  String str(buff, sizeof(buff), system_charset_info), *res;
+  const char *log_file_str;
+  size_t path_length;
+
+  if (!(res= var->value->val_str(&str)))
+    goto err;
+
+  log_file_str= res->c_ptr();
+  bzero(&f_stat, sizeof(MY_STAT));
+
+  /* Get dirname of the file path. */
+  (void) dirname_part(path, log_file_str, &path_length);
+
+  /* Dirname is empty if file path is relative. */
+  if (!path_length)
+    return 0;
+
+  /*
+    Check if directory exists and we have permission to create file and
+    write to file.
+  */
+  if (my_access(path, (F_OK|W_OK)))
+    goto err;
+
+  return 0;
+
+err:
+  /*
+    We print a warning if backupdir is invalid but set it anyway.
+  */
+  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                      ER_BACKUP_BACKUPDIR, ER(ER_BACKUP_BACKUPDIR),
+                      var->value->str_value.c_ptr());
+  return 0;
+}
+
+/**
+  Update the backupdir variable 
+  
+  This method is used to change the backupdir variable.
+
+  @param[IN] thd   Current thread context.
+  @param[IN] var   The new value set in set_var structure.
+
+  @returns 0 valid, 1 invalid.
+*/
+static bool sys_update_backupdir(THD *thd, set_var * var)
+{
+  char buff[FN_REFLEN];
+  char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0);
+  bool result= 0;
+  uint str_length= (var ? var->value->str_value.length() : 0);
+
+  if (!old_value)
+  {
+    old_value= make_default_backupdir(buff);
+    str_length= strlen(old_value);
+  }
+  if (!(res= my_strndup(old_value, str_length, MYF(MY_FAE+MY_WME))))
+  {
+    result= 1;
+    goto err;
+  }
+
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  logger.lock_exclusive();
+  old_value= sys_var_backupdir.value;
+  sys_var_backupdir.value= res;
+  sys_var_backupdir.value_length= str_length;
+  logger.unlock();
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+
+  if (my_access(sys_var_backupdir.value, (F_OK|W_OK)))
+    goto err;
+
+  return result;
+
+err:
+  /*
+    We print a warning if backupdir is invalid but set it anyway.
+  */
+  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                      ER_BACKUP_BACKUPDIR, ER(ER_BACKUP_BACKUPDIR),
+                      var->value->str_value.c_ptr());
+  return result;
+}
+
+/**
+  Set the default value for the backupdir variable 
+  
+  This method is used to reset the backupdir variable to the
+  default by calling the update method without a path.
+
+  @param[IN] thd   Current thread context.
+  @param[IN] type  Ignored (needed for api).
+*/
+static void sys_default_backupdir(THD *thd, enum_var_type type)
+{
+  sys_update_backupdir(thd, 0);
 }
 
 

=== modified file 'sql/set_var.h'
--- a/sql/set_var.h	2008-07-09 07:12:43 +0000
+++ b/sql/set_var.h	2008-07-31 19:32:17 +0000
@@ -1345,7 +1345,8 @@ CHARSET_INFO *get_old_charset_by_name(co
 uchar* find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
 		NAMED_LIST **found);
 
-extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path;
+extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path,
+       sys_var_backupdir;
 
 /* key_cache functions */
 KEY_CACHE *get_key_cache(LEX_STRING *cache_name);

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2008-07-09 07:12:43 +0000
+++ b/sql/share/errmsg.txt	2008-07-31 19:32:17 +0000
@@ -6372,3 +6372,5 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
   eng "Restore failed to obtain the name locks on the tables."
 ER_BACKUP_RELEASE_NAME_LOCK_FAILED
   eng "Restore failed to release the name locks on the tables."
+ER_BACKUP_BACKUPDIR
+  eng "The path specified for the system variable backupdir cannot be accessed or is
invalid. ref: %-.64s"

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-07-14 14:56:49 +0000
+++ b/sql/sql_parse.cc	2008-07-31 19:32:17 +0000
@@ -41,7 +41,7 @@
   @defgroup Runtime_Environment Runtime Environment
   @{
 */
-int execute_backup_command(THD*,LEX*);
+int execute_backup_command(THD*, LEX*, String*);
 
 /* Used in error handling only */
 #define SP_TYPE_STRING(LP) \
@@ -2166,10 +2166,21 @@ mysql_execute_command(THD *thd)
 #else
   {
     /*
+      Create a string from the backupdir system variable and pass
+      to backup system.
+    */
+    String backupdir;
+
+    backupdir.alloc(sys_var_backupdir.value_length);
+    backupdir.set_ascii(sys_var_backupdir.value, 
+                        sys_var_backupdir.value_length);
+    backupdir.length(sys_var_backupdir.value_length);
+
+    /*
       Note: execute_backup_command() sends a correct response to the client
       (either ok, result set or error message).
-     */  
-    if (execute_backup_command(thd,lex))
+     */ 
+    if (execute_backup_command(thd, lex, &backupdir))
       goto error;
     break;
   }

Thread
bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230Chuck Bell31 Jul
  • Re: bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230Jørgen Løland1 Aug
    • RE: bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230Chuck Bell1 Aug
    • RE: bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230Chuck Bell1 Aug
      • Re: bzr commit into mysql-6.0-backup branch (cbell:2674) Bug#35230Jørgen Løland4 Aug