#At file:///C:/source/bzr/mysql-6.0-bug-33364/
2712 Chuck Bell 2008-10-18
BUG#33364 Online Backup: Purge statement missing
This patch adds the following commands:
PURGE BACKUP LOGS;
PURGE BACKUP LOGS TO <id>;
PURGE BACKUP LOGS BEFORE <datetime>;
PURGE BACKUP IMAGES <wildcard>;
PURGE BACKUP IMAGES TO <id>;
PURGE BACKUP IMAGES BEFORE <datetime>;
added:
mysql-test/suite/backup/r/backup_logs_purge.result
mysql-test/suite/backup/t/backup_logs_purge.test
modified:
include/my_base.h
sql/backup/backup_kernel.h
sql/backup/kernel.cc
sql/backup/stream.cc
sql/backup/stream.h
sql/lex.h
sql/log.cc
sql/log.h
sql/mysqld.cc
sql/share/errmsg.txt
sql/sql_lex.h
sql/sql_parse.cc
sql/sql_yacc.yy
storage/csv/ha_tina.cc
per-file messages:
include/my_base.h
Added property to allow delete from logs (table only).
mysql-test/suite/backup/r/backup_logs_purge.result
New result file.
mysql-test/suite/backup/t/backup_logs_purge.test
New test for purge commands.
sql/backup/backup_kernel.h
Added method is_backup_image() to API.
sql/backup/kernel.cc
Added new method to check if a file is a backup image.
sql/backup/stream.cc
Added suppression of bad magic number error message.
sql/backup/stream.h
Added method to allow suppression of bad magic number error for
is_backup_image() method.
sql/lex.h
Added new symbol 'IMAGES' for PURGE BACKUP commands.
sql/log.cc
Added methods to allow purging of backup logs and images.
sql/log.h
Added method declarations to allow purging of backup logs and images.
sql/mysqld.cc
Added new command enums for 'the big switch'.
sql/share/errmsg.txt
New error messages.
sql/sql_lex.h
Added additional values to store backup id and wildcard string specified
on command.
Added new SQLCOM enums for 'the big switch'.
sql/sql_parse.cc
Added code to execute the PURGE BACKUP commands.
sql/sql_yacc.yy
Added new commands to parser.
storage/csv/ha_tina.cc
Added code to allow delete from log tables.
=== modified file 'include/my_base.h'
--- a/include/my_base.h 2008-07-11 13:36:54 +0000
+++ b/include/my_base.h 2008-10-18 22:08:01 +0000
@@ -206,7 +206,8 @@ enum ha_extra_function {
HA_EXTRA_IS_ATTACHED_CHILDREN,
HA_EXTRA_DETACH_CHILDREN,
HA_EXTRA_ORDERBY_LIMIT,
- HA_EXTRA_NO_ORDERBY_LIMIT
+ HA_EXTRA_NO_ORDERBY_LIMIT,
+ HA_EXTRA_ALLOW_LOG_DELETE
};
/* Compatible option, to be deleted in 6.0 */
=== added file 'mysql-test/suite/backup/r/backup_logs_purge.result'
--- a/mysql-test/suite/backup/r/backup_logs_purge.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/backup/r/backup_logs_purge.result 2008-10-18 22:08:01 +0000
@@ -0,0 +1,231 @@
+PURGE BACKUP LOGS;
+PURGE BACKUP IMAGES '%.bak';
+
+Now starting real tests
+
+DROP DATABASE IF EXISTS backup_logs;
+CREATE DATABASE backup_logs;
+Create table and populate with data.
+CREATE TABLE backup_logs.t1 (a char(30)) ENGINE=MYISAM;
+CREATE TABLE backup_logs.t2 (a char(30)) ENGINE=INNODB;
+CREATE TABLE backup_logs.t3 (a char(30)) ENGINE=MEMORY;
+INSERT INTO backup_logs.t1 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("04 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("05 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("06 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("07 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("04 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("05 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("06 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("04 Test #1 - progress");
+SET SESSION debug="d,set_backup_id";
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup1.bak';
+backup_id
+500
+SET SESSION debug="d";
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup2.bak';
+backup_id
+501
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup3.bak';
+backup_id
+502
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup4.bak';
+backup_id
+503
+Do restore of database
+RESTORE from 'backup1.bak';
+backup_id
+504
+Do restore of database
+RESTORE from 'backup2.bak';
+backup_id
+505
+Do restore of database
+RESTORE from 'backup3.bak';
+backup_id
+506
+Do restore of database
+RESTORE from 'backup4.bak';
+backup_id
+507
+Purge all backup images to 502.
+PURGE BACKUP IMAGES TO 502;
+Purge all backup images.
+PURGE BACKUP IMAGES '%.bak';
+Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+backup_id
+500
+501
+502
+503
+504
+505
+506
+507
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+backup_id
+500
+501
+502
+503
+504
+505
+506
+507
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+8
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+36
+Purge all backup log data prior to id = 504.
+PURGE BACKUP LOGS TO 502;
+Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+backup_id
+502
+503
+504
+505
+506
+507
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+backup_id
+502
+503
+504
+505
+506
+507
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+6
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+24
+Purge all backup log data.
+PURGE BACKUP LOGS;
+Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+backup_id
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+backup_id
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+0
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+0
+PURGE BACKUP LOGS BEFORE 123123;
+ERROR HY000: The datetime specified is invalid for the 'PURGE BACKUP LOGS BEFORE'
command.
+PURGE BACKUP IMAGES BEFORE 123123;
+ERROR HY000: The datetime specified is invalid for the 'PURGE BACKUP IMAGES BEFORE'
command.
+Cleanup the logs and images for later testing.
+PURGE BACKUP LOGS;
+SET SESSION debug="d,set_backup_id";
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup1.bak';
+backup_id
+500
+SET SESSION debug="d";
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup2.bak';
+backup_id
+501
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup3.bak';
+backup_id
+502
+SET SESSION debug="d,set_start_time";
+Do backup of database
+BACKUP DATABASE backup_logs to 'backup4.bak';
+backup_id
+503
+SET SESSION debug="d";
+Do restore of database
+RESTORE from 'backup1.bak';
+backup_id
+504
+Do restore of database
+RESTORE from 'backup2.bak';
+backup_id
+505
+Do restore of database
+RESTORE from 'backup3.bak';
+backup_id
+506
+SET SESSION debug="d,set_start_time";
+Do restore of database
+RESTORE from 'backup4.bak';
+backup_id
+507
+SET SESSION debug="d";
+Display the results.
+SELECT backup_id FROM mysql.backup_history;
+backup_id
+500
+501
+502
+503
+504
+505
+506
+507
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+backup_id
+500
+501
+502
+503
+504
+505
+506
+507
+SET @now_time= sysdate();
+PURGE BACKUP IMAGES BEFORE @now_time;
+PURGE BACKUP LOGS BEFORE @now_time;
+SELECT backup_id FROM mysql.backup_history;
+backup_id
+503
+507
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+backup_id
+503
+507
+PURGE BACKUP LOGS;
+Perform backup
+SET SESSION debug="d,set_backup_id";
+BACKUP DATABASE backup_logs TO '../bup_logs_dir.bak';
+backup_id
+500
+SET SESSION debug="d";
+Ensure backup image file went to the correct location
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+1
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+6
+PURGE BACKUP IMAGES TO 501;
+PURGE BACKUP LOGS TO 501;
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+0
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+0
+DROP DATABASE backup_logs;
+PURGE BACKUP LOGS;
=== added file 'mysql-test/suite/backup/t/backup_logs_purge.test'
--- a/mysql-test/suite/backup/t/backup_logs_purge.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/backup/t/backup_logs_purge.test 2008-10-18 22:08:01 +0000
@@ -0,0 +1,266 @@
+#
+# This test includes tests for ensuring the backup logs can be purged
+# of data.
+#
+
+--source include/have_log_bin.inc
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source include/have_debug.inc
+
+PURGE BACKUP LOGS;
+PURGE BACKUP IMAGES '%.bak';
+
+--echo
+--echo Now starting real tests
+--echo
+
+# Create a database and some data to work with.
+
+--disable_warnings
+DROP DATABASE IF EXISTS backup_logs;
+--enable warnings
+CREATE DATABASE backup_logs;
+
+--echo Create table and populate with data.
+
+CREATE TABLE backup_logs.t1 (a char(30)) ENGINE=MYISAM;
+CREATE TABLE backup_logs.t2 (a char(30)) ENGINE=INNODB;
+CREATE TABLE backup_logs.t3 (a char(30)) ENGINE=MEMORY;
+
+INSERT INTO backup_logs.t1 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("04 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("05 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("06 Test #1 - progress");
+INSERT INTO backup_logs.t1 VALUES ("07 Test #1 - progress");
+
+INSERT INTO backup_logs.t2 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("04 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("05 Test #1 - progress");
+INSERT INTO backup_logs.t2 VALUES ("06 Test #1 - progress");
+
+INSERT INTO backup_logs.t3 VALUES ("01 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("02 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("03 Test #1 - progress");
+INSERT INTO backup_logs.t3 VALUES ("04 Test #1 - progress");
+
+#
+# Now test read of backupid with known id using debug insertion
+#
+SET SESSION debug="d,set_backup_id";
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup1.bak';
+
+SET SESSION debug="d";
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup2.bak';
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup3.bak';
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup4.bak';
+
+--echo Do restore of database
+RESTORE from 'backup1.bak';
+
+--echo Do restore of database
+RESTORE from 'backup2.bak';
+
+--echo Do restore of database
+RESTORE from 'backup3.bak';
+
+--echo Do restore of database
+RESTORE from 'backup4.bak';
+
+--file_exists $MYSQLTEST_VARDIR/master-data/backup1.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup2.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup3.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+--echo Purge all backup images to 502.
+PURGE BACKUP IMAGES TO 502;
+
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup1.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup2.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup3.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+--echo Purge all backup images.
+PURGE BACKUP IMAGES '%.bak';
+
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup1.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup2.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup3.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+--echo Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+--echo Purge all backup log data prior to id = 504.
+PURGE BACKUP LOGS TO 502;
+
+--echo Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+--echo Purge all backup log data.
+PURGE BACKUP LOGS;
+
+--echo Display results from backup logs
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+#
+# Test purge before a datetime.
+#
+
+--error ER_BACKUP_PURGE_DATETIME
+PURGE BACKUP LOGS BEFORE 123123;
+
+--error ER_BACKUP_PURGE_DATETIME
+PURGE BACKUP IMAGES BEFORE 123123;
+
+--echo Cleanup the logs and images for later testing.
+PURGE BACKUP LOGS;
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup1.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup2.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup3.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+SET SESSION debug="d,set_backup_id";
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup1.bak';
+
+SET SESSION debug="d";
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup2.bak';
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup3.bak';
+
+SET SESSION debug="d,set_start_time";
+
+--echo Do backup of database
+BACKUP DATABASE backup_logs to 'backup4.bak';
+
+SET SESSION debug="d";
+
+--echo Do restore of database
+RESTORE from 'backup1.bak';
+
+--echo Do restore of database
+RESTORE from 'backup2.bak';
+
+--echo Do restore of database
+RESTORE from 'backup3.bak';
+
+SET SESSION debug="d,set_start_time";
+
+--echo Do restore of database
+RESTORE from 'backup4.bak';
+
+SET SESSION debug="d";
+
+--file_exists $MYSQLTEST_VARDIR/master-data/backup1.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup2.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup3.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+--echo Display the results.
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+
+#
+# We need to sleep to allow time to pass in order to ensure the time
+# compare method in log.cc has two different times to compare.
+#
+real_sleep 3;
+
+SET @now_time= sysdate();
+
+PURGE BACKUP IMAGES BEFORE @now_time;
+
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup1.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup2.bak
+--error 1
+--file_exists $MYSQLTEST_VARDIR/master-data/backup3.bak
+--file_exists $MYSQLTEST_VARDIR/master-data/backup4.bak
+
+PURGE BACKUP LOGS BEFORE @now_time;
+
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+
+PURGE BACKUP LOGS;
+
+#
+# Test purge of images for locations other than backupdir.
+#
+--echo Perform backup
+SET SESSION debug="d,set_backup_id";
+
+BACKUP DATABASE backup_logs TO '../bup_logs_dir.bak';
+
+SET SESSION debug="d";
+
+--echo Ensure backup image file went to the correct location
+--file_exists $MYSQLTEST_VARDIR/bup_logs_dir.bak
+
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+PURGE BACKUP IMAGES TO 501;
+
+--error 1
+--file_exists $MYSQLTEST_VARDIR/bup_backupdir2.bak
+
+PURGE BACKUP LOGS TO 501;
+
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+#
+# Cleanup.
+#
+DROP DATABASE backup_logs;
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup1.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup2.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup3.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/master-data/backup4.bak
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/bup_backupdir2.bak
+
+PURGE BACKUP LOGS;
+
=== modified file 'sql/backup/backup_kernel.h'
--- a/sql/backup/backup_kernel.h 2008-10-15 20:00:48 +0000
+++ b/sql/backup/backup_kernel.h 2008-10-18 22:08:01 +0000
@@ -32,6 +32,13 @@ void backup_shutdown();
*/
int execute_backup_command(THD*, LEX*, String*);
+/*
+ Called from log.cc to check to see if a given file is a backup
+ image. Returns TRUE if file is a backup image, false if the
+ file cannot be read or is not a backup image.
+*/
+bool is_backup_image(THD *thd, String *path);
+
// forward declarations
class Backup_info;
@@ -81,6 +88,11 @@ class Backup_restore_ctx: public backup:
int close();
THD* thd() const { return m_thd; }
+
+ /*
+ Used by is_backup_image() to initialize memory.
+ */
+ void minimal_prepare();
private:
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-10-15 20:00:48 +0000
+++ b/sql/backup/kernel.cc 2008-10-18 22:08:01 +0000
@@ -324,6 +324,59 @@ int send_reply(Backup_restore_ctx &conte
? "BACKUP" : "RESTORE"));
}
+/**
+ Check to see if path is a backup image file.
+
+ This method attempts to open the file specified and identify
+ it as a backup image file. The method is called from the
+ purge_backup_image() method in log.cc.
+
+ @param[IN] thd The current thread.
+ @param[IN] path The full path+filename of the file to check.
+
+ @returns TRUE if file is a backup image, false if the
+ file cannot be read or is not a backup image.
+*/
+bool is_backup_image(THD *thd, String *path)
+{
+ using namespace backup;
+ bool is_image= FALSE;
+
+ /*
+ Get a restore context and logger instance.
+ */
+ Backup_restore_ctx context(thd); // reports errors
+ Logger log= Logger(thd);
+
+ /*
+ Set context to a minimal set for open and reading magic numbers.
+ */
+ context.minimal_prepare();
+
+ /*
+ Attempt to open the stream. This will check the magic numbers and
+ if successful return 0. Any other return value is an error of
+ either cannot read or wrong file type. Either way, we don't want
+ (or can't) delete so return FALSE.
+ */
+ Input_stream *s= new Input_stream(log, path);
+ Stream *m_stream= s;
+ if (!s)
+ return is_image;
+
+ // Turn off the error for magic number failure (not a backup image).
+ s->suppress_errors_on_magic_number(TRUE);
+
+ int my_open_status= s->open();
+ is_image= (my_open_status == 0);
+
+ /*
+ Cleanup. Play nice!
+ */
+ s->close();
+ context.close();
+ return is_image;
+}
namespace backup {
@@ -542,8 +595,6 @@ int Backup_restore_ctx::prepare(String *
*/
prepare_path(backupdir, location);
- //m_path= location.str;
-
// create new instance of memory allocator for backup stream library
using namespace backup;
@@ -993,6 +1044,21 @@ int Backup_restore_ctx::close()
m_state= CLOSED;
return error;
}
+
+
+/**
+ Do minimal setup for reading a backup image file.
+
+ Used by is_backup_image() to initialize memory for reading image file
+ header and set current operation.
+*/
+void Backup_restore_ctx::minimal_prepare()
+{
+ using namespace backup;
+ mem_alloc= new Mem_allocator();
+ current_op= this;
+}
+
/**
Create backup archive.
=== modified file 'sql/backup/stream.cc'
--- a/sql/backup/stream.cc 2008-10-15 20:00:48 +0000
+++ b/sql/backup/stream.cc 2008-10-18 22:08:01 +0000
@@ -478,6 +478,7 @@ Input_stream::Input_stream(Logger &log,
{
m_with_compression= false;
stream.read= stream_read;
+ m_suppress_magic_number_error= FALSE;
}
/**
@@ -512,7 +513,8 @@ bool Input_stream::init()
if (len <= 0)
{
- m_log.report_error(ER_BACKUP_BAD_MAGIC);
+ if (!m_suppress_magic_number_error)
+ m_log.report_error(ER_BACKUP_BAD_MAGIC);
return FALSE;
}
=== modified file 'sql/backup/stream.h'
--- a/sql/backup/stream.h 2008-10-15 20:00:48 +0000
+++ b/sql/backup/stream.h 2008-10-18 22:08:01 +0000
@@ -141,10 +141,16 @@ class Input_stream:
int next_chunk();
+ void suppress_errors_on_magic_number(bool suppress)
+ {
+ m_suppress_magic_number_error= suppress;
+ }
private:
int check_magic_and_version();
bool init();
+
+ bool m_suppress_magic_number_error;
};
=== modified file 'sql/lex.h'
--- a/sql/lex.h 2008-07-09 07:12:43 +0000
+++ b/sql/lex.h 2008-10-18 22:08:01 +0000
@@ -245,6 +245,7 @@ static SYMBOL symbols[] = {
{ "IDENTIFIED", SYM(IDENTIFIED_SYM)},
{ "IF", SYM(IF)},
{ "IGNORE", SYM(IGNORE_SYM)},
+ { "IMAGES", SYM(IMAGES)},
{ "IMPORT", SYM(IMPORT)},
{ "IN", SYM(IN_SYM)},
{ "INDEX", SYM(INDEX_SYM)},
=== modified file 'sql/log.cc'
--- a/sql/log.cc 2008-10-15 20:00:48 +0000
+++ b/sql/log.cc 2008-10-18 22:08:01 +0000
@@ -55,6 +55,11 @@ LOGGER logger;
MYSQL_BIN_LOG mysql_bin_log;
ulong sync_binlog_counter= 0;
+/*
+ Backup kernel API method
+*/
+bool is_backup_image(THD *thd, String *path);
+
static bool test_if_number(const char *str,
long *res, bool allow_wildcards);
static int binlog_init(void *p);
@@ -856,6 +861,11 @@ bool Log_to_csv_event_handler::
if (history_data->start)
{
MYSQL_TIME time;
+ /*
+ Set time ahead a few hours to allow backup purge test to test
+ PURGE BACKUP LOGS/IMAGES BEFORE command.
+ */
+ DBUG_EXECUTE_IF("set_start_time", history_data->start= my_time(0) + 100000;);
my_tz_OFFSET0->gmt_sec_to_TIME(&time, (my_time_t)history_data->start);
table->field[ET_OBH_FIELD_START_TIME]->set_notnull();
@@ -1096,6 +1106,331 @@ err:
return result;
}
+/**
+ Delete a row from a log table.
+
+ This method is used to delete a row from a log that is being
+ accessed as a table.
+
+ @param[IN] hdl Table handler.
+ @param[IN] tbl Table class.
+
+ @returns Result of delete operation
+*/
+bool Log_to_csv_event_handler::delete_log_row(handler *hdl, TABLE *tbl)
+{
+ bool result= 0;
+
+ /*
+ Tell the handler to allow deletes for this log table
+ by turning off log table flag.
+ */
+ hdl->extra(HA_EXTRA_ALLOW_LOG_DELETE);
+ result= hdl->ha_delete_row(tbl->record[0]);
+ hdl->extra(HA_EXTRA_MARK_AS_LOG_TABLE);
+ return result;
+}
+
+/**
+ Delete an image specified by the table field.
+
+ This method attempts to delete a file using the my_delete()
+ method. The name of the file is combined with the path stored
+ in the backup_file_path field.
+
+ @param[IN] tbl Table class.
+ @returns Result of my_delete() method call.
+*/
+bool Log_to_csv_event_handler::delete_image(TABLE *tbl)
+{
+ String path;
+ String fname;
+
+ tbl->field[ET_OBH_FIELD_BACKUP_FILE_PATH]->val_str(&path);
+ tbl->field[ET_OBH_FIELD_BACKUP_FILE]->val_str(&fname);
+ path.append(fname);
+ return (my_delete(path.c_ptr(), MYF(0)));
+}
+
+/**
+ Perform a table scan of the backup log tables for deleting rows.
+
+ This method is a helper method designed to delete rows either by ID or DATE
+ (the table columns backup_id and start respectfully).
+
+ There are several parameters used to control what is being deleted.
+ @c log_name - is used to determine which log to process
+ (backup_history or backup_progress).
+ @c backup_id - value specified on the command for deleting rows by id.
+ @ field_num - is used to tell the method which field to process. For ID,
+ the fields are either ET_OBH_FIELD_BACKUP_ID or
+ ET_OBP_FIELD_BACKUP_ID_FK.
+ @dtm - value specified on the command for deleting rows by date
+ @delete_log_entries - if TRUE, process rows from the logs else delete images
+ specified in the backup_history log file via column
+ 'backup_file'.
+ @rows - the number of rows
+
+ For example, to delete all rows < backup_id for the backup_history table,
+ set the following:
+ * log_name = BACKUP_HISTORY_LOG_NAME
+ * field_num = ET_OBH_FIELD_BACKUP_ID
+ * backup_id = id specified by the user
+ * delete_log_entries = TRUE
+
+ @param[IN] THD The current thread
+ @param[IN] log_name Name of the log to process
+ @param[IN] field_num Number of field where value is stored
+ @param[IN] backup_id Value for backup id if specified
+ @param[IN] dtm Value for start (date) if specified
+ @param[IN] delete_log_entries If TRUE, process logs else process images
+ @param[IN] delete_exact_row If TRUE, delete only the rows = backup_id
+ @param[OUT] num Number of rows affected.
+
+ @retval num Number of rows affected.
+
+ @returns TRUE if error
+*/
+bool Log_to_csv_event_handler::scan_backup_log(THD *thd,
+ LEX_STRING log_name,
+ uint field_num,
+ ulonglong backup_id,
+ my_time_t *dtm,
+ bool delete_log_entries,
+ bool delete_exact_row,
+ int *rows)
+{
+ TABLE_LIST table_list;
+ handler *hdl;
+ TABLE *tbl;
+ bool res= FALSE;
+ uint result= 0;
+ int num_rows= 0;
+ Open_tables_state open_tables_backup;
+
+ /*
+ Setup table list for open.
+ */
+ bzero(&table_list, sizeof(TABLE_LIST));
+ table_list.alias= table_list.table_name= log_name.str;
+ table_list.table_name_length= log_name.length;
+
+ table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
+
+ table_list.db= MYSQL_SCHEMA_NAME.str;
+ table_list.db_length= MYSQL_SCHEMA_NAME.length;
+
+ tbl= open_performance_schema_table(thd, &table_list,
+ &open_tables_backup);
+ hdl= tbl->file;
+ hdl->extra(HA_EXTRA_MARK_AS_LOG_TABLE);
+
+ /*
+ Begin table scan.
+ */
+ result= hdl->ha_rnd_init(1);
+ result= hdl->rnd_next(tbl->record[0]);
+ while (result != HA_ERR_END_OF_FILE)
+ {
+ ulonglong id= tbl->field[0]->val_int();
+ /*
+ Delete by id.
+ */
+ if ((field_num == ET_OBH_FIELD_BACKUP_ID) ||
+ (field_num == ET_OBP_FIELD_BACKUP_ID_FK))
+ {
+ if (delete_exact_row)
+ {
+ if (delete_log_entries && (id == backup_id)) // delete log row
+ result= delete_log_row(hdl, tbl);
+ }
+ else if (id < backup_id)
+ {
+ if (delete_log_entries) // delete log row
+ {
+ result= delete_log_row(hdl, tbl);
+ num_rows++;
+ }
+ else if (field_num == ET_OBH_FIELD_BACKUP_ID) // delete image specified
+ {
+ result= delete_image(tbl);
+ if (!result) // only count deletes that succeed
+ num_rows++;
+ }
+ }
+ }
+ /*
+ Delete by date
+ */
+ else
+ {
+ MYSQL_TIME time;
+ MYSQL_TIME tmp;
+
+ tbl->field[field_num]->get_date(&time, TIME_NO_ZERO_DATE);
+ bzero(&tmp, sizeof(MYSQL_TIME));
+ thd->variables.time_zone->gmt_sec_to_TIME(&tmp, *dtm);
+ if (my_time_compare(&time, &tmp) < 0)
+ {
+ if (delete_log_entries) // delete log row
+ {
+ /*
+ When deleting by date for the backup_history table,
+ we must also delete rows from the backup_progress table
+ for this backup id.
+ */
+ if ((my_strcasecmp(system_charset_info, log_name.str,
+ BACKUP_HISTORY_LOG_NAME.str) == 0) &&
+ opt_backup_progress_log &&
+ (log_backup_output_options & LOG_TABLE))
+ {
+ int r= 0;
+
+ scan_backup_log(thd, BACKUP_PROGRESS_LOG_NAME,
+ ET_OBP_FIELD_BACKUP_ID_FK, id, dtm, TRUE, TRUE, &r);
+ }
+ result= delete_log_row(hdl, tbl);
+ num_rows++;
+ }
+ else if (field_num == ET_OBH_FIELD_START_TIME) // delete images
+ {
+ result= delete_image(tbl);
+ if (!result) // only count deletes that succeed
+ num_rows++;
+ }
+ }
+ }
+ result= hdl->rnd_next(tbl->record[0]);
+ }
+ result= hdl->ha_rnd_end();
+ *rows= num_rows;
+ close_performance_schema_table(thd, &open_tables_backup);
+ return result;
+}
+
+/**
+ Remove all rows from the backup logs.
+
+ This method removes (via truncate) all data from the backup logs. It checks
+ if the backup logs are turned on and if the log_backup_output_option is set
+ to include writing to tables. If these conditions are true, each log
+ (backup_history, backup_progress) is truncated.
+
+ @param[IN] THD current thread
+
+ @returns TRUE = success, FALSE = failed
+*/
+bool Log_to_csv_event_handler::purge_backup_logs(THD *thd)
+{
+ TABLE_LIST tables;
+ bool res= FALSE;
+
+ if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
+ {
+ // need to truncate the table.
+ tables.init_one_table("mysql", strlen("mysql"),
+ "backup_history", strlen("backup_history"),
+ "backup_history", TL_READ);
+ alloc_mdl_locks(&tables, thd->mem_root);
+ res= mysql_truncate(thd, &tables, 1);
+ close_thread_tables(thd);
+ if (res)
+ goto err;
+ }
+ if (opt_backup_progress_log && (log_backup_output_options & LOG_TABLE))
+ {
+ // need to truncate the table.
+ tables.init_one_table("mysql", strlen("mysql"),
+ "backup_progress", strlen("backup_progress"),
+ "backup_progress", TL_READ);
+ alloc_mdl_locks(&tables, thd->mem_root);
+ res= mysql_truncate(thd, &tables, 1);
+ close_thread_tables(thd);
+ }
+err:
+ return res;
+}
+
+/**
+ Purge backup logs of rows less than backup_id passed.
+
+ This method walks the backup log tables deleting all rows whose
+ backup id is less than @c backup_id passed. The parameter
+ @c delete_log_rows is used to indicate delete applies to log rows (TRUE)
+ or images files (FALSE).
+
+ @param[IN] THD The current thread
+ @param[IN] backup_id Value for backup id
+ @param[IN] delete_log_rows If TRUE, process logs else process images
+ @param[OUT] num Number of rows affected.
+
+ @retval num Number of rows/images affected.
+
+ @returns TRUE if error
+*/
+bool
+Log_to_csv_event_handler::purge_backup_logs_before_id(THD *thd,
+ ulonglong backup_id,
+ bool delete_log_rows,
+ int *rows)
+{
+ bool res= FALSE;
+ my_time_t t= 0;
+
+ if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
+ {
+ res= scan_backup_log(thd, BACKUP_HISTORY_LOG_NAME,
+ ET_OBH_FIELD_BACKUP_ID, backup_id,
+ &t, delete_log_rows, FALSE, rows);
+ if (res)
+ goto err;
+ }
+ if (opt_backup_progress_log && (log_backup_output_options & LOG_TABLE)
&&
+ delete_log_rows)
+ {
+ int r= 0; //ignore this for progress log
+
+ res= scan_backup_log(thd, BACKUP_PROGRESS_LOG_NAME,
+ ET_OBP_FIELD_BACKUP_ID_FK, backup_id,
+ &t, delete_log_rows, FALSE, &r);
+ }
+err:
+ return res;
+}
+
+/**
+ Purge backup logs of rows previous to start date passed.
+
+ This method walks the backup log tables deleting all rows whose
+ start column value is less than @c t passed. The parameter
+ @c delete_log_rows is used to indicate delete applies to log rows (TRUE)
+ or images files (FALSE).
+
+ @param[IN] THD The current thread
+ @param[IN] t Value for start datetime
+ @param[IN] delete_log_rows If TRUE, process logs else process images
+ @param[OUT] num Number of rows affected.
+
+ @retval num Number of rows/images affected.
+
+ @returns TRUE if error
+*/
+bool
+Log_to_csv_event_handler::purge_backup_logs_before_date(THD *thd,
+ my_time_t *t,
+ bool delete_log_rows,
+ int *rows)
+{
+ bool res= FALSE;
+
+ if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
+ res= scan_backup_log(thd, BACKUP_HISTORY_LOG_NAME,
+ ET_OBH_FIELD_START_TIME, 0,
+ t, delete_log_rows, FALSE, rows);
+ return res;
+}
+
+
bool Log_to_csv_event_handler::
log_error(enum loglevel level, const char *format, va_list args)
{
@@ -1256,12 +1591,55 @@ void Log_to_file_event_handler::flush_ba
reopen log files
Where TRUE means perform open on history file (backup_history) and
- FALSE means perform open on the progress file (backkup_progress).
+ FALSE means perform open on the progress file (backup_progress).
*/
mysql_backup_history_log.reopen_file(TRUE);
mysql_backup_progress_log.reopen_file(FALSE);
}
+/**
+ Purge the backup logs of all data.
+
+ This method truncates the backup log files. It will only do so if the
+ log_backup_output_options includes logging to FILE. It also checks to
+ see if each log is turned on (backup_history and backup_progress) and
+ if so, truncates it. Truncate in this case means resetting the size
+ to 0 bytes using my_chsize().
+
+ @returns TRUE = success, FALSE = failed.
+*/
+bool Log_to_file_event_handler::purge_backup_logs()
+{
+ bool res= FALSE;
+
+ if (opt_backup_history_log && (log_backup_output_options & LOG_FILE))
+ {
+ MYSQL_BACKUP_LOG *backup_log= logger.get_backup_history_log_file_handler();
+
+ pthread_mutex_lock(backup_log->get_log_lock());
+ res= my_sync(backup_log->get_file(), MYF(MY_WME));
+ if (!res)
+ res= my_chsize(backup_log->get_file(), 0, 0, MYF(MY_WME));
+ pthread_mutex_unlock(backup_log->get_log_lock());
+ if (res)
+ goto err;
+ }
+ if (opt_backup_progress_log && (log_backup_output_options & LOG_FILE))
+ {
+ MYSQL_BACKUP_LOG *backup_log= logger.get_backup_progress_log_file_handler();
+
+ pthread_mutex_lock(backup_log->get_log_lock());
+ res= my_sync(backup_log->get_file(), MYF(MY_WME));
+ if (!res)
+ res= my_chsize(backup_log->get_file(), 0, 0, MYF(MY_WME));
+ pthread_mutex_unlock(backup_log->get_log_lock());
+ if (res)
+ goto err;
+ }
+err:
+ return res;
+}
+
void Log_to_file_event_handler::cleanup()
{
mysql_log.cleanup();
@@ -1411,6 +1789,225 @@ bool LOGGER::flush_backup_logs(THD *thd)
return rc;
}
+/**
+ Delete contents of backup logs.
+
+ This method deletes the data from the backup logs. This applies to both
+ log file and table destinations.
+
+ @param[IN] thd The current thread.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs(THD *thd)
+{
+ my_bool res= FALSE;
+
+ DBUG_ENTER("LOGGER::purge_backup_logs");
+ /*
+ Here we need to truncate the files if writing to files.
+ */
+ res= file_log_handler->purge_backup_logs();
+ if (res)
+ goto err;
+
+ /*
+ We also need to truncate the table if writing to tables.
+ */
+ res= table_log_handler->purge_backup_logs(thd);
+
+err:
+ DBUG_RETURN(res);
+}
+
+/**
+ Delete contents of backup logs where backup id is less than a given id.
+
+ This method deletes the data from the backup logs where a backup id is
+ less than the backup id specified. This applies only to table destinations.
+
+ @param[IN] thd The current thread.
+ @param[IN] backup_id The backup id to compare rows to.
+ @param[OUT] rows The number of rows affected.
+
+ @retval num The number of rows affected.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs_before_id(THD *thd,
+ ulonglong backup_id,
+ int *rows)
+{
+ my_bool res= FALSE;
+
+ DBUG_ENTER("LOGGER::purge_backup_logs_before_id");
+
+ res= table_log_handler->purge_backup_logs_before_id(thd, backup_id,
+ TRUE, rows);
+
+ DBUG_RETURN(res);
+}
+
+/**
+ Delete backup rows less than a certain date.
+
+ This method scans the backup history log and deletes the rows for those
+ operations that were created (start column) previous to the date specified in
+ @c t.
+
+ @param[IN] thd The current thread.
+ @param[IN] t The date to compare rows to.
+ @param[OUT] rows The number of rows affected.
+
+ @retval rows The number of rows affected.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs_before_date(THD *thd,
+ my_time_t *t,
+ int *rows)
+{
+ my_bool res= FALSE;
+
+ DBUG_ENTER("LOGGER::purge_backup_logs_before_date");
+
+ res= table_log_handler->purge_backup_logs_before_date(thd, t, TRUE, rows);
+
+ DBUG_RETURN(res);
+}
+
+/**
+ Delete backup images.
+
+ This method deletes all backup images in the backupdir path.
+
+ @param[IN] thd The current thread.
+ @param[IN] wild Wildcard specified on the command.
+ @param[OUT] num The number of images deleted.
+
+ @retval num The number of images deleted.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_imgs(THD *thd,
+ char *wild,
+ int *num)
+{
+ my_bool res= FALSE;
+ DBUG_ENTER("purge_backup_images");
+
+ /*
+ Delete all backup images in this folder that match the wildcard string.
+ */
+ int num_del= 0;
+ uint i;
+ struct st_my_dir *dir_info;
+ reg1 struct fileinfo *file_info;
+ FILEINFO *file;
+
+ if (!(dir_info = my_dir(sys_var_backupdir.value,MYF(MY_DONT_SORT))))
+ {
+ DBUG_RETURN(1);
+ }
+ file_info= dir_info->dir_entry;
+ for (i=dir_info->number_off_files ; i-- ; file_info++)
+ {
+ /*
+ Skip the usual suspects...and directories.
+ */
+ if ((file_info->name[0] == '.' &&
+ ((file_info->name[1] == '.' &&
+ file_info->name[2] == '\0') ||
+ file_info->name[1] == '\0')))
+ continue;
+ file=dir_info->dir_entry+i;
+ MY_STAT *st= my_stat(file_info->name, file->mystat, MYF(0));
+ if (st && MY_S_ISDIR(st->st_mode))
+ continue;
+ if (wild && !wild_compare(file_info->name, wild, 1))
+ {
+ String full_path;
+ full_path.copy(sys_var_backupdir.value,
+ sys_var_backupdir.value_length,
+ system_charset_info);
+ full_path.append(file_info->name);
+#ifndef EMBEDDED_LIBRARY
+ /*
+ Check to see if file is a backup image and delete it IFF
+ it is a backup image.
+ */
+ if (is_backup_image(thd, &full_path))
+ {
+ res= my_delete(full_path.c_ptr(), MYF(0));
+ if (res)
+ goto err;
+ num_del++;
+ }
+#endif
+ }
+ }
+ my_dirend(dir_info);
+ *num= num_del;
+err:
+ DBUG_RETURN(res);
+}
+
+/**
+ Deletes backup image files where backup id is less than a given id.
+
+ This method deletes the backup image files specified in the backup_history log
+ where the backup id is less than the backup id specified. This applies only to
+ table destinations.
+
+ @param[IN] thd The current thread.
+ @param[IN] backup_id The backup id to compare rows to.
+ @param[OUT] num The number of rows affected.
+
+ @retval num The number of images affected.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_imgs_before_id(THD *thd,
+ ulonglong backup_id,
+ int *num)
+{
+ my_bool res= FALSE;
+
+ DBUG_ENTER("LOGGER::purge_backup_imgs_before_id");
+
+ res= table_log_handler->purge_backup_logs_before_id(thd, backup_id,
+ FALSE, num);
+
+ DBUG_RETURN(res);
+}
+
+/**
+ Delete backup image files less than a certain date.
+
+ This method scans the backup history log and deletes the image files for those
+ operations that were created (start column) previous to the date specified in
+ @c t.
+
+ @param[IN] thd The current thread.
+ @param[IN] t The date to compare rows to.
+ @param[OUT] num The number of images affected.
+
+ @retval num The number of images affected.
+
+ @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_imgs_before_date(THD *thd,
+ my_time_t *t,
+ int *num)
+{
+ my_bool res= FALSE;
+
+ DBUG_ENTER("LOGGER::purge_backup_logs_before_date");
+
+ res= table_log_handler->purge_backup_logs_before_date(thd, t, FALSE, num);
+
+ DBUG_RETURN(res);
+}
/*
Log slow query with all enabled log event handlers
=== modified file 'sql/log.h'
--- a/sql/log.h 2008-10-15 20:00:48 +0000
+++ b/sql/log.h 2008-10-18 22:08:01 +0000
@@ -307,6 +307,8 @@ public:
}
ulonglong get_next_backup_id();
my_bool check_backup_logs(THD *thd);
+ File get_file() { return log_file.file; }
+ inline pthread_mutex_t* get_log_lock() { return &LOCK_log; }
private:
bool write_int(uint num);
@@ -576,9 +578,30 @@ public:
longlong progress,
int error_num,
const char *notes);
+ bool purge_backup_logs(THD *thd);
+ bool purge_backup_logs_before_id(THD *thd,
+ ulonglong backup_id,
+ bool delete_log_rows,
+ int *rows);
+ bool purge_backup_logs_before_date(THD *thd,
+ my_time_t *t,
+ bool delete_log_rows,
+ int *rows);
int activate_log(THD *thd, uint log_type);
+private:
+ bool delete_log_row(handler *hdl, TABLE *tbl);
+ bool delete_image(TABLE *tbl);
+ bool scan_backup_log(THD *thd,
+ LEX_STRING log_name,
+ uint field_num,
+ ulonglong backup_id,
+ my_time_t *dtm,
+ bool delete_log_entries,
+ bool delete_exact_row,
+ int *rows);
+
};
@@ -629,6 +652,7 @@ public:
void flush();
void flush_backup_logs();
+ bool purge_backup_logs();
void init_pthread_objects();
MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
@@ -683,6 +707,20 @@ public:
void init_log_tables();
bool flush_logs(THD *thd);
bool flush_backup_logs(THD *thd);
+ bool purge_backup_logs(THD *thd);
+ bool purge_backup_logs_before_id(THD *thd,
+ ulonglong backup_id,
+ int *rows);
+ bool purge_backup_logs_before_date(THD *thd,
+ my_time_t *t,
+ int *rows);
+ bool purge_backup_imgs(THD *thd, char *wild, int *num);
+ bool purge_backup_imgs_before_id(THD *thd,
+ ulonglong backup_id,
+ int *num);
+ bool purge_backup_imgs_before_date(THD *thd,
+ my_time_t *t,
+ int *num);
/* Perform basic logger cleanup. this will leave e.g. error log open. */
void cleanup_base();
/* Free memory. Nothing could be logged after this function is called */
=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc 2008-09-26 16:30:56 +0000
+++ b/sql/mysqld.cc 2008-10-18 22:08:01 +0000
@@ -3243,6 +3243,12 @@ SHOW_VAR com_status_vars[]= {
{"prepare_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]),
SHOW_LONG_STATUS},
{"purge", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]),
SHOW_LONG_STATUS},
{"purge_before_date", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS},
+ {"purge_bup_log", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_LOG]), SHOW_LONG_STATUS},
+ {"purge_bup_log_date", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_LOG_DATE]), SHOW_LONG_STATUS},
+ {"purge_bup_log_id", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_LOG_ID]), SHOW_LONG_STATUS},
+ {"purge_bup_img", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_IMG]), SHOW_LONG_STATUS},
+ {"purge_bup_img_date", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_IMG_DATE]), SHOW_LONG_STATUS},
+ {"purge_bup_img_id", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_PURGE_BUP_IMG_ID]), SHOW_LONG_STATUS},
{"release_savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_RELEASE_SAVEPOINT]), SHOW_LONG_STATUS},
{"rename_table", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS},
{"rename_user", (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_RENAME_USER]), SHOW_LONG_STATUS},
=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt 2008-10-15 06:48:36 +0000
+++ b/sql/share/errmsg.txt 2008-10-18 22:08:01 +0000
@@ -6404,4 +6404,15 @@ ER_BACKUP_BINLOG
eng "Error on accessing binlog during BACKUP"
nor "Lesing av binlog feilet under BACKUP"
norwegian-ny "Lesing av binlog feila under BACKUP"
-
+ER_BACKUP_LOG_OUTPUT
+ eng "The operation could not be completed because the log_backup_output option does not
include writing to tables."
+ER_BACKUP_CANT_PURGE_IMAGE
+ eng "Cannot delete backup image %-.64s."
+ER_BACKUP_PURGE_NOT_ALLOWED
+ eng "You cannot purge backup images using a global wildcard '%' when backupdir is the
same path as datadir."
+ER_BACKUP_PURGE_DATETIME
+ eng "The datetime specified is invalid for the '%-.64s' command."
+ER_BACKUP_LOGS_DELETED
+ eng "Backup log entries deleted: "
+ER_BACKUP_IMAGES_DELETED
+ eng "Backup image files deleted: "
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h 2008-09-04 18:30:34 +0000
+++ b/sql/sql_lex.h 2008-10-18 22:08:01 +0000
@@ -89,6 +89,8 @@ enum enum_sql_command {
SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER,
SQLCOM_RENAME_TABLE,
SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
+ SQLCOM_PURGE_BUP_LOG, SQLCOM_PURGE_BUP_LOG_DATE, SQLCOM_PURGE_BUP_LOG_ID,
+ SQLCOM_PURGE_BUP_IMG, SQLCOM_PURGE_BUP_IMG_DATE, SQLCOM_PURGE_BUP_IMG_ID,
SQLCOM_SHOW_OPEN_TABLES,
SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,
@@ -1528,6 +1530,8 @@ struct LEX: public Query_tables_list
LEX_STRING backup_dir; /* For RESTORE/BACKUP */
bool backup_compression;
char* to_log; /* For PURGE MASTER LOGS TO */
+ ulonglong backup_id; /* For PURGE BACKUP LOGS|IMAGES */
+ char *backup_log_wild; /* For PURGE BACKUP IMAGES */
char* x509_subject,*x509_issuer,*ssl_cipher;
String *wild;
sql_exchange *exchange;
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2008-09-26 16:30:56 +0000
+++ b/sql/sql_parse.cc 2008-10-18 22:08:01 +0000
@@ -2098,6 +2098,147 @@ mysql_execute_command(THD *thd)
res = purge_master_logs_before_date(thd, (ulong)it->val_int());
break;
}
+ /*
+ Check log status to see if the command applies.
+ */
+ case SQLCOM_PURGE_BUP_LOG_DATE:
+ case SQLCOM_PURGE_BUP_LOG_ID:
+ case SQLCOM_PURGE_BUP_IMG_DATE:
+ case SQLCOM_PURGE_BUP_IMG_ID:
+ {
+ char buff[256];
+ int num= 0;
+ res= 0;
+
+ /*
+ If we are attempting to purge to a specified date or backup_id, we
+ must ensure the backup history log is turned on and is
+ being written to a table.
+ */
+ if (opt_backup_history_log && !(log_backup_output_options & LOG_TABLE))
+ {
+ my_error(ER_BACKUP_LOG_OUTPUT, MYF(0), ER(ER_BACKUP_LOG_OUTPUT));
+ goto error;
+ }
+
+ if (check_global_access(thd, SUPER_ACL))
+ goto error;
+
+ /*
+ We allow the other purge backup commands (except PURGE BACKUP IMAGES;)
+ to float down the switch so that we can consolidate some of the
+ repetitive code.
+ */
+ if ((lex->sql_command == SQLCOM_PURGE_BUP_LOG_DATE) ||
+ (lex->sql_command == SQLCOM_PURGE_BUP_IMG_DATE))
+ {
+ Item *it;
+ it= (Item *)lex->value_list.head();
+ if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
+ it->check_cols(1))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE BACKUP BEFORE");
+ goto error;
+ }
+ it= new Item_func_unix_timestamp(it);
+ /*
+ it is OK only emulate fix_fields, because we need only
+ value of constant
+ */
+ it->quick_fix_field();
+
+ if ((ulong)it->val_int() == 0)
+ {
+ if (lex->sql_command == SQLCOM_PURGE_BUP_LOG_DATE)
+ my_error(ER_BACKUP_PURGE_DATETIME, MYF(0), "PURGE BACKUP LOGS BEFORE");
+ else
+ my_error(ER_BACKUP_PURGE_DATETIME, MYF(0), "PURGE BACKUP IMAGES BEFORE");
+ goto error;
+ }
+
+ my_time_t t= (ulong)it->val_int();
+
+ /*
+ Validate datetime.
+ */
+ if (lex->sql_command == SQLCOM_PURGE_BUP_LOG_DATE)
+ res= logger.purge_backup_logs_before_date(thd, &t, &num);
+ else
+ res= logger.purge_backup_imgs_before_date(thd, &t, &num);
+ }
+ else if (lex->sql_command == SQLCOM_PURGE_BUP_LOG_ID)
+ res= logger.purge_backup_logs_before_id(thd, thd->lex->backup_id, &num);
+ else
+ res= logger.purge_backup_imgs_before_id(thd, thd->lex->backup_id, &num);
+ if (res)
+ goto error;
+ if ((lex->sql_command == SQLCOM_PURGE_BUP_IMG_DATE) ||
+ (lex->sql_command == SQLCOM_PURGE_BUP_IMG_ID))
+ {
+ my_sprintf(buff, (buff, "%s %d.", ER(ER_BACKUP_IMAGES_DELETED), num));
+ my_ok(thd, 0, 0, buff);
+ }
+ else
+ {
+ my_sprintf(buff, (buff, "%s %d.", ER(ER_BACKUP_LOGS_DELETED), num));
+ my_ok(thd, num, 0, buff);
+ }
+ break;
+ }
+ case SQLCOM_PURGE_BUP_LOG:
+ case SQLCOM_PURGE_BUP_IMG:
+ {
+ char buff[256];
+ int num= 0;
+
+ res= 0;
+ if (check_global_access(thd, SUPER_ACL))
+ goto error;
+
+ /*
+ We allow the other purge backup commands (except PURGE BACKUP IMAGES;)
+ to float down the switch so that we can consolidate some of the
+ repetitive code.
+ */
+ if (lex->sql_command == SQLCOM_PURGE_BUP_LOG) // PURGE BACKUP LOGS;
+ {
+ if (sys_var_backupdir.value_length > 0)
+ res= logger.purge_backup_logs(thd);
+ if (!res)
+ {
+ my_sprintf(buff, (buff, "%s %d.", ER(ER_BACKUP_LOGS_DELETED), num));
+ my_ok(thd, num, 0, buff);
+ }
+ break;
+ }
+ else // PURGE BACKUP IMAGES;
+ {
+ /*
+ Check to see if user is attempting a delete when backupdir is
+ the same as datadir and a global wildcard is specified.
+ */
+ if ((my_strcasecmp(system_charset_info, sys_var_backupdir.value,
+ mysql_real_data_home) == 0) &&
+ (my_strcasecmp(system_charset_info, lex->backup_log_wild, "%") == 0))
+ {
+ my_error(ER_BACKUP_PURGE_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (sys_var_backupdir.value_length > 0)
+ res= logger.purge_backup_imgs(thd, lex->backup_log_wild, &num);
+ if (!res)
+ {
+ my_sprintf(buff, (buff, "%s %d.", ER(ER_BACKUP_IMAGES_DELETED), num));
+ my_ok(thd, 0, 0, buff);
+ }
+ break;
+ }
+
+ if (res)
+ goto error;
+
+ break;
+ }
#endif
case SQLCOM_SHOW_WARNS:
{
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2008-09-16 08:34:30 +0000
+++ b/sql/sql_yacc.yy 2008-10-18 22:08:01 +0000
@@ -819,6 +819,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
%token IDENT_QUOTED
%token IF
%token IGNORE_SYM
+%token IMAGES
%token IMPORT
%token INDEXES
%token INDEX_SYM
@@ -10796,7 +10797,36 @@ reset_option:
;
purge:
- PURGE
+ PURGE BACKUP_SYM LOGS_SYM
+ {
+ LEX *lex=Lex;
+ lex->type=0;
+ lex->sql_command = SQLCOM_PURGE_BUP_LOG;
+ }
+ purge_bup_log_option {}
+ | PURGE BACKUP_SYM IMAGES TEXT_STRING_sys
+ {
+ LEX *lex=Lex;
+ lex->type=0;
+ lex->backup_log_wild= $4.str;
+ lex->sql_command = SQLCOM_PURGE_BUP_IMG;
+ }
+ | PURGE BACKUP_SYM IMAGES BEFORE_SYM expr
+ {
+ LEX *lex=Lex;
+ lex->type=0;
+ lex->value_list.empty();
+ lex->value_list.push_front($5);
+ lex->sql_command= SQLCOM_PURGE_BUP_IMG_DATE;
+ }
+ | PURGE BACKUP_SYM IMAGES TO_SYM NUM_literal
+ {
+ LEX *lex= Lex;
+ lex->type=0;
+ lex->backup_id= (ulonglong)$5->val_int();
+ lex->sql_command= SQLCOM_PURGE_BUP_IMG_ID;
+ }
+ | PURGE
{
LEX *lex=Lex;
lex->type=0;
@@ -10804,8 +10834,24 @@ purge:
}
purge_options
{}
- ;
+ ;
+purge_bup_log_option:
+ {}
+ | BEFORE_SYM expr
+ {
+ LEX *lex= Lex;
+ lex->value_list.empty();
+ lex->value_list.push_front($2);
+ lex->sql_command= SQLCOM_PURGE_BUP_LOG_DATE;
+ }
+ | TO_SYM NUM_literal
+ {
+ LEX *lex= Lex;
+ lex->backup_id= (ulonglong)$2->val_int();
+ lex->sql_command= SQLCOM_PURGE_BUP_LOG_ID;
+ }
+ ;
purge_options:
master_or_binary LOGS_SYM purge_option
;
=== modified file 'storage/csv/ha_tina.cc'
--- a/storage/csv/ha_tina.cc 2008-08-15 18:34:18 +0000
+++ b/storage/csv/ha_tina.cc 2008-10-18 22:08:01 +0000
@@ -80,7 +80,6 @@ static handler *tina_create_handler(hand
TABLE_SHARE *table,
MEM_ROOT *mem_root);
-
/*****************************************************************************
** TINA tables
*****************************************************************************/
@@ -995,7 +994,11 @@ int ha_tina::delete_row(const uchar * bu
share->rows_recorded--;
pthread_mutex_unlock(&share->mutex);
- /* DELETE should never happen on the log table */
+ /*
+ DELETE should never happen on the log table
+ UNLESS it is a backup log table in which case extra() should be
+ called with HA_EXTRA_ALLOW_LOG_DELETE
+ */
DBUG_ASSERT(!share->is_log_table);
DBUG_RETURN(0);
@@ -1169,6 +1172,12 @@ int ha_tina::extra(enum ha_extra_functio
{
pthread_mutex_lock(&share->mutex);
share->is_log_table= TRUE;
+ pthread_mutex_unlock(&share->mutex);
+ }
+ else if (operation == HA_EXTRA_ALLOW_LOG_DELETE)
+ {
+ pthread_mutex_lock(&share->mutex);
+ share->is_log_table= FALSE;
pthread_mutex_unlock(&share->mutex);
}
DBUG_RETURN(0);
| Thread |
|---|
| • bzr commit into mysql-6.0-backup branch (cbell:2712) Bug#33364 | Chuck Bell | 19 Oct |