Below is the list of changes that have just been committed into a local
5.2 repository of cbell. When cbell does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-06-27 14:55:49-04:00, cbell@mysql_cab_desk. +11 -0
WL#3570 : Default algorithm backup & restore (blocking)
This patch implements the default backup blocking algorithm. The patch
also includes enhancements the kernel for better open tables behavior
for backup and better open and lock methods for restore.
The patch also contains the Buffer_iterator class and header files. The
Buffer_iterator class is used to buffer data during read and write that
is larger than the kernel's stream buffer.
mysql-test/r/backup.result@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +430 -0
WL#3570 : Default algorithm backup & restore (blocking)
This patch adds the results file for the basic backup and default
backup test.
mysql-test/r/backup.result@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +0 -0
mysql-test/t/backup.test@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +288 -0
WL#3570 : Default algorithm backup & restore (blocking)
This patch adds the basic backup and default backup test.
mysql-test/t/backup.test@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +0 -0
sql/CMakeLists.txt@stripped, 2007-06-27 14:55:41-04:00, cbell@mysql_cab_desk. +2 -1
WL#3570 : Default backup algorithm
This patch adds the default_backup and buffer_iterator classes to the
cmake input file.
sql/backup/Makefile.am@stripped, 2007-06-27 14:55:42-04:00, cbell@mysql_cab_desk. +6 -2
WL#3570 : Default backup algorithm
This patch adds the default_backup and buffer_iterator classes to the
make input file.
sql/backup/archive.cc@stripped, 2007-06-27 14:55:42-04:00, cbell@mysql_cab_desk. +4 -1
WL#3570 : Default backup algorithm
This patch adds the default driver to the stream creation code in the
kernel.
sql/backup/be_default.cc@stripped, 2007-06-27 14:55:43-04:00, cbell@mysql_cab_desk. +862 -0
WL#3570 : Default backup algorithm backup & restore
This patch adds a buffering mechanism to read and write operations. The
Buffer_iterator class used by the default driver algorithm provides
a mechanism to store the record buffer and blob fields and split the
data into smaller pieces for reading (backup) and to reassemble them
for writing (restore).
sql/backup/be_default.cc@stripped, 2007-06-27 14:55:43-04:00, cbell@mysql_cab_desk. +0 -0
sql/backup/be_default.h@stripped, 2007-06-27 14:55:44-04:00, cbell@mysql_cab_desk. +242 -0
WL#3570 : Default backup algorithm backup & restore
This patch adds a Buffer_iterator class used by the default driver
algorithm. The patch also includes numerous documentation updates for
doxygen formatting.
sql/backup/be_default.h@stripped, 2007-06-27 14:55:44-04:00, cbell@mysql_cab_desk. +0 -0
sql/backup/buffer_iterator.cc@stripped, 2007-06-27 14:55:44-04:00, cbell@mysql_cab_desk. +201 -0
WL#3570 : Default algorithm backup & restore (blocking)
This patch adds the buffer_iterator class file for the Buffer_iterator
class. This class can be used to buffer data into smaller chunks during
the backup operation or recombine them during the restore operation.
sql/backup/buffer_iterator.cc@stripped, 2007-06-27 14:55:44-04:00, cbell@mysql_cab_desk. +0 -0
sql/backup/buffer_iterator.h@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +90 -0
WL#3570 : Default algorithm backup & restore (blocking)
This patch adds the buffer_iterator header file for the Buffer_iterator
class. This class can be used to buffer data into smaller chunks during
the backup operation or recombine them during the restore operation.
sql/backup/buffer_iterator.h@stripped, 2007-06-27 14:55:45-04:00, cbell@mysql_cab_desk. +0 -0
sql/backup/data_backup.cc@stripped, 2007-06-27 14:55:43-04:00, cbell@mysql_cab_desk. +70 -1
WL#3570 : Default backup algorithm
This patch adds the default driver to the write_table_data and
restore_table_data code in the kernel.
In write_table_data, the close_thread_tables has been placed to close
the tables for the default driver.
In restore_table_data, the code was modified to open and lock the
tables and close the tables in the kernel instead of the driver. This
allows the default driver to work with the snapshot driver.
sql/backup/sql_backup.cc@stripped, 2007-06-27 14:55:43-04:00, cbell@mysql_cab_desk. +21 -0
WL#3570 : Default backup algorithm
This patch adds the default driver to find image logic in the kernel.
It allows the default driver to be used if no other drivers accept
the table.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: cbell
# Host: mysql_cab_desk.
# Root: C:/source/c++/mysql-5.2_WL_3570
--- New file ---
+++ mysql-test/r/backup.result 07/06/27 14:55:45
DROP DATABASE IF EXISTS db1;
Warnings:
Note 1008 Can't drop database 'db1'; database doesn't exist
DROP DATABASE IF EXISTS db2;
Warnings:
Note 1008 Can't drop database 'db2'; database doesn't exist
CREATE DATABASE db1;
CREATE DATABASE db2;
USE db1;
DROP TABLE IF EXISTS `building`;
Warnings:
Note 1051 Unknown table 'building'
CREATE TABLE `building` (
`dir_code` char(4),
`building` char(6)
) ENGINE=archive DEFAULT CHARSET=latin1;
LOCK TABLES `building` WRITE;
INSERT INTO `building` VALUES ('N41','1300'),('N01','1453'),('M00','1000'),('N41','1301'),('N41','1305');
UNLOCK TABLES;
DROP TABLE IF EXISTS `directorate`;
Warnings:
Note 1051 Unknown table 'directorate'
CREATE TABLE `directorate` (
`dir_code` char(4),
`dir_name` char(30),
`dir_head_id` char(9)
) ENGINE=archive DEFAULT CHARSET=latin1;
LOCK TABLES `directorate` WRITE;
INSERT INTO `directorate` VALUES ('N41','Development','333445555'),('N01','Human Resources','123654321'),('M00','Management','333444444');
UNLOCK TABLES;
USE db2;
DROP TABLE IF EXISTS `staff`;
Warnings:
Note 1051 Unknown table 'staff'
CREATE TABLE `staff` (
`id` char(9),
`first_name` char(20),
`mid_name` char(20),
`last_name` char(30),
`sex` char(1),
`salary` int(11),
`mgr_id` char(9)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `staff` WRITE;
INSERT INTO `staff` VALUES ('333445555','John','Q','Smith','M',30000,'333444444'),('123763153','William','E','Walters','M',25000,'123654321'),('333444444','Alicia','F','St.Cruz','F',25000,NULL),('921312388','Goy','X','Hong','F',40000,'123654321'),('800122337','Rajesh','G','Kardakarna','M',38000,'333445555'),('820123637','Monty','C','Smythe','M',38000,'333445555'),('830132335','Richard','E','Jones','M',38000,'333445555'),('333445665','Edward','E','Engles','M',25000,'333445555'),('123654321','Beware','D','Borg','F',55000,'333444444'),('123456789','Wilma','N','Maxima','F',43000,'333445555');
UNLOCK TABLES;
DROP TABLE IF EXISTS `tasking`;
Warnings:
Note 1051 Unknown table 'tasking'
CREATE TABLE `tasking` (
`id` char(9),
`project_number` char(9),
`hours_worked` double(10,2)
) ENGINE=blackhole DEFAULT CHARSET=latin1;
LOCK TABLES `tasking` WRITE;
INSERT INTO `tasking` VALUES ('333445555','405',23),('123763153','405',33.5),('921312388','601',44),('800122337','300',13),('820123637','300',9.5),('830132335','401',8.5),('333445555','300',11),('921312388','500',13),('800122337','300',44),('820123637','401',500.5),('830132335','400',12),('333445665','600',300.25),('123654321','607',444.75),('123456789','300',1000);
UNLOCK TABLES;
BACKUP DATABASE db1,db2 TO 'test.ba';
Backup Summary
header = 52 bytes
meta-data = 807 bytes
data = 1217 bytes
--------------
total 2076 bytes
DROP DATABASE db1;
DROP DATABASE db2;
USE mysql;
RESTORE FROM 'test.ba';
Restore Summary
header = 52 bytes
meta-data = 807 bytes
data = 1217 bytes
--------------
total 2076 bytes
USE db1;
SHOW TABLES;
Tables_in_db1
building
directorate
SELECT * FROM building;
dir_code building
N41 1300
N01 1453
M00 1000
N41 1301
N41 1305
SELECT * FROM directorate;
dir_code dir_name dir_head_id
N41 Development 333445555
N01 Human Resources 123654321
M00 Management 333444444
USE db2;
SHOW TABLES;
Tables_in_db2
staff
tasking
SELECT * FROM staff;
id first_name mid_name last_name sex salary mgr_id
333445555 John Q Smith M 30000 333444444
123763153 William E Walters M 25000 123654321
333444444 Alicia F St.Cruz F 25000 NULL
921312388 Goy X Hong F 40000 123654321
800122337 Rajesh G Kardakarna M 38000 333445555
820123637 Monty C Smythe M 38000 333445555
830132335 Richard E Jones M 38000 333445555
333445665 Edward E Engles M 25000 333445555
123654321 Beware D Borg F 55000 333444444
123456789 Wilma N Maxima F 43000 333445555
SHOW CREATE TABLE tasking;
Table Create Table
tasking CREATE TABLE `tasking` (
`id` char(9) DEFAULT NULL,
`project_number` char(9) DEFAULT NULL,
`hours_worked` double(10,2) DEFAULT NULL
) ENGINE=BLACKHOLE DEFAULT CHARSET=latin1
DROP DATABASE IF EXISTS db1;
DROP DATABASE IF EXISTS db2;
DROP DATABASE IF EXISTS bup_default;
CREATE DATABASE bup_default;
CREATE TABLE bup_default.wide (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` char(255) DEFAULT NULL,
`c` char(255) DEFAULT NULL,
`d` char(255) DEFAULT NULL,
`e` char(255) DEFAULT NULL,
`f` char(255) DEFAULT NULL,
`g` char(255) DEFAULT NULL,
`h` char(255) DEFAULT NULL,
`i` char(255) DEFAULT NULL,
`j` char(255) DEFAULT NULL,
`k` char(255) DEFAULT NULL,
`l` char(255) DEFAULT NULL,
`m` char(255) DEFAULT NULL,
`n` char(255) DEFAULT NULL,
`o` char(255) DEFAULT NULL,
`p` char(255) DEFAULT NULL,
`q` TEXT,
PRIMARY KEY (`a`)
) ENGINE=myisam DEFAULT CHARSET=latin1;
CREATE TABLE bup_default.t1 (a int) engine=myisam;
CREATE TABLE bup_default.t2 (a int) engine=myisam;
CREATE TABLE bup_default.t1_blob (a int, b text);
INSERT INTO bup_default.wide VALUES (
NULL,
"This is column b pass 01",
"This is column c pass 01",
"This is column d pass 01",
"This is column e pass 01",
"This is column f pass 01",
"This is column g pass 01",
"This is column h pass 01",
"This is column i pass 01",
"This is column j pass 01",
"This is column k pass 01",
"This is column l pass 01",
"This is column m pass 01",
"This is column n pass 01",
"This is column o pass 01",
"This is column p pass 01",
"Running the server in debug:
linux:/home/Chuck # mysqld-debug -u root --debug=d,enter,exit:t:F:L:g:O,/tmp/mys
qld.trace &
[2] 7492
[1] Done kwrite /tmp/mysqld.trace
linux:/home/Chuck # 060601 21:18:45 InnoDB: Started; log sequence number 0 4640
3
060601 21:18:45 [Note] mysqld-debug: ready for connections.
Version: '5.1.9-beta-debug-log' socket: '/var/lib/mysql/mysql.sock' port: 3306
MySQL Community Server - Debug (GPL)
060601 21:18:57 [Note] mysqld-debug: Normal shutdown
060601 21:18:59 InnoDB: Starting shutdown...
060601 21:19:01 InnoDB: Shutdown completed; log sequence number 0 46403
060601 21:19:01 [Note] mysqld-debug: Shutdown complete
Running the client:
Chuck@linux:~> mysql -uroot -p
Enter password:");
INSERT INTO bup_default.t1 VALUES (1);
INSERT INTO bup_default.t1 VALUES (2);
INSERT INTO bup_default.t1 VALUES (3);
INSERT INTO bup_default.t1 VALUES (4);
INSERT INTO bup_default.t2 VALUES (1);
INSERT INTO bup_default.t2 VALUES (2);
INSERT INTO bup_default.t2 VALUES (3);
INSERT INTO bup_default.t2 VALUES (4);
INSERT INTO bup_default.t1_blob VALUES (1,"Short text will fit in buffer.");
INSERT INTO bup_default.t1_blob VALUES (2,"Replication Failover
--------------------
One of the greatest advantages of MySQL replication is the ability to failover in the event of a server crash. More specifically, if you need to take your master server offline you can promote one of your slaves as a master and thereby minimize the interruption to your users.
Promoting a Slave to a Master
-----------------------------
When your master fails beyond repair, you can quickly replace it with your slave and reestablish service to your databases and applications. The process for promoting a slave to a master involves taking the master offline (if not already), flushing the logs and safely shutting down the slave, then restarting the slave to run as the master. A simplified process is shown below.
1. Lock the tables on your master.
2. Record the location of the binlog on the master for point in time recovery (if needed).
3. Flush the logs on the slave.
4. Shutdown the master.
5. Shutdown the slave you want to promote.
6. Restart the slave specifying the startup options for the master.
Notes
-----
Your configuration may require slightly differing steps in the process to match your environment. Things to consider include how the applications connect to the server, where the data is store (NAS or other detached storage), and the mode of replication among your master and slave(s).
Remember to keep the server_id the same as it was when the promoted slave was a slave!
If you have applications that use embedded hostnames or IP addresses in their connections to the master, you have two choices; 1) you can change the slave?s hostname and IP to that of the master and restart the server, or 2) you can redirect your clients to the promoted slave.
Failover and Invoked Objects
----------------------------
For most applications, the failover sequence described above will get you a viable master and return your database system to service. If you use invoked objects, specifically events, the promotion of the slave to a master involves some additional steps.
If you are unsure if you have any events that are replicated, you can issue the following command on the slave (you need to have privileges to run the show commands):
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
This query will list all of the events on the slave that have been replicated FROM the master. You will also see a column named ORIGINATOR that lists the server_id of the originating master. Together with the STATUS column, you can quickly determine which events need to be activated on the newly promoted slave.
To turn the events on, you can create and run the following stored procedure:
NORMAL END!");
SELECT * FROM bup_default.t1;
a
1
2
3
4
SELECT * FROM bup_default.t2;
a
1
2
3
4
SELECT COUNT(*) FROM bup_default.t1_blob;
COUNT(*)
2
SELECT * FROM bup_default.t1_blob;;
a 1
b Short text will fit in buffer.
a 2
b Replication Failover
--------------------
One of the greatest advantages of MySQL replication is the ability to failover in the event of a server crash. More specifically, if you need to take your master server offline you can promote one of your slaves as a master and thereby minimize the interruption to your users.
Promoting a Slave to a Master
-----------------------------
When your master fails beyond repair, you can quickly replace it with your slave and reestablish service to your databases and applications. The process for promoting a slave to a master involves taking the master offline (if not already), flushing the logs and safely shutting down the slave, then restarting the slave to run as the master. A simplified process is shown below.
1. Lock the tables on your master.
2. Record the location of the binlog on the master for point in time recovery (if needed).
3. Flush the logs on the slave.
4. Shutdown the master.
5. Shutdown the slave you want to promote.
6. Restart the slave specifying the startup options for the master.
Notes
-----
Your configuration may require slightly differing steps in the process to match your environment. Things to consider include how the applications connect to the server, where the data is store (NAS or other detached storage), and the mode of replication among your master and slave(s).
Remember to keep the server_id the same as it was when the promoted slave was a slave!
If you have applications that use embedded hostnames or IP addresses in their connections to the master, you have two choices; 1) you can change the slave?s hostname and IP to that of the master and restart the server, or 2) you can redirect your clients to the promoted slave.
Failover and Invoked Objects
----------------------------
For most applications, the failover sequence described above will get you a viable master and return your database system to service. If you use invoked objects, specifically events, the promotion of the slave to a master involves some additional steps.
If you are unsure if you have any events that are replicated, you can issue the following command on the slave (you need to have privileges to run the show commands):
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
This query will list all of the events on the slave that have been replicated FROM the master. You will also see a column named ORIGINATOR that lists the server_id of the originating master. Together with the STATUS column, you can quickly determine which events need to be activated on the newly promoted slave.
To turn the events on, you can create and run the following stored procedure:
NORMAL END!
SELECT COUNT(*) FROM bup_default.wide;
COUNT(*)
1
SELECT * FROM bup_default.wide;;
a 1
b This is column b pass 01
c This is column c pass 01
d This is column d pass 01
e This is column e pass 01
f This is column f pass 01
g This is column g pass 01
h This is column h pass 01
i This is column i pass 01
j This is column j pass 01
k This is column k pass 01
l This is column l pass 01
m This is column m pass 01
n This is column n pass 01
o This is column o pass 01
p This is column p pass 01
q Running the server in debug:
linux:/home/Chuck # mysqld-debug -u root --debug=d,enter,exit:t:F:L:g:O,/tmp/mys
qld.trace &
[2] 7492
[1] Done kwrite /tmp/mysqld.trace
linux:/home/Chuck # 060601 21:18:45 InnoDB: Started; log sequence number 0 4640
3
060601 21:18:45 [Note] mysqld-debug: ready for connections.
Version: '5.1.9-beta-debug-log' socket: '/var/lib/mysql/mysql.sock' port: 3306
MySQL Community Server - Debug (GPL)
060601 21:18:57 [Note] mysqld-debug: Normal shutdown
060601 21:18:59 InnoDB: Starting shutdown...
060601 21:19:01 InnoDB: Shutdown completed; log sequence number 0 46403
060601 21:19:01 [Note] mysqld-debug: Shutdown complete
Running the client:
Chuck@linux:~> mysql -uroot -p
Enter password:
BACKUP DATABASE bup_default TO "bup_default.bak";
Backup Summary
header = 40 bytes
meta-data = 890 bytes
data = 7425 bytes
--------------
total 8355 bytes
DROP DATABASE bup_default;
RESTORE FROM "bup_default.bak";
Restore Summary
header = 40 bytes
meta-data = 890 bytes
data = 7425 bytes
--------------
total 8355 bytes
SELECT * FROM bup_default.t1;
a
1
2
3
4
SELECT * FROM bup_default.t2;
a
1
2
3
4
SELECT COUNT(*) FROM bup_default.t1_blob;
COUNT(*)
2
SELECT * FROM bup_default.t1_blob;;
a 1
b Short text will fit in buffer.
a 2
b Replication Failover
--------------------
One of the greatest advantages of MySQL replication is the ability to failover in the event of a server crash. More specifically, if you need to take your master server offline you can promote one of your slaves as a master and thereby minimize the interruption to your users.
Promoting a Slave to a Master
-----------------------------
When your master fails beyond repair, you can quickly replace it with your slave and reestablish service to your databases and applications. The process for promoting a slave to a master involves taking the master offline (if not already), flushing the logs and safely shutting down the slave, then restarting the slave to run as the master. A simplified process is shown below.
1. Lock the tables on your master.
2. Record the location of the binlog on the master for point in time recovery (if needed).
3. Flush the logs on the slave.
4. Shutdown the master.
5. Shutdown the slave you want to promote.
6. Restart the slave specifying the startup options for the master.
Notes
-----
Your configuration may require slightly differing steps in the process to match your environment. Things to consider include how the applications connect to the server, where the data is store (NAS or other detached storage), and the mode of replication among your master and slave(s).
Remember to keep the server_id the same as it was when the promoted slave was a slave!
If you have applications that use embedded hostnames or IP addresses in their connections to the master, you have two choices; 1) you can change the slave?s hostname and IP to that of the master and restart the server, or 2) you can redirect your clients to the promoted slave.
Failover and Invoked Objects
----------------------------
For most applications, the failover sequence described above will get you a viable master and return your database system to service. If you use invoked objects, specifically events, the promotion of the slave to a master involves some additional steps.
If you are unsure if you have any events that are replicated, you can issue the following command on the slave (you need to have privileges to run the show commands):
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
This query will list all of the events on the slave that have been replicated FROM the master. You will also see a column named ORIGINATOR that lists the server_id of the originating master. Together with the STATUS column, you can quickly determine which events need to be activated on the newly promoted slave.
To turn the events on, you can create and run the following stored procedure:
NORMAL END!
SELECT COUNT(*) FROM bup_default.wide;
COUNT(*)
1
SELECT * FROM bup_default.wide;;
a 1
b This is column b pass 01
c This is column c pass 01
d This is column d pass 01
e This is column e pass 01
f This is column f pass 01
g This is column g pass 01
h This is column h pass 01
i This is column i pass 01
j This is column j pass 01
k This is column k pass 01
l This is column l pass 01
m This is column m pass 01
n This is column n pass 01
o This is column o pass 01
p This is column p pass 01
q Running the server in debug:
linux:/home/Chuck # mysqld-debug -u root --debug=d,enter,exit:t:F:L:g:O,/tmp/mys
qld.trace &
[2] 7492
[1] Done kwrite /tmp/mysqld.trace
linux:/home/Chuck # 060601 21:18:45 InnoDB: Started; log sequence number 0 4640
3
060601 21:18:45 [Note] mysqld-debug: ready for connections.
Version: '5.1.9-beta-debug-log' socket: '/var/lib/mysql/mysql.sock' port: 3306
MySQL Community Server - Debug (GPL)
060601 21:18:57 [Note] mysqld-debug: Normal shutdown
060601 21:18:59 InnoDB: Starting shutdown...
060601 21:19:01 InnoDB: Shutdown completed; log sequence number 0 46403
060601 21:19:01 [Note] mysqld-debug: Shutdown complete
Running the client:
Chuck@linux:~> mysql -uroot -p
Enter password:
DROP DATABASE IF EXISTS bup_default;
--- New file ---
+++ mysql-test/t/backup.test 07/06/27 14:55:45
connect (backup,localhost,root,,);
connect (restore,localhost,root,,);
connection backup;
DROP DATABASE IF EXISTS db1;
DROP DATABASE IF EXISTS db2;
CREATE DATABASE db1;
CREATE DATABASE db2;
USE db1;
# SHOW ENGINES;
#SET @@storage_engine = 'archive';
DROP TABLE IF EXISTS `building`;
CREATE TABLE `building` (
`dir_code` char(4),
`building` char(6)
) ENGINE=archive DEFAULT CHARSET=latin1;
--
-- Dumping data for table `building`
--
LOCK TABLES `building` WRITE;
INSERT INTO `building` VALUES ('N41','1300'),('N01','1453'),('M00','1000'),('N41','1301'),('N41','1305');
UNLOCK TABLES;
--
-- Table structure for table `directorate`
--
DROP TABLE IF EXISTS `directorate`;
CREATE TABLE `directorate` (
`dir_code` char(4),
`dir_name` char(30),
`dir_head_id` char(9)
) ENGINE=archive DEFAULT CHARSET=latin1;
--
-- Dumping data for table `directorate`
--
LOCK TABLES `directorate` WRITE;
INSERT INTO `directorate` VALUES ('N41','Development','333445555'),('N01','Human Resources','123654321'),('M00','Management','333444444');
UNLOCK TABLES;
USE db2;
--
-- Table structure for table `staff`
--
DROP TABLE IF EXISTS `staff`;
CREATE TABLE `staff` (
`id` char(9),
`first_name` char(20),
`mid_name` char(20),
`last_name` char(30),
`sex` char(1),
`salary` int(11),
`mgr_id` char(9)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `staff`
--
LOCK TABLES `staff` WRITE;
INSERT INTO `staff` VALUES ('333445555','John','Q','Smith','M',30000,'333444444'),('123763153','William','E','Walters','M',25000,'123654321'),('333444444','Alicia','F','St.Cruz','F',25000,NULL),('921312388','Goy','X','Hong','F',40000,'123654321'),('800122337','Rajesh','G','Kardakarna','M',38000,'333445555'),('820123637','Monty','C','Smythe','M',38000,'333445555'),('830132335','Richard','E','Jones','M',38000,'333445555'),('333445665','Edward','E','Engles','M',25000,'333445555'),('123654321','Beware','D','Borg','F',55000,'333444444'),('123456789','Wilma','N','Maxima','F',43000,'333445555');
UNLOCK TABLES;
--
-- Table structure for table `tasking`
--
DROP TABLE IF EXISTS `tasking`;
CREATE TABLE `tasking` (
`id` char(9),
`project_number` char(9),
`hours_worked` double(10,2)
) ENGINE=blackhole DEFAULT CHARSET=latin1;
--
-- Dumping data for table `tasking`
--
LOCK TABLES `tasking` WRITE;
INSERT INTO `tasking` VALUES ('333445555','405',23),('123763153','405',33.5),('921312388','601',44),('800122337','300',13),('820123637','300',9.5),('830132335','401',8.5),('333445555','300',11),('921312388','500',13),('800122337','300',44),('820123637','401',500.5),('830132335','400',12),('333445665','600',300.25),('123654321','607',444.75),('123456789','300',1000);
UNLOCK TABLES;
BACKUP DATABASE db1,db2 TO 'test.ba';
DROP DATABASE db1;
DROP DATABASE db2;
#Disabled until SHOW commands are implemented.
#SHOW BACKUP 'test.ba';
connection restore;
USE mysql;
RESTORE FROM 'test.ba';
USE db1;
SHOW TABLES;
SELECT * FROM building;
SELECT * FROM directorate;
USE db2;
SHOW TABLES;
SELECT * FROM staff;
SHOW CREATE TABLE tasking;
DROP DATABASE IF EXISTS db1;
DROP DATABASE IF EXISTS db2;
# This test is for the default online backup driver
# The test is designed to show that a consistent snapshot
# backup can be taken while data is being inserted and deleted.
#
connection backup;
--disable_warnings
DROP DATABASE IF EXISTS bup_default;
--enable_warnings
CREATE DATABASE bup_default;
# Create tables and load with data.
CREATE TABLE bup_default.wide (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` char(255) DEFAULT NULL,
`c` char(255) DEFAULT NULL,
`d` char(255) DEFAULT NULL,
`e` char(255) DEFAULT NULL,
`f` char(255) DEFAULT NULL,
`g` char(255) DEFAULT NULL,
`h` char(255) DEFAULT NULL,
`i` char(255) DEFAULT NULL,
`j` char(255) DEFAULT NULL,
`k` char(255) DEFAULT NULL,
`l` char(255) DEFAULT NULL,
`m` char(255) DEFAULT NULL,
`n` char(255) DEFAULT NULL,
`o` char(255) DEFAULT NULL,
`p` char(255) DEFAULT NULL,
`q` TEXT,
PRIMARY KEY (`a`)
) ENGINE=myisam DEFAULT CHARSET=latin1;
CREATE TABLE bup_default.t1 (a int) engine=myisam;
CREATE TABLE bup_default.t2 (a int) engine=myisam;
CREATE TABLE bup_default.t1_blob (a int, b text);
# Insert some data.
INSERT INTO bup_default.wide VALUES (
NULL,
"This is column b pass 01",
"This is column c pass 01",
"This is column d pass 01",
"This is column e pass 01",
"This is column f pass 01",
"This is column g pass 01",
"This is column h pass 01",
"This is column i pass 01",
"This is column j pass 01",
"This is column k pass 01",
"This is column l pass 01",
"This is column m pass 01",
"This is column n pass 01",
"This is column o pass 01",
"This is column p pass 01",
"Running the server in debug:
linux:/home/Chuck # mysqld-debug -u root --debug=d,enter,exit:t:F:L:g:O,/tmp/mys
qld.trace &
[2] 7492
[1] Done kwrite /tmp/mysqld.trace
linux:/home/Chuck # 060601 21:18:45 InnoDB: Started; log sequence number 0 4640
3
060601 21:18:45 [Note] mysqld-debug: ready for connections.
Version: '5.1.9-beta-debug-log' socket: '/var/lib/mysql/mysql.sock' port: 3306
MySQL Community Server - Debug (GPL)
060601 21:18:57 [Note] mysqld-debug: Normal shutdown
060601 21:18:59 InnoDB: Starting shutdown...
060601 21:19:01 InnoDB: Shutdown completed; log sequence number 0 46403
060601 21:19:01 [Note] mysqld-debug: Shutdown complete
Running the client:
Chuck@linux:~> mysql -uroot -p
Enter password:");
INSERT INTO bup_default.t1 VALUES (1);
INSERT INTO bup_default.t1 VALUES (2);
INSERT INTO bup_default.t1 VALUES (3);
INSERT INTO bup_default.t1 VALUES (4);
INSERT INTO bup_default.t2 VALUES (1);
INSERT INTO bup_default.t2 VALUES (2);
INSERT INTO bup_default.t2 VALUES (3);
INSERT INTO bup_default.t2 VALUES (4);
INSERT INTO bup_default.t1_blob VALUES (1,"Short text will fit in buffer.");
INSERT INTO bup_default.t1_blob VALUES (2,"Replication Failover
--------------------
One of the greatest advantages of MySQL replication is the ability to failover in the event of a server crash. More specifically, if you need to take your master server offline you can promote one of your slaves as a master and thereby minimize the interruption to your users.
Promoting a Slave to a Master
-----------------------------
When your master fails beyond repair, you can quickly replace it with your slave and reestablish service to your databases and applications. The process for promoting a slave to a master involves taking the master offline (if not already), flushing the logs and safely shutting down the slave, then restarting the slave to run as the master. A simplified process is shown below.
1. Lock the tables on your master.
2. Record the location of the binlog on the master for point in time recovery (if needed).
3. Flush the logs on the slave.
4. Shutdown the master.
5. Shutdown the slave you want to promote.
6. Restart the slave specifying the startup options for the master.
Notes
-----
Your configuration may require slightly differing steps in the process to match your environment. Things to consider include how the applications connect to the server, where the data is store (NAS or other detached storage), and the mode of replication among your master and slave(s).
Remember to keep the server_id the same as it was when the promoted slave was a slave!
If you have applications that use embedded hostnames or IP addresses in their connections to the master, you have two choices; 1) you can change the slave?s hostname and IP to that of the master and restart the server, or 2) you can redirect your clients to the promoted slave.
Failover and Invoked Objects
----------------------------
For most applications, the failover sequence described above will get you a viable master and return your database system to service. If you use invoked objects, specifically events, the promotion of the slave to a master involves some additional steps.
If you are unsure if you have any events that are replicated, you can issue the following command on the slave (you need to have privileges to run the show commands):
SHOW EVENTS WHERE STATUS = 'SLAVESIDE_DISABLED';
This query will list all of the events on the slave that have been replicated FROM the master. You will also see a column named ORIGINATOR that lists the server_id of the originating master. Together with the STATUS column, you can quickly determine which events need to be activated on the newly promoted slave.
To turn the events on, you can create and run the following stored procedure:
NORMAL END!");
# Show the data
SELECT * FROM bup_default.t1;
SELECT * FROM bup_default.t2;
SELECT COUNT(*) FROM bup_default.t1_blob;
--query_vertical SELECT * FROM bup_default.t1_blob;
SELECT COUNT(*) FROM bup_default.wide;
--query_vertical SELECT * FROM bup_default.wide;
BACKUP DATABASE bup_default TO "bup_default.bak";
connection restore;
# Now restore the database and then check to make sure the data is there.
DROP DATABASE bup_default;
RESTORE FROM "bup_default.bak";
# Show the data
SELECT * FROM bup_default.t1;
SELECT * FROM bup_default.t2;
SELECT COUNT(*) FROM bup_default.t1_blob;
--query_vertical SELECT * FROM bup_default.t1_blob;
SELECT COUNT(*) FROM bup_default.wide;
--query_vertical SELECT * FROM bup_default.wide;
--disable_warnings
DROP DATABASE IF EXISTS bup_default;
--enable_warnings
--exec rm $MYSQLTEST_VARDIR/master-data/bup_default.bak
--- New file ---
+++ sql/backup/be_default.cc 07/06/27 14:55:43
/* Copyright (C) 2004-2007 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* @file
*
* @brief Contains the default backup algorithm driver.
*
* This file contains the default backup algorithm (also called a "driver"
* in the online backup terminology. The default backup algorithm may be
* used in place of an engine-specific driver if one does not exist or if
* chosen by the user.
*
* The default backup algorithm is a blocking algorithm that locks all of
* the tables given at the start of the backup/restore process. Once all of
* the data is backed up or restored, the locks are removed. The default
* backup is a row-level backup and therefore does not backup the indexes
* or any of the engine-specific files.
*
* The classes in this file use the namespace @c default_backup to distinguish
* these classes from other backup drivers. The backup functionality is
* contained in the backup class shown below. Similarly, the restore
* functionality is contained in the restore class below.
*
* The format of the backup is written as a series of data blocks where each
* block contains a flag indicating what kind of data is in the block. The
* flags are:
*
* <code>
* RCD_ONCE - Single data block for record data
* RCD_FIRST - First data block in buffer for record buffer
* RCD_DATA - Intermediate data block for record buffer
* RCD_LAST - Last data block in buffer for record buffer
* BLOB_ONCE - Single data block for blob data
* BLOB_FIRST - First data block in buffer for blob buffer
* BLOB_DATA - Intermediate data block for blob buffer
* BLOB_LAST - Last data block in buffer for blob buffer
* </code>
*
* The flag is the first byte in the block. The remaining space in the block
* is the data -- either record data or blob fields.
*
* The block flagged as BLOB_FIRST also contains a 4-byte field which
* contains the total size of the blob field. This is necessary for restore
* because the size of the blob field is unknown and the size is needed to
* allocate memory for the buffer_iterator used to buffer large data from
* the kernel.
*
* TODO
* - Consider making the enums for BACKUP_MODE and RESTORE_MODE bit fields.
* - Change code to ignore blobs with no data (NULL).
*/
#include "../mysql_priv.h"
#include "backup_engine.h"
#include "be_default.h"
#include "backup_aux.h"
namespace default_backup {
using backup::byte;
using backup::result_t;
using backup::version_t;
using backup::Table_list;
using backup::Table_ref;
using backup::Buffer;
using namespace backup;
Engine::Engine(THD *t_thd)
{
m_thd= t_thd;
}
/**
* Create a default backup backup driver.
*
* Given a list of tables to be backed-up, create instance of backup
* driver which will create backup image of these tables.
*
* @param tables (in) list of tables to be backed-up.
* @param eng (out) pointer to backup driver instance.
*
* @retval ERROR if cannot create backup driver class.
* @retval OK on success.
*/
result_t Engine::get_backup(const uint32, const Table_list &tables,
Backup_driver* &drv)
{
DBUG_ENTER("Engine::get_backup");
Backup *ptr= new default_backup::Backup(tables, m_thd);
if (!ptr)
DBUG_RETURN(ERROR);
drv= ptr;
DBUG_RETURN(OK);
}
Backup::Backup(const Table_list &tables, THD *t_thd): Backup_driver(tables)
{
DBUG_PRINT("default_backup",("Creating backup driver"));
m_thd= t_thd; /* save current thread */
cur_table= NULL; /* flag current table as null */
tbl_num= 0; /* set table number to 0 */
mode= INITIALIZE; /* initialize read */
/*
Create a TABLE_LIST * list for iterating through the tables.
Initialize the list for opening the tables in read mode.
*/
tables_in_backup= build_table_list(tables, TL_READ_NO_INSERT);
all_tables= tables_in_backup;
}
/**
* @brief Start table read.
*
* This method saves the handler for the table and initializes the
* handler for reading.
*
* @retval OK handler initialized properly.
* @retval ERROR problem with hander initialization.
*/
result_t Backup::start_tbl_read(TABLE *tbl)
{
int last_read_res;
DBUG_ENTER("Default_backup::start_tbl_read)");
DBUG_ASSERT(tbl->file);
hdl= tbl->file;
last_read_res= hdl->ha_rnd_init(1);
if (last_read_res != 0)
DBUG_RETURN(ERROR);
DBUG_RETURN(OK);
}
/**
* @brief End table read.
*
* This method signals the handler that the reading process is complete.
*
* @retval OK handler read stopped properly.
* @retval ERROR problem with hander.
*/
result_t Backup::end_tbl_read()
{
int last_read_res;
DBUG_ENTER("Default_backup::end_tbl_read)");
last_read_res= hdl->ha_rnd_end();
if (last_read_res != 0)
DBUG_RETURN(ERROR);
DBUG_RETURN(OK);
}
/**
* @brief Start backup process.
*
* This method opens all of the tables for reading.
*
* @retval OK all tables locked properly.
* @retval ERROR problem with locking tables.
*/
result_t Backup::begin(const size_t)
{
uint count;
DBUG_ENTER("Default_backup::start_backup");
if (open_tables(m_thd, &all_tables, &count, 0))
{
DBUG_PRINT("backup", ( "error!" ));
DBUG_RETURN(ERROR);
}
DBUG_RETURN(OK);
}
/**
* @brief Get next table in the list.
*
* This method iterates through the list of tables selecting the
* next table in the list and starting the read process.
*
* @retval 0 no errors.
* @retval -1 no more tables in list.
*/
int Backup::next_table()
{
DBUG_ENTER("Backup::next_table()");
if (cur_table == NULL)
{
cur_table= tables_in_backup->table;
read_set= cur_table->read_set;
}
else
{
tables_in_backup= tables_in_backup->next_global;
if (tables_in_backup != NULL)
{
cur_table= tables_in_backup->table;
read_set= cur_table->read_set;
}
else
{
cur_table= NULL;
DBUG_RETURN(-1);
}
}
DBUG_RETURN(0);
}
/**
* @brief Get the data for a row in the table.
* This method is the main method used in the backup operation. It is
* responsible for reading a row from the table and placing the data in
* the buffer (buf.data) and setting the correct attributes for processing
* (e.g., buf.size = size of record data).
*
* Control of the method is accomplished by using several modes that
* signal portions of the method to run. These modes are:
*
* <code>
* INITIALIZE Indicates time to initialize read
* GET_NEXT_TABLE Open next table in the list
* READ_RCD Reading rows from table mode
* READ_RCD_BUFFER Buffer records mode
* CHECK_BLOBS See if record has blobs
* READ_BLOB Reading blobs from record mode
* READ_BLOB_BUFFER Buffer blobs mode
* </code>
*
* @retval READY initialization phase complete.
* @retval OK data read.
* @retval ERROR problem with reading data.
* @retval DONE driver finished reading from all tables.
*/
result_t Backup::get_data(Buffer &buf)
{
int last_read_res;
DBUG_ENTER("Default_backup::get_data(Buffer &buf)");
buf.table_no= tbl_num;
buf.last= FALSE;
/*
Determine mode of operation and execute mode.
*/
switch (mode) {
/*
Nothing to do in Initialize, continue to GET_NEXT_TABLE.
*/
case INITIALIZE:
/*
If class has been initialized and we need to read the next table,
advance the current table pointer and initialize read process.
*/
case GET_NEXT_TABLE:
{
mode= READ_RCD;
int res= next_table();
/*
If no more tables in list, tell backup algorithm we're done else
fall through to reading mode.
*/
if (res)
{
buf.last= TRUE;
buf.size= 0;
buf.table_no= 0;
DBUG_RETURN(OK);
}
else
{
start_tbl_read(cur_table);
tbl_num++;
buf.table_no= tbl_num;
}
}
/*
Read a row from the table and save the data in the buffer.
*/
case READ_RCD:
{
uint32 size= cur_table->s->reclength;
buf.last= FALSE;
cur_blob= 0;
cur_table->use_all_columns();
last_read_res = hdl->rnd_next(cur_table->record[0]);
DBUG_EXECUTE_IF("SLEEP_DRIVER", sleep(4););
/*
If we are end of file, stop the read process and signal the
backup algorithm that we're done. Turn get_next_table mode on.
*/
if (last_read_res == HA_ERR_END_OF_FILE)
{
end_tbl_read();
buf.size= 0;
buf.last= TRUE;
mode= GET_NEXT_TABLE;
}
else if (last_read_res != 0)
DBUG_RETURN(ERROR);
else
{
/*
Check size of buffer to ensure data fits in the buffer. If it does
not fit, create new blob_buffer object.
*/
if ((size + META_SIZE) <= buf.size)
{
*buf.data= RCD_ONCE; //only part 1 of 1
memcpy((byte *)buf.data + META_SIZE, cur_table->record[0], size);
buf.size = size + META_SIZE;
mode= CHECK_BLOBS;
}
else
{
uint rec_size= 0;
byte *rec_ptr= 0;
rec_buffer.initialize(cur_table->record[0], size);
rec_size= rec_buffer.get_next((::byte **)&rec_ptr,
(buf.size - META_SIZE));
*buf.data= RCD_FIRST; // first part
memcpy((byte *)buf.data + META_SIZE, rec_ptr, rec_size);
buf.size = rec_size + META_SIZE;
mode= READ_RCD_BUFFER;
}
}
break;
}
/*
Read data from the record buffer and write to the kernel buffer.
*/
case READ_RCD_BUFFER:
{
uint rec_size= 0;
rec_size= rec_buffer.get_next((::byte **)&ptr, (buf.size - META_SIZE));
if (rec_buffer.num_windows(buf.size - META_SIZE) == 0)
{
*buf.data= RCD_LAST;
mode= CHECK_BLOBS; // Check for blobs.
rec_buffer.reset(); // dump the memory
}
else
*buf.data= RCD_DATA;
memcpy((byte *)buf.data + META_SIZE, ptr, rec_size);
buf.size = rec_size + META_SIZE;
break;
}
/*
Check for the existence of blobs. If no blobs, we're finished
reading data for this row. If there are blobs, turn read_blob
mode on and get the first blob in the list.
*/
case CHECK_BLOBS:
{
buf.size= 0;
if (cur_table->s->blob_fields > 0)
{
mode= READ_BLOB;
if (!cur_blob)
{
cur_blob= cur_table->s->blob_field;
last_blob_ptr = cur_blob + cur_table->s->blob_fields;
}
/*
Iterate to the next blob. If no more blobs, we're finished reading
the row.
*/
else
{
cur_blob++;
if (cur_blob == last_blob_ptr)
mode= READ_RCD;
}
}
else
mode= READ_RCD;
break;
}
/*
Get next blob. Use blob buffer if blob field is too large for buffer.data.
*/
case READ_BLOB:
{
uint32 size= ((Field_blob*) cur_table->field[*cur_blob])->get_length();
/*
Check size of buffer to ensure data fits in the buffer. If it does
not fit, create new blob_buffer object.
*/
if ((size + META_SIZE) <= buf.size)
{
*buf.data= BLOB_ONCE;
((Field_blob*) cur_table->field[*cur_blob])->get_ptr((uchar **)&ptr);
memcpy((byte *)buf.data + META_SIZE, ptr, size);
buf.size = size + META_SIZE;
mode= CHECK_BLOBS;
}
else
{
uint bb_size= 0;
byte *blob_ptr= 0;
((Field_blob*) cur_table->field[*cur_blob])->get_ptr((uchar **)&ptr);
blob_buffer.initialize((::byte *)ptr, size);
*buf.data= BLOB_FIRST; //first block
uint32 field_size=
((Field_blob*) cur_table->field[*cur_blob])->get_length();
int4store(buf.data + META_SIZE, field_size); //save max size
bb_size= blob_buffer.get_next((::byte **)&blob_ptr,
(buf.size - META_SIZE - 4));
memcpy((byte *)buf.data + META_SIZE + 4, blob_ptr, bb_size);
buf.size = bb_size + META_SIZE + 4;
mode= READ_BLOB_BUFFER;
}
break;
}
/*
Read data from the blob buffer.
*/
case READ_BLOB_BUFFER:
{
uint bb_size= 0;
bb_size= blob_buffer.get_next((::byte **)&ptr, (buf.size - META_SIZE));
if (blob_buffer.num_windows(buf.size - META_SIZE) == 0)
{
*buf.data= BLOB_LAST;
mode= CHECK_BLOBS;
blob_buffer.reset(); // dump the memory
}
else
*buf.data= 1;
memcpy((byte *)buf.data + META_SIZE, ptr, bb_size);
buf.size = bb_size + META_SIZE;
break;
}
default:
DBUG_RETURN(ERROR);
}
DBUG_RETURN(OK);
}
/**
* Create a default backup restore driver.
*
* Given a list of tables to be restored, create instance of restore
* driver which will restore these tables from a backup image.
*
* @param version (in) version of the backup image.
* @param tables (in) list of tables to be restored.
* @param eng (out) pointer to restore driver instance.
*
* @retval ERROR if cannot create restore driver class.
* @retval OK on success.
*/
result_t Engine::get_restore(version_t ver, const uint32,
const Table_list &tables, Restore_driver* &drv)
{
DBUG_ENTER("Engine::get_restore");
Restore *ptr= new default_backup::Restore(tables, m_thd);
if (!ptr)
DBUG_RETURN(ERROR);
drv= ptr;
DBUG_RETURN(OK);
}
Restore::Restore(const Table_list &tables, THD *t_thd): Restore_driver(tables)
{
DBUG_PRINT("default_backup",("Creating restore driver"));
m_thd= t_thd; /* save current thread */
cur_table= NULL; /* flag current table as null */
tbl_num= 0; /* set table number to 0 */
mode= INITIALIZE; /* initialize write */
/*
Create a TABLE_LIST * list for iterating through the tables.
Initialize the list for opening the tables in write mode.
*/
tables_in_backup= build_table_list(tables, TL_WRITE);
all_tables= tables_in_backup;
}
/**
* @brief Truncate table.
*
* This method saves the handler for the table and deletes all rows in
* the table.
*
* @retval OK rows deleted.
* @retval ERROR problem with deleting rows.
*/
result_t Restore::truncate_table(TABLE *tbl)
{
int last_write_res;
DBUG_ENTER("Default_backup::truncate_table)");
DBUG_ASSERT(tbl->file);
hdl= tbl->file;
last_write_res= cur_table->file->delete_all_rows();
/*
Check to see if delete all rows was ok. Ignore if the handler
doesn't support it.
*/
if ((last_write_res != 0) && (last_write_res != HA_ERR_WRONG_COMMAND))
DBUG_RETURN(ERROR);
DBUG_RETURN(OK);
}
/**
* @brief End restore process.
*
* This method unlocks and closes all of the tables.
*
* @retval OK all tables unlocked.
*/
result_t Restore::end()
{
DBUG_ENTER("Restore::end");
close_thread_tables(m_thd);
DBUG_RETURN(OK);
}
/**
* @brief Get next table in the list.
*
* This method iterates through the list of tables selecting the
* next table in the list and starting the write process.
*
* @retval 0 no errors.
* @retval -1 no more tables in list.
*/
int Restore::next_table()
{
DBUG_ENTER("Restore::next_table()");
if (cur_table == NULL)
cur_table= tables_in_backup->table;
else
{
tables_in_backup= tables_in_backup->next_global;
if (tables_in_backup != NULL)
{
DBUG_ASSERT(tables_in_backup->table);
cur_table= tables_in_backup->table;
}
else
{
cur_table= NULL;
DBUG_RETURN(-1);
}
}
DBUG_RETURN(0);
}
/**
* @brief Restore the data for a row in the table.
*
* This method is the main method used in the restore operation. It is
* responsible for writing a row to the table.
*
* Control of the method is accomplished by using several modes that
* signal portions of the method to run. These modes are:
*
* <code>
* INITIALIZE Indicates time to initialize read
* GET_NEXT_TABLE Open next table in the list
* WRITE_RCD Writing rows from table mode
* CHECK_BLOBS See if record has blobs
* WRITE_BLOB Writing blobs from record mode
* WRITE_BLOB_BUFFER Buffer blobs mode
* </code>
*
* @retval READY initialization phase complete.
* @retval OK data written.
* @retval ERROR problem with writing data.
* @retval PROCESSING switching modes -- do not advance stream.
* @retval DONE driver finished writing to all tables.
*/
result_t Restore::send_data(Buffer &buf)
{
byte *ptr= 0;
int last_write_res;
byte block_type= 0;
DBUG_ENTER("Restore::send_data");
DBUG_PRINT("default/restore",("Got packet with %lu bytes from stream %u",
(unsigned long)buf.size, buf.table_no));
/*
Determine mode of operation and execute mode.
*/
switch (mode) {
/*
Nothing to do in Initialize, continue to GET_NEXT_TABLE.
*/
case INITIALIZE:
/*
If class has been initialized and we need to read the next table,
advance the current table pointer and initialize read process.
*/
case GET_NEXT_TABLE:
{
int res;
mode= WRITE_RCD;
/*
It is possible for the backup system to send data to this
engine out of sequence from the table list. When a non-sequential
access is detected, start the table list at the beginning and
find the table in question. This is needed if any tables (more
than MAX_RETRIES are empty!
*/
if ((tbl_num + 1) == buf.table_no) //do normal sequential lookup
res= next_table();
else //do linear search
{
uint i= 0;
cur_table= NULL;
tables_in_backup= all_tables;
do
{
i++;
res= next_table();
}
while ((i != buf.table_no) && !res);
tbl_num= i - 1;
}
if (res)
{
buf.last= TRUE;
buf.size= 0;
buf.table_no= 0;
DBUG_RETURN(OK);
}
else
{
truncate_table(cur_table); /* delete all rows from table */
tbl_num++;
}
}
/*
Write a row to the table from the data in the buffer.
*/
case WRITE_RCD:
{
cur_blob= 0;
max_blob_size= 0;
/*
If the table number is different from the stream number, we're
receiving data from the backup for a different table. Set the mode to
get the next table in the list.
*/
if (tbl_num != buf.table_no)
{
mode= GET_NEXT_TABLE;
DBUG_RETURN(PROCESSING);
}
else
{
uint32 size= buf.size - META_SIZE;
block_type= *buf.data;
cur_table->use_all_columns();
/*
Now we're reconstructing the rec from multiple parts.
*/
switch (block_type) {
/*
Buffer iterator not needed, just write the data.
*/
case RCD_ONCE:
{
if (size == cur_table->s->reclength)
{
ptr= (byte *)my_malloc(size, MYF(MY_WME));
memcpy(cur_table->record[0], (byte *)buf.data + META_SIZE, size);
mode= CHECK_BLOBS;
DBUG_RETURN(PROCESSING);
}
else
DBUG_RETURN(ERROR);
}
/*
This is the first part of several, create new iterator.
*/
case RCD_FIRST:
{
rec_buffer.initialize(cur_table->s->reclength);
rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
mode= WRITE_RCD;
break;
}
/*
Save the part and keep reading.
*/
case RCD_DATA:
{
rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
mode= WRITE_RCD;
break;
}
/*
If this is the last part, assemble and write.
*/
case RCD_LAST:
{
rec_buffer.put_next((::byte *)buf.data + META_SIZE, size);
ptr= (byte *)rec_buffer.get_base_ptr();
memcpy(cur_table->record[0], ptr, cur_table->s->reclength);
mode= CHECK_BLOBS;
}
default:
DBUG_RETURN(ERROR);
}
}
if (mode != CHECK_BLOBS)
break;
}
/*
Check for the existence of blobs. If no blobs, we're finished
writing data for this row so just run the write_row(). If there
are blobs, turn write_blob mode on and get the first blob in
the list. The write_row() call will be delayed until after all
blobs are written.
*/
case CHECK_BLOBS:
{
my_bool write_row= (cur_table->s->blob_fields == 0);
if (cur_table->s->blob_fields > 0)
{
mode= WRITE_BLOB;
if (!cur_blob)
{
cur_blob= cur_table->s->blob_field;
last_blob_ptr = cur_blob + cur_table->s->blob_fields;
}
/*
Iterate to the next blob. If no more blobs, we're finished writing
the row.
*/
else
{
cur_blob++;
if (cur_blob == last_blob_ptr)
write_row= TRUE;
}
}
if (write_row)
{
last_write_res = hdl->write_row(cur_table->record[0]);
if (last_write_res == 0)
mode= WRITE_RCD;
else
DBUG_RETURN(ERROR);
}
break;
}
/*
Write the blobs for the row. Get the size from the buffer and write
the buffer to the blob field for the specified number of bytes.
*/
case WRITE_BLOB:
{
uint32 size= buf.size - META_SIZE;
block_type= *buf.data;
switch (block_type) {
/*
Buffer iterator not needed, just write the data.
*/
case BLOB_ONCE:
{
ptr= (byte *)my_malloc(size, MYF(MY_WME));
memcpy(ptr, (byte *)buf.data + META_SIZE, size);
((Field_blob*) cur_table->field[*cur_blob])->set_ptr(size, (uchar *)ptr);
mode= CHECK_BLOBS;
DBUG_RETURN(PROCESSING);
}
/*
This is the first part of several, create new iterator.
*/
case BLOB_FIRST:
{
max_blob_size= uint4korr(buf.data + META_SIZE);
blob_buffer.initialize(max_blob_size);
size= buf.size - META_SIZE - 4;
blob_buffer.put_next((::byte *)buf.data + META_SIZE + 4, size);
mode= WRITE_BLOB;
break;
}
/*
Save the part and keep reading.
*/
case BLOB_DATA:
{
blob_buffer.put_next((::byte *)buf.data + META_SIZE, size);
mode= WRITE_BLOB;
break;
}
/*
If this is the last part, assemble and write.
*/
case BLOB_LAST:
{
blob_buffer.put_next((::byte *)buf.data + META_SIZE, size);
ptr= (byte *)blob_buffer.get_base_ptr();
((Field_blob*) cur_table->field[*cur_blob])->set_ptr(max_blob_size,
(uchar *)ptr);
mode= CHECK_BLOBS;
DBUG_RETURN(PROCESSING);
}
default:
DBUG_RETURN(ERROR);
}
break;
}
default:
DBUG_RETURN(ERROR);
}
DBUG_RETURN(OK);
}
} /* default_backup namespace */
--- New file ---
+++ sql/backup/be_default.h 07/06/27 14:55:44
#ifndef _DEFAULT_BACKUP_H
#define _DEFAULT_BACKUP_H
#include <backup/backup_engine.h>
#include <backup/archive.h> // to define default backup image class
#include "archive.h"
#include "buffer_iterator.h"
namespace default_backup {
using backup::byte;
using backup::result_t;
using backup::version_t;
using backup::Table_list;
using backup::Table_ref;
using backup::Buffer;
const uint META_SIZE= 1;
/*
The following are the flags for the first byte in the data layout for
the default and consistent snapshot algorithms. They describe what is
included in the buffer going to the kernel.
*/
const byte RCD_ONCE= 1U; // Single data block for record data
const byte RCD_FIRST= (1U<<1); // First data block in buffer for record buffer
const byte RCD_DATA= (1U<<2); // Intermediate data block for record buffer
const byte RCD_LAST= (1U<<3); // Last data block in buffer for record buffer
const byte BLOB_ONCE= 3U; // Single data block for blob data
const byte BLOB_FIRST= (3U<<1); // First data block in buffer for blob buffer
const byte BLOB_DATA= (3U<<2); // Intermediate data block for blob buffer
const byte BLOB_LAST= (3U<<3); // Last data block in buffer for blob buffer
/**
* @class Engine
*
* @brief Encapsulates default online backup/restore functionality.
*
* This class is used to initiate the default backup algorithm, which is used
* by the backup kernel to create a backup image of data stored in any
* engine that does not have a native backup driver. It may also be used as
* an option by the user.
*
* Using this class, the caller can create an instance of the default backup
* backup and restore class. The backup class is used to backup data for a
* list of tables. The restore class is used to restore data from a
* previously created default backup image.
*/
class Engine: public Backup_engine
{
public:
Engine(THD *t_thd);
/*
Return version of backup images created by this engine.
*/
const version_t version() const { return 0; };
result_t get_backup(const uint32, const Table_list &tables,
Backup_driver* &drv);
result_t get_restore(version_t ver, const uint32, const Table_list &tables,
Restore_driver* &drv);
/*
Free any resources allocated by the default backup engine.
*/
void free() { delete this; }
private:
THD *m_thd; ///< Pointer to the current thread.
};
/**
* @class Backup
*
* @brief Contains the default backup algorithm backup functionality.
*
* The backup class is a row-level backup mechanism designed to perform
* a table scan on each table reading the rows and saving the data to the
* buffer from the backup algorithm.
*
* @see <backup driver>
*/
class Backup: public Backup_driver
{
public:
enum has_data_info { YES, WAIT, EOD };
Backup(const Table_list &tables, THD *t_thd);
virtual ~Backup() {};
size_t size() { return UNKNOWN_SIZE; };
size_t init_size() { return UNKNOWN_SIZE; };
result_t begin(const size_t);
result_t end() { return backup::OK; };
result_t get_data(Buffer &buf);
result_t lock() { return backup::OK; };
result_t unlock() { return backup::OK; };
result_t cancel() { return backup::OK; };
void free() { delete this; };
protected:
THD *m_thd; ///< Pointer to current thread struct.
TABLE_LIST *all_tables; ///< Reference to list of tables used.
private:
/*
We use an enum to control the flow of the algorithm. Each mode
invokes a different behavior through a large switch. The mode is
set in the code as a response to conditions or flow of data.
*/
typedef enum {
INITIALIZE, ///< Indicates time to initialize read
GET_NEXT_TABLE, ///< Open next table in the list
READ_RCD, ///< Reading rows from table mode
READ_RCD_BUFFER, ///< Buffer records mode
CHECK_BLOBS, ///< See if record has blobs
READ_BLOB, ///< Reading blobs from record mode
READ_BLOB_BUFFER ///< Buffer blobs mode
} BACKUP_MODE;
result_t start_tbl_read(TABLE *tbl);
result_t end_tbl_read();
int next_table();
BACKUP_MODE mode; ///< Indicates which mode the code is in
int tbl_num; ///< The index of the current table.
TABLE *cur_table; ///< The table currently being read.
handler *hdl; ///< Pointer to table handler.
uint *cur_blob; ///< The current blob field.
uint *last_blob_ptr; ///< Position of last blob field.
MY_BITMAP *read_set; ///< The file read set.
Buffer_iterator rec_buffer; ///< Buffer iterator for windowing records
Buffer_iterator blob_buffer; ///< Buffer iterator for windowing BLOB fields
byte *ptr; ///< Pointer to blob data from record.
TABLE_LIST *tables_in_backup; ///< List of tables used in backup.
};
/**
* @class Restore
*
* @brief Contains the default backup algorithm restore functionality.
*
* The restore class is a row-level backup mechanism designed to restore
* data for each table by writing the data for the rows from the
* buffer given by the backup algorithm.
*
* @see <restore driver>
*/
class Restore: public Restore_driver
{
public:
enum has_data_info { YES, WAIT, EOD };
Restore(const Table_list &tables, THD *t_thd);
virtual ~Restore() {};
result_t begin(const size_t) { return backup::OK; };
result_t end();
result_t send_data(Buffer &buf);
result_t cancel() { return backup::OK; };
TABLE_LIST *get_table_list() { return all_tables; }
void free() { delete this; };
protected:
THD *m_thd; ///< Pointer to current thread struct.
TABLE_LIST *all_tables; ///< Reference to list of tables used.
private:
/*
We use an enum to control the flow of the algorithm. Each mode
invokes a different behavior through a large switch. The mode is
set in the code as a response to conditions or flow of data.
*/
typedef enum {
INITIALIZE, ///< Indicates time to initialize read
GET_NEXT_TABLE, ///< Open next table in the list
WRITE_RCD, ///< Writing rows from table mode
CHECK_BLOBS, ///< See if record has blobs
WRITE_BLOB, ///< Writing blobs from record mode
WRITE_BLOB_BUFFER ///< Buffer blobs mode
} RESTORE_MODE;
result_t truncate_table(TABLE *tbl);
int next_table();
RESTORE_MODE mode; ///< Indicates which mode the code is in
uint tbl_num; ///< The index of the current table.
uint32 max_blob_size; ///< The total size (sum of parts) for the blob.
TABLE *cur_table; ///< The table currently being read.
handler *hdl; ///< Pointer to table handler.
uint *cur_blob; ///< The current blob field.
uint *last_blob_ptr; ///< Position of last blob field.
Buffer_iterator rec_buffer; ///< Buffer iterator for windowing records
Buffer_iterator blob_buffer; ///< Buffer iterator for windowing BLOB fields
TABLE_LIST *tables_in_backup; ///< List of tables used in backup.
};
} // default_backup namespace
/*********************************************************************
Default image class
*********************************************************************/
namespace backup {
class Default_image: public Image_info
{
public:
Default_image(Archive_info &info): Image_info(info)
{ ver= 1; }
image_type type() const
{ return DEFAULT_IMAGE; }
const char* name() const
{ return "Default"; }
bool accept(const Table_ref&, const ::handlerton*)
{ return TRUE; }; // accept all tables
result_t get_backup_driver(Backup_driver* &ptr)
{ return (ptr= new default_backup::Backup(tables,::current_thd)) ? OK : ERROR; }
result_t get_restore_driver(Restore_driver* &ptr)
{ return (ptr= new default_backup::Restore(tables,::current_thd)) ? OK : ERROR; }
result_t do_write_description(OStream&)
{ return OK; } // nothing to write
static result_t
create_from_stream(version_t, Archive_info &info, IStream&,
Image_info* &ptr)
{
return (ptr= new Default_image(info)) ? OK : ERROR;
}
bool is_valid(){ return TRUE; };
};
} // backup namespace
#endif
--- New file ---
+++ sql/backup/buffer_iterator.cc 07/06/27 14:55:44
/* Copyright (C) 2004-2007 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* @file
*
* @brief Contains a buffering class for breaking large data into parts.
*
* This file contains a buffering class for buffering large chunks of
* data. It can be used to store a large chunk of data and iterate
* through windows of a specified size until all the data is read.
* It can also be used to recombine the data from smaller windows.
*/
#include "buffer_iterator.h"
/**
* @brief Create a buffer iterator.
*
* Given a pointer to a block of data, its maximum size, and
* window size, start iterator for reading or writing data.
*
* @param buff_ptr (in) a pointer to a block of memory
* @param size (in) the maximum size of the data
*/
int Buffer_iterator::initialize(byte *buff_ptr, long size)
{
DBUG_ENTER("buffer_iterator::initialize(buff_ptr, size, window)");
buffer= buff_ptr;
max_size= size;
window_size= 0;
alloc_used= false;
cur_bytes_read= 0;
cur_ptr= buffer;
DBUG_RETURN(0);
}
/**
* @brief Create a buffer iterator.
*
* Given the maximum size of a block of data and the
* window size, start iterator for reading or writing data.
*
* @param size (in) the maximum size of the data
*/
int Buffer_iterator::initialize(long size)
{
DBUG_ENTER("buffer_iterator::initialize(size, window)");
buffer= (byte *)my_malloc(size, MYF(MY_WME));
max_size= size;
window_size= 0;
alloc_used= true;
cur_bytes_read= 0;
cur_ptr= buffer;
DBUG_RETURN(0);
}
/**
* @brief Reset buffer iterator.
*
* Destroy any memory used.
*/
int Buffer_iterator::reset()
{
DBUG_ENTER("buffer_iterator::reset()");
if (alloc_used && buffer)
my_free(buffer, MYF(0));
DBUG_RETURN(0);
}
/**
* @brief Get the next window of data in the iterator.
*
* This method retrieves the next window in the iterator. It
* returns the number of bytes read (may be less if last
* window is smaller than the max window size), and updates
* the pointer passed as an argument.
*
* @param buff_ptr (in) a pointer to the window to be read
* @param window (in) the size of the window
*
* @retval the size of the window
*/
long Buffer_iterator::get_next(byte **buff_ptr, long window)
{
long bytes_read;
DBUG_ENTER("buffer_iterator::get_next()");
*buff_ptr= cur_ptr;
if (*buff_ptr)
{
if (!window_size)
window_size= window;
if ((cur_bytes_read + window_size) < max_size)
{
cur_ptr= cur_ptr + window_size;
bytes_read= window_size;
cur_bytes_read= cur_bytes_read + window_size;
}
else
{
cur_ptr= 0;
bytes_read= max_size - cur_bytes_read;
cur_bytes_read= max_size;
}
}
else
bytes_read= 0;
DBUG_RETURN(bytes_read);
}
/**
* @brief Insert the next window of data into the iterator.
*
* This method inserts the next window into the iterator. It
* uses the pointer passed as an argument to copy data from
* that location to the internal buffer based on the size of
* the window passed as an argument.
*
* @param buff_ptr (in/out) a pointer to the window to be written
* @param size (in) the size of the window to be written
*
* @retval 0 success
* @retval 1 window size exceeds maximum size of the block of data
*/
int Buffer_iterator::put_next(byte *buff_ptr, long size)
{
DBUG_ENTER("buffer_iterator::put_next()");
/*
This needs to be a memory copy. Copy to the cur_ptr.
*/
if (cur_bytes_read + size > max_size)
DBUG_RETURN(-1); // error buffer overrun
memcpy(cur_ptr, buff_ptr, size);
cur_bytes_read= cur_bytes_read + size;
cur_ptr= cur_ptr + size;
DBUG_RETURN(0);
}
/**
* @brief Determines the number of windows left to read.
*
* This method calculates how many windows are left to read in
* the iterator. Use this method following initialize() to
* determine the maximum windows you can write to the buffer or
* use this method to determine how many more windows are
* remaining to be read.
*
* @param size (in) the size of the window
*
* @retval the number of windows left to read
*/
int Buffer_iterator::num_windows(long size)
{
int num_windows;
DBUG_ENTER("buffer_iterator::num_windows()");
window_size= size;
num_windows= (max_size - cur_bytes_read) / window_size;
if ((max_size - cur_bytes_read) % window_size)
num_windows++;
DBUG_RETURN(num_windows);
}
/**
* @brief Retrieve the pointer to the block of data.
*
* This method gets the base pointer to the block of data and
* returns it to the caller. This method can be used after writing
* a series of windows to a buffer. When called, the method turns
* off the free() mechanism for freeing the base memory allocated.
* This was included to allow callers to reuse the memory. For
* example, this method is used in the default algorithm to read
* and write blob data. On write, the pointer to the blob data (the
* data in the buffer) is needed to write to the storage engine. Thus,
* when this method is called the memory is not freed on destruction.
*
* @retval the pointer to the buffer
*/
byte *Buffer_iterator::get_base_ptr()
{
byte *ptr;
DBUG_ENTER("buffer_iterator::get_base_ptr()");
cur_bytes_read= 0;
ptr= buffer;
cur_ptr= 0;
buffer= 0;
DBUG_RETURN(ptr);
}
--- New file ---
+++ sql/backup/buffer_iterator.h 07/06/27 14:55:45
#ifndef _BUFFER_ITERATOR_H
#define _BUFFER_ITERATOR_H
#include "mysql_priv.h"
/**
* @class Buffer_iterator
*
* @brief Encapsulates data buffering functionality.
*
* This class is used in the backup drivers for buffering large blocks
* of data into smaller windows. This allows the backup drivers to
* store a large field in multiple blocks of data allocated by the
* backup kernel.
*
* For example, if a driver needs to store a blob field of size 8000
* bytes, but the kernel provides a buffer of size 1024 bytes, this
* class can be used to break the data into 8 parts. Upon restore,
* this class can be used to reassemble the parts into the original
* size of data and thus write the data as one block to the engine.
*
* The class provides two methods for creation. The class can take
* a pointer to an existing memory block or if omitted will allocate
* a buffer of the size passed. See the class constructor for more
* details.
*
* To use this class for reading, instantiate the class passing in
* a pointer to the block you want to read, the total size of the
* block, and the size of the window you want to read. Then call
* get_next() for each window you want to read. You can use the
* num_windows() method to indicate how many windows are left to
* read. This is best used in a loop-like arrangement like the
* following:
*
* byte *ptr;
* byte *outptr;
* ptr= (byte *)my_malloc(8000, MYF(0));
* Buffer_iterator *my_buff = new Buffer_iterator(ptr, 8000, 1024);
* while (my_buff->num_windows())
* {
* bytes_read= my_buff->get_next(&out_ptr);
* // do something with the window in out_ptr here
* }
*
* Note: If you want to permit the Buffer_iterator class to create
* it's own buffer, you must use the put_next() method to copy the data
* from your own buffer into the buffer in the class.
*
* To use this class for writing, instantiate the class passing in
* the total size of the block, and the size of the window you will be
* writing. Note: The window size is not used for writing. Then call
* put_next() to insert a window into the buffer. Once all of the data
* has been placed into the buffer, you can use the get_base_ptr() to
* retrieve the pointer to the buffer. This is best used in a loop-like
* arrangement like the following:
*
* long size; //contains size of window to write
* byte *ptr; //contains the pointer to the window
*
* size= read_my_data(&ptr); //read data here
* Buffer_iterator *my_buff = new Buffer_iterator(8000, 1024);
* while (there is data to read)
* {
* my_buff->put_next(&out_ptr, size);
* // read more here
* }
* write_my_reassembled_block(my_buff->get_base_ptr(), total_size);
*
*/
class Buffer_iterator
{
public:
int initialize(byte *buff_ptr, long size);
int initialize(long size);
int reset();
long get_next(byte **buff_ptr, long window);
int put_next(byte *buff_ptr, long size);
int num_windows(long size);
byte *get_base_ptr();
private:
byte *buffer; ///< The pointer to the block of data to iterate
byte *cur_ptr; ///< The current position in the buffer
long max_size; ///< The maximum size of the block of data
long window_size; ///< The size of the window to read
long cur_bytes_read; ///< The number of bytes read
bool alloc_used; ///< Indicates whether to dealloc memory or not
};
#endif
--- 1.4/sql/backup/Makefile.am 2007-06-27 14:55:56 -04:00
+++ 1.5/sql/backup/Makefile.am 2007-06-27 14:55:56 -04:00
@@ -30,7 +30,9 @@
archive.cc \
meta_backup.cc \
data_backup.cc \
- sql_backup.cc
+ sql_backup.cc \
+ be_default.cc \
+ buffer_iterator.cc
noinst_HEADERS = \
api_types.h \
@@ -42,7 +44,9 @@
logger.h \
string_pool.h \
archive.h \
- meta_backup.h
+ meta_backup.h \
+ be_default.h \
+ buffer_iterator.h
DEFS = \
-DMYSQL_SERVER \
--- 1.1/sql/backup/archive.cc 2007-06-27 14:55:56 -04:00
+++ 1.2/sql/backup/archive.cc 2007-06-27 14:55:56 -04:00
@@ -24,7 +24,7 @@
#include "backup_engine.h"
#include "backup_aux.h"
#include "archive.h"
-
+#include "be_default.h"
/***************************************
@@ -351,6 +351,9 @@
case NATIVE_IMAGE:
return Native_image::create_from_stream(ver,info,s,ptr);
+
+ case DEFAULT_IMAGE:
+ return Default_image::create_from_stream(ver,info,s,ptr);
default:
DBUG_PRINT("restore",("Unknown image type %d",t));
--- 1.1/sql/backup/data_backup.cc 2007-06-27 14:55:56 -04:00
+++ 1.2/sql/backup/data_backup.cc 2007-06-27 14:55:56 -04:00
@@ -30,13 +30,16 @@
- If an error from driver is ignored (and operation retried) leave trace
of the error in the log.
+
+ - Move the open_and_lock_tables in restore_table_data to before the begin()
+ calls for the drivers.
*/
#include "backup_engine.h"
#include "stream.h"
#include "backup_kernel.h"
#include "debug.h"
-
+#include "be_default.h"
/***********************************************
@@ -305,6 +308,7 @@
*/
int write_table_data(THD*, Backup_info &info, OStream &s)
{
+ my_bool def_or_snap_used= FALSE; // Are default or snapshot used?
DBUG_ENTER("backup::write_table_data");
info.data_size= 0;
@@ -356,6 +360,8 @@
inactive.push_back(p);
}
+ if (!def_or_snap_used)
+ def_or_snap_used= (i->type() == Image_info::DEFAULT_IMAGE);
}
/*
@@ -492,6 +498,12 @@
DBUG_PRINT("backup/data",("-- DONE --"));
}
+ /*
+ If the default or snapshot drivers are used, close the tables.
+ */
+ if (def_or_snap_used)
+ close_thread_tables(::current_thd);
+
info.data_size= s.bytes - start_bytes;
DBUG_RETURN(0);
@@ -1193,6 +1205,9 @@
Restore_driver* drv[MAX_IMAGES];
+ TABLE_LIST *table_list= 0;
+ TABLE_LIST *table_list_last= 0;
+
if (info.img_count > MAX_IMAGES)
{
info.report_error(ER_BACKUP_TOO_MANY_IMAGES, info.img_count, MAX_IMAGES);
@@ -1228,6 +1243,37 @@
ret= -3;
goto error;
}
+ /*
+ Collect tables from default and snapshot for open and lock tables.
+ There should be at most only 1 of each driver.
+ */
+ if (img->type() == Image_info::DEFAULT_IMAGE)
+ {
+ /*
+ If the table list is defined and the last pointer is
+ defined then we are seeing a duplicate of either default
+ or snapshot drivers. There should be at most 1 of each.
+ */
+ if (table_list && table_list_last->next_global)
+ {
+ DBUG_PRINT("restore",("Duplicate default or snapshot subimage"));
+ DBUG_RETURN(ERROR);
+ }
+ /*
+ If the table list is empty, use the first one and loop
+ until the end then record the end of the first one.
+ */
+ if (!table_list)
+ {
+ table_list= ((default_backup::Restore *)drv[no])->get_table_list();
+ table_list_last= table_list;
+ while (table_list_last->next_global != NULL)
+ table_list_last= table_list_last->next_global;
+ }
+ else
+ table_list_last->next_global=
+ ((default_backup::Restore *)drv[no])->get_table_list();
+ }
}
{
@@ -1243,6 +1289,23 @@
Restore_driver *drvr= NULL; // pointer to the current driver
Image_info *img= NULL; // corresponding restore image object
+ /*
+ Open tables for default and snapshot drivers.
+ */
+ if (table_list)
+ {
+ table_list->lock_type= TL_WRITE;
+ query_cache.invalidate_locked_for_write(table_list);
+ if (open_and_lock_tables(::current_thd, table_list))
+ {
+ DBUG_PRINT("restore",
+ ( "error on open tables for default and snapshot drivers!" ));
+ DBUG_RETURN(backup::ERROR);
+ }
+ if (table_list_last)
+ table_list_last->next_global= NULL; // break lists
+ }
+
// main data reading loop
while ( state != DONE && state != ERROR )
@@ -1393,6 +1456,12 @@
if (!bad_drivers.is_empty())
info.report_error(ER_BACKUP_STOP_RESTORE_DRIVERS, bad_drivers.c_ptr());
}
+
+ /*
+ Close all tables if default or snapshot driver used.
+ */
+ if (table_list)
+ close_thread_tables(::current_thd);
DBUG_RETURN(state == ERROR ? -9 : 0);
--- 1.1/sql/backup/sql_backup.cc 2007-06-27 14:55:56 -04:00
+++ 1.2/sql/backup/sql_backup.cc 2007-06-27 14:55:56 -04:00
@@ -28,6 +28,7 @@
#include "meta_backup.h"
#include "archive.h"
#include "debug.h"
+#include "be_default.h"
namespace backup {
@@ -363,6 +364,8 @@
3. Otherwise check if one of the existing sub-images would accept table from
this location.
+ 4. When everything else fails, use default (blocking) backup driver.
+
Note: 1 is not implemented yet and hence we start with 3.
*/
@@ -422,6 +425,24 @@
DBUG_RETURN(no);
}
+
+ // Points 4: try default driver...
+
+ int ino= default_image_no; //now try default driver
+
+ if (ino < 0) //image doesn't exist
+ {
+ ino= img_count;
+ default_image_no= img_count;
+ images[default_image_no]= new Default_image(*this);
+ img_count++;
+ DBUG_PRINT("backup",("Default image added to archive"));
+ }
+
+ img= images[ino];
+ DBUG_ASSERT(img);
+ if (img->accept(tbl,hton))
+ DBUG_RETURN(ino); // table accepted
Table_ref::describe_buf buf;
report_error(ER_BACKUP_NO_BACKUP_DRIVER,tbl.describe(buf,sizeof(buf)));
--- 1.42/sql/CMakeLists.txt 2007-06-27 14:55:56 -04:00
+++ 1.43/sql/CMakeLists.txt 2007-06-27 14:55:56 -04:00
@@ -24,6 +24,7 @@
${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/zlib
+ ${CMAKE_SOURCE_DIR}/sql/backup
)
SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/message.rc
@@ -75,7 +76,7 @@
sql_connect.cc scheduler.cc
backup/stream.cc backup/logger.cc backup/string_pool.cc
backup/archive.cc backup/meta_backup.cc backup/data_backup.cc
- backup/sql_backup.cc
+ backup/sql_backup.cc backup/be_default.cc backup/buffer_iterator.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
| Thread |
|---|
| • bk commit into 5.2 tree (cbell:1.2526) | cbell | 27 Jun |