Chuck,
This should do the trick. The tables are locked just before VP creation which
should be fine for both default and CS backup driver (given that they start
sending data only after the lock() call).
What is missing is proper error reporting inside write_table_data() in case
table collecting function or the lock fails. This would require defining
appropriate messages in errmsg.txt and calling info.report_error() when errors
are detected.
Other than that the patch should be fine (and we keep in mind that the table
locking code should be replaced by something better in version beta).
Rafal
cbell@stripped wrote:
> 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-28 13:33:47-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-28 13:33:44-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-28 13:33:44-04:00, cbell@mysql_cab_desk. +0
> -0
>
> mysql-test/t/backup.test@stripped, 2007-06-28 13:33:44-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-28 13:33:44-04:00, cbell@mysql_cab_desk. +0
> -0
>
> sql/CMakeLists.txt@stripped, 2007-06-28 13:33: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-28 13:33:41-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-28 13:33:41-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-28 13:33:43-04:00, cbell@mysql_cab_desk. +853
> -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-28 13:33:43-04:00, cbell@mysql_cab_desk. +0
> -0
>
> sql/backup/be_default.h@stripped, 2007-06-28 13:33:43-04:00, cbell@mysql_cab_desk. +248
> -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-28 13:33:43-04:00, cbell@mysql_cab_desk. +0
> -0
>
> sql/backup/buffer_iterator.cc@stripped, 2007-06-28 13:33:43-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-28 13:33:43-04:00, cbell@mysql_cab_desk.
> +0 -0
>
> sql/backup/buffer_iterator.h@stripped, 2007-06-28 13:33:44-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-28 13:33:44-04:00, cbell@mysql_cab_desk.
> +0 -0
>
> sql/backup/data_backup.cc@stripped, 2007-06-28 13:33:42-04:00, cbell@mysql_cab_desk.
> +115 -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 open_and_lock_tables and the
> close_thread_tables have been moved to kernel 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-28 13:33:42-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/28 13:33:44
> 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/28 13:33:44
> 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/28 13:33: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 */
> lock_called= FALSE; /* lock has not been called */
>
> /*
> 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 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)");
>
> /*
> If locks have not been obtained, wait until they have.
> */
> if (!lock_called)
> {
> buf.size= 0;
> buf.table_no= 0;
> buf.last= TRUE;
> DBUG_RETURN(READY);
> }
>
> 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/28 13:33:43
> #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 0; };
> result_t begin(const size_t) { return backup::OK; };
> result_t end() { return backup::OK; };
> result_t get_data(Buffer &buf);
> result_t lock()
> {
> lock_called= TRUE;
> return backup::OK;
> };
> result_t unlock() { return backup::OK; };
> 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.
> my_bool lock_called; ///< Checks to see if locks have been reached.
>
> 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/28 13:33: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 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/28 13:33:44
> #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-28 13:33:55 -04:00
> +++ 1.5/sql/backup/Makefile.am 2007-06-28 13:33:55 -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.3/sql/backup/archive.cc 2007-06-28 13:33:55 -04:00
> +++ 1.4/sql/backup/archive.cc 2007-06-28 13:33:55 -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.4/sql/backup/data_backup.cc 2007-06-28 13:33:55 -04:00
> +++ 1.5/sql/backup/data_backup.cc 2007-06-28 13:33:55 -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"
>
> /***********************************************
>
> @@ -295,6 +298,54 @@
> { return start_pos + bytes_in; }
> };
>
> +/*
> + Collect tables from default and snapshot for open and lock tables.
> + There should be at most only 1 of each driver.
> +*/
> +int get_default_snapshot_tables(backup::Backup_driver *backup_drv,
> + backup::Restore_driver *restore_drv,
> + TABLE_LIST **tables,
> + TABLE_LIST **tables_last)
> +{
> + TABLE_LIST *table_list= *tables;
> + TABLE_LIST *table_list_last= *tables_last;
> +
> + DBUG_ENTER("backup::get_default_snapshot_tables");
> + /*
> + 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)
> + {
> + if (backup_drv)
> + table_list= ((default_backup::Backup *)backup_drv)->get_table_list();
> + else if (restore_drv)
> + table_list= ((default_backup::Restore *)restore_drv)->get_table_list();
> + else
> + DBUG_RETURN(ERROR);
> + *tables= table_list;
> + table_list_last= table_list;
> + while (table_list_last->next_global != NULL)
> + table_list_last= table_list_last->next_global;
> + }
> + else
> + if (backup_drv)
> + (*tables_last)->next_global= ((default_backup::Backup
> *)backup_drv)->get_table_list();
> + else if (restore_drv)
> + (*tables_last)->next_global= ((default_backup::Restore
> *)restore_drv)->get_table_list();
> + else
> + DBUG_RETURN(ERROR);
> +}
>
> /**
> Save data from tables being backed up.
> @@ -307,6 +358,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;
> @@ -322,6 +374,9 @@
>
> DBUG_PRINT("backup/data",("initializing scheduler"));
>
> + TABLE_LIST *table_list= 0;
> + TABLE_LIST *table_list_last= 0;
> +
> // add unknown "at end" drivers to scheduler, rest to inactive list
>
> for (uint no=0; no < info.img_count; ++no)
> @@ -353,6 +408,11 @@
>
> inactive.push_back(p);
> }
> + if (!def_or_snap_used)
> + def_or_snap_used= (i->type() == Image_info::DEFAULT_IMAGE);
> + if (def_or_snap_used)
> + get_default_snapshot_tables(&p->drv(), NULL,
> + &table_list, &table_list_last);
> }
>
> /*
> @@ -437,6 +497,21 @@
> if (sch.prepare())
> goto error;
>
> + /*
> + Open tables for default and snapshot drivers.
> + */
> + if (table_list)
> + {
> + if (open_and_lock_tables(::current_thd, table_list))
> + {
> + DBUG_PRINT("backup",
> + ( "error on open tables for default and snapshot drivers!" ));
> + DBUG_RETURN(ERROR);
> + }
> + if (table_list_last)
> + table_list_last->next_global= NULL; // break lists
> + }
> +
> while (sch.prepare_count > 0)
> if (sch.step())
> goto error;
> @@ -462,6 +537,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);
> @@ -1165,6 +1246,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);
> @@ -1197,6 +1281,13 @@
> info.report_error(ER_BACKUP_INIT_RESTORE_DRIVER,img->name());
> 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)
> + get_default_snapshot_tables(NULL, (default_backup::Restore *)drv[no],
> + &table_list, &table_list_last);
> }
>
> {
> @@ -1212,6 +1303,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 )
> @@ -1357,6 +1465,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 ? backup::ERROR : 0);
>
>
> --- 1.3/sql/backup/sql_backup.cc 2007-06-28 13:33:55 -04:00
> +++ 1.4/sql/backup/sql_backup.cc 2007-06-28 13:33:55 -04:00
> @@ -28,6 +28,7 @@
> #include "meta_backup.h"
> #include "archive.h"
> #include "debug.h"
> +#include "be_default.h"
>
> namespace backup {
>
> @@ -352,6 +353,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.
> */
>
> @@ -411,6 +414,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-28 13:33:55 -04:00
> +++ 1.43/sql/CMakeLists.txt 2007-06-28 13:33:55 -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
>
>