#At file:///C:/source/bzr/mysql-6.0-bug-33364/
2717 Chuck Bell 2008-10-24
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>;
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/kernel.cc
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
storage/csv/ha_tina.h
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/kernel.cc
Added debug sync point for test.
sql/log.cc
Added methods to allow purging of backup logs.
sql/log.h
Added method declarations to allow purging of backup logs.
sql/mysqld.cc
Added new command enums for 'the big switch'.
sql/share/errmsg.txt
New error messages.
sql/sql_lex.h
Added new command 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.
storage/csv/ha_tina.h
Added new attribute to allow delete from logs.
=== 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-24 20:50:03 +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-24 20:50:03 +0000
@@ -0,0 +1,342 @@
+SET @@global.log_backup_output = 'FILE,TABLE';
+PURGE BACKUP LOGS;
+
+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
+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
+Cleanup the logs and images for later testing.
+PURGE BACKUP LOGS;
+SET @@time_zone = '+00:00';
+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_log_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_log_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= now();
+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
+SET @@time_zone = @@global.time_zone;
+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 LOGS TO 501;
+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.
+SET @@global.log_backup_output = 'FILE';
+PURGE BACKUP LOGS TO 99999999;
+ERROR HY000: The operation could not be completed because the log_backup_output option
does not include writing to tables.
+PURGE BACKUP LOGS BEFORE @now_time;
+ERROR HY000: The operation could not be completed because the log_backup_output option
does not include writing to tables.
+SET @@global.log_backup_output = 'TABLE';
+Test backup and purge concurrent execution.
+PURGE BACKUP LOGS;
+Get rid of any lingering image files.
+SET DEBUG_SYNC= 'RESET';
+con2: Do some backups to add entries in logs.
+BACKUP DATABASE backup_logs to 'backup1.bak';
+backup_id
+#
+BACKUP DATABASE backup_logs to 'backup2.bak';
+backup_id
+#
+BACKUP DATABASE backup_logs to 'backup3.bak';
+backup_id
+#
+BACKUP DATABASE backup_logs to 'backup4.bak';
+backup_id
+#
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+4
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+24
+SET SESSION debug="d,set_backup_id";
+con2: Activate sync points for the backup statement.
+SET DEBUG_SYNC= 'before_backup_done SIGNAL ready WAIT_FOR proceed';
+BACKUP DATABASE backup_logs to 'backup5.bak';
+con1: Wait for the backup to be ready.
+SET DEBUG_SYNC= 'now WAIT_FOR ready';
+PURGE BACKUP LOGS;
+SET DEBUG_SYNC= 'now SIGNAL proceed';
+backup_id
+#
+SET SESSION debug="d";
+There should be one row in this table: the backup id from last
+backup (500).
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+1
+SELECT backup_id, command FROM mysql.backup_history;
+backup_id command
+500 BACKUP DATABASE backup_logs to 'backup5.bak'
+There should be one row in this table: the backup id from last
+backup (500). We should only see the complete progress
+statement because all others were deleted while backup was
+in progress.
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+1
+SELECT * FROM mysql.backup_progress;
+backup_id 500
+object backup kernel
+start_time #
+stop_time #
+total_bytes 0
+progress 0
+error_num 0
+notes complete
+Now do the same test for restore.
+RESTORE FROM 'backup1.bak';
+backup_id
+#
+RESTORE FROM 'backup2.bak';
+backup_id
+#
+RESTORE FROM 'backup3.bak';
+backup_id
+#
+RESTORE FROM 'backup4.bak';
+backup_id
+#
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+5
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+13
+con2: Activate sync points for the backup statement.
+SET DEBUG_SYNC= 'before_restore_done SIGNAL ready WAIT_FOR proceed';
+RESTORE FROM 'backup5.bak';
+con1: Wait for the backup to be ready.
+SET DEBUG_SYNC= 'now WAIT_FOR ready';
+PURGE BACKUP LOGS;
+SET DEBUG_SYNC= 'now SIGNAL proceed';
+backup_id
+#
+There should be one row in this table: the backup id from last
+backup (505).
+SELECT count(*) FROM mysql.backup_history;
+count(*)
+1
+SELECT backup_id, command FROM mysql.backup_history;
+backup_id command
+505 RESTORE FROM 'backup5.bak'
+There should be one row in this table: the backup id from last
+restore (505). We should only see the complete progress
+statement because all others were deleted while restore was
+in progress.
+SELECT count(*) FROM mysql.backup_progress;
+count(*)
+1
+SELECT * FROM mysql.backup_progress;
+backup_id 505
+object backup kernel
+start_time #
+stop_time #
+total_bytes 0
+progress 0
+error_num 0
+notes complete
+SET DEBUG_SYNC= 'RESET';
+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-24 20:50:03 +0000
@@ -0,0 +1,383 @@
+#
+# 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_sync.inc
+--source include/have_debug.inc
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+connection con1;
+
+SET @@global.log_backup_output = 'FILE,TABLE';
+
+PURGE BACKUP LOGS;
+
+--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 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;
+
+--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 @@time_zone = '+00:00';
+
+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_log_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_log_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= now();
+
+PURGE BACKUP LOGS BEFORE @now_time;
+
+SELECT backup_id FROM mysql.backup_history;
+SELECT DISTINCT backup_id FROM mysql.backup_progress;
+
+SET @@time_zone = @@global.time_zone;
+
+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 LOGS TO 501;
+
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+#
+# Test error conditions.
+#
+
+--error ER_BACKUP_PURGE_DATETIME
+PURGE BACKUP LOGS BEFORE 123123;
+
+SET @@global.log_backup_output = 'FILE';
+
+--error ER_BACKUP_LOG_OUTPUT
+PURGE BACKUP LOGS TO 99999999;
+
+--error ER_BACKUP_LOG_OUTPUT
+PURGE BACKUP LOGS BEFORE @now_time;
+
+SET @@global.log_backup_output = 'TABLE';
+
+#
+# Now test what happens when PURGE is run at the same
+# time as a backup or restore is run.
+#
+
+--echo Test backup and purge concurrent execution.
+
+PURGE BACKUP LOGS;
+
+--echo Get rid of any lingering image files.
+--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_logs_dir.bak
+
+SET DEBUG_SYNC= 'RESET';
+
+connection con1;
+
+--echo con2: Do some backups to add entries in logs.
+--replace_column 1 #
+BACKUP DATABASE backup_logs to 'backup1.bak';
+--replace_column 1 #
+BACKUP DATABASE backup_logs to 'backup2.bak';
+--replace_column 1 #
+BACKUP DATABASE backup_logs to 'backup3.bak';
+--replace_column 1 #
+BACKUP DATABASE backup_logs to 'backup4.bak';
+
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+connection con2;
+
+SET SESSION debug="d,set_backup_id";
+
+#
+# Stop backup after it has written rows to the logs but before
+# it writes the history log and last complete log entry.
+#
+--echo con2: Activate sync points for the backup statement.
+SET DEBUG_SYNC= 'before_backup_done SIGNAL ready WAIT_FOR proceed';
+SEND BACKUP DATABASE backup_logs to 'backup5.bak';
+
+connection con1;
+
+--echo con1: Wait for the backup to be ready.
+SET DEBUG_SYNC= 'now WAIT_FOR ready';
+
+PURGE BACKUP LOGS;
+
+SET DEBUG_SYNC= 'now SIGNAL proceed';
+
+connection con2;
+--replace_column 1 #
+reap;
+
+SET SESSION debug="d";
+
+connection con1;
+
+--echo There should be one row in this table: the backup id from last
+--echo backup (500).
+SELECT count(*) FROM mysql.backup_history;
+SELECT backup_id, command FROM mysql.backup_history;
+
+--echo There should be one row in this table: the backup id from last
+--echo backup (500). We should only see the complete progress
+--echo statement because all others were deleted while backup was
+--echo in progress.
+SELECT count(*) FROM mysql.backup_progress;
+--replace_column 3 # 4 #
+--query_vertical SELECT * FROM mysql.backup_progress
+
+--echo Now do the same test for restore.
+
+--replace_column 1 #
+RESTORE FROM 'backup1.bak';
+--replace_column 1 #
+RESTORE FROM 'backup2.bak';
+--replace_column 1 #
+RESTORE FROM 'backup3.bak';
+--replace_column 1 #
+RESTORE FROM 'backup4.bak';
+
+SELECT count(*) FROM mysql.backup_history;
+SELECT count(*) FROM mysql.backup_progress;
+
+connection con2;
+
+#
+# Stop restore after it has written rows to the logs but before
+# it writes the history log and last complete log entry.
+#
+--echo con2: Activate sync points for the backup statement.
+SET DEBUG_SYNC= 'before_restore_done SIGNAL ready WAIT_FOR proceed';
+SEND RESTORE FROM 'backup5.bak';
+
+connection con1;
+
+--echo con1: Wait for the backup to be ready.
+SET DEBUG_SYNC= 'now WAIT_FOR ready';
+
+PURGE BACKUP LOGS;
+
+SET DEBUG_SYNC= 'now SIGNAL proceed';
+
+connection con2;
+--replace_column 1 #
+reap;
+
+connection con1;
+
+--echo There should be one row in this table: the backup id from last
+--echo backup (505).
+SELECT count(*) FROM mysql.backup_history;
+SELECT backup_id, command FROM mysql.backup_history;
+
+--echo There should be one row in this table: the backup id from last
+--echo restore (505). We should only see the complete progress
+--echo statement because all others were deleted while restore was
+--echo in progress.
+SELECT count(*) FROM mysql.backup_progress;
+--replace_column 3 # 4 #
+--query_vertical SELECT * FROM mysql.backup_progress
+
+#
+# Cleanup.
+#
+
+SET DEBUG_SYNC= 'RESET';
+
+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/master-data/backup5.bak
+
+PURGE BACKUP LOGS;
+
+
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-10-21 16:35:31 +0000
+++ b/sql/backup/kernel.cc 2008-10-24 20:50:03 +0000
@@ -1243,6 +1243,8 @@ int Backup_restore_ctx::do_restore()
report_stats_post(info); // Never errors
+ DEBUG_SYNC(m_thd, "before_restore_done");
+
DBUG_RETURN(0);
}
=== modified file 'sql/log.cc'
--- a/sql/log.cc 2008-10-21 10:44:29 +0000
+++ b/sql/log.cc 2008-10-24 20:50:03 +0000
@@ -856,6 +856,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 BEFORE command.
+ */
+ DBUG_EXECUTE_IF("set_log_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 +1101,319 @@ err:
return result;
}
+/**
+ Delete the current row from a log table.
+
+ This method is used to delete a row from a log that is being
+ accessed as a table. The row to be deleted is the current
+ row that the handler is pointing to via tbl->record[0].
+
+ Note: The handler must be positioned correctly and the
+ tbl->record[0] must be fetched by the appropriate
+ method (e.g., hdl->next()). T
+
+ @param[IN] hdl Table handler.
+ @param[IN] tbl Table class.
+
+ @returns Result of delete operation
+*/
+static bool delete_current_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;
+}
+
+/**
+ Perform a table scan of the backup log tables for deleting rows.
+
+ This method is a helper method designed to delete rows from the
+ backup logs when the destination includes writing to tables.
+
+ The method is designed to work with either the backup_history or
+ backup_progress log. The log is specified using the @c log_name
+ parameter and show be either BACKUP_HISTORY_LOG_NAME or
+ BACKUP_PROGRESS_LOG_NAME.
+
+ There are three ways this method can be used. The choice of which of
+ the operations to run depends on the values of the parameters as stated.
+ 1. Delete rows whose backup_id is < a given value. This is used
+ for @c PURGE BACKUP LOGS TO <@c backup_id>.
+ To use this method, set @c backup_id to the value passed from the
+ command, @c delete_exact_row = FALSE, and @c datetime_val = 0.
+ 2. Delete rows in the log where backuip_id is == to a given value.
+ This is used when cascading deletes from the backup_history log
+ to the backup_progress log.
+ To use this method, set @c backup_id to the value of the rows needing
+ deletion, @c delete_exact_row = TRUE, and @c datetime_val = 0.
+ 3. Delete rows whose start_time < a given value. This is used for
+ @c PURGE BACKUP LOGS BEFORE <datetimeval>.
+ To use this method, set @c backup_id = 0, @c delete_exact_row = FALSE,
+ and @c datetime_val = value passed from the command.
+
+ @param[IN] THD The current thread
+ @param[IN] log_name is used to determine which log to process
+ (backup_history or backup_progress).
+ @param[IN] backup_id value specified on the command for deleting
+ rows by id or value for exact match
+ @param[IN] datetime_val value specified on the command for deleting
+ rows by date
+ @param[IN] delete_exact_row if TRUE, use the comparison for the log
+ column backup_id_in_row == @c backup_id else
+ use the comparison backup_id_in_row <
+ @c backup_id.
+ @param[OUT] num Number of rows affected.
+
+ @returns TRUE if error
+*/
+static bool scan_backup_log(THD *thd,
+ LEX_STRING log_name,
+ ulonglong backup_id,
+ my_time_t datetime_val,
+ 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;
+ MYSQL_TIME tmp;
+
+ DBUG_ASSERT((my_strcasecmp(system_charset_info, log_name.str,
+ BACKUP_HISTORY_LOG_NAME.str) == 0) ||
+ (my_strcasecmp(system_charset_info, log_name.str,
+ BACKUP_PROGRESS_LOG_NAME.str) == 0));
+
+ /*
+ 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);
+
+ /*
+ Get time zone conversion of value to compare for
+ PURGE BACKUP LOGS BEFORE <datetime>.
+ */
+ bzero(&tmp, sizeof(MYSQL_TIME));
+ thd->variables.time_zone->gmt_sec_to_TIME(&tmp, datetime_val);
+
+ /*
+ Begin table scan.
+ */
+ result= hdl->ha_rnd_init(1);
+ result= hdl->rnd_next(tbl->record[0]);
+ while (result != HA_ERR_END_OF_FILE)
+ {
+ // backup_id is field number 0 in both logs.
+ ulonglong id= tbl->field[0]->val_int();
+ /*
+ Delete by id.
+ */
+ if (backup_id)
+ {
+ /*
+ Here we delete a specific row. For example, a specific
+ row in the backup_history log as the result of a
+ cascade delete initiated from backup_history log.
+ */
+ if (delete_exact_row)
+ {
+ if (id == backup_id) // delete log row
+ {
+ result= delete_current_log_row(hdl, tbl);
+ if (result)
+ goto error;
+ }
+ }
+ /*
+ Here we execute the delete all rows 'to' a specific
+ id, e.g. PURGE BACKUP LOGS TO 17 -- we delete id < 17.
+ */
+ else if (id < backup_id)
+ {
+ result= delete_current_log_row(hdl, tbl);
+ if (result)
+ goto error;
+ num_rows++;
+ }
+ }
+ /*
+ Delete by date
+ */
+ else
+ {
+ MYSQL_TIME time;
+
+ tbl->field[ET_OBH_FIELD_START_TIME]->get_date(&time, TIME_NO_ZERO_DATE);
+ if (my_time_compare(&time, &tmp) < 0)
+ {
+ /*
+ 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,
+ id, datetime_val, TRUE, &r);
+ }
+ result= delete_current_log_row(hdl, tbl);
+ if (result)
+ goto error;
+ num_rows++;
+ }
+ }
+ result= hdl->rnd_next(tbl->record[0]);
+ }
+error:
+ 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.
+
+ @param[IN] THD The current thread
+ @param[IN] backup_id Value for backup id
+ @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,
+ 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,
+ backup_id, t, FALSE, rows);
+ if (res)
+ goto err;
+ }
+ if (opt_backup_progress_log && (log_backup_output_options & LOG_TABLE))
+ {
+ int r= 0; //ignore this for progress log
+
+ res= scan_backup_log(thd, BACKUP_PROGRESS_LOG_NAME,
+ backup_id, t, 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.
+
+ Note: This method uses cascading delete functionality to remove
+ rows from the backup_progress log when rows from the
+ backup_history log are removed. Thus, only one call to delete
+ rows from the logs is necessary.
+
+ @param[IN] THD The current thread
+ @param[IN] t Value for start datetime
+ @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,
+ 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,
+ 0, t, FALSE, rows);
+ return res;
+}
+
+
bool Log_to_csv_event_handler::
log_error(enum loglevel level, const char *format, va_list args)
{
@@ -1256,12 +1574,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 +1772,91 @@ 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, 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, rows);
+
+ DBUG_RETURN(res);
+}
/*
Log slow query with all enabled log event handlers
=== modified file 'sql/log.h'
--- a/sql/log.h 2008-10-21 10:44:29 +0000
+++ b/sql/log.h 2008-10-24 20:50:03 +0000
@@ -268,6 +268,14 @@ private:
time_t last_time;
};
+/*
+ Values for the type enum. This reflects the order of the enum
+ declaration in the PURGE BACKUP LOGS command.
+*/
+#define TYPE_ENUM_PURGE_BACKUP_LOGS 1
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_ID 2
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_DATE 3
+
class MYSQL_BACKUP_LOG: public MYSQL_LOG
{
public:
@@ -307,6 +315,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);
@@ -577,6 +587,13 @@ 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,
+ int *rows);
+ bool purge_backup_logs_before_date(THD *thd,
+ my_time_t t,
+ int *rows);
int activate_log(THD *thd, uint log_type);
@@ -630,6 +647,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; }
@@ -684,6 +702,13 @@ 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);
/* 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-10-13 14:10:00 +0000
+++ b/sql/mysqld.cc 2008-10-24 20:50:03 +0000
@@ -3242,6 +3242,7 @@ 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},
{"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-24 20:50:03 +0000
@@ -6404,4 +6404,11 @@ 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_PURGE_DATETIME
+ eng "The datetime specified is invalid for the '%-.64s' command."
+ER_BACKUP_LOGS_DELETED
+ eng "Backup log entries deleted: "
+ER_BACKUP_LOGS_TRUNCATED
+ eng "All backup logs entries have been deleted"
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h 2008-10-07 22:05:23 +0000
+++ b/sql/sql_lex.h 2008-10-24 20:50:03 +0000
@@ -89,7 +89,7 @@ enum enum_sql_command {
SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER,
SQLCOM_RENAME_TABLE,
SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
- SQLCOM_SHOW_OPEN_TABLES,
+ SQLCOM_PURGE_BUP_LOG, SQLCOM_SHOW_OPEN_TABLES,
SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,
SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,
@@ -1529,6 +1529,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-10-08 11:46:49 +0000
+++ b/sql/sql_parse.cc 2008-10-24 20:50:03 +0000
@@ -2100,6 +2100,97 @@ 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:
+ {
+ char buff[256];
+ int num= 0;
+ res= 0;
+
+ if (check_global_access(thd, SUPER_ACL))
+ goto error;
+
+ /*
+ 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 (((lex->type == TYPE_ENUM_PURGE_BACKUP_LOGS_ID) ||
+ (lex->type == TYPE_ENUM_PURGE_BACKUP_LOGS_DATE)) &&
+ (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;
+ }
+
+ /*
+ Check the type of purge command and process accordingly.
+ */
+ switch (lex->type) {
+ case TYPE_ENUM_PURGE_BACKUP_LOGS:
+ {
+ if (sys_var_backupdir.value_length > 0)
+ res= logger.purge_backup_logs(thd);
+ break;
+ }
+ case TYPE_ENUM_PURGE_BACKUP_LOGS_ID:
+ {
+ res= logger.purge_backup_logs_before_id(thd, thd->lex->backup_id, &num);
+ break;
+ }
+ case TYPE_ENUM_PURGE_BACKUP_LOGS_DATE:
+ {
+ Item *it;
+
+ /*
+ Perform additional error checking for the
+ PURGE BACKUP LOGS BEFORE <date> command.
+ */
+ 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 to only emulate fix_fields, because we need only
+ value of constant
+ */
+ it->quick_fix_field();
+
+ if ((ulong)it->val_int() == 0)
+ {
+ my_error(ER_BACKUP_PURGE_DATETIME, MYF(0), "PURGE BACKUP LOGS BEFORE");
+ goto error;
+ }
+
+ my_time_t t= (ulong)it->val_int();
+
+ /*
+ Validate datetime.
+ */
+ res= logger.purge_backup_logs_before_date(thd, t, &num);
+ break;
+ }
+ }
+
+ /*
+ Check result.
+ */
+ if (res)
+ goto error;
+ if (lex->type == TYPE_ENUM_PURGE_BACKUP_LOGS)
+ my_sprintf(buff, (buff, "%s.", ER(ER_BACKUP_LOGS_TRUNCATED)));
+ else
+ my_sprintf(buff, (buff, "%s %d.", ER(ER_BACKUP_LOGS_DELETED), num));
+ my_ok(thd, num, 0, buff);
+ break;
+ }
#endif
case SQLCOM_SHOW_WARNS:
{
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2008-10-02 14:03:57 +0000
+++ b/sql/sql_yacc.yy 2008-10-24 20:50:03 +0000
@@ -826,6 +826,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
%token IDENT_QUOTED
%token IF
%token IGNORE_SYM
+%token IMAGES
%token IMPORT
%token INDEXES
%token INDEX_SYM
@@ -10813,10 +10814,32 @@ purge:
lex->type=0;
lex->sql_command = SQLCOM_PURGE;
}
- purge_options
- {}
- ;
+ purge_options {}
+ | PURGE BACKUP_SYM LOGS_SYM
+ {
+ LEX *lex=Lex;
+ lex->type=TYPE_ENUM_PURGE_BACKUP_LOGS;
+ lex->sql_command = SQLCOM_PURGE_BUP_LOG;
+ }
+ purge_bup_log_option {}
+ ;
+purge_bup_log_option:
+ {}
+ | TO_SYM NUM_literal
+ {
+ LEX *lex= Lex;
+ lex->backup_id= (ulonglong)$2->val_int();
+ lex->type=TYPE_ENUM_PURGE_BACKUP_LOGS_ID;
+ }
+ | BEFORE_SYM expr
+ {
+ LEX *lex= Lex;
+ lex->value_list.empty();
+ lex->value_list.push_front($2);
+ lex->type=TYPE_ENUM_PURGE_BACKUP_LOGS_DATE;
+ }
+ ;
purge_options:
master_or_binary LOGS_SYM purge_option
;
=== modified file 'storage/csv/ha_tina.cc'
--- a/storage/csv/ha_tina.cc 2008-08-23 00:18:35 +0000
+++ b/storage/csv/ha_tina.cc 2008-10-24 20:50:03 +0000
@@ -80,7 +80,6 @@ static handler *tina_create_handler(hand
TABLE_SHARE *table,
MEM_ROOT *mem_root);
-
/*****************************************************************************
** TINA tables
*****************************************************************************/
@@ -169,6 +168,7 @@ static TINA_SHARE *get_share(const char
share->update_file_opened= FALSE;
share->tina_write_opened= FALSE;
share->data_file_version= 0;
+ share->allow_log_delete= FALSE;
strmov(share->table_name, table_name);
fn_format(share->data_file_name, table_name, "", CSV_EXT,
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
@@ -995,8 +995,13 @@ int ha_tina::delete_row(const uchar * bu
share->rows_recorded--;
pthread_mutex_unlock(&share->mutex);
- /* DELETE should never happen on the log table */
- DBUG_ASSERT(!share->is_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 then reset with
+ HA_EXTRA_MARK_AS_LOG_TABLE.
+ */
+ DBUG_ASSERT(!share->is_log_table || share->allow_log_delete);
DBUG_RETURN(0);
}
@@ -1169,6 +1174,13 @@ int ha_tina::extra(enum ha_extra_functio
{
pthread_mutex_lock(&share->mutex);
share->is_log_table= TRUE;
+ share->allow_log_delete= FALSE;
+ pthread_mutex_unlock(&share->mutex);
+ }
+ else if (operation == HA_EXTRA_ALLOW_LOG_DELETE)
+ {
+ pthread_mutex_lock(&share->mutex);
+ share->allow_log_delete= TRUE;
pthread_mutex_unlock(&share->mutex);
}
DBUG_RETURN(0);
=== modified file 'storage/csv/ha_tina.h'
--- a/storage/csv/ha_tina.h 2008-06-28 11:00:59 +0000
+++ b/storage/csv/ha_tina.h 2008-10-24 20:50:03 +0000
@@ -50,6 +50,7 @@ typedef struct st_tina_share {
bool crashed; /* Meta file is crashed */
ha_rows rows_recorded; /* Number of rows in tables */
uint data_file_version; /* Version of the data file used */
+ bool allow_log_delete;
} TINA_SHARE;
struct tina_set {
| Thread |
|---|
| • bzr commit into mysql-6.0 branch (cbell:2717) Bug#33364 | Chuck Bell | 24 Oct |