MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:cbell Date:November 6 2007 4:12pm
Subject:bk commit into 6.0 tree (cbell:1.2647)
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 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-11-06 11:11:58-05:00, cbell@mysql_cab_desk. +39 -0
  Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup.result@stripped, 2007-11-06 11:11:28-05:00, cbell@mysql_cab_desk. +430 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup.result@stripped, 2007-11-06 11:11:28-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/r/backup_errors.result@stripped, 2007-11-06 11:11:29-05:00, cbell@mysql_cab_desk. +24 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup_errors.result@stripped, 2007-11-06 11:11:29-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/r/backup_no_data.result@stripped, 2007-11-06 11:11:30-05:00, cbell@mysql_cab_desk. +105 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup_no_data.result@stripped, 2007-11-06 11:11:30-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/r/backup_no_engine.result@stripped, 2007-11-06 11:11:30-05:00, cbell@mysql_cab_desk. +30 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup_no_engine.result@stripped, 2007-11-06 11:11:30-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/r/backup_snapshot.result@stripped, 2007-11-06 11:11:31-05:00, cbell@mysql_cab_desk. +58 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/r/backup_snapshot.result@stripped, 2007-11-06 11:11:31-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup.test@stripped, 2007-11-06 11:11:31-05:00, cbell@mysql_cab_desk. +286 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup.test@stripped, 2007-11-06 11:11:31-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup_errors.test@stripped, 2007-11-06 11:11:32-05:00, cbell@mysql_cab_desk. +29 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup_errors.test@stripped, 2007-11-06 11:11:32-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup_no_data.test@stripped, 2007-11-06 11:11:33-05:00, cbell@mysql_cab_desk. +82 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup_no_data.test@stripped, 2007-11-06 11:11:33-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup_no_engine.test@stripped, 2007-11-06 11:11:33-05:00, cbell@mysql_cab_desk. +35 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup_no_engine.test@stripped, 2007-11-06 11:11:33-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup_snapshot.opt@stripped, 2007-11-06 11:11:34-05:00, cbell@mysql_cab_desk. +1 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup_snapshot.opt@stripped, 2007-11-06 11:11:34-05:00, cbell@mysql_cab_desk. +0 -0

  mysql-test/t/backup_snapshot.test@stripped, 2007-11-06 11:11:35-05:00, cbell@mysql_cab_desk. +81 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  mysql-test/t/backup_snapshot.test@stripped, 2007-11-06 11:11:34-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/CMakeLists.txt@stripped, 2007-11-06 11:11:35-05:00, cbell@mysql_cab_desk. +32 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/CMakeLists.txt@stripped, 2007-11-06 11:11:35-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/Doxyfile@stripped, 2007-11-06 11:11:36-05:00, cbell@mysql_cab_desk. +225 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/Doxyfile@stripped, 2007-11-06 11:11:36-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/Makefile.am@stripped, 2007-11-06 11:11:36-05:00, cbell@mysql_cab_desk. +66 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/Makefile.am@stripped, 2007-11-06 11:11:36-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/api_types.h@stripped, 2007-11-06 11:11:37-05:00, cbell@mysql_cab_desk. +286 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/api_types.h@stripped, 2007-11-06 11:11:37-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/archive.cc@stripped, 2007-11-06 11:11:38-05:00, cbell@mysql_cab_desk. +823 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/archive.cc@stripped, 2007-11-06 11:11:38-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/archive.h@stripped, 2007-11-06 11:11:38-05:00, cbell@mysql_cab_desk. +486 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/archive.h@stripped, 2007-11-06 11:11:38-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/backup_aux.h@stripped, 2007-11-06 11:11:39-05:00, cbell@mysql_cab_desk. +82 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/backup_aux.h@stripped, 2007-11-06 11:11:39-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/backup_engine.h@stripped, 2007-11-06 11:11:39-05:00, cbell@mysql_cab_desk. +535 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/backup_engine.h@stripped, 2007-11-06 11:11:39-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/backup_kernel.h@stripped, 2007-11-06 11:11:40-05:00, cbell@mysql_cab_desk. +225 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/backup_kernel.h@stripped, 2007-11-06 11:11:40-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/be_default.cc@stripped, 2007-11-06 11:11:41-05:00, cbell@mysql_cab_desk. +871 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/be_default.cc@stripped, 2007-11-06 11:11:41-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/be_default.h@stripped, 2007-11-06 11:11:41-05:00, cbell@mysql_cab_desk. +250 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/be_default.h@stripped, 2007-11-06 11:11:41-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/be_snapshot.cc@stripped, 2007-11-06 11:11:42-05:00, cbell@mysql_cab_desk. +142 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/be_snapshot.cc@stripped, 2007-11-06 11:11:42-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/be_snapshot.h@stripped, 2007-11-06 11:11:42-05:00, cbell@mysql_cab_desk. +144 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/be_snapshot.h@stripped, 2007-11-06 11:11:42-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/buffer_iterator.cc@stripped, 2007-11-06 11:11:43-05:00, cbell@mysql_cab_desk. +204 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/buffer_iterator.cc@stripped, 2007-11-06 11:11:43-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/buffer_iterator.h@stripped, 2007-11-06 11:11:44-05:00, cbell@mysql_cab_desk. +92 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/buffer_iterator.h@stripped, 2007-11-06 11:11:44-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/data_backup.cc@stripped, 2007-11-06 11:11:44-05:00, cbell@mysql_cab_desk. +1573 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/data_backup.cc@stripped, 2007-11-06 11:11:44-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/debug.h@stripped, 2007-11-06 11:11:45-05:00, cbell@mysql_cab_desk. +81 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/debug.h@stripped, 2007-11-06 11:11:45-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/error.h@stripped, 2007-11-06 11:11:45-05:00, cbell@mysql_cab_desk. +34 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/error.h@stripped, 2007-11-06 11:11:45-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/logger.cc@stripped, 2007-11-06 11:11:46-05:00, cbell@mysql_cab_desk. +86 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/logger.cc@stripped, 2007-11-06 11:11:46-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/logger.h@stripped, 2007-11-06 11:11:47-05:00, cbell@mysql_cab_desk. +125 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/logger.h@stripped, 2007-11-06 11:11:47-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/map.h@stripped, 2007-11-06 11:11:47-05:00, cbell@mysql_cab_desk. +189 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/map.h@stripped, 2007-11-06 11:11:47-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/meta_backup.cc@stripped, 2007-11-06 11:11:48-05:00, cbell@mysql_cab_desk. +380 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/meta_backup.cc@stripped, 2007-11-06 11:11:48-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/meta_backup.h@stripped, 2007-11-06 11:11:49-05:00, cbell@mysql_cab_desk. +133 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/meta_backup.h@stripped, 2007-11-06 11:11:49-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/sql_backup.cc@stripped, 2007-11-06 11:11:49-05:00, cbell@mysql_cab_desk. +1364 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/sql_backup.cc@stripped, 2007-11-06 11:11:49-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/stream.cc@stripped, 2007-11-06 11:11:50-05:00, cbell@mysql_cab_desk. +330 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/stream.cc@stripped, 2007-11-06 11:11:50-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/stream.h@stripped, 2007-11-06 11:11:50-05:00, cbell@mysql_cab_desk. +677 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/stream.h@stripped, 2007-11-06 11:11:50-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/string_pool.cc@stripped, 2007-11-06 11:11:51-05:00, cbell@mysql_cab_desk. +240 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/string_pool.cc@stripped, 2007-11-06 11:11:51-05:00, cbell@mysql_cab_desk. +0 -0

  sql/backup/string_pool.h@stripped, 2007-11-06 11:11:52-05:00, cbell@mysql_cab_desk. +59 -0
    Adding backup to mysql-6.0-backup tree. Part 1 of 2.

  sql/backup/string_pool.h@stripped, 2007-11-06 11:11:52-05:00, cbell@mysql_cab_desk. +0 -0

diff -Nrup a/mysql-test/r/backup.result b/mysql-test/r/backup.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/backup.result	2007-11-06 11:11:28 -05:00
@@ -0,0 +1,430 @@
+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=innodb;
+CREATE TABLE bup_default.t2 (a int) engine=myisam;
+CREATE TABLE bup_default.t1_blob (a int, b text) engine=innodb;
+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     =       43 bytes
+ meta-data  =      890 bytes
+ data       =     7411 bytes
+              --------------
+ total            8344 bytes
+DROP DATABASE bup_default;
+RESTORE FROM "bup_default.bak";
+Restore Summary
+ header     =       43 bytes
+ meta-data  =      890 bytes
+ data       =     7411 bytes
+              --------------
+ total            8344 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;
diff -Nrup a/mysql-test/r/backup_errors.result b/mysql-test/r/backup_errors.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/backup_errors.result	2007-11-06 11:11:29 -05:00
@@ -0,0 +1,24 @@
+DROP DATABASE IF EXISTS adb;
+Warnings:
+Note	1008	Can't drop database 'adb'; database doesn't exist
+DROP DATABASE IF EXISTS bdb;
+Warnings:
+Note	1008	Can't drop database 'bdb'; database doesn't exist
+RESTORE FROM 'test.bak';
+ERROR HY000: Can't read backup location 'test.bak'
+CREATE DATABASE adb;
+CREATE DATABASE bdb;
+BACKUP DATABASE adb TO '';
+ERROR HY000: Invalid backup location ''
+DROP DATABASE IF EXISTS foo;
+Warnings:
+Note	1008	Can't drop database 'foo'; database doesn't exist
+DROP DATABASE IF EXISTS bar;
+Warnings:
+Note	1008	Can't drop database 'bar'; database doesn't exist
+BACKUP DATABASE foo TO 'test.bak';
+ERROR 42000: Unknown database 'foo'
+BACKUP DATABASE test,foo,bdb,bar TO 'test.bak';
+ERROR 42000: Unknown database 'foo,bar'
+DROP DATABASE IF EXISTS adb;
+DROP DATABASE IF EXISTS bdb;
diff -Nrup a/mysql-test/r/backup_no_data.result b/mysql-test/r/backup_no_data.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/backup_no_data.result	2007-11-06 11:11:30 -05:00
@@ -0,0 +1,105 @@
+DROP DATABASE IF EXISTS empty_db;
+CREATE DATABASE empty_db;
+BACKUP DATABASE empty_db TO 'empty_db.bak';
+Backup Summary
+ header     =       11 bytes
+ meta-data  =        2 bytes
+ data       =        0 bytes
+              --------------
+ total              13 bytes
+BACKUP DATABASE * TO 'all.bak';
+Backup Summary
+ header     =       16 bytes
+ meta-data  =        4 bytes
+ data       =        0 bytes
+              --------------
+ total              20 bytes
+DROP DATABASE empty_db;
+DROP DATABASE test;
+SHOW DATABASES;
+Database
+information_schema
+mysql
+SHOW DATABASES;
+Database
+information_schema
+mysql
+RESTORE FROM 'all.bak';
+Restore Summary
+ header     =       16 bytes
+ meta-data  =        4 bytes
+ data       =        0 bytes
+              --------------
+ total              20 bytes
+SHOW DATABASES;
+Database
+information_schema
+empty_db
+mysql
+test
+DROP DATABASE empty_db;
+DROP DATABASE test;
+RESTORE FROM 'empty_db.bak';
+Restore Summary
+ header     =       11 bytes
+ meta-data  =        2 bytes
+ data       =        0 bytes
+              --------------
+ total              13 bytes
+SHOW DATABASES;
+Database
+information_schema
+empty_db
+mysql
+SHOW TABLES IN empty_db;
+Tables_in_empty_db
+RESTORE FROM 'all.bak';
+Restore Summary
+ header     =       16 bytes
+ meta-data  =        4 bytes
+ data       =        0 bytes
+              --------------
+ total              20 bytes
+SHOW DATABASES;
+Database
+information_schema
+empty_db
+mysql
+test
+SHOW TABLES IN empty_db;
+Tables_in_empty_db
+USE test;
+DROP TABLE IF EXISTS t1;
+Warnings:
+Note	1051	Unknown table 't1'
+CREATE TABLE t1 (
+`dir_code` char(4),
+`building` char(6)
+) ENGINE=myisam DEFAULT CHARSET=latin1;
+USE empty_db;
+DROP VIEW IF EXISTS v1;
+CREATE VIEW v1 AS SELECT * FROM test.t1;
+BACKUP DATABASE empty_db TO 'empty_db.bak';
+Backup Summary
+ header     =       11 bytes
+ meta-data  =        2 bytes
+ data       =        0 bytes
+              --------------
+ total              13 bytes
+SHOW DATABASES;
+Database
+information_schema
+empty_db
+mysql
+test
+RESTORE FROM 'empty_db.bak';
+Restore Summary
+ header     =       11 bytes
+ meta-data  =        2 bytes
+ data       =        0 bytes
+              --------------
+ total              13 bytes
+USE empty_db;
+SHOW TABLES;
+Tables_in_empty_db
+DROP DATABASE IF EXISTS empty_db;
diff -Nrup a/mysql-test/r/backup_no_engine.result b/mysql-test/r/backup_no_engine.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/backup_no_engine.result	2007-11-06 11:11:30 -05:00
@@ -0,0 +1,30 @@
+DROP DATABASE IF EXISTS db;
+CREATE DATABASE db;
+CREATE TABLE db.t1 (a int, b char(32))
+ENGINE=myisam;
+BACKUP DATABASE db TO "db.backup";
+Backup Summary
+ header     =       12 bytes
+ meta-data  =      120 bytes
+ data       =        0 bytes
+              --------------
+ total             132 bytes
+DROP DATABASE db;
+CREATE DATABASE db;
+RESTORE FROM "db.backup";
+Restore Summary
+ header     =       12 bytes
+ meta-data  =      120 bytes
+ data       =        0 bytes
+              --------------
+ total             132 bytes
+SHOW TABLES IN db;
+Tables_in_db
+t1
+SHOW CREATE TABLE db.t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL,
+  `b` char(32) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP DATABASE db;
diff -Nrup a/mysql-test/r/backup_snapshot.result b/mysql-test/r/backup_snapshot.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/backup_snapshot.result	2007-11-06 11:11:31 -05:00
@@ -0,0 +1,58 @@
+DROP DATABASE IF EXISTS bup_snapshot;
+CREATE DATABASE bup_snapshot;
+CREATE TABLE bup_snapshot.t1 (word CHAR(20)) ENGINE=INNODB;
+INSERT INTO bup_snapshot.t1 VALUES ("01 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("02 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("03 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("04 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("05 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("06 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("07 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("08 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("09 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("10 Some data to test");
+con1: Show that the new data doesn't exist before backup.
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+word
+SELECT COUNT(*) FROM bup_snapshot.t1;
+COUNT(*)
+10
+con1: Backing up database.
+BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
+Backup Summary
+ header     =       22 bytes
+ meta-data  =       95 bytes
+ data       =      260 bytes
+              --------------
+ total             377 bytes
+con2: Inserting new data.
+INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews");
+INSERT INTO bup_snapshot.t1 VALUES("- Yes");
+INSERT INTO bup_snapshot.t1 VALUES("- Jethro Tull");
+DELETE FROM bup_snapshot.t1 WHERE word LIKE '10%';
+con2: Showing the data after inserts.
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+word
+- Dave Mathews
+- Yes
+- Jethro Tull
+SELECT COUNT(*) FROM bup_snapshot.t1;
+COUNT(*)
+12
+con1: Dropping the database
+DROP TABLE bup_snapshot.t1;
+con1: Restoring the database
+RESTORE FROM "bup_snapshot.bak";
+Restore Summary
+ header     =       22 bytes
+ meta-data  =       95 bytes
+ data       =      260 bytes
+              --------------
+ total             377 bytes
+con1: Showing the data (no new data should be here).
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+word
+SELECT COUNT(*) FROM bup_snapshot.t1;
+COUNT(*)
+10
+DROP DATABASE bup_snapshot;
diff -Nrup a/mysql-test/t/backup.test b/mysql-test/t/backup.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup.test	2007-11-06 11:11:31 -05:00
@@ -0,0 +1,286 @@
+--source include/have_innodb.inc
+
+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 and snapshot online backup drivers
+#
+
+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=innodb;
+CREATE TABLE bup_default.t2 (a int) engine=myisam;
+CREATE TABLE bup_default.t1_blob (a int, b text) engine=innodb;
+
+# 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";
+
+# 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
+
diff -Nrup a/mysql-test/t/backup_errors.test b/mysql-test/t/backup_errors.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup_errors.test	2007-11-06 11:11:32 -05:00
@@ -0,0 +1,29 @@
+DROP DATABASE IF EXISTS adb;
+DROP DATABASE IF EXISTS bdb;
+
+# non-existent backup archive
+-- exec rm -f $MYSQLTEST_VARDIR/master-data/test.bak
+-- error ER_BACKUP_READ_LOC
+RESTORE FROM 'test.bak';
+
+CREATE DATABASE adb;
+CREATE DATABASE bdb;
+
+# bad archive name
+-- error ER_BACKUP_INVALID_LOC
+BACKUP DATABASE adb TO '';
+
+# non-existent database
+DROP DATABASE IF EXISTS foo;
+DROP DATABASE IF EXISTS bar;
+-- error ER_BAD_DB_ERROR
+BACKUP DATABASE foo TO 'test.bak';
+-- error ER_BAD_DB_ERROR
+BACKUP DATABASE test,foo,bdb,bar TO 'test.bak';
+
+# TODO: fix error injection and test more errors
+
+DROP DATABASE IF EXISTS adb;
+DROP DATABASE IF EXISTS bdb;
+
+
diff -Nrup a/mysql-test/t/backup_no_data.test b/mysql-test/t/backup_no_data.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup_no_data.test	2007-11-06 11:11:33 -05:00
@@ -0,0 +1,82 @@
+connect (backup,localhost,root,,);
+connect (restore,localhost,root,,);
+
+connection backup;
+
+# save empty test database on the side
+--exec cp -rf $MYSQLTEST_VARDIR/master-data/test $MYSQLTEST_VARDIR/test.orig
+
+--disable_warnings
+DROP DATABASE IF EXISTS empty_db;
+--enable_warnings
+
+CREATE DATABASE empty_db;
+
+BACKUP DATABASE empty_db TO 'empty_db.bak';
+BACKUP DATABASE * TO 'all.bak';
+
+DROP DATABASE empty_db;
+DROP DATABASE test;
+
+SHOW DATABASES;
+
+connection restore;
+
+SHOW DATABASES;
+
+RESTORE FROM 'all.bak';
+
+SHOW DATABASES;
+
+DROP DATABASE empty_db;
+DROP DATABASE test;
+
+RESTORE FROM 'empty_db.bak';
+
+SHOW DATABASES;
+SHOW TABLES IN empty_db;
+
+RESTORE FROM 'all.bak';
+
+SHOW DATABASES;
+SHOW TABLES IN empty_db;
+
+connection backup;
+
+USE test;
+
+DROP TABLE IF EXISTS t1;
+
+CREATE TABLE t1 (
+  `dir_code` char(4),
+  `building` char(6)
+) ENGINE=myisam DEFAULT CHARSET=latin1;
+
+USE empty_db;
+
+--disable_warnings
+DROP VIEW IF EXISTS v1;
+--enable_warnings
+
+CREATE VIEW v1 AS SELECT * FROM test.t1;
+
+BACKUP DATABASE empty_db TO 'empty_db.bak';
+
+disconnect backup;
+
+connection restore;
+
+SHOW DATABASES;
+
+RESTORE FROM 'empty_db.bak';
+
+USE empty_db;
+
+SHOW TABLES;
+DROP DATABASE IF EXISTS empty_db;
+
+# restore test database to the exact state from the beginning of this test
+--exec rm -rf $MYSQLTEST_VARDIR/master-data/test/*
+--exec cp -rf $MYSQLTEST_VARDIR/test.orig $MYSQLTEST_VARDIR/master-data/test
+--exec rm -rf $MYSQLTEST_VARDIR/test.orig/*
+
diff -Nrup a/mysql-test/t/backup_no_engine.test b/mysql-test/t/backup_no_engine.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup_no_engine.test	2007-11-06 11:11:33 -05:00
@@ -0,0 +1,35 @@
+# Test how online backup handles tables using non-existent storage engines
+# The server used to crash in this situation (BUG#30938)
+
+# .frm file for a table using non-existent storage engine
+--let $table_def=$MYSQL_TEST_DIR/std_data/bug30938.frm
+--let $backup_out_path=$MYSQLTEST_VARDIR/master-data
+
+--disable_warnings
+DROP DATABASE IF EXISTS db;
+--enable_warnings
+
+CREATE DATABASE db;
+CREATE TABLE db.t1 (a int, b char(32))
+ENGINE=myisam;
+
+# copy description of a table using non-existent storage engine
+--copy_file $table_def $MYSQLTEST_VARDIR/master-data/db/t2.frm
+
+# backup test database, the table with no storage engine will be ignored
+BACKUP DATABASE db TO "db.backup";
+
+# wipe-out backed-up database
+DROP DATABASE db;
+CREATE DATABASE db;
+
+RESTORE FROM "db.backup";
+
+# check that the table with no engine was excluded
+SHOW TABLES IN db;
+# check that table t was correctly restored
+SHOW CREATE TABLE db.t1;	
+
+# clean up
+DROP DATABASE db;
+--remove_file $backup_out_path/db.backup
diff -Nrup a/mysql-test/t/backup_snapshot.opt b/mysql-test/t/backup_snapshot.opt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup_snapshot.opt	2007-11-06 11:11:34 -05:00
@@ -0,0 +1 @@
+--loose-debug=d,SLEEP_SNAPSHOT
diff -Nrup a/mysql-test/t/backup_snapshot.test b/mysql-test/t/backup_snapshot.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/backup_snapshot.test	2007-11-06 11:11:35 -05:00
@@ -0,0 +1,81 @@
+#
+# This test is for the consistent snapshot online backup driver
+# The test is designed to show that a consistent snapshot
+# backup can be taken while data is being inserted and deleted.
+#
+# TODO
+#  - Make the test run the insert statements in parallel with the backup
+#    command using --send and --reap and signals from backup code.
+#
+
+--source include/have_innodb.inc
+--source include/have_debug.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS bup_snapshot;
+--enable_warnings
+
+CREATE DATABASE bup_snapshot;
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+connection con1;
+
+# Create a table and load it with lots of data.
+
+CREATE TABLE bup_snapshot.t1 (word CHAR(20)) ENGINE=INNODB;
+
+INSERT INTO bup_snapshot.t1 VALUES ("01 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("02 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("03 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("04 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("05 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("06 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("07 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("08 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("09 Some data to test");
+INSERT INTO bup_snapshot.t1 VALUES ("10 Some data to test");
+
+--echo con1: Show that the new data doesn't exist before backup.
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+SELECT COUNT(*) FROM bup_snapshot.t1;
+
+# While a consistent snapshot backup is executed,
+# no external inserts should be visible to the transaction.
+
+--echo con1: Backing up database.
+BACKUP DATABASE bup_snapshot TO "bup_snapshot.bak";
+
+connection con2;
+
+--echo con2: Inserting new data.
+INSERT INTO bup_snapshot.t1 VALUES("- Dave Mathews");
+INSERT INTO bup_snapshot.t1 VALUES("- Yes");
+INSERT INTO bup_snapshot.t1 VALUES("- Jethro Tull");
+DELETE FROM bup_snapshot.t1 WHERE word LIKE '10%';
+
+--echo con2: Showing the data after inserts.
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+SELECT COUNT(*) FROM bup_snapshot.t1;
+
+connection con1;
+
+# Now restore the database and then check to make sure the new rows
+# were not backed up.
+
+--echo con1: Dropping the database
+DROP TABLE bup_snapshot.t1;
+
+--echo con1: Restoring the database
+RESTORE FROM "bup_snapshot.bak";
+
+--echo con1: Showing the data (no new data should be here).
+SELECT * FROM bup_snapshot.t1 WHERE word LIKE '-%';
+SELECT COUNT(*) FROM bup_snapshot.t1;
+
+DROP DATABASE bup_snapshot;
+
+--exec rm $MYSQLTEST_VARDIR/master-data/bup_snapshot.bak
+
+
diff -Nrup a/sql/backup/CMakeLists.txt b/sql/backup/CMakeLists.txt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/CMakeLists.txt	2007-11-06 11:11:35 -05:00
@@ -0,0 +1,32 @@
+# Copyright (C) 2006 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX")
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
+                    ${CMAKE_SOURCE_DIR}/sql
+                    ${CMAKE_SOURCE_DIR}/sql/backup
+                    ${CMAKE_SOURCE_DIR}/regex
+                    ${CMAKE_SOURCE_DIR}/extra/yassl/include)
+
+SET(BACKUP_SOURCES stream.cc logger.cc string_pool.cc
+               archive.cc meta_backup.cc data_backup.cc
+               sql_backup.cc be_default.cc buffer_iterator.cc
+               be_snapshot.cc)
+
+IF(NOT SOURCE_SUBLIBS)
+  ADD_LIBRARY(backup ${BACKUP_SOURCES})
+ENDIF(NOT SOURCE_SUBLIBS)
diff -Nrup a/sql/backup/Doxyfile b/sql/backup/Doxyfile
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/Doxyfile	2007-11-06 11:11:36 -05:00
@@ -0,0 +1,225 @@
+# Doxyfile 1.5.1-p1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = Online Backup
+PROJECT_NUMBER         = 
+OUTPUT_DIRECTORY       = ./docs
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = YES
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = 
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = NO
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = NO
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = 
+FILE_PATTERNS          = 
+RECURSIVE              = NO
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = 
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff -Nrup a/sql/backup/Makefile.am b/sql/backup/Makefile.am
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/Makefile.am	2007-11-06 11:11:36 -05:00
@@ -0,0 +1,66 @@
+# Copyright (C) 2000 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
+
+## Process this file with automake to create Makefile.in
+
+noinst_LTLIBRARIES = libbackup.la
+
+INCLUDES = \
+	-I$(top_builddir)/include \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/sql \
+	-I$(top_srcdir)/regex
+
+libbackup_la_SOURCES = \
+	stream.cc \
+	logger.cc \
+	string_pool.cc \
+	archive.cc \
+	meta_backup.cc \
+	data_backup.cc \
+	sql_backup.cc \
+      be_default.cc \
+      be_snapshot.cc \
+      buffer_iterator.cc
+
+noinst_HEADERS = \
+	api_types.h \
+	backup_engine.h \
+	backup_kernel.h \
+    debug.h \
+    error.h \
+    stream.h \
+    backup_aux.h \
+    logger.h \
+    map.h \
+    string_pool.h \
+    archive.h \
+    meta_backup.h \
+      be_default.h \
+      be_snapshot.h \
+      buffer_iterator.h
+
+DEFS = \
+	-DMYSQL_SERVER \
+	-DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
+	-DDATADIR="\"$(MYSQLDATAdir)\"" \
+	-DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
+	-DLIBDIR="\"$(MYSQLLIBdir)\"" \
+	@DEFS@
+
+EXTRA_DIST =	CMakeLists.txt 
+	
+# Don't update the files from bitkeeper
+%::SCCS/s.%
diff -Nrup a/sql/backup/api_types.h b/sql/backup/api_types.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/api_types.h	2007-11-06 11:11:37 -05:00
@@ -0,0 +1,286 @@
+#ifndef _BACKUP_API_TYPES_H
+#define _BACKUP_API_TYPES_H
+
+/**
+  @file
+
+  Declarations of common data types used in the backup API's
+ */
+
+/*
+ Note: Structures defined in this file use String class which introduces
+ dependency on its implementation defined in sql/ directory. Thus to correctly
+ compile any code using the backup API one needs to:
+ 1) include sql/mysql_priv.h header
+ 2) link with library ...
+
+ This seems to be not a problem for storage engine plugins as they use String
+ class anyway.
+ */
+
+extern const String my_null_string;
+
+namespace backup {
+
+typedef unsigned char byte;
+
+/**
+  Values returned by backup/restore driver methods and other backup functions.
+
+  @see @c Backup_driver::get_data and @c Restore_driver::send_data
+ */
+
+enum result_t { OK=0, READY, PROCESSING, BUSY, DONE, ERROR };
+
+typedef uint  version_t;
+
+//@{
+
+/**
+   Classes @c Db_ref and @c Table_ref are used to identify databases and tables
+   inside mysql server instance.
+
+   These classes abstract the way a table or database is identified inside mysqld,
+   so that when this changes (introduction of global db/table ids, introduction
+   of catalogues) it is easy to adapt backup code to the new identification schema.
+
+   Regardless of the internal representation, classes provide methods returning
+   db/table name as a @c String object. Also, each table belongs to some database
+   and a method returning @c Db_ref object identifying this database is present. 
+   For @c Db_ref objects there is @c catalog() method returning name of the 
+   catalogue, but currently it always returns null string.
+
+   Classes are implemented so that the memory for storing names can be allocated
+   outside an instance. This allows for sharing space used e.g., to store 
+   database names among several @c Table_ref instances.
+
+   Instances of @c Table_ref and @c Db_ref should be considered cheap to use, 
+   equivalent to using pointers or other base types. Currently, single instance 
+   of each class uses as much memory as a single pointer (+some external memory 
+   to store names which can be shared among different instances). The methods 
+   are inlined to avoid function call costs.
+ */
+
+class Db_ref
+{
+  const String *m_name;
+
+ public:
+
+  // Construct invalid reference
+  Db_ref(): m_name(NULL)
+  {}
+
+  const bool is_valid() const
+  { return m_name != NULL; }
+
+  const String& name() const
+  { return *m_name; }
+
+  const String& catalog() const
+  { return my_null_string; }
+
+  bool operator==(const Db_ref &db) const
+  { return stringcmp(m_name,&db.name())==0; }
+
+  bool operator!=(const Db_ref &db) const
+  { return ! this->operator==(db); }
+
+ protected:
+
+  // Constructors are made protected as clients of this class are
+  // not supposed to create instances (see comment inside Table_ref)
+
+  Db_ref(const String &name): m_name(&name)
+  {}
+
+  friend class Table_ref;
+};
+
+
+class Table_ref
+{
+  const Db_ref  m_db;
+  const String  *m_name;
+
+ public:
+
+  // Construct invalid reference
+  Table_ref(): m_name(NULL)
+  {}
+
+  const bool is_valid() const
+  { return m_name != NULL; }
+
+  const Db_ref& db() const
+  { return m_db; }
+
+  const String& name() const
+  { return *m_name; }
+
+  bool operator==(const Table_ref &t) const
+  {
+    return m_db == t.db() &&
+           stringcmp(m_name,&t.name()) == 0;
+  }
+
+  bool operator!=(const Table_ref &db) const
+  { return ! this->operator==(db); }
+
+  typedef char describe_buf[512];
+
+  /// Produce string identifying the table (e.g. for error reporting)
+  const char* describe(char *buf, size_t len) const
+  {
+    my_snprintf(buf,len,"%s.%s",db().name().ptr(),name().ptr());
+    return buf;
+  }
+
+  const char* describe(describe_buf &buf) const
+  { return describe(buf,sizeof(buf)); }
+  
+ protected:
+
+  /*
+    Constructor is made protected as it should not be used by
+    clients of this class -- they obtain already constructed
+    instances from the backup kernel via Table_list object passed
+    when creating backup/restore driver.
+  */
+
+  Table_ref(const String &db, const String &name):
+    m_db(db), m_name(&name)
+  {}
+};
+
+
+//@}
+
+
+/**
+   @class Table_list
+
+   @brief This abstract class defines interface used to access a list of
+   tables (e.g. when such a list is passed to a backup/restore driver).
+
+   Elements of the list can be accessed by index, counting from 0. E.g.
+   @code
+    Table_list &tables;
+    Table_ref  t2 = tables[1];  // t2 refers to the second element of the list.
+   @endcode
+
+   Interface is made abstract, so that different implementations can be
+   used in the backup code. For example it is possible to create a class which
+   adds this interface to a list of tables represented by a linked list of
+   @c TABLE_LIST structures as used elsewhere in the code. On the other hand, 
+   much more space efficient implementations are possible, as for each table we 
+   need to store only table's identity (db/table name). In any case, the interface
+   to the list remains the same, as defined by this class.
+
+   TODO: add iterators.
+ */
+
+class Table_list
+{
+  public:
+
+    virtual ~Table_list() {}
+
+    /// Return reference to given list element. Elements are counted from 0.
+    virtual Table_ref operator[](uint pos) const =0;
+
+    /// Return number of elements in the list.
+    virtual uint  count() const =0;
+};
+
+
+/**
+  @class Buffer
+
+  @brief Used for data transfers between backup kernel and backup/restore
+  drivers.
+
+  Apart from allocated memory a @c Buffer structure contains fields informing about
+  its size and holding other information about contained data. Buffers are
+  created and memory is allocated by backup kernel. It is also kernel's
+  responsibility to write contents of buffers to a backup stream.
+
+  Data created by a backup driver is opaque to the kernel. However, to support
+  selective restores, each block of data can be assigned to one of the tables
+  being backed-up. This is done by setting @c table_no member of the
+  buffer structure to the number of the table to which this data belongs. Tables
+  are numbered from 1 according to their position in the list passed when driver
+  is created (@c m_tables member of @c Driver class). If
+  some of the data doesn't correspond to any particular table, then
+  @c table_no should be set to 0.
+
+  This way, driver can create several "streams" of data blocks. For each table
+  there is a stream corresponding to that table and there is one "shared stream"
+  consisting of blocks with @c table_no set to 0. Upon restore, kernel
+  sends to a restore driver only blocks corresponding to the tables being
+  restored plus all the blocks from the shared stream.
+
+  For example, consider backing-up three tables t1, t2 and t3. Data blocks
+  produced by a backup driver are divided into four streams:
+  @verbatim
+  #0: shared data
+  #1: data for table t1
+  #2: data for table t2
+  #3: data for table t3
+  @endverbatim
+  When a user restores tables t1 and t3, only blocks from streams #0, #1 and #3
+  will be sent to a restore driver, but not the ones from stream #2.
+
+  Using this approach, backup engine can arrange its backup image data in the
+  way which best suits its internal data representation. If needed, all data can
+  be put in the shared stream #0, so that all of it will be sent back to
+  a restore driver. On the other hand, if possible, backup data can be
+  distributed into per table streams to reduce the amount of data transferred
+  upon a selective restore.
+
+  Backup driver signals end of data in a given stream by setting
+  @c buf.last flag to TRUE when get_data(buf) fills the last block of
+  data from that stream (otherwise @c buf.last should be FALSE). This
+  should be done for each stream used by the driver. Upon restore, kernel sets
+  @c buf.last to TRUE when sending to a restore driver the last block
+  of data from a stream.
+
+  A driver learns about the size of a buffer provided by the kernel from its
+  @c size member. It does not have to fill the buffer completely.
+  It should update the @c size member to reflect the actual size
+  of the data in the buffer. It is possible to return no data in which case
+  @c size should be zero. Such empty buffers are ignored by the
+  kernel (no data is written to the archive).
+ */
+
+struct Buffer
+{
+  size_t  size;       ///< size of the buffer (of memory block pointed by data).
+  uint    table_no;   ///< Number of the table to which data in the buffer belongs.
+  bool    last;       ///< TRUE if this is last block of data in the stream.
+  byte    *data;      ///< Pointer to data area.
+
+  Buffer(): size(0),table_no(0),last(FALSE), data(NULL)
+  {}
+
+  void reset(size_t len)
+  {
+    size= len;
+    table_no= 0;
+    last= FALSE;
+  }
+};
+
+// forward declaration
+class Engine;
+class Backup_driver;
+class Restore_driver;
+
+} // backup namespace
+
+typedef backup::result_t Backup_result_t;
+typedef backup::Engine   Backup_engine;
+typedef Backup_result_t backup_factory(::handlerton *,Backup_engine*&);
+
+#endif
+
diff -Nrup a/sql/backup/archive.cc b/sql/backup/archive.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/archive.cc	2007-11-06 11:11:38 -05:00
@@ -0,0 +1,823 @@
+#include "../mysql_priv.h"
+
+/**
+  @file
+
+  Implementation of @c Archive_info and related classes.
+ */
+
+/*
+  TODO:
+
+  - Add to Archive_info storage for other meta-data items.
+  - Make existing storage solutions more rational (e.g., string pool).
+  - Make reading code resistant to unknown image formats or meta-data types
+    (or, assume it is handled by format version number).
+  - Improve Image_info::Tables implementation (use some existing data structure).
+  - Add more information to backup archive header , for example server's version
+    string.
+  - Handle backward compatibility (new code reads archive with earlier version
+    number)
+  - Add to Archive_info methods for browsing contents of the archive.
+ */
+
+#if defined(USE_PRAGMA_IMPLEMENTATION) || defined(__APPLE_CC__)
+/*
+  #pragma implementation is needed on powermac platform as otherwise compiler 
+  doesn't create/export vtable for Image_info::Tables class (if you know a 
+  better way for fixing this issue let me know! /Rafal).
+  
+  Apparently, configuration macro USE_PRAGMA_IMPLEMENTATION is not set by 
+  ./configure on powermac platform - this is why __APPLE_CC__ is also checked.
+ */ 
+#pragma implementation
+#endif
+
+#include "backup_engine.h"
+#include "backup_aux.h"
+#include "archive.h"
+#include "be_default.h"
+#include "be_snapshot.h"
+
+
+/***************************************
+
+   Implementation of Archive_info class
+
+ ***************************************/
+
+namespace backup {
+
+Archive_info::~Archive_info()
+{
+  for (uint i=0; i<256; ++i)
+   if (images[i])
+   {
+     delete images[i];
+     images[i]= NULL;
+   }
+}
+
+
+/**
+  Write header and catalogue of a backup archive.
+
+  Header forms the first chunk of archive. Currently it contains archive's
+  format version number followed by a list of table data images used in the
+  archive.
+
+  Next chunk contains the pool of database names. After that there is one chunk
+  per image containing list of tables whose data is saved in that image.
+  @verbatim
+  =====================
+   version number        }
+  ---------------------  }  header
+   image descriptions    }
+  =====================
+   db names              }
+  =====================  }
+   tables of image 1     }
+  =====================  }
+                         }  catalogue
+          ...            }
+                         }
+  =====================  }
+   tables of image N     }
+  =====================
+  @endverbatim
+  In the picture "====" denotes chunk boundaries. Number of images is known
+  from the header.
+
+  The format in which image descriptions are saved is determined by
+  @c Image_info::write_description() method.
+
+  For list of databases and tables the format in which they are saved is
+  defined by @c StringPool::save() and @c Archive_info::Tables::save() methods
+  respectively.
+ */
+
+result_t Archive_info::save(OStream &s)
+{
+  DBUG_ENTER("Archive_info::save");
+
+  size_t start_bytes= s.bytes;
+  stream_result::value res;
+
+  res= s.write2int(ver);
+  if (res != stream_result::OK)
+  {
+    DBUG_PRINT("backup",("Can't write archive version number (stream_res=%d)",(int)res));
+    DBUG_RETURN(ERROR);
+  }
+
+  // write list of images
+  DBUG_PRINT("backup",(" writing image list"));
+
+  uint ino;
+  Image_info *img;
+
+  for (ino=0; ino < img_count ; ++ino)
+    if ((img= images[ino]))
+    {
+      DBUG_PRINT("backup",(" %2d: %s image",ino,img->name()));
+      if (ERROR == img->write_description(s))
+      {
+        DBUG_PRINT("backup",("Can't write description of %s image (#%d)",
+                             img->name(),ino));
+        DBUG_RETURN(ERROR);
+      }
+    }
+
+  // close the header chunk
+  res= s.end_chunk();
+  if (res != stream_result::OK)
+  {
+    DBUG_PRINT("backup",("Error when closing image list chunk (stream_res=%d)",(int)res));
+    DBUG_RETURN(ERROR);
+  }
+
+
+  // write catalogue
+  DBUG_PRINT("backup",(" writing archive catalogue"));
+
+  // db names (one chunk)
+  if (ERROR == db_names.save(s)) // note: this closes the chunk
+  {
+    DBUG_PRINT("backup",("Error saving pool of db names (stream_res=%d)",(int)res));
+    DBUG_RETURN(ERROR);
+  }
+
+  // table lists (one chunk per image/list)
+  for (ino=0; ino < img_count ; ++ino)
+    if ((img= images[ino]))
+    {
+      DBUG_PRINT("backup",("  saving %s image's tables",img->name()));
+      if (ERROR == img->tables.save(s))
+      {
+        DBUG_PRINT("backup",("Error saving tables (stream_res=%d)",(int)res));
+        DBUG_RETURN(ERROR);
+      }
+    }
+
+  header_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(OK);
+}
+
+/**
+  Fill @c Archive_info structure reading data from backup archive header and
+  catalogue.
+
+  @returns OK or ERROR
+ */
+result_t Archive_info::read(IStream &s)
+{
+  DBUG_ENTER("Archive_info::read");
+
+  size_t start_bytes= s.bytes;
+  result_t res;
+  version_t ver;
+
+  /*
+    We read archive's header which starts with archive format version number.
+    If we can't read the version number (end of stream or no data in the chunk)
+    there is something wrong and we signal error.
+   */
+
+  stream_result::value rres= s.read2int(ver);
+  if ( rres != stream_result::OK)
+  {
+    DBUG_PRINT("restore",("Error reading archive version number"
+                          " (stream_res=%d)",(int)rres));
+    DBUG_RETURN(ERROR);
+  }
+
+  if (ver != Archive_info::ver)
+  {
+    DBUG_PRINT("restore",("Backup archive version %d not supported",ver));
+    DBUG_RETURN(ERROR);
+  }
+
+  /*
+    What follows (until the end of the data chunk) is a list of entries
+    describing data images of the archive. It is read using
+    Image_info::create_from_stream() function which returns DONE when end of
+    chunk is reached.
+   */
+
+  DBUG_PRINT("restore",(" reading image list"));
+
+  uint ino= 0;
+
+  do
+  {
+    Image_info *img;
+
+    res= Image_info::create_from_stream(*this,s,img);
+
+    if (res == OK)
+    {
+      DBUG_ASSERT(img);
+      DBUG_PRINT("restore",("  %2d: %s image",ino,img->name()));
+      images[ino++]= img;
+    }
+
+  } while (res == OK && ino < MAX_IMAGES);
+
+  img_count= ino;
+
+  // If res != DONE we haven't reached end of the chunk - something is wrong
+  if (res != DONE)
+  {
+    DBUG_PRINT("restore",("Error when reading image list (%d images read)",
+                          img_count));
+    DBUG_RETURN(ERROR);
+  }
+
+  /*
+    Next chunk starts archive's catalogue. We proceed with reading it.
+    Note that the catalogue should always contain at least one chunk (db names
+    pool) and hence we should not hit end of stream here.
+   */
+
+  table_count= 0;
+
+  if (s.next_chunk() != stream_result::OK)
+  {
+    DBUG_PRINT("restore",("Can't proceed to the catalogue"));
+    DBUG_RETURN(ERROR);
+  }
+
+  DBUG_PRINT("restore",(" reading catalogue (%d images)",img_count));
+
+  /*
+    First chunk of the catalogue contains db names pool - we read it and
+    proceed to the next chunk. We should never hit end of stream here and
+    so the only acceptable result of db.names.read() is OK.
+   */
+  res= db_names.read(s);
+  if (res != OK)
+  {
+    DBUG_PRINT("restore",("Can't read db names pool (res=%d)",(int)res));
+    DBUG_RETURN(ERROR);
+  }
+
+  /*
+    The following chunks contain lists of tables for each image. There are
+    as many lists as there are images (possibly 0) and each list occupies
+    one chunk.
+   */
+  for (uint ino=0; ino < img_count; ++ino)
+  {
+    Image_info *img= images[ino];
+
+    DBUG_PRINT("restore",(" reading %s image's tables (#%d)",img->name(),ino));
+
+    /*
+      There should be as many lists in the stream as there were images in the
+      header. Thus we should never hit end of stream here.
+     */
+    res= img->tables.read(s); // note: proceeds to the next chunk in the stream
+
+    if (res != OK)
+    {
+      DBUG_PRINT("restore",("Can't read table list for %s image (#%d)",
+                            img->name(),ino));
+      DBUG_RETURN(ERROR); // neither stream nor chunk should end here
+    }
+
+    table_count+= img->tables.count();
+
+    DBUG_PRINT("restore",(" finished reading tables"));
+  }
+
+  header_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(OK);
+}
+
+} // backup namespace
+
+
+/**********************************
+
+  Write/read image descriptions
+
+ **********************************/
+
+namespace backup {
+
+/**
+  Write entry describing (format of) a backup driver's image.
+
+  Entry has the form:
+  @verbatim
+  | type | version | image description |
+  @endverbatim
+  where type is a byte holding Image_info::image_type value, version is 2 byte
+  integer holding image format version. The format of optional image description
+  is determined by @c X::do_write_description() method where X is a subclass of
+  Image_info corresponding to given image type.
+ */
+result_t
+Image_info::write_description(OStream &s)
+{
+  // TODO: to handle unknown description formats, write description length here
+
+  stream_result::value res= s.writebyte(type());
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  res= s.write2int(ver);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  return do_write_description(s);
+}
+
+/**
+  Create @c Image_info instance from a saved entry describing it.
+
+  @retval OK
+  @retval DONE  end of chunk/stream hit
+  @retval ERROR
+ */
+result_t
+Image_info::create_from_stream(Archive_info &info, IStream &s, Image_info* &ptr)
+{
+  uint ver;
+  byte t;
+
+  stream_result::value res= s.readbyte(t);
+
+  // if we are at end of data chunk or stream, we should tell the caller
+  if (res != stream_result::OK)
+    return report_stream_result(res);
+
+  res= s.read2int(ver);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  switch (image_type(t)) {
+
+  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);
+
+  case SNAPSHOT_IMAGE:
+    return Snapshot_image::create_from_stream(ver,info,s,ptr);
+
+  default:
+    DBUG_PRINT("restore",("Unknown image type %d",t));
+    return ERROR;
+  }
+}
+
+} // backup namespace
+
+
+/*******************
+
+  Serialization of meta-data items
+
+ *******************/
+
+namespace backup {
+
+/**
+  Write an entry describing single meta-data item.
+
+  Entry has format:
+  @verbatim
+  | type | id data | create data |
+  @endverbatim
+  Type is a single byte holding meta::Item::enum_type value. Id data is
+  used to determine which item (from the archive catalogue) the entry
+  corresponds to. Create data is used to create the item.
+
+  The format of id data and create data for item of type X is determined
+  by methods @c Archive_info::X_item::save_id() and @c meta::X::save(),
+  respectively.
+
+  @see @c write_meta_data() for information about the format of the meta-data
+  section of backup archive.
+*/
+result_t
+Archive_info::Item::save(THD *thd, OStream &s)
+{
+  byte b= meta().type();
+
+  stream_result::value res=s.writebyte(b);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  if (ERROR == save_id(s))
+    return ERROR;
+
+  return meta().save(thd,s);
+}
+
+/**
+  Create meta-data item from a saved entry.
+
+  This function reads the type byte and calls @c create_from_stream method of
+  corresponding class to create the item. It stores pointer to the created
+  item in @c ptr argument.
+
+  @retval OK    if new item was created
+  @retval DONE  if end of chunk/stream was reached
+  @retval ERROR if error has happened
+ */
+result_t
+Archive_info::Item::create_from_stream(const Archive_info &info,
+                                       IStream &s, Item* &ptr)
+{
+  byte b;
+
+  stream_result::value res= s.readbyte(b);
+
+  if (res != stream_result::OK)
+    return report_stream_result(res);
+
+  ptr= NULL;
+
+  result_t res1;
+
+  switch (meta::Item::enum_type(b)) {
+
+  case meta::Item::DB:
+    res1= Db_item::create_from_stream(info,s,ptr);
+    break;
+
+  case meta::Item::TABLE:
+    res1= Table_item::create_from_stream(info,s,ptr);
+    break;
+
+  default: return ERROR;
+
+  }
+
+  /*
+    Note that create_from_stream() should return OK - end of data should not
+    happen here.
+   */
+  if (res1 != OK || ptr == NULL)
+    return ERROR;
+
+  return ptr->meta().read(s);
+}
+
+// Db items
+
+result_t Archive_info::Db_item::save_id(OStream &s)
+{
+  uint k= key;
+  DBUG_PRINT("backup",(" saving db-item (%d)",k));
+  return stream_result::OK == s.writeint(k) ? OK : ERROR;
+}
+
+result_t
+Archive_info::Db_item::create_from_stream(const Archive_info &i,
+                                          IStream &s,
+                                          Archive_info::Item* &ptr)
+{
+  uint k;
+  stream_result::value res= s.readint(k);
+
+  if (res != stream_result::OK)
+    return report_stream_result(res);
+
+  return (ptr= new Db_item(i,k)) ? OK : ERROR;
+}
+
+// Table items
+
+result_t Archive_info::Table_item::save_id(OStream &s)
+{
+  DBUG_PRINT("backup",(" saving table-item (%d,%d)",img,pos));
+  stream_result::value res= s.writeint(img);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  res= s.writeint(pos);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  return OK;
+}
+
+result_t
+Archive_info::Table_item::create_from_stream(const Archive_info &i,
+                                             IStream &s,
+                                             Archive_info::Item* &ptr)
+{
+  uint img,no;
+  stream_result::value res= s.readint(img);
+
+  if (res != stream_result::OK)
+    return report_stream_result(res);
+
+  res= s.readint(no);
+
+  if (res != stream_result::OK)
+    return ERROR;
+
+  return (ptr= new Table_item(i,img,no)) ? OK : ERROR;
+}
+
+} // backup namespace
+
+
+/**********************************
+
+  Implementation of Image_info::Tables
+
+ **********************************/
+
+namespace backup {
+
+// TODO: use better implementation (red-black tree from mysys?)
+
+struct Image_info::Tables::node {
+  StringPool::Key db;
+  String          name;
+  node            *next;
+
+  node(const Image_info::Tables&,
+       const StringPool::Key &k,
+       const String &nm): db(k), next(NULL)
+  {
+    name.copy(nm);
+  }
+};
+
+/// Empty the list.
+void Image_info::Tables::clear()
+{
+  for (node *ptr= m_head; ptr;)
+  {
+    node *n=ptr;
+    ptr= n->next;
+    delete n;
+  }
+
+  m_head= m_last= NULL;
+  m_count= 0;
+}
+
+/**
+  Add a table to the list.
+
+  @returns Position of the table or -1 if error
+ */
+int Image_info::Tables::add(const backup::Table_ref &t)
+{
+  StringPool::Key k= m_db_names.add(t.db().name());
+
+  if (!k.is_valid())
+    return -1;
+
+  return add(k,t.name());
+}
+
+/// Add table at given position.
+int Image_info::Tables::add(const StringPool::Key &k, const String &name)
+{
+  node *n= new node(*this,k,name);
+
+  if (!n)
+    return -1;
+
+  if (m_head == NULL)
+  {
+    m_count=1;
+    m_head= m_last= n;
+  }
+  else
+  {
+    m_count++;
+    m_last->next= n;
+    m_last= n;
+  };
+
+  return m_count-1;
+}
+
+/**
+  Locate table at given position.
+
+  @returns Pointer to table's list node or NULL if position is not occupied
+ */
+Image_info::Tables::node*
+Image_info::Tables::find_table(uint pos) const
+{
+  DBUG_ASSERT(pos < m_count);
+
+  node *ptr;
+
+  for (ptr= m_head; ptr && pos; ptr= ptr->next)
+   pos--;
+
+  //if( !ptr ) ptr= m_last;
+
+  return ptr;
+}
+
+/// Return table at a given position.
+inline
+Table_ref Image_info::Tables::operator[](uint pos) const
+{
+  // Get access to backup::Table_ref protected constructor
+
+  struct Table_ref: public backup::Table_ref
+  {
+    Table_ref(const StringPool &db_names, Image_info::Tables::node &n):
+      backup::Table_ref(db_names[n.db],n.name)
+    {}
+  };
+
+  node *ptr= find_table(pos);
+  DBUG_ASSERT(ptr);
+
+  return Table_ref(m_db_names,*ptr);
+}
+
+/******************
+
+   Serialization for Image_info::Tables class
+
+ ******************/
+
+/**
+  Save list of tables in a backup stream.
+
+  The format used assumes that a pool of database names is stored elsewhere.
+  Thus for each table only the key of the database is stored as var-length
+  integer followed by table name. Empty list is saved as single NIL value.
+
+  The list is stored in a single stream chunk which determines its end.
+
+  @returns OK or ERROR
+ */
+result_t
+Image_info::Tables::save(OStream &s)
+{
+  DBUG_ENTER("Image_info::Tables::save");
+  stream_result::value res;
+
+  if (count() == 0)
+  {
+    res= s.writenil();
+    if (res != stream_result::OK)
+      DBUG_RETURN(ERROR);
+  }
+  else
+    for (Tables::node *n= m_head ; n ; n= n->next)
+    {
+      res= s.writeint(n->db);
+      if (res != stream_result::OK)
+        DBUG_RETURN(ERROR);
+
+      res= s.writestr(n->name);
+      if (res != stream_result::OK)
+        DBUG_RETURN(ERROR);
+    };
+
+  res= s.end_chunk();
+  DBUG_RETURN(res == stream_result::ERROR ? ERROR : OK);
+}
+
+/**
+  Read a list from a backup stream.
+
+  @pre Stream is positioned at the first entry of the saved list.
+  @post Stream is positioned at the beginning of next chunk or at its end.
+
+  @retval OK
+  @retval DONE  end of stream or chunk hit (nothing has been read)
+  @retval ERROR
+ */
+result_t
+Image_info::Tables::read(IStream &s)
+{
+  DBUG_ENTER("Image_info::Tables::read");
+
+  stream_result::value res;
+  uint k,tno=0;
+
+  /*
+    Read first entry - if it is NIL, we have empty list. Otherwise it should
+    be db index of the first table.
+   */
+
+  res= s.readint(k);
+
+  // If unexpected result, report an error or end of stream/chunk
+  if (res != stream_result::OK && res != stream_result::NIL)
+    DBUG_RETURN(report_stream_result(res));
+
+  // empty the list
+  clear();
+
+  if (res == stream_result::OK) // this is non-empty list
+    do
+    {
+      String                name;
+
+      res= s.readstr(name);
+      if (res != stream_result::OK)
+        break;
+
+      tno= add(k,name);
+      DBUG_PRINT("restore",("got next table %s.%s (pos %d, dbkey %d)",
+                             (*this)[tno].db().name().ptr(),
+                             (*this)[tno].name().ptr(),tno,k));
+      res= s.readint(k);
+    }
+    while (res == stream_result::OK);
+  else
+   /*
+     If we have read NIL value, pretend we are at the end of chunk so that
+     no errors are reported below.
+    */
+   res= stream_result::EOC;
+
+  // we should be now at end of chunk/stream
+  if (res != stream_result::EOC && res != stream_result::EOS)
+  {
+    DBUG_PRINT("restore",("Error when reading table no %d in table list",tno));
+    DBUG_RETURN(ERROR);
+  }
+
+  res= s.next_chunk();
+  DBUG_RETURN(res == stream_result::ERROR ? ERROR : OK);
+}
+
+} // backup namespace
+
+/**************************************
+
+     Native image type definition
+
+ **************************************/
+
+namespace backup {
+
+/*
+  For native image its format (apart from the version number) is determined
+  by the storage engine whose backup driver created it. Thus we save the name
+  of storage engine.
+
+  TODO: add more information here. E.g. the version number of the storage engine.
+ */
+result_t
+Native_image::do_write_description(OStream &s)
+{
+  String name(::ha_resolve_storage_engine_name(m_hton),&::my_charset_bin);
+  return stream_result::OK == s.writestr(name) ? OK : ERROR;
+}
+
+result_t
+Native_image::create_from_stream(version_t ver,
+                                 Archive_info &info,
+                                 IStream &s, Image_info* &img)
+{
+  String name;
+  stream_result::value res= s.readstr(name);
+
+  if (res != stream_result::OK)
+    return report_stream_result(res);
+
+  LEX_STRING name_lex= name;
+
+  
+  ::handlerton *hton= plugin_data(::ha_resolve_by_name(::current_thd,&name_lex),
+                                  handlerton*);
+  if (!hton)
+    return ERROR;
+
+  img= new Native_image(info,hton);
+  if (!img)
+    return ERROR;
+
+  if (ver > img->ver)
+  {
+    DBUG_PRINT("restore",("Restore diver version %d can't read image version %d",
+                          img->ver,ver));
+    return ERROR;
+  }
+
+  img->ver= ver;
+
+  return OK;
+}
+
+} // backup namespace
diff -Nrup a/sql/backup/archive.h b/sql/backup/archive.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/archive.h	2007-11-06 11:11:38 -05:00
@@ -0,0 +1,486 @@
+#ifndef _BACKUP_ARCHIVE_H
+#define _BACKUP_ARCHIVE_H
+
+/**
+  @file
+
+  Data types used to represent contents of a backup archive and to read/write
+  its description (catalogue)
+ */
+
+#if defined(USE_PRAGMA_INTERFACE) || defined(__APPLE_CC__)
+/*
+  #pragma interface is needed on powermac platform as otherwise compiler 
+  doesn't create/export vtable for Image_info::Tables class (if you know a 
+  better way for fixing this issue let me know! /Rafal).
+  
+  Apparently, configuration macro USE_PRAGMA_INTERFACE is not set by ./configure,
+  on powermac platform - this is why __APPLE_CC__ is also checked.
+ */ 
+#pragma interface
+#endif
+
+#include <backup/api_types.h>
+#include <backup/string_pool.h>
+#include <backup/stream.h>
+#include <backup/backup_engine.h>
+#include <backup/meta_backup.h>
+
+namespace backup {
+
+// Forward declaration for a class describing an image inside backup archive.
+class Image_info;
+
+#define MAX_IMAGES  256
+typedef Image_info* Img_list[MAX_IMAGES]; ///< List (vector) of image descriptions.
+
+/**
+  Describes contents of a backup archive.
+
+  This class stores a catalogue of a backup archive, that is, description of
+  all items stored in the archive (currently only databases and tables). It also
+  determines how to save and read the catalogue to/from a backup stream.
+
+  Only item names are stored in the catalogue. Other item data is stored
+  in the meta-data part of an archive and in case of tables, their data is
+  stored in images created by backup drivers.
+
+  The @c images member stores a list of @c Image_info objects describing the
+  images included in the archive. Each image description contains a list of
+  tables stored in that image (note that no table can be stored in more than
+  one image).
+
+  To save space, we have a separate pool of database names (@c db_names member).
+  In table references, only the key of the database name is stored, not the
+  whole name.
+
+  When reading or writing backup archive, statistics about the size of its parts
+  is stored in the members of this class for later reporting.
+ */
+class Archive_info
+{
+ public:
+ 
+   static const version_t  ver=1;
+   uint  img_count;       ///< number of images in the archive
+   uint  table_count;     ///< total number of tables in the archive
+
+   size_t total_size;   ///< size of processed backup archive
+   size_t header_size;  ///< size of archive's header (after reading or writing an archive)
+   size_t meta_size;    ///< size of archive's meta-data (after reading or writing an archive)
+   size_t data_size;    ///< size of archive's table data images (after reading or writing an archive)
+
+   // Classes representing various types of meta-data items.
+
+   class Item;
+   class Db_item;
+   class Table_item;
+
+  /*
+    Classes which might be used to implement contents browsing.
+
+   class Item_iterator;  // for iterating over all meta-data items
+   class Db_iterator;    // iterates over databases in archive
+   class Ditem_iterator; // iterates over per-db items
+  */
+
+   Img_list images;  ///< list of archive's images
+
+   /// Write archive's header and save the catalogue.
+   result_t save(OStream&);
+   /// Read the header and catalogue from a stream.
+   result_t read(IStream&);
+
+   virtual ~Archive_info();
+
+ protected:
+
+   Archive_info():
+     img_count(0), table_count(0),
+     total_size(0), header_size(0), meta_size(0), data_size(0)
+   {
+     for (uint i=0; i<256; ++i)
+       images[i]= NULL;
+   }
+
+   // storage for meta-data items
+
+   StringPool      db_names; ///< Pool of database names.
+
+ private:
+
+  friend class Image_info;
+  friend class Db_item;
+  friend class Table_item;
+};
+
+/**
+  Describes an image of table data stored in a backup archive.
+
+  An instance of this class:
+  - informs about the type of image,
+  - stores list of tables whose data is kept in the image,
+  - provides methods for creating backup and restore drivers to write/read the
+    image,
+  - determines which tables can be stored in the image,
+  - defines how image's format is described inside backup archive
+    (via @c do_write_description() method)
+ */
+class Image_info
+{
+ public:
+ 
+  enum image_type {NATIVE_IMAGE, DEFAULT_IMAGE, SNAPSHOT_IMAGE};
+
+  virtual image_type type() const =0; ///< Return type of the image.
+  version_t ver;  ///< Image format version.
+
+  /// Check if instance was correctly constructed
+  virtual bool is_valid() =0;
+  /// Create backup driver for the image.
+  virtual result_t get_backup_driver(Backup_driver*&) =0;
+  /// Create restore driver for the image.
+  virtual result_t get_restore_driver(Restore_driver*&) =0;
+
+  size_t init_size; ///< Size of the initial data transfer (estimate). This is
+                    ///< meaningful only after a call to get_backup_driver().
+
+  /// Write header entry describing the image.
+  result_t write_description(OStream&);
+
+  /**
+    Create instance of @c Image_info described by an entry in backup stream.
+
+    @retval OK    entry successfully read
+    @retval DONE  end of chunk or stream has been reached.
+    @retval ERROR an error was detected
+   */
+  static result_t create_from_stream(Archive_info&, IStream&, Image_info*&);
+
+  /// Determine if a table stored in given engine can be saved in this image.
+  virtual bool accept(const Table_ref&, const ::handlerton*) =0;
+
+  /** 
+    Return name identifying the image in debug messages.
+   
+    The name should fit into "%s backup/restore driver" pattern.
+   */
+  virtual const char* name() const
+  { return "<Unknown>"; }
+
+  virtual ~Image_info()
+  {}
+
+   /*
+     Implementation of Table_list interface used to store the
+     list of tables of an image. Database names are stored in
+     external StringPool
+    */
+   class Tables: public Table_list
+   {
+    public:
+
+     Tables(StringPool &db_names):
+       m_db_names(db_names),
+       m_head(NULL),m_last(NULL),m_count(0)
+     {}
+
+     ~Tables() { clear(); }
+
+     int add(const backup::Table_ref&);
+     void clear();
+
+     backup::Table_ref operator[](uint pos) const;
+     //::TABLE_LIST* get_table_ptr(uint pos) const;
+
+     uint count() const
+     { return m_count; }
+
+     result_t save(OStream&);
+     result_t read(IStream&);
+
+    private:
+
+     struct node;
+
+     int add(const StringPool::Key&, const String&);
+     node* find_table(uint pos) const;
+
+     StringPool &m_db_names;
+     node *m_head, *m_last;
+     uint m_count;
+
+     friend class Table_ref;
+     friend class  Archive_info::Table_item;
+   };
+
+  Tables tables; ///< List of tables stored in the image.
+
+ protected:
+
+  Image_info(Archive_info &info):
+    init_size(Driver::UNKNOWN_SIZE), tables(info.db_names)
+  {}
+
+  /**
+    Write image specific data describing it.
+
+    Method redefined in subclasses corresponding to different image types.
+   */
+  virtual result_t do_write_description(OStream&) =0;
+};
+
+/**
+  Represents a meta-data item in a backup archive.
+
+  Instances of this class:
+
+  - identify a meta-data item inside backup archive,
+  - provide storage for a corresponding meta::Item instance,
+  - write item identification data to a backup stream.
+
+  For each type of meta-data there is a specialized subclass of
+  @c Archive_info::Item implementing the above tasks. Each subclass has static
+  @c create_from_stream() method which can create class instance using an
+  identity stored in a stream. For examples, see @c Archive_info::Table_item
+  class.
+
+  Class @c Archive_info::Item defines the format of an entry describing a
+  meta-data item inside the meta-data part of an archive. Such entry is created
+  by @c Archive_info::save() method. These entries are read by
+  @c Restore_info::read_item() method.
+ */
+
+class Archive_info::Item
+{
+ protected:
+
+  /// Pointer to @c Archive_info instance to which this item belongs.
+  const Archive_info *const m_info;
+  Item  *next; ///< Used to create a linked list of all meta-data items.
+
+ public:
+
+  virtual ~Item() {}
+
+  /// Returns reference to the corresponding @c meta::Item instance.
+  virtual meta::Item& meta() =0;
+
+  result_t save(THD*,OStream&); ///< Write entry describing the item.
+
+  // Create item from a saved entry.
+  static result_t create_from_stream(const Archive_info&, IStream&, Item*&);
+
+  class Iterator;
+
+ protected:
+
+  Item(const Archive_info &i): m_info(&i), next(NULL)
+  {}
+
+  /// Save data identifying the item inside the archive.
+  virtual result_t save_id(OStream&) =0;
+
+  friend class Archive_info;
+  friend class Backup_info;
+  friend class Restore_info;
+  friend class Iterator;
+};
+
+
+/**
+  Used to iterate over meta-data items.
+
+  Usage:
+  @code
+   Item *head;
+   for (Item::Iterator it(head); it ; it++)
+   {
+     it->archive_item_method()
+   }
+  @endcode
+  or
+  @code
+   Item *head, *p;
+   Item::Iterator it(head);
+
+   while ((p=it++))
+   {
+     @<use p here>
+   }
+  @endcode
+ */
+class Archive_info::Item::Iterator
+{
+  Item *m_curr;
+  Item *m_prev;
+
+ public:
+
+  Iterator(Item *const head): m_curr(head), m_prev(NULL)
+  {}
+
+  operator bool() const
+  { return m_curr != NULL; }
+
+  Item* operator++(int)
+  {
+    m_prev= m_curr;
+
+    if (m_curr)
+     m_curr= m_curr->next;
+
+    return m_prev;
+  }
+
+  Item* operator->()
+  { DBUG_ASSERT(m_curr);
+    return m_curr; }
+};
+
+
+
+/**
+  Specialization of @c Archive_info::Item representing a database.
+
+  A database is identified by a key into Archive_info::db_names string pool.
+  Using the key one can read database name from the pool. The key is saved
+  as a var-length coded integer.
+ */
+class Archive_info::Db_item:
+  public Archive_info::Item, public meta::Db, public Db_ref
+{
+  StringPool::Key   key;
+
+  Db_item(const Archive_info &i, const StringPool::Key &key):
+    Archive_info::Item(i), Db_ref(m_info->db_names[key]), key(key)
+  {}
+
+  meta::Item& meta()
+  { return *this; }
+
+  /// Get the name from @c db_names pool.
+  const char* sql_name() const
+  { return m_info->db_names[key].ptr(); }
+
+ public:
+
+  result_t save_id(OStream&);
+  /// Create instance reading its identity from a stream.
+  static
+  result_t create_from_stream(const Archive_info&,IStream&, Archive_info::Item*&);
+
+  friend class Archive_info;
+  friend class Backup_info;
+};
+
+
+/**
+  Specialization of @c Archive_info::Item representing a table.
+
+  A table is identified by its position inside the table list of
+  one of archive's images. Its identity is saved as two var-length coded
+  integers: first being the image number and second the table position inside
+  image's table list.
+ */
+class Archive_info::Table_item:
+  public Archive_info::Item, public meta::Table, public Table_ref
+{
+  uint  img;  ///< Image in which this table is saved.
+  uint  pos;  ///< Position of the table in image's table list.
+
+  Table_item(const Archive_info &i, uint no, uint tno):
+    Archive_info::Item(i), Table_ref(i.images[no]->tables[tno]),
+    img(no), pos(tno)
+  {}
+
+ public:
+
+  meta::Item& meta()
+  { return *this; }
+
+  const char* sql_name() const
+  { return m_info->images[img]->tables[pos].name().ptr(); }
+
+  /// Table is a per-db item -- indicate to which database it belongs.
+  const Db_ref in_db()
+  { return m_info->images[img]->tables[pos].db(); }
+
+  result_t save_id(OStream&);
+  /// Create instance reading its identity from a stream.
+  static
+  result_t create_from_stream(const Archive_info&,IStream&, Archive_info::Item*&);
+
+  friend class Backup_info;
+};
+
+} // backup namespace
+
+
+/************************************************************
+
+   Class describing native backup image
+
+ ************************************************************/
+
+namespace backup {
+
+/**
+  Specialization of @c Image_info for images created by native backup drivers.
+ */
+class Native_image: public Image_info
+{
+  const ::handlerton  *m_hton; ///< Pointer to storage engine.
+  Engine   *m_be;  ///< Pointer to the native backup engine.
+
+  const char *m_name;  ///< Used to identify image in debug messages.
+
+ public:
+
+  Native_image(Archive_info &info, const ::handlerton *hton):
+    Image_info(info), m_hton(hton)
+  {
+    DBUG_ASSERT(hton);
+    DBUG_ASSERT(hton->get_backup_engine);
+
+    hton->get_backup_engine(const_cast< ::handlerton* >(hton),m_be);
+
+    if(m_be)
+    {
+      ver= m_be->version();
+      m_name= ::ha_resolve_storage_engine_name(hton);
+    }
+  }
+
+  bool is_valid()
+  { return m_be != NULL; }
+
+  image_type type() const
+  { return NATIVE_IMAGE; }
+
+  const char* name() const
+  { return m_name; }
+
+  result_t get_backup_driver(Backup_driver* &drv)
+  {
+    DBUG_ASSERT(m_be);
+    return m_be->get_backup(Driver::PARTIAL,tables,drv);
+  }
+
+  result_t get_restore_driver(Restore_driver* &drv)
+  {
+    DBUG_ASSERT(m_be);
+    return m_be->get_restore(ver,Driver::PARTIAL,tables,drv);
+  }
+
+  result_t do_write_description(OStream&);
+  static result_t create_from_stream(version_t,Archive_info&,IStream&,Image_info*&);
+
+  bool accept(const Table_ref&, const ::handlerton *hton)
+  { return hton == m_hton; }; // this assumes handlertons are single instance objects!
+};
+
+} // backup namespace
+
+
+#endif
diff -Nrup a/sql/backup/backup_aux.h b/sql/backup/backup_aux.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/backup_aux.h	2007-11-06 11:11:39 -05:00
@@ -0,0 +1,82 @@
+#ifndef _BACKUP_AUX_H
+#define _BACKUP_AUX_H
+
+#include <backup/api_types.h>
+
+namespace backup {
+
+/**
+  Local version of LEX_STRING structure.
+  
+  Defines various constructors for convenience.
+ */ 
+struct LEX_STRING: public ::LEX_STRING
+{
+  LEX_STRING()
+  {
+    str= NULL;
+    length= 0;
+  }
+  
+  LEX_STRING(const ::LEX_STRING &s)
+  {
+    str= s.str;
+    length= s.length;
+  }
+  
+  LEX_STRING(const char *s)
+  {
+    str= const_cast<char*>(s);
+    length= strlen(s);
+  }
+  
+  LEX_STRING(const String &s)
+  {
+    str= const_cast<char*>(s.ptr());
+    length= s.length();
+  }
+};
+
+
+TABLE_LIST *build_table_list(const Table_list&,thr_lock_type);
+
+
+/// Check if a database exists.
+
+inline
+bool db_exists(const Db_ref &db)
+{
+  //  This is how database existence is checked inside mysql_change_db().
+  return ! ::check_db_dir_existence(db.name().ptr());
+}
+
+inline
+bool change_db(THD *thd, const Db_ref &db)
+{
+  LEX_STRING db_name= db.name();
+  
+  return 0 == ::mysql_change_db(thd,&db_name,TRUE);
+}
+
+/*
+  Free the memory for the table list.
+*/
+inline int free_table_list(TABLE_LIST *all_tables)
+{
+  if (all_tables)
+  {
+    TABLE_LIST *tbl= all_tables; 
+    TABLE_LIST *prev= tbl;
+    while (tbl != NULL)
+    {
+      prev= tbl;
+      tbl= tbl->next_global;
+      my_free(prev, MYF(0));
+    }
+  }
+  return 0;
+}
+
+} // backup namespace
+
+#endif
diff -Nrup a/sql/backup/backup_engine.h b/sql/backup/backup_engine.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/backup_engine.h	2007-11-06 11:11:39 -05:00
@@ -0,0 +1,535 @@
+#ifndef MYSQL_PRIV_H
+  #error backup_engine.h must be included after mysql_priv.h
+#endif
+
+#ifndef _BACKUP_ENGINE_API_H
+#define _BACKUP_ENGINE_API_H
+
+/**
+ @file backup_engine.h
+ @brief Backup engine and backup/restore driver API
+
+ The interface between online backup kernel and a backup solution has form of
+ two abstract classes: @c Backup_driver implementing backup
+ functionality and @c Restore_driver for restore functionality.
+ Instances of these two classes are created by a factory class
+ @c Backup_engine which encapsulates and represents the backup
+ solution as a whole.
+ */
+
+#include <backup/api_types.h>
+
+namespace backup {
+
+// Forward declarations
+class Backup_driver;
+class Restore_driver;
+
+/**
+ @class Engine
+ @brief Encapsulates online backup/restore functionality.
+
+ Any backup solution is represented in the online backup kernel by an instance
+ of this class, so called <em>backup engine</em>. This object is used to find
+ out general information about the solution (e.g. version number). It also
+ constructs backup and restore drivers (instances of @c Backup_driver
+ and @c Restore_driver classes) to perform backup/restore operations
+ for a given list of tables.
+
+ @note The backup engine instance is created using get_backup function of
+ handlerton structure. It should be cheap to create instances of @c Engine
+ class as they might be used by the kernel to query backup capabilities of an
+ engine. On the other hand, kernel will take care to create driver instances
+ only when they are really needed.
+ */
+
+class Engine
+{
+ public:
+
+  virtual ~Engine() {}
+
+  /// Return version of backup images created by this engine.
+  virtual const version_t version() const =0;
+
+  /**
+   Create a backup driver.
+
+   Given a list of tables to be backed-up, create instance of backup
+   driver which will create backup image of these tables.
+
+   The @c flags parameter gives additional information about
+   the backup process to be performed by the driver. Currently, we only set
+   @c Driver::FULL flag if the driver is supposed to backup all the
+   tables stored in a given storage engine.
+
+   @param  flags  (in) additional info about backup operation.
+   @param  tables (in) list of tables to be backed-up.
+   @param  drv    (out) pointer to backup driver instance.
+
+   @return  Error code or @c OK on success.
+  */
+  virtual result_t get_backup(const uint32      flags,
+                              const Table_list  &tables,
+                              Backup_driver*    &drv) =0;
+
+  /**
+   Create a restore driver.
+
+   Given a list of tables to be restored, create instance of restore
+   driver which will restore these tables from a backup image.
+
+   The @c flags parameter gives additional information about
+   the restore process to be performed by the driver. Currently, we only set
+   @c Driver::FULL flag if the driver is supposed to replace all the
+   tables stored in a given storage engine with the restored ones.
+
+   @param  version  (in) version of the backup image.
+   @param  flags    (in) additional info about restore operation.
+   @param  tables   (in) list of tables to be restored.
+   @param  drv      (out) pointer to restore driver instance.
+
+   @return  Error code or @c OK on success.
+  */
+  virtual result_t get_restore(const version_t  version,
+                               const uint32     flags,
+                               const Table_list &tables,
+                               Restore_driver*  &drv) =0;
+
+  /**
+   Free any resources allocated by the backup engine.
+
+   It is possible to delete the instance here since backup kernel
+   will never use an instance after a call to @c free() method.
+
+   Backup kernel does not assume that backup engine is allocated
+   dynamically and therefore will never delete an instance it has obtained.
+   However, it will call @c free() method when done with the instance.
+   If the instance is dynamically allocated it should be deleted in this method.
+  */
+  virtual void free() {};
+
+}; // Engine class
+
+
+/**
+ @class Driver
+
+ @brief  This class contains methods which are common to both types of drivers.
+
+ A driver is created to backup or restore given list of tables. This list
+ is passed as an argument when constructing a driver. A reference to
+ the list is stored in @c m_tables member for future use (the memory
+ to store the list is allocated/deallocated by the kernel).
+
+ Driver methods return value of type result_t to inform backup kernel about the
+ result of each operation. If ERROR is returned, it means that the driver is
+ not able to proceed. The kernel will shutdown the driver by calling
+ @c free() method. No other methods will be called after signalling
+ error by a driver.
+*/
+
+class Driver
+{
+ public:
+
+  /// Types of backup/restore operations.
+  enum enum_flags { FULL    =0x1,  ///< concerns all tables from given storage engine
+                    PARTIAL =0     ///< backup/restore only selected tables
+                  };
+
+  /// Construct from list of tables. The list is stored for future use.
+  Driver(const Table_list &tables):m_tables(tables) {};
+
+  virtual ~Driver() {}; // We want to inherit from this class.
+
+  /**
+   @brief Initialize backup/restore process.
+
+   After return from @c begin() call, driver should be ready to
+   serve requests for sending/receiving image data.
+
+   @param buf_size (in)  this is the minimal size of buffers backup kernel
+                         will provide in @c get_data(), @c send_data() methods. 
+                         The buffer can be actually bigger (and its real size 
+                         will be stored in buffers size member) but it will 
+                         never be smaller.
+
+   @return  Error code or @c OK on success.
+  */
+
+  virtual result_t  begin(const size_t buf_size) =0;
+
+
+  /**
+   @brief Finalize backup/restore process.
+
+   This method is called when all data has been sent (from kernel
+   to restore driver or from backup driver to kernel) so that the
+   backup/restore process can be finalized inside the driver.
+
+   @note All DDL operations on tables being backed-up are
+   blocked in the server. An engine which can alter tables (e.g. NDB) should
+   participate in this block by not allowing any such changes between calls to
+   @c begin() and @c end().
+
+   @return  Error code or @c OK on success.
+  */
+
+  virtual result_t  end()   =0;
+
+  /// Cancel ongoing backup/restore process.
+  virtual result_t  cancel() =0;
+
+  /**
+   @brief Free resources allocated by the driver.
+
+   Driver can be deleted here. @see Engine::free()
+  */
+  virtual void  free() {};
+
+  /// Unknown size constant used for backup image size estimates.
+  static const size_t UNKNOWN_SIZE= static_cast<size_t>(-1);
+
+ protected:
+
+  /// Refers to the list of tables passed when the driver was created.
+  const Table_list &m_tables;
+
+}; // Driver class
+
+
+/**
+ @class Backup_driver
+
+ @brief Represents backup driver for backing-up a given list of tables.
+
+ This class provides all the methods used to implement the backup protocol
+ for communication between backup kernel and the driver. The most important
+ method is @c get_data() which is used by the kernel to poll the
+ backup image data and at the same time learn about state of the backup
+ process.
+
+ Backup process consists of the following phases (not all
+ phases are meaningful for all backup methods).
+
+ -# <b>Idle</b>, after creation of the driver instance and before
+    @c begin() call from kernel. Note that any resources should be
+    allocated inside @c begin() method, not upon driver
+    creation.
+ -# <b>Initial transfer</b>, when initial data is sent before driver can create
+    a <em>validity point</em>. "At end" backup drivers will send majority of
+    their data in this phase.
+ -# <b>Waiting for prelock</b>, when driver waits for other drivers to finish
+    sending their initial transfer phase. This phase is ended by a call to
+    @c prelock() method.
+ -# <b>Preparing for lock</b>, when driver does necessary preparations (if any)
+    to be able to instantly crate a validity point upon request from kernel.
+ -# <b>Waiting for lock</b>, when driver waits for other drivers to finish their
+    preparations. Phase is finished by a call to @c lock() method.
+ -# <b>Synchronization</b>, when the validity point is created inside
+    @c lock() method. For synchronization reasons, data in all tables
+    being backed-up should be frozen during that phase. Phase is ended by a call
+    to @c unlock() method.
+ -# <b>Final transfer</b>, when final backup image data (if any) is sent to the
+    kernel. "At begin" will send all their data in this phase. This phase is
+    ended by a call to @c end() method.
+
+ In each phase, except for the synchronization phase (6),  kernel is polling
+ driver using @c get_data() method. Thus a driver has a chance to send
+ data in each phase of the backup process. For example, when waiting in phase 3
+ or 5, driver can send log recording changes which happen during that time.
+
+ A driver informs the kernel about finishing the initial transfer phase (2) or
+ the lock preparation phase (4) by the value returned from the
+ @c get_data() method (see description of the method).
+
+ Not all drivers will need all the phases to perform backup but they should
+ still follow the protocol and give correct replies from @c get_data()
+ method.
+
+ @note The list of tables being backed-up is accessible via @c m_tables
+ member inherited from @c Driver class
+
+ @see Methods @c begin(), @c end(), @c get_data(), @c prelock(), @c lock(), 
+ @c unlock() and @c Driver class.
+*/
+
+class Backup_driver: public Driver
+{
+ public:
+
+  Backup_driver(const Table_list &tables):Driver(tables) {};
+
+  virtual ~Backup_driver() {}; // Each specific implementation will derive from this class.
+
+ /**
+   @fn result_t get_data(Buffer &buf)
+
+   @brief Accept a request for filling a buffer with next chunk of backup data
+   or check status of a previously accepted request.
+
+   Backup driver can implement its own policy of handling these requests. It can
+   return immediately from the call and use a separate thread to fill the buffer
+   or the calling thread can be used to do the job. It is also possible that
+   a driver accepts new requests while processing old ones, implementing
+   internal queue of requests.
+
+   The kernel learns about what happened to the request from the value returned
+   by the method (see below). The returned value is also used to inform the
+   kernel that the driver has finished the initial transfer phase (2) or the
+   prepare to lock phase (4) (see description of the class).
+
+   When a request is completed, members of @c buf should be filled
+   as described in the documentation of Buffer class. It is possible to complete
+   a request without putting any data in the buffer. In that case
+   @c buf.size should be set to zero. The return value (OK or READY)
+   and the @c buf.table_no and @c buf.last members are
+   interpreted as usual. However, no data is written to backup archive and such
+   empty buffers are not sent back to restore driver.
+
+
+   @param  buf (in/out) buffer to be filled with backup data. Its members are
+               initialized by backup kernel: @c buf.data points to a memory area 
+               where the data should be placed and @c buf.size is the size of 
+               the area. Upon completion of the request (@c OK or @c READY
+               returned), members of @c buf should be filled as described in 
+               the documentation of Buffer class.
+
+   @retval OK  The request is completed - new data is in the buffer and
+               @c size, @c table_no and @c last members of the buffer structure
+               are set accordingly.
+
+   @retval READY Same as OK and additionally informs that the initial transfer
+               phase (2) or the prepare to lock phase (4) are finished for that
+               driver.
+
+   @retval DONE Same as OK but also indicates that the backup process is
+               completed. This result can be returned only in the final transfer
+               phase (7).
+
+   @retval PROCESSING The request was accepted but is not completed yet. Further
+               calls to get_data() are needed to complete it (until it returns
+               OK or READY). Kernel will not reuse the buffer before it knows
+               that it is completely filled with data.
+
+   @retval BUSY The request can not be accepted now. Kernel can try to place a
+               request later. The buffer is not blocked and can be used for
+               other transfers.
+
+   @retval ERROR An error has happened while processing the request.
+
+   @note
+   If backup kernel calls @c get_data() when there is no more data
+   to be sent, the driver should:
+   -# set @c buf.size and @c buf.table_no to 0,
+   -# set @c buf.last to TRUE,
+   -# return @c DONE.
+
+   @see @c Buffer class.
+  */
+
+  virtual result_t  get_data(Buffer &buf) =0;
+
+
+  /**
+   @fn result_t prelock()
+
+   @brief Prepare for synchronization of backup image.
+
+   This method is called by backup kernel when all engines participating in
+   creation of the backup have finished their initial data transfer. After this
+   call the driver should prepare for the following @c lock() call
+   from the kernel.
+
+   It can do the preparations inside the @c prelock() method if it
+   doesn't require too long time. In that case it should return
+   @c READY. If the preparations require longer time (waiting for
+   ongoing operations to finish) or sending additional data to the kernel then
+   @c prelock() should return @c OK. Later on, the kernel
+   will call @c get_data() and driver can signal that it has finished
+   the preparations by returning @c READY result.
+
+   @retval READY The driver is ready for synchronization, i.e. it can accept the
+              following @c lock() call.
+
+   @retval OK The driver is preparing for synchronization. Kernel should call
+              @c get_data() and wait until driver is ready.
+
+   @retval ERROR The driver can not prepare for synchronization.
+  */
+
+  virtual result_t  prelock()
+  {  return READY; };
+
+  /**
+   @brief Create validity point and freeze all backed-up data.
+
+   After sending @c prelock() requests to all backup drivers and
+   receiving @c READY confirmations, backup kernel calls
+   @c lock() method of each driver. The driver is supposed to do two
+   things in response:
+
+   -# Create a validity point of its backup image. The whole backup image should
+      describe data at this exact point in time.
+   -# Freeze its state until the following @c unlock() call. This
+      means that from now on the data stored in the backed-up tables should not
+      change in any way, so that the validity point remains valid during the
+      time other engines create their own validity points.
+
+   When all drivers have locked, backup kernel will call @c unlock()
+   on all of them. After this call the driver should unfreeze. Kernel will
+   continue polling backup data using @c get_data() method until
+   driver signals that there is no more data to be sent.
+
+   @note <b>Important!</b>. A call to @c lock() should return as
+   quickly as possible since it blocks other drivers. Ideally, only fast memory
+   access and/or (non-blocking) mutex manipulations should happen but no disk
+   operations. The backup kernel expects that this call will return in at most
+   few seconds.
+
+   @returns Error code or @c OK upon success.
+  */
+
+  virtual result_t  lock() =0;
+
+  /**
+   @brief Unlock data after the @c lock() call.
+
+   After call to @c unlock() driver enters the final data transfer
+   phase. Any remaining data should be sent in the following
+   @c get_data() calls and all data streams should be closed. The
+   process is ended by returning @c DONE from the last
+   @c get_data() call.
+
+   @note <b>Important!</b>. Similar as with @c lock(), a call to
+   @c unlock() should return as quickly as possible to not block other
+   drivers.
+  */
+
+  virtual result_t  unlock() =0;
+
+
+  /**
+   Return estimate (in bytes) of the size of the backup image.
+
+   This estimate is used by backup kernel to give backup progress feedback to
+   users.
+   If estimating the size is impossible or very costly, the driver can return
+   @c UNKNOWN_SIZE.
+  */
+
+  virtual size_t    size() =0;
+
+  /**
+   Estimate how much data will be sent in the initial phase of backup.
+
+   This information is used by backup kernel to initialize backup drivers of
+   different types at correct times. Roughly, drivers with biggest
+   @c init_size() will be initialized and polled first. Drivers whose
+   @c init_size() is zero, will be initialized and polled last in the
+   process.
+
+   Thus "at begin" drivers which send all backup data in the final phase
+   of backup should return 0 here. Drivers of "at end" type should return
+   estimate for the size of data to be sent before they are ready for validity
+   point creation. If estimating this size is impossible or very expensive, the
+   driver can return UNKNOWN_SIZE. In that case  the driver will be initialized
+   and polled before any other drivers.
+  */
+
+  virtual size_t    init_size() =0;
+
+}; // Backup_driver class
+
+/**
+ @class Restore_driver
+
+ @brief Represents restore driver used for restoring a given list of tables.
+
+ This class provides all the methods used to implement the restore protocol
+ for communication between backup kernel and the driver. Apart from the common
+ driver methods of @c Driver class it provides
+ @c send_data() method which is used by the kernel to send
+ backup image data to the driver.
+
+ It is assumed that all tables are blocked during restore process.
+
+ @note The list of tables being restored is accessible via @c m_tables
+ member inherited from @c Driver class
+
+ @see @c Driver class.
+*/
+
+class Restore_driver: public Driver
+{
+ public:
+
+  Restore_driver(const Table_list &tables):Driver(tables) {};
+  virtual ~Restore_driver() {};
+
+  /**
+   @fn result_t send_data(Buffer &buf)
+
+   @brief Request processing of next block of backup image data or check
+   status of a previously accepted request.
+
+   Upon restore, backup kernel calls this method periodically sending
+   consecutive blocks of data from the backup image. The @c table_no
+   field in the buffer is set to indicate from which stream the data comes.
+   Also, @c buf.last is TRUE if this is the last block
+   in the stream.
+
+   Blocks are sent to restore driver in the same order in which they were
+   created by a backup driver. This is true also when only selected blocks are
+   sent.
+
+   Restore driver can implement its own policy of handling data processing
+   requests. It is possible that it returns immediately from the call and uses
+   a separate thread to process data in the buffer and it is also possible that
+   the calling thread is used to do the job.
+
+   Returning OK means that the data has been successfully processed
+   and the buffer can be re-used for further transfers. If method returns
+   PROCESSING, it means that the request was accepted but is not
+   completed yet. The buffer will not be used for other purposes until a further
+   call to @c get_data() with the same buffer as argument returns OK.
+
+   @param  buf   (in) buffer filled with backup data. Fields @c size,
+                 @c table_no and @c last are set
+                 accordingly.
+
+   @retval OK    The data has been successfully processed - the buffer can be
+                 used for other transfers.
+
+   @retval DONE  Same as OK but also indicates that the restore process is
+                 completed.
+
+   @retval PROCESSING  The request was accepted but data is not processed yet -
+                 further calls to @c send_data() are needed to
+                 complete it. The buffer is blocked and can't be used for other
+                 transfers.
+
+   @retval BUSY  The request can not be processed right now. A call to
+                 @c send_data() should be repeated later.
+
+   @retval ERROR An error has happened. The request is cancelled and the buffer
+                 can be used for other transfers.
+
+   @see @c Buffer class.
+  */
+
+  virtual result_t  send_data(Buffer &buf) =0;
+
+}; // Restore_driver
+
+
+} // backup namespace
+
+// export Backup/Restore_driver classes to global namespace
+
+using backup::Backup_driver;
+using backup::Restore_driver;
+
+
+#endif
diff -Nrup a/sql/backup/backup_kernel.h b/sql/backup/backup_kernel.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/backup_kernel.h	2007-11-06 11:11:40 -05:00
@@ -0,0 +1,225 @@
+#ifndef _BACKUP_KERNEL_API_H
+#define _BACKUP_KERNEL_API_H
+
+#include <backup/api_types.h>
+#include <backup/archive.h>
+#include <backup/stream.h>
+#include <backup/logger.h>
+
+
+/*
+  Called from the big switch in mysql_execute_command() to execute
+  backup related statement
+ */
+int execute_backup_command(THD*, LEX*);
+
+/**
+  @file
+
+  Functions and types forming the backup kernel API
+
+ */
+
+namespace backup {
+
+class Archive_info;
+class Backup_info;
+class Restore_info;
+
+} // backup namespace
+
+// Backup kernel API
+
+int mysql_show_archive(THD*,const backup::Archive_info&);
+int mysql_backup(THD*, backup::Backup_info&, backup::OStream&);
+int mysql_restore(THD*, backup::Restore_info&, backup::IStream&);
+
+
+namespace backup {
+
+/**
+  Represents a location where backup archive can be stored.
+
+  The class is supposed to represent the location on the abstract level
+  so that it is easier to add new types of locations.
+
+  Currently we support only files on the server's file system. Thus the
+  only type of location is a path to a file.
+ */
+
+struct Location
+{
+  /// Type of location  
+  enum enum_type {SERVER_FILE, INVALID};
+  bool read;
+
+  virtual enum_type type() const
+  { return INVALID; }
+
+  virtual ~Location() {}  // we want to inherit this class
+
+  /// Deallocate any resources used by Location object.
+  virtual void free()
+  { delete this; }  // use destructor to free resources
+
+  /// Describe location for debug purposes
+  virtual const char* describe()
+  { return "Invalid location"; }
+
+  /**
+    Interpret string passed to BACKUP/RESTORE statement as backup location
+    and construct corresponding Location object.
+
+    @returns NULL if the string doesn't denote a valid location
+   */
+  static Location* find(const LEX_STRING&);
+};
+
+
+/**
+  Specialization of @c Archive_info which adds methods for selecting items
+  to backup.
+
+  When Backup_info object is created it is empty and ready for adding items
+  to it. Methods @c add_table() @c add_db(), @c add_dbs() and @c add_all_dbs()
+  can be used for that purpose (currently only databases and tables are
+  supported). After populating info object with items it should be "closed"
+  with a call to @c close() method. After that it is ready for use as a
+  description of backup archive to be created.
+
+  A linked list of all meta-data items is pointed by @c m_items member. It
+  consists of three parts: first all the global items, then all per-database
+  items and finally all per-table items. Inside each part, items are stored in
+  dependency order so that if item A depends on B then B is before A in the
+  list (currently dependencies are not checked). One should iterate through the
+  meta-data item list using @c Backup_info::Item_iterator class.
+ */
+
+class Backup_info: public Archive_info, public Logger
+{
+  class Table_ref;
+  class Db_ref;
+
+ public:
+
+  Backup_info(THD*);
+  ~Backup_info();
+
+  bool is_valid()
+  {
+    bool ok= TRUE;
+
+    switch (m_state) {
+
+    case ERROR:
+      ok= FALSE;
+      break;
+
+    case INIT:
+      ok= (i_s_tables != NULL);
+      break;
+
+    default:
+      ok= TRUE;
+    }
+
+    if (!ok)
+      m_state= ERROR;
+
+    return ok;
+  }
+
+  int save(OStream&);
+
+  int add_dbs(List< ::LEX_STRING >&);
+  int add_all_dbs();
+
+  bool close();
+
+   class Item_iterator;  // for iterating over all meta-data items
+
+ private:
+
+  /// State of the info structure.
+  enum {INIT,   // structure ready for filling
+        READY,  // structure ready for backup (tables opened)
+        DONE,   // tables are closed
+        ERROR
+       }   m_state;
+
+  int find_image(const Table_ref&);
+
+  int default_image_no; ///< Position of the default image in @c images list, -1 if not used.
+  int snapshot_image_no; ///< Position of the snapshot image in @c images list, -1 if not used.
+
+  Db_item*    add_db(const backup::Db_ref&);
+  Table_item* add_table(const Table_ref&);
+  
+  /// Value returned by @c add_table if it decides that the table should be skipped.
+  static const Table_item *const skip_table;
+
+  int add_db_items(Db_item&);
+  int add_table_items(Table_item&);
+
+  THD    *m_thd;
+  TABLE  *i_s_tables;
+
+  Item   *m_items;
+  Item   *m_last_item;
+  Item   *m_last_db;
+  
+  friend class Item_iterator;
+};
+
+class Backup_info::Item_iterator: public Archive_info::Item::Iterator
+{
+ public:
+  Item_iterator(const Backup_info &info):
+    Archive_info::Item::Iterator(info.m_items)
+  {}
+};
+
+/**
+  Specialization of @c Archive_info which is used to select and restore items
+  from a backup archive.
+
+  An instance of this class is created by reading backup archive header and it
+  describes contents of the archive. @c Restore_info methods select which items
+  should be restored. Instances of @c Restore_info::Item class are created when
+  reading meta-data info stored in the archive. They are used to restore the
+  meta-data items (but not the table data, which is done by restore drivers).
+
+  @note This class is not fully implemented. Right now it is not possible to
+  select items to restore - always all items are restored.
+ */
+
+class Restore_info: public Archive_info, public Logger
+{
+  bool m_valid;
+
+ public:
+
+  Restore_info(IStream &s): Logger(Logger::RESTORE), m_valid(TRUE)
+  {
+    result_t res= read(s);
+    if (res == ERROR)
+    {
+      report_error(ER_BACKUP_READ_HEADER);
+      m_valid= FALSE;
+    }
+  }
+
+  bool is_valid() const
+  { return m_valid; }
+
+  int restore_all_dbs()
+  { return 0; }
+
+  /// Determine if given item is selected for restore.
+  bool selected(const Archive_info::Item&)
+  { return TRUE; }
+};
+
+} // backup namespace
+
+#endif
diff -Nrup a/sql/backup/be_default.cc b/sql/backup/be_default.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/be_default.cc	2007-11-06 11:11:41 -05:00
@@ -0,0 +1,871 @@
+/* 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, TL_READ_NO_INSERT);
+  if (!ptr)
+    DBUG_RETURN(ERROR);
+  drv= ptr;
+  DBUG_RETURN(OK);
+}
+
+Backup::Backup(const Table_list &tables, THD *t_thd, thr_lock_type lock_type): 
+               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, lock_type);
+  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
+      {
+        size_t 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:
+  {
+    size_t rec_size= 0; 
+
+    rec_size= rec_buffer.get_next((byte **)&ptr, (buf.size - META_SIZE));
+    memcpy((byte *)buf.data + META_SIZE, ptr, rec_size);
+    buf.size = rec_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;
+    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
+    {
+      size_t 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:
+  {
+    size_t bb_size= 0;
+
+    bb_size= blob_buffer.get_next((byte **)&ptr, (buf.size - META_SIZE));
+    memcpy((byte *)buf.data + META_SIZE, ptr, bb_size);
+    buf.size = bb_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= BLOB_DATA;
+    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, 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;
+  for (int i=0; i < MAX_FIELDS; i++)
+    blob_ptrs[i]= 0;
+  blob_ptr_index= 0;
+}
+
+/**
+  * @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)
+        {
+          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);
+        rec_buffer.reset();
+        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]);
+      /*
+        Free the blob pointers used.
+      */
+      for (int i=0; i < blob_ptr_index; i++)
+        if (blob_ptrs[i])
+        {
+          my_free(blob_ptrs[i], MYF(0));
+          blob_ptrs[i]= 0;
+        }
+      blob_ptr_index= 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:
+    {
+      blob_ptrs[blob_ptr_index]= (byte *)my_malloc(size, MYF(MY_WME));
+      memcpy(blob_ptrs[blob_ptr_index], (byte *)buf.data + META_SIZE, size);
+      ((Field_blob*) cur_table->field[*cur_blob])->set_ptr(size, 
+        (uchar *)blob_ptrs[blob_ptr_index]);
+      blob_ptr_index++;
+      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_ptrs[blob_ptr_index]= (byte *)my_malloc(max_blob_size, MYF(MY_WME));
+      blob_buffer.initialize(blob_ptrs[blob_ptr_index], 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);
+      blob_ptr_index++;
+      mode= CHECK_BLOBS;
+      DBUG_RETURN(PROCESSING);
+    }
+    default:
+      DBUG_RETURN(ERROR);
+    }
+    break;
+  }
+
+  default:
+    DBUG_RETURN(ERROR);
+  }
+  DBUG_RETURN(OK); 
+}
+
+} /* default_backup namespace */
+
+
diff -Nrup a/sql/backup/be_default.h b/sql/backup/be_default.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/be_default.h	2007-11-06 11:11:41 -05:00
@@ -0,0 +1,250 @@
+#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"
+#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;
+
+const size_t 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(const 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, thr_lock_type lock_type);
+    virtual ~Backup() { backup::free_table_list(all_tables); }; 
+    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:
+    my_bool lock_called;           ///< Checks to see if locks have been reached.
+    THD *m_thd;                    ///< Pointer to current thread struct.
+
+  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 *all_tables;        ///< Reference to list of tables used.
+    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() { backup::free_table_list(all_tables); };
+    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; };
+
+ 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.
+    byte *blob_ptrs[MAX_FIELDS];   ///< List of blob pointers used
+    int blob_ptr_index;            ///< Position in blob pointer list
+    THD *m_thd;                    ///< Pointer to current thread struct.
+    TABLE_LIST *all_tables;        ///< Reference to list of tables used.
+};
+} // 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, 
+                                            TL_READ_NO_INSERT)) ? 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
+
diff -Nrup a/sql/backup/be_snapshot.cc b/sql/backup/be_snapshot.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/be_snapshot.cc	2007-11-06 11:11:42 -05:00
@@ -0,0 +1,142 @@
+/* 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 snapshot backup algorithm driver.
+ *
+ * This file contains the snapshot backup algorithm (also called a "driver"
+ * in the online backup terminology. The snapshot backup algorithm may be
+ * used in place of an engine-specific driver if one does not exist or if
+ * chosen by the user.
+ *
+ * The snapshot backup algorithm is a non-blocking algorithm that enables a
+ * consistent read of the tables given at the start of the backup/restore 
+ * process. This is accomplished by using a consistent snapshot transaction
+ * and table locks. Once all of the data is backed up or restored, the locks 
+ * are removed. The snapshot 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 "snapshot_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 the same as the default backup driver.
+ * Please see <code> be_default.cc </code> for a complete description.
+ */
+
+#include "mysql_priv.h"
+#include "backup_engine.h"
+#include "be_snapshot.h"
+#include "backup_aux.h"
+
+namespace snapshot_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;
+
+/**
+ * Create a snapshot 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 code or backup::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 snapshot_backup::Backup(tables, m_thd);
+  if (!ptr)
+    DBUG_RETURN(ERROR);
+  drv= (backup::Backup_driver *)ptr;
+  DBUG_RETURN(OK);
+}
+
+/**
+ * @brief End backup process.
+ *
+ * This method unlocks all of the tables.
+ *
+ * @retval backup::OK    all tables unlocked.
+ */
+result_t Backup::end()
+{
+  DBUG_ENTER("Snapshot_backup::end");
+  end_active_trans(m_thd);
+  DBUG_RETURN(OK);
+}
+
+result_t Backup::lock()
+{
+  DBUG_ENTER("Snapshot_backup::lock()");
+  /*
+    We must fool the locking code to think this is a select because
+    any other command type places the engine in a non-consistent read
+    state. 
+  */
+  m_thd->lex->sql_command= SQLCOM_SELECT; 
+  m_thd->lex->start_transaction_opt|= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
+  int res= begin_trans(m_thd);
+  if (res)
+    DBUG_RETURN(ERROR);
+  lock_called= TRUE;
+  DBUG_RETURN(OK);
+}
+
+result_t Backup::unlock()
+{
+  DBUG_ENTER("Snapshot_backup::unlock()");
+  DBUG_RETURN(OK);
+}
+
+/**
+ * Create a snapshot 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 code or backup::OK on success.
+ */
+result_t Engine::get_restore(version_t, const uint32, const Table_list &tables,
+Restore_driver* &drv)
+{
+  DBUG_ENTER("Engine::get_restore");
+  Restore *ptr= new snapshot_backup::Restore(tables, m_thd);
+  if (!ptr)
+    DBUG_RETURN(ERROR);
+  drv= ptr;
+  DBUG_RETURN(OK);
+}
+
+} /* snapshot_backup namespace */
+
+
diff -Nrup a/sql/backup/be_snapshot.h b/sql/backup/be_snapshot.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/be_snapshot.h	2007-11-06 11:11:42 -05:00
@@ -0,0 +1,144 @@
+#ifndef _SNAPSHOT_BACKUP_H
+#define _SNAPSHOT_BACKUP_H
+
+#include "archive.h"        
+#include "buffer_iterator.h"
+#include "be_default.h"
+
+namespace snapshot_backup {
+
+using backup::byte;
+using backup::result_t;
+using backup::version_t;
+using backup::Table_list;
+using backup::Table_ref;
+using backup::Buffer;
+
+/**
+ * @class Engine
+ *
+ * @brief Encapsulates snapshot online backup/restore functionality.
+ *
+ * This class is used to initiate the snapshot 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 but supports consisten reads.
+ * It may also be used as an option by the user.
+ *
+ * Using this class, the caller can create an instance of the snapshot 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 snapshot backup image.
+ */
+class Engine: public Backup_engine
+{
+  public:
+    Engine(THD *t_thd) { m_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(const version_t ver, const uint32, const Table_list &tables,
+                         Restore_driver* &drv);
+  private:
+    THD *m_thd;     ///< Pointer to the current thread.
+};
+
+/**
+ * @class Backup
+ *
+ * @brief Contains the snapshot 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 using a consistent read transaction.
+ *
+ * @see <backup driver>
+ */
+class Backup: public default_backup::Backup
+{
+  public:
+    Backup(const Table_list &tables, THD *t_thd): 
+      default_backup::Backup(tables, t_thd, TL_READ) {};
+    virtual ~Backup() {};
+    result_t begin(const size_t) { return backup::OK; };
+    result_t end();
+    result_t prelock() { return backup::READY; }
+    result_t lock();
+    result_t unlock();
+};
+
+/**
+ * @class Restore
+ *
+ * @brief Contains the snapshot 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 default_backup::Restore
+{
+  public:
+    Restore(const Table_list &tables, THD *t_thd):
+      default_backup::Restore(tables, t_thd){};
+    virtual ~Restore(){};
+    void free() { delete this; };
+};
+} // snapshot_backup namespace
+
+
+/*********************************************************************
+
+  Snapshot image class
+
+ *********************************************************************/
+
+namespace backup {
+
+
+class Snapshot_image: public Image_info
+{
+ public:
+
+  Snapshot_image(Archive_info &info): Image_info(info)
+  { ver= 1; }
+
+  image_type type() const
+  { return SNAPSHOT_IMAGE; }
+
+  const char* name() const
+  { return "Snapshot"; }
+
+  bool accept(const Table_ref&, const ::handlerton* h)
+  { 
+    return (h->start_consistent_snapshot != NULL); 
+  }; // accept all tables that support consistent read
+
+  result_t get_backup_driver(Backup_driver* &ptr)
+  { return (ptr= new snapshot_backup::Backup(tables,::current_thd)) ? OK : ERROR; }
+
+  result_t get_restore_driver(Restore_driver* &ptr)
+  { return (ptr= new snapshot_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 Snapshot_image(info)) ? OK : ERROR;
+  }
+
+  bool is_valid(){ return TRUE; };
+
+};
+
+} // backup namespace
+
+
+#endif
+
diff -Nrup a/sql/backup/buffer_iterator.cc b/sql/backup/buffer_iterator.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/buffer_iterator.cc	2007-11-06 11:11:43 -05:00
@@ -0,0 +1,204 @@
+/* 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, size_t 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(size_t 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));
+  alloc_used= FALSE;
+  buffer= NULL;
+  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
+ */
+size_t Buffer_iterator::get_next(byte **buff_ptr, size_t window)
+{
+  size_t 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, size_t 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(size_t 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;
+  if (!alloc_used && buffer)
+    buffer= 0;
+  DBUG_RETURN(ptr);
+}
diff -Nrup a/sql/backup/buffer_iterator.h b/sql/backup/buffer_iterator.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/buffer_iterator.h	2007-11-06 11:11:44 -05:00
@@ -0,0 +1,92 @@
+#ifndef _BUFFER_ITERATOR_H
+#define _BUFFER_ITERATOR_H
+
+#include "mysql_priv.h"
+
+using backup::byte;
+
+/**
+ * @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, size_t size);
+    int initialize(size_t size);
+    int reset();
+    size_t get_next(byte **buff_ptr, size_t window);
+    int put_next(byte *buff_ptr, size_t size);
+    int num_windows(size_t 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
+    size_t max_size;       ///< The maximum size of the block of data
+    size_t window_size;    ///< The size of the window to read
+    size_t cur_bytes_read; ///< The number of bytes read
+    bool alloc_used;       ///< Indicates whether to dealloc memory or not
+};
+
+#endif
diff -Nrup a/sql/backup/data_backup.cc b/sql/backup/data_backup.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/data_backup.cc	2007-11-06 11:11:44 -05:00
@@ -0,0 +1,1573 @@
+#include "../mysql_priv.h"
+
+/**
+  @file
+
+  Code used to backup table data.
+
+  Function @c write_table_data() and @c restore_table_data() use backup/restore
+  drivers and protocols to create image of the data stored in the tables being
+  backed up.
+ */
+
+/*
+  TODO
+
+  - Implement better scheduling strategy in Scheduler::step
+
+     The driver with least data read is chosen. If there are several,
+     they are polled in round-robin fashion.
+
+  - For consistency, always use OStream for data serialization (?)
+
+  - In Backup_pump::pump(), handle DONE response from backup driver.
+
+  - In case of backup/restore of empty archive, check that all resources
+    are correctly deallocated.
+
+  - Try to announce block size when initializing restore driver with begin()
+    method (is it possible?).
+
+  - 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.
+
+  - The table locking code in backup and restore for default and snapshot drivers
+    should be replaced by something better in version beta.
+
+ */
+
+#include "backup_engine.h"
+#include "stream.h"
+#include "backup_kernel.h"
+#include "debug.h"
+#include "be_default.h"
+#include "be_snapshot.h"
+
+/***********************************************
+
+                  DATA BACKUP
+
+ ***********************************************/
+
+namespace backup {
+
+struct backup_state {
+
+ /// State of a single backup driver.
+ enum value { INACTIVE,   ///< Before backup process is started (phase 0).
+              INIT,       ///< During initial data transfer (phase 1).
+              WAITING,    ///< Waiting for other drivers to finish init phase (phase 2).
+              PREPARING,  ///< Preparing for @c lock() call (phase 3).
+              READY,      ///< Ready for @c lock() call (phase 4)
+              FINISHING,  ///< Final data transfer (phase 7).
+              DONE,       ///< Backup complete.
+              SHUT_DOWN,  ///< After @c end() call.
+              CANCELLED,   ///< After cancelling backup process.
+              ERROR,
+              MAX };
+
+#ifndef DBUG_OFF
+
+  static const char* name[];
+
+  struct Initializer
+  {
+    Initializer()
+    {
+      name[INACTIVE]= "INACTIVE";
+      name[INIT]= "INIT";
+      name[WAITING]= "WAITING";
+      name[PREPARING]= "PREPARING";
+      name[READY]= "READY";
+      name[FINISHING]= "FINISHING";
+      name[DONE]= "DONE";
+      name[SHUT_DOWN]= "SHUT DOWN";
+      name[CANCELLED]= "CANCELLED";
+      name[ERROR]= "ERROR";
+    }
+  };
+
+ private:
+
+  static Initializer init;
+
+#endif
+
+};
+
+#ifndef DBUG_OFF
+
+const char* backup_state::name[backup_state::MAX];
+backup_state::Initializer init;
+
+#endif
+
+/**
+  Used to write data blocks to a stream.
+
+  This class defines how buffers are allocated for data transfers
+  (@c get_buf() method). Each block is written as a separate chunk of data.
+ */
+class Block_writer
+{
+ public:
+ 
+  enum result_t { OK, NO_RES, ERROR };
+
+  result_t  get_buf(Buffer &);
+  result_t  write_buf(const Buffer&);
+  result_t  drop_buf(Buffer&);
+
+  Block_writer(size_t size, OStream &s):
+    m_str(s), buf_size(size)
+  {}
+
+ private:
+
+  OStream  &m_str;
+  size_t   buf_size;
+
+  friend class Backup_pump;
+};
+
+/**
+  @class Backup_pump
+
+  Poll backup driver for backup data and send it to a stream. Monitors stages
+  of the backup process, keeps track of closed streams etc.
+
+  Usage: Initialize using begin() method, then call pump() method repeatedly.
+  The state member informs about the current state of the backup process. When
+  done, call end() method. Methods prepare(), lock() and unlock() are forwarded
+  to backup driver to implement multi-engine synchronization.
+*/
+
+class Backup_pump
+{
+ public:
+ 
+  backup_state::value  state; ///< State of the backup driver.
+
+  enum { READING, ///< Pump is polling driver for data.
+         WRITING  ///< Pump sends data to the stream.
+       } mode;
+
+  size_t  init_size;  ///< The estimate returned by backup driver's @c init_data() method.
+  size_t  bytes_in, bytes_out;
+
+  const char *m_name; ///< Name of the driver (for debug purposes).
+
+  Backup_pump(uint, Image_info&, Block_writer&);
+  ~Backup_pump();
+
+  bool is_valid()
+  { return m_drv && state != backup_state::ERROR; }
+
+  int pump(size_t*);
+
+  int begin();
+  int end();
+  int prepare();
+  int lock();
+  int unlock();
+  int cancel();
+
+  /// Return the backup driver used by the pump.
+  Backup_driver &drv() const
+  {
+    DBUG_ASSERT(m_drv);
+    return *m_drv;
+  }
+
+  void set_logger(Logger *log)
+  { m_log= log; }
+
+ private:
+
+  static const uint m_buf_size= 2048; ///< Size of buffers used for data transfers.
+  static const uint get_buf_retries= 3; /**< if block writer has no buffers, retry
+                                              this many times before giving up */
+  Logger        *m_log;   ///< Used to report errors if not NULL.
+  uint          m_drv_no; ///< The number of this image in the backup archive.
+  Backup_driver *m_drv;   ///< Pointer to the backup driver.
+  Block_writer  &m_bw;    ///< Block writer used for writing data blocks.
+  Buffer        m_buf;    ///< Buffer used for data transfers.
+
+  /**
+    Pointer to the memory area used as write buffer.
+
+    If m_buf_head is NULL and we are in READ state, then we should allocate new write
+    buffer and ask driver to fill it with data. M_buf_head is not NULL only when the
+    write buffer is being filled with data but the operation is not complete.
+   */
+  byte          *m_buf_head;
+
+  uint          m_buf_retries; ///< how many times failed to get a buffer from block writer
+
+  bool get_buf();
+  bool drop_buf();
+  bool write_buf();
+
+  /// Bitmap showing which streams have been closed by the driver.
+  MY_BITMAP     m_closed_streams;
+
+  void mark_stream_closed(uint stream_no)
+  {
+    bitmap_set_bit(&m_closed_streams, stream_no);
+  }
+
+  bool all_streams_closed()
+  {
+     return bitmap_is_set_all(&m_closed_streams);
+  }
+
+};
+
+/**
+ @class Scheduler
+
+ Used to drive several backup pumps in a fair fashion. Also, keeps track of the
+ state of these pumps.
+
+*/
+class Scheduler
+{
+  class Pump;
+  class Pump_iterator;
+
+ public:
+
+  int add(Pump*);
+  int step();
+
+  int prepare();
+  int lock();
+  int unlock();
+
+  uint  init_count;     ///< no. drivers sending init data
+  uint  prepare_count;  ///< no. drivers preparing for lock
+  uint  finish_count;   ///< no. drivers sending final data
+
+  size_t init_left() const
+  { return m_known_count? m_init_left/m_known_count + 1 : 0; }
+
+  bool is_empty() const
+  { return m_count == 0; }
+
+  ~Scheduler() { cancel_backup(); }
+
+ private:
+
+  LIST   *m_pumps, *m_last;
+  Logger *m_log;        ///< used to report errors if not NULL
+  uint   m_count;       ///< current number of pumps
+  size_t m_total;       ///< accumulated position of all drivers
+  size_t m_init_left;   ///< how much of init data is left (estimate)
+  uint   m_known_count; ///< no. drivers which can estimate init data size
+  OStream &m_str;       ///< stream to which we write
+  bool   cancelled;      ///< true if backup process was cancelled
+
+  Scheduler(OStream &s, Logger *log):
+    init_count(0), prepare_count(0), finish_count(0),
+    m_pumps(NULL), m_last(NULL), m_log(log),
+    m_count(0), m_total(0), m_init_left(0), m_known_count(0),
+    m_str(s), cancelled(FALSE)
+  {}
+
+  void move_pump_to_end(const Pump_iterator&);
+  void remove_pump(Pump_iterator&);
+  void cancel_backup();
+
+  friend int write_table_data(THD*, Backup_info&, OStream&);
+  friend class Pump_iterator;
+};
+
+/**
+  Extend Backup_pump with information about its position relative
+  to other pumps.
+ */
+class Scheduler::Pump: public Backup_pump
+{
+  size_t start_pos;
+  Block_writer bw;
+
+  friend class Scheduler;
+
+ public:
+
+  Pump(uint no, Image_info &img, OStream &s):
+    Backup_pump(no,img,bw), start_pos(0), bw(io_buffer_size,s)
+  {}
+
+  size_t pos() const
+  { 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;
+    *tables_last= table_list_last;
+  }
+  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);
+  DBUG_RETURN(0);
+}
+
+/**
+  Save data from tables being backed up.
+
+  Function initializes and controls backup drivers which create the image
+  of table data. Currently single thread is used and drivers are polled in
+  a round robin fashion.
+
+  @returns 0 on success.
+ */
+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;
+
+  if (info.img_count==0 || info.table_count==0) // nothing to backup
+    DBUG_RETURN(0);
+
+  Scheduler   sch(s,&info);         // scheduler instance
+  List<Scheduler::Pump>  inactive;  // list of images not yet being created
+  size_t      max_init_size=0;      // keeps maximal init size for images in inactive list
+
+  size_t      start_bytes= s.bytes;
+
+  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)
+  {
+    Image_info *i= info.images[no];
+
+    if (!i)
+      continue;
+
+    Scheduler::Pump *p= new Scheduler::Pump(no,*i,s);
+
+    if (!p || !p->is_valid())
+    {
+      info.report_error(ER_OUT_OF_RESOURCES);
+      goto error;
+    }
+
+    size_t init_size= p->init_size;
+
+    if (init_size == Driver::UNKNOWN_SIZE)
+    {
+      if (sch.add(p))
+        goto error;
+    }
+    else
+    {
+      if (init_size > max_init_size)
+        max_init_size= init_size;
+
+      inactive.push_back(p);
+    }
+    if (!def_or_snap_used)
+      def_or_snap_used=  ((i->type() == Image_info::DEFAULT_IMAGE) ||
+                          (i->type() == Image_info::SNAPSHOT_IMAGE));
+    if (def_or_snap_used)
+      get_default_snapshot_tables(&p->drv(), NULL, 
+                                  &table_list, &table_list_last);
+  }
+
+  /*
+    Each driver should be either in the scheduler or on inactive list.
+   */
+  DBUG_ASSERT( !sch.is_empty() || !inactive.is_empty() );
+
+  DBUG_PRINT("backup/data",("%u drivers initialized, %u inactive",
+                            sch.init_count,
+                            inactive.elements));
+
+  DBUG_PRINT("backup/data",("%u drivers initialized, %u inactive",
+                            sch.init_count,
+                            inactive.elements));
+
+  DBUG_PRINT("backup/data",("-- INIT PHASE --"));
+  BACKUP_SYNC("data_init");
+
+  /*
+   Poll "at end" drivers activating inactive ones on the way.
+
+   Note: if scheduler is empty and there are images with non-zero
+   init size (max_init_size > 0) then enter the loop as one such image
+   will be added to the scheduler inside.
+  */
+
+  while (sch.init_count > 0 || sch.is_empty() && max_init_size > 0)
+  {
+
+    // add inactive image if it is a time for it
+
+    if (max_init_size > 0 && sch.init_left() <= max_init_size)
+    {
+      List_iterator<Scheduler::Pump>  it(inactive);
+      Scheduler::Pump *p;
+
+      size_t second_max= 0;
+      max_init_size= 0;
+      Scheduler::Pump *p1= NULL;
+
+      while ((p= it++))
+      {
+        if ( p->init_size >= max_init_size )
+        {
+          second_max= max_init_size;
+          max_init_size= p->init_size;
+          p1= p;
+        }
+      }
+
+      max_init_size= second_max;
+
+      if (sch.add(p1))
+        goto error;
+    }
+
+    // poll drivers
+
+    if (sch.step())
+      goto error;
+  }
+
+  {
+    // start "at begin" drivers
+    DBUG_PRINT("backup/data",("- activating \"at begin\" drivers"));
+
+    List_iterator<Scheduler::Pump>  it1(inactive);
+    Scheduler::Pump *p;
+
+    while ((p= it1++))
+    if (sch.add(p))
+      goto error;
+
+    while (sch.init_count > 0)
+    if (sch.step())
+      goto error;
+
+    // prepare for VP
+    DBUG_PRINT("backup/data",("-- PREPARE PHASE --"));
+    BACKUP_SYNC("data_prepare");
+
+    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!" ));
+        info.report_error(ER_BACKUP_OPEN_TABLES, "backup");
+        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;
+
+    // VP creation
+    DBUG_PRINT("backup/data",("-- SYNC PHASE --"));
+    BACKUP_SYNC("data_lock");
+    if (sch.lock())
+      goto error;
+
+    BACKUP_SYNC("data_unlock");
+    if (sch.unlock())
+      goto error;
+
+    // get final data from drivers
+    DBUG_PRINT("backup/data",("-- FINISH PHASE --"));
+    BACKUP_SYNC("data_finish");
+
+    while (sch.finish_count > 0)
+    if (sch.step())
+      goto error;
+
+    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);
+
+ error:
+
+  DBUG_RETURN(ERROR);
+}
+
+} // backup namespace
+
+/**************************************************
+
+        Implementation of Scheduler
+
+ **************************************************/
+
+namespace backup {
+
+/**
+  Used to iterate over backup pumps of a scheduler.
+ */
+class Scheduler::Pump_iterator
+{
+ public:
+ 
+  LIST  *el;
+
+  Pump* operator->()
+  {
+    return el? static_cast<Pump*>(el->data) : NULL;
+  }
+
+  void  operator++()
+  {
+    if(el) el= el->next;
+  }
+
+  operator bool() const
+  { return el && el->data; }
+
+  void operator=(const Pump_iterator &p)
+  { el= p.el; }
+
+  Pump_iterator(): el(NULL)
+  {}
+
+  Pump_iterator(const Scheduler &sch): el(sch.m_pumps)
+  {}
+
+};
+
+/**
+  Pick next backup pump and call its @c pump() method.
+
+  Method updates statistics of number of drivers in each phase which is used
+  to detect end of a backup process.
+ */
+int Scheduler::step()
+{
+  // Pick next driver to pump data from.
+
+  Pump_iterator p(*this);
+
+/*
+  An attempt to implement more advanced scheduling strategy (not working).
+
+  for (Pump_ptr it(m_pumps); it; ++it)
+    if ( !p || it->pos() < p->pos() )
+      p= it;
+*/
+
+  if (!p) // No active pumps
+  {
+    init_count= prepare_count= finish_count= 0;  // safety
+    return 0;
+  }
+
+  move_pump_to_end(p);
+
+  DBUG_PRINT("backup/data",("polling %s",p->m_name));
+
+  backup_state::value before_state= p->state;
+
+  size_t howmuch;
+  int res= p->pump(&howmuch);
+
+  backup_state::value after_state= p->state;
+
+  // The error state should be set iff error was reported
+  DBUG_ASSERT(!(res && after_state != backup_state::ERROR));
+  DBUG_ASSERT(!(!res && after_state == backup_state::ERROR));
+
+  // update statistics
+
+  if (!res && howmuch > 0)
+  {
+    m_total += howmuch;
+
+    if (before_state == backup_state::INIT
+        && p->init_size != Driver::UNKNOWN_SIZE)
+      m_init_left -= howmuch;
+  }
+
+  if (after_state != before_state)
+  {
+    switch (before_state) {
+
+    case backup_state::INIT:
+      if (init_count>0)
+        init_count--;
+      break;
+
+    case backup_state::PREPARING:
+      if (prepare_count>0)
+        prepare_count--;
+      break;
+
+    case backup_state::FINISHING:
+      if (finish_count>0)
+        finish_count--;
+      break;
+
+    default: break;
+    }
+
+    switch (after_state) {
+
+    case backup_state::INIT:
+      init_count++;
+      break;
+
+    case backup_state::PREPARING:
+      prepare_count++;
+      break;
+
+    case backup_state::FINISHING:
+      finish_count++;
+      break;
+
+    case backup_state::DONE:
+      p->end();
+
+    case backup_state::ERROR:
+      remove_pump(p);
+      if (res)
+        cancel_backup(); // we hit an error - bail out
+      break;
+
+    default: break;
+    }
+
+    DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
+                              m_count, init_count, prepare_count, finish_count));
+  }
+
+  return res;
+}
+
+/**
+  Add backup pump to the scheduler.
+
+  The pump is initialized with begin() call. In case of error, it is deleted.
+ */
+int Scheduler::add(Pump *p)
+{
+  size_t  avg= m_count? m_total/m_count + 1 : 0;
+
+  if (!p)  // no pump to add
+    return 0;
+
+  p->set_logger(m_log);
+  p->start_pos= avg;
+
+  DBUG_PRINT("backup/data",("Adding %s to scheduler (at pos %lu)",
+                            p->m_name, (unsigned long)avg));
+
+  m_pumps= list_cons(p,m_pumps);
+  if (!m_last)
+    m_last= m_pumps;
+
+  m_count++;
+  m_total += avg;
+
+  if (p->begin())
+    goto error;
+
+  // in case of error, above call should return non-zero code (and report error)
+  DBUG_ASSERT(p->state != backup_state::ERROR);
+
+  if (p->init_size != Driver::UNKNOWN_SIZE)
+  {
+    m_init_left += p->init_size;
+    m_known_count++;
+  }
+
+  switch (p->state) {
+
+  case backup_state::INIT:
+    init_count++;
+    break;
+
+  case backup_state::PREPARING:
+    prepare_count++;
+    break;
+
+  case backup_state::FINISHING:
+    finish_count++;
+    break;
+
+  default: break;
+  }
+
+  DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
+                            m_count, init_count, prepare_count, finish_count));
+  DBUG_PRINT("backup/data",("total init data size estimate: %lu",(unsigned long)m_init_left));
+
+  return 0;
+
+ error:
+
+  delete p;
+  cancel_backup();
+  return ERROR;
+}
+
+/// Move backup pump to the end of scheduler's list.
+void Scheduler::move_pump_to_end(const Pump_iterator &p)
+{
+  // The pump to move is in the m_pumps list so the list can't be empty.
+  DBUG_ASSERT(m_pumps);
+  if (m_last != p.el)
+  {
+    m_pumps= list_delete(m_pumps,p.el);
+    m_last->next= p.el;
+    p.el->prev= m_last;
+    p.el->next= NULL;
+    m_last= p.el;
+  }
+}
+
+/**
+  Remove backup pump from the scheduler.
+
+  The corresponding backup driver is shut down using @c end() call.
+ */
+void Scheduler::remove_pump(Pump_iterator &p)
+{
+  DBUG_ASSERT(p.el);
+
+  if (m_last == p.el)
+    m_last= m_last->prev;
+
+  if (m_pumps)
+  {
+    m_pumps= list_delete(m_pumps,p.el);
+    m_count--;
+  }
+
+  if (p)
+  {
+    // destructor calls driver's free() method
+    delete static_cast<Pump*>(p.el->data);
+    my_free(p.el,MYF(0));
+  }
+}
+
+/// Shut down backup process.
+void Scheduler::cancel_backup()
+{
+  if (cancelled)
+    return;
+
+  // shutdown any remaining drivers
+  while (m_count && m_pumps)
+  {
+    Pump_iterator p(*this);
+    p->cancel();
+    remove_pump(p);
+  }
+
+  cancelled= TRUE;
+}
+
+
+/// Start prepare phase for all drivers.
+int Scheduler::prepare()
+{
+  DBUG_ASSERT(!cancelled);
+  // we should start prepare phase only when init phase is finished
+  DBUG_ASSERT(init_count==0);
+  DBUG_PRINT("backup/data",("calling prepare() for all drivers"));
+
+  for (Pump_iterator it(*this); it; ++it)
+  {
+    if (it->prepare())
+    {
+      cancel_backup();
+      return ERROR;
+    }
+    if (it->state == backup_state::PREPARING)
+     prepare_count++;
+  }
+
+  DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
+                            m_count, init_count, prepare_count, finish_count));
+  return 0;
+}
+
+/// Lock all drivers.
+int Scheduler::lock()
+{
+  DBUG_ASSERT(!cancelled);
+  // lock only when init and prepare phases are finished
+  DBUG_ASSERT(init_count==0 && prepare_count==0);
+  DBUG_PRINT("backup/data",("calling lock() for all drivers"));
+
+  for (Pump_iterator it(*this); it; ++it)
+   if (it->lock())
+   {
+     cancel_backup();
+     return ERROR;
+   }
+
+  DBUG_PRINT("backup/data",("driver counts: total=%u, init=%u, prepare=%u, finish=%u.",
+                            m_count, init_count, prepare_count, finish_count));
+  return 0;
+}
+
+/// Unlock all drivers.
+int Scheduler::unlock()
+{
+  DBUG_ASSERT(!cancelled);
+  DBUG_PRINT("backup/data",("calling unlock() for all drivers"));
+
+  for(Pump_iterator it(*this); it; ++it)
+  {
+    if (it->unlock())
+    {
+      cancel_backup();
+      return ERROR;
+    }
+    if (it->state == backup_state::FINISHING)
+      finish_count++;
+  }
+
+  return 0;
+}
+
+
+/**************************************************
+
+         Implementation of Backup_pump
+
+ **************************************************/
+
+Backup_pump::Backup_pump(uint no, Image_info &img, Block_writer &bw):
+  state(backup_state::INACTIVE), mode(READING),
+  init_size(0), bytes_in(0), bytes_out(0),
+  m_drv_no(no), m_drv(NULL), m_bw(bw), m_buf_head(NULL),
+  m_buf_retries(0)
+{
+  m_buf.data= NULL;
+  bitmap_init(&m_closed_streams,
+              NULL,
+              1+img.tables.count(),
+              FALSE); // not thread safe
+  m_name= img.name();
+  if (ERROR == img.get_backup_driver(m_drv) || !m_drv)
+    state= backup_state::ERROR;
+  else
+    init_size= m_drv->init_size();
+}
+
+Backup_pump::~Backup_pump()
+{
+  if (m_drv)
+    m_drv->free();
+  bitmap_free(&m_closed_streams);
+}
+
+/// Initialize backup driver.
+int Backup_pump::begin()
+{
+  state= backup_state::INIT;
+  DBUG_PRINT("backup/data",(" %s enters INIT state",m_name));
+
+  if (ERROR == m_drv->begin(m_buf_size))
+  {
+    state= backup_state::ERROR;
+    // We check if logger is always setup. Later the assertion can
+    // be replaced with "if (m_log)"
+    DBUG_ASSERT(m_log);
+      m_log->report_error(ER_BACKUP_INIT_BACKUP_DRIVER,m_name);
+    return ERROR;
+  }
+
+  return 0;
+}
+
+/// Shut down the driver.
+int Backup_pump::end()
+{
+  if (state != backup_state::SHUT_DOWN)
+  {
+    DBUG_PRINT("backup/data",(" shutting down %s",m_name));
+
+    if (ERROR == m_drv->end())
+    {
+      state= backup_state::ERROR;
+      DBUG_ASSERT(m_log);
+        m_log->report_error(ER_BACKUP_STOP_BACKUP_DRIVER,m_name);
+      return ERROR;
+    }
+
+    state= backup_state::SHUT_DOWN;
+  }
+
+  return 0;
+}
+
+/// Start prepare phase for the driver.
+int Backup_pump::prepare()
+{
+  result_t res= m_drv->prelock();
+
+  switch (res) {
+
+  case READY:
+    state= backup_state::READY;
+    break;
+
+  case OK:
+    state= backup_state::PREPARING;
+    break;
+
+  case ERROR:
+  default:
+    state= backup_state::ERROR;
+    DBUG_ASSERT(m_log);
+      m_log->report_error(ER_BACKUP_PREPARE_DRIVER, m_name);
+      return ERROR;
+  }
+
+  DBUG_PRINT("backup/data",(" preparing %s, goes to %s state",
+                            m_name,backup_state::name[state]));
+  return 0;
+}
+
+/// Request VP from the driver.
+int Backup_pump::lock()
+{
+  DBUG_PRINT("backup/data",(" locking %s",m_name));
+  if (ERROR == m_drv->lock())
+  {
+    state= backup_state::ERROR;
+    DBUG_ASSERT(m_log);
+      m_log->report_error(ER_BACKUP_CREATE_VP,m_name);
+    return ERROR;
+  }
+
+  return 0;
+}
+
+/// Unlock the driver after VP creation.
+int Backup_pump::unlock()
+{
+  DBUG_PRINT("backup/data",(" unlocking %s, goes to FINISHING state",m_name));
+  state= backup_state::FINISHING;
+  if (ERROR == m_drv->unlock())
+  {
+    state= backup_state::ERROR;
+    DBUG_ASSERT(m_log);
+      m_log->report_error(ER_BACKUP_UNLOCK_DRIVER,m_name);
+    return ERROR;
+  }
+
+  return 0;
+}
+
+int Backup_pump::cancel()
+{
+  if (ERROR == m_drv->cancel())
+  {
+    state= backup_state::ERROR;
+    DBUG_ASSERT(m_log);
+      m_log->report_error(ER_BACKUP_CANCEL_BACKUP,m_name);
+    return ERROR;
+  }
+  state= backup_state::CANCELLED;
+  return 0;
+}
+
+/**
+  Poll the driver for next block of data and/or write data to stream.
+
+  Depending on the current mode in which the pump is operating (@c mode member)
+  the backup driver is polled for image data or data obtained before is written
+  to the stream. Answers from drivers @c get_data() method are interpreted and
+  the state of the driver is updated accordingly.
+
+  Each block of data obtained from a driver is prefixed with the image
+  number and the table number stored in @c buf.table_no before it is written to
+  the stream:
+  @verbatim
+    | img no | table no | data from the driver |
+  @endverbatim
+ */
+
+// Consider: report number of bytes *written* to stream.
+
+int Backup_pump::pump(size_t *howmuch)
+{
+  // pumping not allowed in these states
+  DBUG_ASSERT(state != backup_state::INACTIVE);
+  DBUG_ASSERT(state != backup_state::SHUT_DOWN);
+  DBUG_ASSERT(state != backup_state::CANCELLED);
+
+  // we have detected error before - report it once more
+  if (state == backup_state::ERROR)
+    return ERROR;
+
+  // we are done and thus there is nothing to do
+  if (state == backup_state::DONE)
+    return 0;
+
+  backup_state::value before_state= state;
+
+  if (howmuch)
+    *howmuch= 0;
+
+  if (all_streams_closed())
+  {
+    switch (state) {
+
+    case backup_state::INIT:
+      state= backup_state::WAITING;
+      break;
+
+    case backup_state::PREPARING:
+      state= backup_state::READY;
+      break;
+
+    case backup_state::FINISHING:
+      state= backup_state::DONE;
+      break;
+
+    case backup_state::ERROR:
+      return ERROR;
+
+    default: break;
+    }
+  }
+  else
+  {
+    if (mode == READING)
+    {
+      /*
+        If m_buf_head is NULL then a new request to the driver
+        should be made. We should allocate a new output buffer.
+       */
+
+      if (!m_buf_head)
+        switch (m_bw.get_buf(m_buf)) {
+
+        case Block_writer::OK:
+          m_buf_retries= 0;
+          m_buf_head= m_buf.data;
+          m_buf.data+= 4; // leave space for image and stream numbers
+          m_buf.size-= 4;
+          break;
+
+        case Block_writer::NO_RES:
+          if (++m_buf_retries <= get_buf_retries)
+            return 0; // we shall try again
+
+        case Block_writer::ERROR:
+        default:
+          DBUG_ASSERT(m_log);
+            m_log->report_error(ER_BACKUP_GET_BUF);
+          state= backup_state::ERROR;
+          return ERROR;
+        }
+
+      DBUG_ASSERT(m_buf_head);
+
+      result_t res= m_drv->get_data(m_buf);
+
+      switch (res) {
+
+      case READY:
+
+        if( state == backup_state::INIT )
+          state= backup_state::WAITING;
+        else if( state == backup_state::WAITING )
+          state= backup_state::READY;
+
+      case OK:
+
+        if (m_buf.last)
+        {
+          mark_stream_closed(m_buf.table_no);
+          if (all_streams_closed())
+            DBUG_PRINT("backup/data",(" all streams of %s closed",m_name));
+          else
+            DBUG_PRINT("backup/data",(" stream %u closed",m_buf.table_no));
+        }
+
+        m_buf.data= m_buf_head;
+        m_buf_head= NULL;
+
+        if ( m_buf.size > 0 )
+        {
+          m_buf.size+= 4;
+          int2store(m_buf.data,m_drv_no+1);
+          int2store(m_buf.data+2,m_buf.table_no);
+          mode= WRITING;
+        }
+        else
+          m_bw.drop_buf(m_buf);
+
+        break;
+
+      case PROCESSING:
+
+        break;
+
+      case ERROR:
+      default:
+        DBUG_ASSERT(m_log);
+          m_log->report_error(ER_BACKUP_GET_DATA,m_name);
+        state= backup_state::ERROR;
+        return ERROR;
+
+      case BUSY:
+
+        m_bw.drop_buf(m_buf);
+        m_buf_head=NULL;  // thus a new request will be made
+      }
+
+    } // if (mode == READING)
+
+    if (mode == WRITING && state != backup_state::ERROR)
+    {
+      switch (m_bw.write_buf(m_buf)) {
+
+      case Block_writer::OK:
+
+        if (howmuch)
+          *howmuch= m_buf.size;
+
+        DBUG_PRINT("backup/data",(" added %lu bytes from %s to archive (drv_no=%u, table_no=%u)",
+                                  (unsigned long)howmuch, m_name, m_drv_no, m_buf.table_no));
+        mode= READING;
+        break;
+
+      case Block_writer::ERROR:
+
+        DBUG_ASSERT(m_log);
+          m_log->report_error(ER_BACKUP_WRITE_DATA, m_name, m_buf.table_no);
+        state= backup_state::ERROR;
+        return ERROR;
+
+      default:  // retry write
+        break;
+
+      }
+    }
+  }
+
+  if (state != before_state)
+    DBUG_PRINT("backup/data",(" %s changes state %s->%s",
+                              m_name,backup_state::name[before_state],
+                                     backup_state::name[state]));
+  return 0;
+}
+
+} // backup namespace
+
+
+/***********************************************
+
+                  DATA RESTORE
+
+ ***********************************************/
+
+namespace backup {
+
+
+/**
+  Read backup image data from a backup stream and forward it to restore drivers.
+ */
+int restore_table_data(THD*, Restore_info &info, IStream &s)
+{
+  DBUG_ENTER("restore::restore_table_data");
+
+  enum { READING, SENDING, DONE, ERROR } state= READING;
+
+  if (info.img_count==0 || info.table_count==0) // nothing to restore
+    DBUG_RETURN(0);
+
+  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);
+    DBUG_RETURN(ERROR);
+  }
+
+  // Initializing restore drivers
+  result_t res;
+
+  for (uint no=0; no < info.img_count; ++no)
+  {
+    drv[no]= NULL;
+
+    Image_info *img= info.images[no];
+
+    // note: img can be NULL if it is not used in restore.
+    if (!img)
+      continue;
+
+    res= img->get_restore_driver(drv[no]);
+    if (res == backup::ERROR)
+    {
+      info.report_error(ER_BACKUP_CREATE_RESTORE_DRIVER,img->name());
+      goto error;
+    };
+
+    res= drv[no]->begin(0);
+    if (res == backup::ERROR)
+    {
+      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) ||
+        (img->type() == Image_info::SNAPSHOT_IMAGE))
+      get_default_snapshot_tables(NULL, (default_backup::Restore *)drv[no], 
+                                  &table_list, &table_list_last);
+  }
+
+  {
+    Buffer  buf;
+    uint    img_no=0;
+    uint    repeats=0, errors= 0;
+
+    static const uint MAX_ERRORS= 3;
+    static const uint MAX_REPEATS= 7;
+
+    size_t start_bytes= s.bytes;
+
+    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!" ));
+        info.report_error(ER_BACKUP_OPEN_TABLES, "restore");
+        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 )
+    {
+      switch (state) {
+
+      case READING:
+
+        switch ((int)(s >> buf)) {
+
+        case stream_result::EOS:
+          state= DONE;
+          break;
+
+        case stream_result::OK:
+          state= SENDING;
+          break;
+
+        case stream_result::ERROR:
+          info.report_error(ER_BACKUP_READ_DATA);
+        default:
+          state= ERROR;
+          goto error;
+
+        }
+
+        if (state != SENDING)
+          break;
+
+        DBUG_ASSERT(buf.data);
+
+        img_no= uint2korr(buf.data);
+        buf.table_no= uint2korr(buf.data+2);
+        buf.data += 4;
+        buf.size -= 4;
+
+        if (img_no < 1 || img_no > info.img_count || !(drvr= drv[img_no-1]))
+        {
+          DBUG_PRINT("restore",("Skipping data for image #%u",img_no));
+          state= READING;
+          break;
+        }
+
+        img= info.images[img_no-1];
+        // Each restore driver should have corresponding Image_info object.
+        DBUG_ASSERT(img);
+
+        DBUG_PRINT("restore",("Got %lu bytes of %s image data (for table #%u)",
+                   (unsigned long)buf.size, img->name(), buf.table_no));
+
+      case SENDING:
+
+        /*
+          If we are here, the img pointer should point at the image for which
+          we have next data block and drvr at its restore driver.
+         */
+        DBUG_ASSERT(img && drvr);
+
+        switch( drvr->send_data(buf) ) {
+
+        case backup::OK:
+          switch ((int)s.next_chunk()) {
+
+          case stream_result::OK:
+            state= READING;
+            img= NULL;
+            drvr= NULL;
+            repeats= 0;
+            break;
+
+          case stream_result::EOS:
+            state= DONE;
+            break;
+
+          case stream_result::ERROR:
+            info.report_error(ER_BACKUP_NEXT_CHUNK);
+          default:
+            state= ERROR;
+            goto error;
+          }
+
+          break;
+
+        case backup::ERROR:
+          if( errors > MAX_ERRORS )
+          {
+            info.report_error(ER_BACKUP_SEND_DATA, buf.table_no, img->name());
+            state= ERROR;
+            goto error;
+          }
+          errors++;
+          break;
+
+        case backup::PROCESSING:
+        case backup::BUSY:
+        default:
+          if( repeats > MAX_REPEATS )
+          {
+            info.report_error(ER_BACKUP_SEND_DATA_RETRY, repeats, img->name());
+            state= ERROR;
+            goto error;
+          }
+          repeats++;
+          break;
+        }
+
+      default:
+        break;
+      } // switch(state)
+
+    } // main reading loop
+
+    DBUG_PRINT("restore",("End of backup stream"));
+    if (state != DONE)
+      DBUG_PRINT("restore",("state is %d",state));
+
+    info.data_size= s.bytes - start_bytes;
+  }
+
+  { // Shutting down drivers
+
+    String bad_drivers;
+
+    for (uint no=0; no < info.img_count; ++no)
+    {
+      if (!drv[no])
+        continue;
+
+      DBUG_PRINT("restore",("Shutting down restore driver %s",
+                            info.images[no]->name()));
+      res= drv[no]->end();
+      if (res == backup::ERROR)
+      {
+        state= ERROR;
+
+        if (!bad_drivers.is_empty())
+          bad_drivers.append(",");
+        bad_drivers.append(info.images[no]->name());
+      }
+      drv[no]->free();
+    }
+
+    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);
+
+ error:
+
+  DBUG_PRINT("restore",("Cancelling restore process"));
+
+  for (uint no=0; no < info.img_count; ++no)
+  {
+    if (!drv[no])
+      continue;
+
+    drv[no]->cancel();
+    drv[no]->free();
+  }
+
+  DBUG_RETURN(backup::ERROR);
+}
+
+} // backup namespace
+
+
+/**************************************************
+
+       Implementation of Block_writer
+
+ **************************************************/
+
+namespace backup {
+
+/**
+  Allocate new buffer for data transfer.
+
+  The buffer size is given by @c buf.size member.
+
+  Current implementation tries to allocate the data transfer buffer in the
+  stream. It can handle only one buffer at a time.
+
+  @returns @c NO_RES if buffer can not be allocated, @c OK otherwise.
+ */
+Block_writer::result_t
+Block_writer::get_buf(Buffer &buf)
+{
+  buf.table_no= 0;
+  buf.last= FALSE;
+  buf.size= buf_size;
+  buf.data= m_str.get_window(buf.size);
+
+  if (!buf.data)
+    return NO_RES;
+
+  return OK;
+}
+
+/**
+  Write block of data to stream.
+
+  The buffer containing data must be obtained from a previous @c get_buf() call.
+  After this call, buffer is returned to the buffer pool and can be reused for
+  other transfers.
+ */
+Block_writer::result_t
+Block_writer::write_buf(const Buffer &buf)
+{
+  stream_result::value  res= m_str.write_window(buf.size);
+  m_str.end_chunk();
+  return  res == stream_result::OK ? OK : ERROR;
+}
+
+/**
+  Return buffer to the buffer pool.
+
+  If a buffer obtained from @c get_buf() is not written to the stream, this
+  method can return it to the buffer pool so that it can be reused for other
+  transfers.
+ */
+Block_writer::result_t
+Block_writer::drop_buf(Buffer &buf)
+{
+  m_str.drop_window();
+  buf.data= NULL;
+  buf.size= 0;
+  return OK;
+}
+
+} // backup namespace
diff -Nrup a/sql/backup/debug.h b/sql/backup/debug.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/debug.h	2007-11-06 11:11:45 -05:00
@@ -0,0 +1,81 @@
+#ifndef _BACKUP_DEBUG_H
+#define _BACKUP_DEBUG_H
+
+#define BACKUP_SYNC_TIMEOUT 300
+
+/*
+  TODO
+  - decide how to configure DEBUG_BACKUP
+ */
+
+#ifndef DBUG_OFF
+# define DBUG_BACKUP
+#endif
+
+#ifdef DBUG_BACKUP
+
+/*
+  Macros for debugging error (or other) conditions. Usage:
+
+  TEST_ERROR_IF(<condition deciding if TEST_ERROR should be true>);
+
+  if (<other conditions> || TEST_ERROR)
+  {
+    <report error>
+  }
+
+  The additional TEST_ERROR condition will be set only if "backup_error_test"
+  error injection is set in the server.
+
+  Notes:
+   - Whenever TEST_ERROR is used in a condition, TEST_ERROR_IF() should
+     be called before - otherwise TEST_ERROR might be unintentionally TRUE.
+   - This mechanism is not thread safe.
+ */
+
+namespace backup {
+ extern bool test_error_flag;
+}
+
+#define TEST_ERROR  backup::test_error_flag
+// FIXME: DBUG_EXECUTE_IF below doesn't work
+#define TEST_ERROR_IF(X) \
+ do { \
+   backup::test_error_flag= FALSE; \
+   DBUG_EXECUTE_IF("backup_error_test", backup::test_error_flag= (X);); \
+ } while(0)
+
+/*
+  Macros for creating synchronization points in tests.
+
+  Usage
+
+  In the backup code:
+    BACKUP_SYNC("<synchronization point name>");
+
+  In a client:
+    SELECT get_lock("<synchronization point name>",<timeout>);
+    ...
+    SELECT release_lock("<synchronization point name>");
+
+  If the lock is kept by a client, server code will wait on the corresponding
+  BACKUP_SYNC() until it is released.
+
+  Consider: set thd->proc_info when waiting on lock
+ */
+
+#define BACKUP_SYNC(S) \
+ do { \
+  DBUG_PRINT("backup",("== synchronization on '%s' ==",(S))); \
+  DBUG_SYNC_POINT((S),BACKUP_SYNC_TIMEOUT); \
+ } while (0)
+
+#else
+
+#define BACKUP_SYNC(S)
+#define TEST_ERROR  FALSE
+#define TEST_ERROR_IF(X)
+
+#endif
+
+#endif
diff -Nrup a/sql/backup/error.h b/sql/backup/error.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/error.h	2007-11-06 11:11:45 -05:00
@@ -0,0 +1,34 @@
+#ifndef _BACKUP_ERROR_H
+#define _BACKUP_ERROR_H
+
+namespace util {
+
+/**
+  Report error stored in MYSQL_ERROR structure to a client.
+
+  If @c err doesn't contain any error code, the given error code is reported.
+
+  @returns 0 if error was reported, non-zero otherwise.
+ */
+inline
+int report_mysql_error(THD*, MYSQL_ERROR *err, int code= 0)
+{
+  DBUG_ASSERT(err);
+
+  if (err->level == MYSQL_ERROR::WARN_LEVEL_END
+      && !err->msg && !err->code ) // err doesn't store any error
+    return -1;
+
+  switch (err->level) {
+
+  case MYSQL_ERROR::WARN_LEVEL_ERROR:
+    return my_printf_error(err->code ? err->code : code, err->msg, MYF(0));
+
+  default: // Q: What to do with warnings and notes? push them... ?
+    return -1;
+  }
+}
+
+} // util namespace
+
+#endif
diff -Nrup a/sql/backup/logger.cc b/sql/backup/logger.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/logger.cc	2007-11-06 11:11:46 -05:00
@@ -0,0 +1,86 @@
+#include "../mysql_priv.h"
+
+#include "logger.h"
+
+namespace backup {
+
+
+/**
+  Output message on a given level.
+
+  This is the low-level method used by all other methods to output messages.
+  It's implementation determines how messages are delivered to the user.
+  Currently they are appended to the server's error log.
+
+  @param level       level of the message (INFO,WARNING,ERROR)
+  @param error_code  optional code for message coming from errmsg.txt database -
+                     for other messages set to 0
+  @param msg         message text
+
+  @returns 0 on success.
+ */
+int Logger::write_message(log_level::value level, int error_code,
+                          const char *msg)
+{
+   const char *prefix= m_type == BACKUP ? "Backup" : "Restore";
+   char buf[ERRMSGSIZE + 30];
+   
+   my_snprintf(buf,sizeof(buf),"%s: %s",prefix,msg);
+  
+   switch (level) {
+   case log_level::ERROR:
+     if (m_save_errors)
+       errors.push_front(new MYSQL_ERROR(::current_thd,error_code,
+                                         MYSQL_ERROR::WARN_LEVEL_ERROR,msg));
+     sql_print_error(buf);
+     DBUG_PRINT("backup log",("[ERROR] %s",buf));
+     return 0;
+
+   case log_level::WARNING:
+     sql_print_warning(buf);
+     DBUG_PRINT("backup log",("[Warning] %s",buf));
+     return 0;
+
+   case log_level::INFO:
+     sql_print_information(buf);
+     DBUG_PRINT("backup log",("[Info] %s",buf));
+     return 0;
+
+   default: return ERROR;
+   }
+}
+
+/**
+  Output message registered in errmsg.txt database.
+
+  @param level       level of the message (INFO,WARNING,ERROR)
+  @param error_code  code assigned to the message in errmsg.txt
+
+  If the message contains placeholders, additional arguments provide
+  values to be put there.
+
+  @returns 0 on success.
+ */
+int Logger::v_report_error(log_level::value level, int error_code, va_list args)
+{
+  return v_write_message(level,error_code,ER_SAFE(error_code),args);
+}
+
+/**
+  Output unregistered message.
+
+  Format string is given explicitly as an argument.
+
+  Note: no localization support.
+ */
+int Logger::v_write_message(log_level::value level, int error_code,
+                            const char *format, va_list args)
+{
+  char buf[ERRMSGSIZE + 20];
+
+  my_vsnprintf(buf,sizeof(buf),format,args);
+  return write_message(level,error_code,buf);
+}
+
+
+} // backup namespace
diff -Nrup a/sql/backup/logger.h b/sql/backup/logger.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/logger.h	2007-11-06 11:11:47 -05:00
@@ -0,0 +1,125 @@
+#ifndef _BACKUP_LOGGER_H
+#define _BACKUP_LOGGER_H
+
+#include <backup/error.h>
+
+
+namespace backup {
+
+/// Logging levels for messages generated by backup system
+struct log_level {
+ enum value {
+    INFO=    MYSQL_ERROR::WARN_LEVEL_NOTE,
+    WARNING= MYSQL_ERROR::WARN_LEVEL_WARN,
+    ERROR=   MYSQL_ERROR::WARN_LEVEL_ERROR
+ };
+};
+
+
+/**
+  This class exposes API used to output various messages during backup/restore
+  process.
+
+  Destination of the messages is determined by the implementation. Currently
+  messages are output the the servers error log.
+
+  Messages are intended for a DBA and thus should be localized. A message should
+  be registered in errmsg.txt database and have assigned an error code. Printing
+  message corresponding to an error code is done using @c report_error() methods.
+ */
+class Logger
+{  
+ public:
+
+   enum enum_type { BACKUP, RESTORE } m_type;
+
+   Logger(enum_type type): m_type(type),m_save_errors(FALSE)
+   {}
+
+   int write_message(log_level::value level , int error_code, const char *msg);
+
+   int write_message(log_level::value level, const char *msg, ...)
+   {
+     va_list args;
+
+     va_start(args,msg);
+     int res= v_write_message(level, 0, msg, args);
+     va_end(args);
+
+     return res;
+   }
+
+   /// Reports error with log_level::ERROR.
+   int report_error(int error_code, ...)
+   {
+     va_list args;
+
+     va_start(args,error_code);
+     int res= v_report_error(log_level::ERROR, error_code, args);
+     va_end(args);
+
+     return res;
+   }
+
+   /// Reports error with registered error description string.
+   int report_error(log_level::value level, int error_code, ...)
+   {
+     va_list args;
+
+     va_start(args,error_code);
+     int res= v_report_error(level, error_code, args);
+     va_end(args);
+
+     return res;
+   }
+
+   /// Reports error with given description.
+   int report_error(const char *format, ...)
+   {
+     va_list args;
+
+     va_start(args,format);
+     int res= v_write_message(log_level::ERROR, 0, format, args);
+     va_end(args);
+
+     return res;
+   }
+
+   ///  Request that all reported errors are saved in the logger.
+   void save_errors()
+   {
+     if (m_save_errors)
+       return;
+     clear_saved_errors();
+     m_save_errors= TRUE;
+   }
+
+   /// Stop saving errors.
+   void stop_save_errors()
+   {
+     if (!m_save_errors)
+       return;
+     m_save_errors= FALSE;
+   }
+
+   /// Delete all saved errors to free resources.
+   void clear_saved_errors()
+   { errors.delete_elements(); }
+
+   /// Return a pointer to most recent saved error.
+   MYSQL_ERROR *last_saved_error()
+   { return errors.is_empty() ? NULL : errors.head(); }
+
+ private:
+
+  List<MYSQL_ERROR> errors;  ///< Used to store saved errors
+  bool m_save_errors;        ///< Flag telling if errors should be saved
+
+  int v_report_error(log_level::value,int,va_list);
+  int v_write_message(log_level::value,int, const char*,va_list);
+};
+
+
+} // backup namespace
+
+#endif
diff -Nrup a/sql/backup/map.h b/sql/backup/map.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/map.h	2007-11-06 11:11:47 -05:00
@@ -0,0 +1,189 @@
+#ifndef _UTIL_MAP_H
+#define _UTIL_MAP_H
+
+namespace util {
+
+/*******************************************************
+
+  Map (associative array) datatype templates.
+
+ *******************************************************/
+
+struct key8;
+
+template<class D, class K=key8>
+class Map
+{
+ public:
+
+  typedef typename D::Element  El;
+  typedef typename K::Key      Key;
+  static const size_t size=    K::size;
+
+  Key   add(const El &e)
+  {
+    Key k=  D::hash(e);
+    return find_el(k,e,TRUE);
+  }
+  
+  El&   operator[](const Key &k) const
+  {
+    if (!occupied(k))
+      return const_cast<El&>(D::null);
+  
+    return *(entries[k].el);
+  }
+  
+  Key   find(const El &e) const
+  {
+    Key k=  D::hash(e);
+    return const_cast<Map<D,K>*>(this)->find_el(k,e);
+  }
+  
+  bool  occupied(const Key &k) const
+  {
+    return K::valid_key(k) && entries[k].el != NULL;
+  }
+  
+  size_t count() const 
+  { return m_count; }
+
+  Map(): m_count(0) {}
+
+  ~Map() { clear(); }
+
+  void clear()
+  {
+    for (uint i=0; i < size; i++)
+     if (entries[i].el)
+       delete entries[i].el;
+  }
+
+#ifdef MAP_DEBUG
+
+  void print()
+  {
+    for(uint i=0 ; i < K::size ; i++ )
+    {
+      node &n= entries[i];
+  
+      if( n.el )
+      {
+        printf("entry %02d (%d,%d): ", i, (int)n.bigger, (int)n.smaller );
+        D::print(*n.el);
+        printf("\n");
+      }
+    }
+  }
+
+#endif
+
+ protected:
+
+  // Use binary search tree for storing entries.
+
+  struct node {
+    El  *el;
+    Key bigger,smaller;
+    node(): el(NULL), bigger(K::null), smaller(K::null) {};
+    void operator=(const El &e)
+    {
+      if (el) delete el;
+      el= D::create(e);
+      bigger= K::null;
+      smaller= K::null;
+    };
+  } entries[K::size];
+
+  uint m_count;
+
+  // PRE: k is valid.
+  Key   find_el(const Key &k, const El &e, bool insert= FALSE)
+  {
+    El *x= entries[k].el;
+  
+    if (!x)
+      if (insert)
+      {
+        set(k,e);
+        return k;
+      }
+      else return K::null;
+  
+    int res;
+  
+    if ((res= D::cmp(e,*x)) == 0)
+      return k;
+  
+    Key &k1 = res>0 ? entries[k].bigger : entries[k].smaller;
+  
+    if (K::valid_key(k1))
+      return find_el(k1,e);
+  
+    Key k2;
+    if (K::valid_key(k2= find_free_loc()))
+    {
+      k1= k2;
+      set(k2,e);
+    }
+  
+    return k2;
+  }
+
+  Key   find_free_loc() const
+  {
+    if (m_count >= K::size)
+      return K::null;
+  
+    for (uint k=0; k < size; k++)
+      if (entries[k].el == NULL)
+        return k;
+  
+    return K::null;
+  }
+  
+  // PRE k is valid
+  void  set(const Key &k, const El &e)
+  {
+    entries[k]= e;
+    m_count++;
+  }
+
+};
+
+
+// 8 bit keys
+
+struct key8
+{
+  typedef key8 Key;
+  static const size_t size= (size_t)255;
+  static const unsigned int null= 0xFF;
+
+  operator int() const { return val; };
+  bool is_valid() const { return key8::valid_key(*this); };
+  static bool valid_key(const Key &k) { return k.val != 0xFF; };
+
+  key8(): val(0xFF) {};
+  key8(unsigned int x) { operator=(x); };
+
+  Key &operator=(unsigned int x)
+  {
+    val= x & 0xFF;
+    // simple hashing
+    for (int bits= sizeof(unsigned int) ; bits > 8 ; bits-= 8)
+    {
+      x >>= 8;
+      val ^= x &0xFF;
+    };
+    return *this;
+  }
+
+ private:
+
+  unsigned char val;
+};
+
+} // util namespace
+
+#endif
diff -Nrup a/sql/backup/meta_backup.cc b/sql/backup/meta_backup.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/meta_backup.cc	2007-11-06 11:11:48 -05:00
@@ -0,0 +1,380 @@
+/**
+  @file
+
+  Functions used by kernel to save/restore meta-data
+ */
+
+/*
+  TODO:
+
+  - Handle events, routines, triggers and other item types (use Chuck's code).
+
+  - When saving database info, save its default charset and collation.
+    Alternatively, save a complete "CREATE DATABASE ..." statement with all
+    clauses which should work even when new clauses are added in the future.
+
+  - In silent_exec_query() - reset, collect and report errors from statement
+    execution.
+
+  - In the same function: investigate what happens if query triggers error -
+    there were signals that the code might hang in that case (some clean-up
+    needed?)
+ */
+
+#include <mysql_priv.h>
+#include <sql_show.h>
+
+#include "backup_aux.h"
+#include "backup_kernel.h"
+#include "meta_backup.h"
+
+
+namespace backup {
+
+/**
+  Write meta-data description to backup stream.
+
+  Meta-data information is stored as a sequence of entries, each describing a
+  single meta-data item. The format of a single entry depends on the type of
+  item (see @c Archive_info::Item::save() method). The entries are saved in a
+  single chunk.
+
+  The order of entries is important. Items on which other items depend should
+  be saved first in the sequence. This order is determined by the implementation
+  of @c Backup_info::Item_iterator class.
+
+  @note Meta-data description is added to the current chunk of the stream which
+  is then closed.
+
+  @returns 0 on success, error code otherwise.
+ */
+int write_meta_data(THD *thd, Backup_info &info, OStream &s)
+{
+  DBUG_ENTER("backup::write_meta_data");
+
+  size_t start_bytes= s.bytes;
+
+  for (Backup_info::Item_iterator it(info); it; it++)
+  {
+    result_t res= it->save(thd,s); // this calls Archive_info::Item::save() method
+
+    if (res != OK)
+    {
+      meta::Item::description_buf buf;
+      info.report_error(ER_BACKUP_WRITE_META,
+                         it->meta().describe(buf,sizeof(buf)));
+      DBUG_RETURN(ERROR);
+    }
+  }
+
+  if (stream_result::ERROR == s.end_chunk())
+    DBUG_RETURN(ERROR);
+
+  info.meta_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(0);
+}
+
+
+/**
+  Read meta-data items from a stream and create them if they are selected
+  for restore.
+
+  @pre  Stream is at the beginning of a saved meta-data chunk.
+  @post Stream is at the beginning of the next chunk.
+ */
+int restore_meta_data(THD *thd, Restore_info &info, IStream &s)
+{
+  DBUG_ENTER("restore_meta_data");
+  Archive_info::Item *it; // save pointer to item read form the stream
+  Db_ref  curr_db;        // remember the current database
+  result_t res;
+
+  size_t start_bytes= s.bytes;
+
+  // read items from the stream until error or end of data is reached.
+  while (OK == (res= Archive_info::Item::create_from_stream(info,s,it)))
+  {
+    DBUG_PRINT("restore",(" got next meta-item."));
+
+    if (info.selected(*it)) // if the item was selected for restore ...
+    {
+     DBUG_PRINT("restore",("  creating it!"));
+
+     /*
+       change the current database if we are going to create a per-db item
+       and we are not already in the correct one.
+      */
+     const Db_ref db= it->meta().in_db();
+
+     if (db.is_valid() && (!curr_db.is_valid() || db != curr_db))
+     {
+       DBUG_PRINT("restore",("  changing current db to %s",db.name().ptr()));
+       curr_db= db;
+       change_db(thd,db);
+     }
+
+     if (OK != (res= it->meta().create(thd)))
+     {
+       meta::Item::description_buf buf;
+       info.report_error(ER_BACKUP_CREATE_META,
+                          it->meta().describe(buf,sizeof(buf)));
+       DBUG_RETURN(ERROR);
+     }
+
+     delete it;
+    }
+  }
+
+  // We should reach end of chunk now - if not something went wrong
+
+  if (res != DONE)
+  {
+    info.report_error(ER_BACKUP_READ_META);
+    DBUG_RETURN(ERROR);
+  }
+
+  DBUG_ASSERT(res == DONE);
+
+  if (stream_result::ERROR == s.next_chunk())
+  {
+    info.report_error(ER_BACKUP_NEXT_CHUNK);
+    DBUG_RETURN(ERROR);
+  }
+
+  info.meta_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(0);
+}
+
+} // backup namespace
+
+
+/*********************************************
+
+  Save/restore for different meta-data items.
+
+ *********************************************/
+
+namespace backup {
+
+int silent_exec_query(THD*, String&);
+
+/**
+  Write data needed to restore an item.
+
+  By default, a complete DDL CREATE statement for the item is saved.
+  This statement is constructed using @c meta::X::build_create_stmt() method
+  where meta::X is the class representing the item. The method stores statement
+  in the @c create_stmt member.
+
+  @returns OK or ERROR
+ */
+
+result_t
+meta::Item::save(THD *thd, OStream &s)
+{
+  create_stmt.free();
+
+  if (build_create_stmt(thd))
+    return ERROR;
+
+  return stream_result::OK == s.writestr(create_stmt) ? OK : ERROR;
+}
+
+/**
+  Read data written by @c save().
+
+  By default, the CREATE statement is read and stored in the @c create_stmt
+  member.
+
+  @retval OK    everything went ok
+  @retval DONE  end of data chunk detected
+  @retval ERROR error has happened
+ */
+result_t
+meta::Item::read(IStream &s)
+{
+  stream_result::value res= s.readstr(create_stmt);
+
+  // Saved string should not be NIL
+  return res == stream_result::NIL ? ERROR : report_stream_result(res);
+}
+
+/**
+  Create item.
+
+  Default implementation executes the statement stored in the @c create_stmt
+  member.
+
+  @returns OK or ERROR
+ */
+result_t
+meta::Item::create(THD *thd)
+{
+  if (create_stmt.is_empty())
+  { return ERROR; }
+
+  if (ERROR == drop(thd))
+    return ERROR;
+
+  return silent_exec_query(thd,create_stmt) ? ERROR : OK;
+}
+
+/**
+  Destroy item if it exists.
+
+  Default implementation executes SQL statement of the form:
+  @verbatim
+     DROP @<object type> IF EXISTS @<name>
+  @endverbatim
+  strings @<object type> and @<name> are returned by @c X::sql_object_name()
+  and @c X::sql_name() methods of the class @c meta::X representing the item.
+  If necessary, method @c drop() can be overwritten in a specialized class
+  corresponding to a given type of meta-data item.
+
+  @returns OK or ERROR
+ */
+result_t
+meta::Item::drop(THD *thd)
+{
+  const char *ob= sql_object_name();
+
+  /*
+    An item class should define object name for DROP statement
+    or redefine drop() method.
+   */
+  DBUG_ASSERT(ob);
+
+  String drop_stmt;
+
+  drop_stmt.append("DROP ");
+  drop_stmt.append(ob);
+  drop_stmt.append(" IF EXISTS ");
+  drop_stmt.append(sql_name());
+
+  return silent_exec_query(thd,drop_stmt) ? ERROR : OK;
+}
+
+/**** SAVE/RESTORE DATABASES ***********************************/
+
+/**
+  Save data needed to create a database.
+
+  Currently we don't save anything. A database is always created using
+  "CREATE DATABASE @<name>" statement.
+ */
+result_t
+meta::Db::save(THD*,OStream&)
+{
+  return OK;
+}
+
+/**
+  Read data needed to create a database.
+
+  Nothing to read. We just build the "CREATE DATABASE ..." statement in
+  @c create_stmt.
+ */
+result_t
+meta::Db::read(IStream&)
+{
+  create_stmt.append("CREATE DATABASE ");
+  create_stmt.append(sql_name());
+  return OK;
+}
+
+
+/**** SAVE/RESTORE TABLES ***************************************/
+
+/**
+  Build a CREATE statement for a table.
+
+  We use @c store_create_info() function defined in the server. For that
+  we need to open the table. After building the statement the table is closed to
+  save resources. Actually, all tables of the thread are closed as we use
+  @c close_thread_tables() function.
+ */
+int meta::Table::build_create_stmt(THD *thd)
+{
+  TABLE_LIST t, *tl= &t;
+
+  bzero(&t,sizeof(TABLE_LIST));
+
+  t.db= const_cast<char*>(in_db().name().ptr());
+  t.alias= t.table_name= const_cast<char*>(sql_name());
+
+  uint cnt;
+  int res= ::open_tables(thd,&tl,&cnt,0);
+
+  if (res)
+  {
+    DBUG_PRINT("backup",("Can't open table %s to save its description (error=%d)",
+                         t.alias,res));
+    return res;
+  }
+
+  res= ::store_create_info(thd,&t,&create_stmt,NULL);
+
+  if (res)
+    DBUG_PRINT("backup",("Can't get CREATE statement for table %s (error=%d)",
+                         t.alias,res));
+
+  ::close_thread_tables(thd);
+
+  return res;
+}
+
+} // backup namespace
+
+
+/************************************************
+
+              Helper functions
+
+ ************************************************/
+
+namespace backup {
+
+/// Execute SQL query without sending anything to client.
+
+/*
+  Note: the change net.vio idea taken from execute_init_command in
+  sql_parse.cc
+ */
+
+int silent_exec_query(THD *thd, String &query)
+{
+  Vio *save_vio= thd->net.vio;
+
+  DBUG_PRINT("restore",("executing query %s",query.c_ptr()));
+
+  thd->net.vio= 0;
+  thd->net.no_send_error= 0;
+
+  thd->query=         query.c_ptr();
+  thd->query_length=  query.length();
+
+  thd->set_time(time(NULL));
+  pthread_mutex_lock(&::LOCK_thread_count);
+  thd->query_id= ::next_query_id();
+  pthread_mutex_unlock(&::LOCK_thread_count);
+
+  const char *ptr;
+  ::mysql_parse(thd,thd->query,thd->query_length,&ptr);
+
+  thd->net.vio= save_vio;
+
+  if (thd->query_error)
+  {
+    DBUG_PRINT("restore",
+              ("error executing query %s!", thd->query));
+    DBUG_PRINT("restore",("last error (%d): %s",thd->net.last_errno
+                                               ,thd->net.last_error));
+    return thd->net.last_errno ? (int)thd->net.last_errno : -1;
+  }
+
+  return 0;
+}
+
+} // backup namespace
diff -Nrup a/sql/backup/meta_backup.h b/sql/backup/meta_backup.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/meta_backup.h	2007-11-06 11:11:49 -05:00
@@ -0,0 +1,133 @@
+#ifndef _META_BACKUP_H
+#define _META_BACKUP_H
+
+/**
+  @file
+
+  Declarations of classes used to handle meta-data items.
+ */
+
+#include <backup/api_types.h>
+#include <backup/stream.h>
+
+#define META_ITEM_DESCRIPTION_LEN 128
+
+namespace backup {
+
+namespace meta {
+
+/**
+  Defines how to backup and restore a meta-data item.
+
+  This class provides @c create() and @c drop() methods used to create and
+  destroy items. The default implementation uses SQL "CREATE ..." and "DROP ..."
+  statements for that purpose. Its instances determine how to save and read data
+  needed for item creation and provide any other item specific data. For each
+  type of meta-data there is a specialized subclass of @c meta::Item which
+  implements these tasks.
+ */
+
+class Item
+{
+ public:
+
+  /// Possible types of meta-data items.
+  enum enum_type {DB, TABLE};
+
+  virtual ~Item() {}
+
+  /// Return type of the item.
+  virtual const enum_type type() const =0;
+
+  /**
+    For per-db items return the database to which this item belongs.
+    For other items returns an invalid db reference.
+   */
+  virtual const Db_ref in_db()
+  { return Db_ref(); }
+
+  /// Save data needed to create the item.
+  virtual result_t save(THD*,OStream&);
+
+  /// Read data saved by @c save() method.
+  virtual result_t read(IStream&);
+
+  /// Destroy the item if it exists.
+  virtual result_t drop(THD*);
+
+  /// Create the item.
+  virtual result_t create(THD*);
+
+  typedef char description_buf[META_ITEM_DESCRIPTION_LEN+1];
+
+  /// Describe item for log purposes.
+  virtual const char* describe(char *buf, size_t buf_len)
+  {
+    my_snprintf(buf,buf_len,"%s %s",sql_object_name(),sql_name());
+    return buf;
+  }
+
+ protected:
+
+  String create_stmt;  /// Storage for a create statement of the item.
+
+  /**
+    Return SQL name of the object represented by this item like TABLE
+    or DATABASE. This is used to construct a "DROP ..." statement for
+    the item.
+   */
+  virtual const char* sql_object_name() const
+  { return NULL; }
+
+  /**
+    Return name under which this item is known to the server. In case of
+    per-db items the name should *not* be qualified by db name.
+   */
+  virtual const char* sql_name() const =0;
+
+  /// Store in @c create_stmt a DDL statement which will create the item.
+  /*
+    We give a default implementation because an item can not use create
+    statements and then it doesn't have to worry about this method.
+  */
+  virtual int build_create_stmt(THD*)
+  { return ERROR; }
+
+};
+
+/**
+  Specialization of @c meta::Item representing a database.
+ */
+class Db: public Item
+{
+  const enum_type type() const
+  { return DB; }
+
+  const char* sql_object_name() const
+  { return "DATABASE"; }
+
+  // Overwrite default implementations.
+  result_t save(THD*,OStream&);
+  result_t read(IStream&);
+
+};
+
+/**
+  Specialization of @c meta::Item representing a table.
+ */
+class Table: public Item
+{
+  const enum_type type() const
+  { return TABLE; }
+
+  const char* sql_object_name() const
+  { return "TABLE"; }
+
+  int build_create_stmt(THD*);
+};
+
+} // meta namespace
+
+} // backup namespace
+
+#endif
diff -Nrup a/sql/backup/sql_backup.cc b/sql/backup/sql_backup.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/sql_backup.cc	2007-11-06 11:11:49 -05:00
@@ -0,0 +1,1364 @@
+/**
+  @file
+
+  Implementation of the backup kernel API.
+ */
+
+/*
+  TODO:
+
+  - Handle SHOW BACKUP ARCHIVE command.
+  - Add database list to the backup/restore summary.
+  - Implement meta-data freeze during backup/restore.
+  - Improve logic selecting backup driver for a table.
+  - Handle other types of meta-data in Backup_info methods.
+  - Handle item dependencies when adding new items.
+  - Lock I_S tables when reading table list and similar (is it needed?)
+  - When reading table list from I_S tables, use select conditions to
+    limit amount of data read. (check prepare_select_* functions in sql_help.cc)
+  - Handle other kinds of backup locations (far future).
+  - Complete feedback given by BACKUP/RESTORE statements (send_summary function).
+ */
+
+#include "../mysql_priv.h"
+
+#include "backup_aux.h"
+#include "stream.h"
+#include "backup_kernel.h"
+#include "meta_backup.h"
+#include "archive.h"
+#include "debug.h"
+#include "be_default.h"
+#include "be_snapshot.h"
+
+namespace backup {
+
+// Helper functions
+
+static IStream* open_for_read(const Location&);
+static OStream* open_for_write(const Location&);
+
+/*
+  Report errors. The main error code and optional arguments for its description
+  are given plus a logger object which can contain stored errors.
+ */
+static int report_errors(THD*, Logger&, int, ...);
+
+/*
+  Check if info object is valid. If not, report error to client.
+ */
+static int check_info(THD*,Backup_info&);
+static int check_info(THD*,Restore_info&);
+
+static bool send_summary(THD*,const Backup_info&);
+static bool send_summary(THD*,const Restore_info&);
+
+#ifdef DBUG_BACKUP
+// Flag used for testing error reporting
+bool test_error_flag= FALSE;
+#endif
+}
+
+/**
+  Call backup kernel API to execute backup related SQL statement.
+
+  @param lex  results of parsing the statement.
+
+  @note This function sends response to the client (ok, result set or error).
+ */
+
+int
+execute_backup_command(THD *thd, LEX *lex)
+{
+  DBUG_ENTER("execute_backup_command");
+  DBUG_ASSERT(thd && lex);
+
+  BACKUP_SYNC("backup_command");
+
+  backup::Location *loc= backup::Location::find(lex->backup_dir);
+
+  if (!loc)
+  {
+    my_error(ER_BACKUP_INVALID_LOC,MYF(0),lex->backup_dir.str);
+    DBUG_RETURN(ER_BACKUP_INVALID_LOC);
+  }
+
+  int res= 0;
+
+  switch (lex->sql_command) {
+
+  case SQLCOM_SHOW_ARCHIVE:
+  case SQLCOM_RESTORE:
+  {
+    backup::IStream *stream= backup::open_for_read(*loc);
+
+    if (!stream)
+    {
+      my_error(ER_BACKUP_READ_LOC,MYF(0),loc->describe());
+      goto restore_error;
+    }
+    else
+    {
+      backup::Restore_info info(*stream);
+
+      if (check_info(thd,info))
+        goto restore_error;
+
+      if (lex->sql_command == SQLCOM_SHOW_ARCHIVE)
+      {
+        my_error(ER_NOT_ALLOWED_COMMAND,MYF(0));
+        goto restore_error;
+        /*
+          Uncomment when mysql_show_archive() is implemented
+
+          res= mysql_show_archive(thd,info);
+         */
+      }
+      else
+      {
+        info.report_error(backup::log_level::INFO,ER_BACKUP_RESTORE_START);
+
+        // TODO: freeze all DDL operations here
+
+        info.save_errors();
+        info.restore_all_dbs();
+
+        if (check_info(thd,info))
+          goto restore_error;
+
+        info.clear_saved_errors();
+
+        if (mysql_restore(thd,info,*stream))
+        {
+          report_errors(thd,info,ER_BACKUP_RESTORE);
+          goto restore_error;
+        }
+        else
+        {
+          info.report_error(backup::log_level::INFO,ER_BACKUP_RESTORE_DONE);
+          info.total_size += info.header_size;
+          send_summary(thd,info);
+        }
+
+        // TODO: unfreeze DDL here
+      }
+    } // if (!stream)
+
+    goto finish_restore;
+    
+   restore_error:
+
+    res= res ? res : backup::ERROR;
+    
+   finish_restore:
+
+    if (stream)
+      stream->close();
+
+    break;
+  }
+
+  case SQLCOM_BACKUP:
+  {
+    backup::OStream *stream= backup::open_for_write(*loc);
+
+    if (!stream)
+    {
+      my_error(ER_BACKUP_WRITE_LOC,MYF(0),loc->describe());
+      goto backup_error;
+    }
+    else
+    {
+      backup::Backup_info info(thd);
+
+      if (check_info(thd,info))
+        goto backup_error;
+
+      info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_START);
+
+      // TODO: freeze all DDL operations here
+
+      info.save_errors();
+
+      if (lex->db_list.is_empty())
+      {
+        info.write_message(backup::log_level::INFO,"Backing up all databases");
+        info.add_all_dbs(); // backup all databases
+      }
+      else
+      {
+        info.write_message(backup::log_level::INFO,"Backing up selected databases");
+        info.add_dbs(lex->db_list); // backup databases specified by user
+      }
+
+      if (check_info(thd,info))
+        goto backup_error;
+
+      info.close();
+
+      if (check_info(thd,info))
+        goto backup_error;
+
+      info.clear_saved_errors();
+
+      if (mysql_backup(thd,info,*stream))
+      {
+        report_errors(thd,info,ER_BACKUP_BACKUP);
+        goto backup_error;
+      }
+      else
+      {
+        info.report_error(backup::log_level::INFO,ER_BACKUP_BACKUP_DONE);
+        send_summary(thd,info);
+      }
+
+      // TODO: unfreeze DDL here
+    } // if (!stream)
+
+    goto finish_backup;
+   
+   backup_error:
+   
+    res= res ? res : backup::ERROR;
+   
+   finish_backup:
+
+    if (stream)
+      stream->close();
+
+#ifdef  DBUG_BACKUP
+    backup::IStream *is= backup::open_for_read(*loc);
+    if (is)
+    {
+      dump_stream(*is);
+      is->close();
+    }
+#endif
+
+    break;
+  }
+
+   default:
+     /*
+       execute_backup_command() should be called with correct command id
+       from the parser. If not, we fail on this assertion.
+      */
+     DBUG_ASSERT(FALSE);
+  }
+
+  loc->free();
+  DBUG_RETURN(res);
+}
+
+
+/*************************************************
+
+                   BACKUP
+
+ *************************************************/
+
+// Declarations for functions used in backup operation
+
+namespace backup {
+
+// defined in meta_backup.cc
+int write_meta_data(THD*, Backup_info&, OStream&);
+
+// defined in data_backup.cc
+int write_table_data(THD*, Backup_info&, OStream&);
+
+} // backup namespace
+
+
+/**
+  Create backup archive.
+*/
+
+int mysql_backup(THD *thd,
+                 backup::Backup_info &info,
+                 backup::OStream &s)
+{
+  DBUG_ENTER("mysql_backup");
+
+  // This function should not be called with invalid backup info.
+  DBUG_ASSERT(info.is_valid());
+
+  size_t start_bytes= s.bytes;
+
+  BACKUP_SYNC("backup_meta");
+
+  DBUG_PRINT("backup",("Writing image header"));
+
+  if (info.save(s))
+    goto error;
+
+  DBUG_PRINT("backup",("Writing meta-data"));
+
+  if (backup::write_meta_data(thd,info,s))
+    goto error;
+
+  DBUG_PRINT("backup",("Writing table data"));
+
+  BACKUP_SYNC("backup_data");
+
+  if (backup::write_table_data(thd,info,s))
+    goto error;
+
+  DBUG_PRINT("backup",("Backup done."));
+  BACKUP_SYNC("backup_done");
+
+  info.total_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(0);
+
+ error:
+
+  DBUG_RETURN(backup::ERROR);
+}
+
+namespace backup {
+
+
+class Backup_info::Table_ref:
+ public backup::Table_ref
+{
+  String  m_db_name;
+  String  m_name;
+  ::TABLE *m_table;
+
+ public:
+
+  Table_ref(const Db_item&, const String&);
+  ~Table_ref()
+  { close(); }
+  
+  ::TABLE* open(THD*);
+  void close();
+  
+  /**
+   Return pointer to @c handlerton structure of table's storage engine.
+   
+   @return @c NULL if table has not been opened or pointer to the @c handlerton
+   structure of table's storage engine.
+   */ 
+  ::handlerton* hton() const
+  { return m_table && m_table->s ? m_table->s->db_type() : NULL; }
+  
+  /// Check if table has been opened.
+  bool is_open() const 
+  { return m_table != NULL; }
+};
+
+/**
+  Find image to which given table can be added and add it.
+
+  Creates new images as needed.
+
+  @returns Position of the image found in @c images[] table or -1 on error.
+ */
+
+/*
+   Logic for selecting image format for a given table location:
+
+   1. If tables from that location have been already stored in one of the
+      sub-images then choose that sub-image.
+
+   2. If location has "native" backup format, put it in a new sub-image with
+      that format.
+
+   3. Otherwise check if one of the existing sub-images would accept table from
+      this location.
+
+   4. If table has no native backup engine, try a consistent snapshot one.
+
+   5. When everything else fails, use default (blocking) backup driver.
+
+   Note: 1 is not implemented yet and hence we start with 3.
+ */
+
+int Backup_info::find_image(const Backup_info::Table_ref &tbl)
+{
+   DBUG_ENTER("Backup_info::find_image");
+   // we assume that table has been opened already
+   DBUG_ASSERT(tbl.is_open());
+   
+   Image_info *img;
+   const ::handlerton *hton= tbl.hton();
+   // If table has no handlerton something is really bad - we crash here
+   DBUG_ASSERT(hton);
+
+   Table_ref::describe_buf buf;
+   
+   DBUG_PRINT("backup",("Adding table %s using storage %s to archive%s",
+                        tbl.describe(buf),
+                        ::ha_resolve_storage_engine_name(hton),
+                        hton->get_backup_engine ? " (has native backup)." : "."));
+
+   // Point 3: try existing images but not the default or snapshot one.
+
+   for (uint no=0; no < img_count && no < MAX_IMAGES ; ++no)
+   {
+     if (default_image_no >= 0 && no == (uint)default_image_no)
+       continue;
+
+     if (snapshot_image_no >= 0 && no == (uint)snapshot_image_no)
+       continue;
+
+     img= images[no];
+
+     // An image object decides if it can handle given table or not
+     if( img && img->accept(tbl,hton) )
+       DBUG_RETURN(no);
+   }
+
+   // Point 2: try native backup of table's storage engine.
+
+   if (hton->get_backup_engine)
+   {
+     uint no= img_count;
+
+     // We handle at most MAX_IMAGES many images
+     DBUG_ASSERT(no<MAX_IMAGES);
+
+     img= images[no]= new Native_image(*this,hton);
+
+     if (!img)
+     {
+       report_error(ER_OUT_OF_RESOURCES);
+       DBUG_RETURN(-1);
+     }
+
+     DBUG_PRINT("backup",("%s image added to archive",img->name()));
+     img_count++;
+
+#ifdef DBUG_OFF 
+     // avoid "unused variable" compilation warning
+     img->accept(tbl,hton);
+#else
+     // native image should accept all tables from its own engine
+     bool res= img->accept(tbl,hton);
+     DBUG_ASSERT(res);
+#endif
+
+     DBUG_RETURN(no);
+   }
+
+   // Points 4 & 5: try consistent snapshot and default drivers..
+
+   // try snapshot driver first
+   int ino= snapshot_image_no;
+   if (hton->start_consistent_snapshot != NULL)
+   {
+     if (snapshot_image_no < 0) //image doesn't exist
+     {
+       ino= img_count;
+       snapshot_image_no= img_count;
+       images[snapshot_image_no]= new Snapshot_image(*this);
+       img_count++;
+       DBUG_PRINT("backup",("Snapshot image added to archive"));
+     }
+   }
+   else
+     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
+
+   report_error(ER_BACKUP_NO_BACKUP_DRIVER,tbl.describe(buf,sizeof(buf)));
+   DBUG_RETURN(-1);
+}
+
+} // backup namespace
+
+
+/****************************
+
+  Backup_info implementation
+
+ ****************************/
+
+namespace backup {
+
+//  Returns tmp table containing records from a given I_S table
+TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st);
+
+/* 
+  Backup_info::skip_table pointer is just for indicating that a table
+  added with Backup_info::add_table() was skipped. It should have value not 
+  possible for regular pointers.
+ */
+const Backup_info::Table_item *const 
+Backup_info::skip_table= reinterpret_cast<Backup_info::Table_item*>(1);
+
+/**
+  Create @c Backup_info structure and prepare it for populating with meta-data
+  items.
+
+  When adding a complete database to the archive, all its tables are added.
+  These are found by reading INFORMATION_SCHEMA.TABLES table. The table is
+  opened here so that it is ready for use in @c add_db_items() method. It is
+  closed when the structure is closed with the @c close() method.
+ */
+Backup_info::Backup_info(THD *thd):
+  Logger(Logger::BACKUP),
+  m_state(INIT), default_image_no(-1), snapshot_image_no(-2), 
+  m_thd(thd), i_s_tables(NULL),
+  m_items(NULL), m_last_item(NULL), m_last_db(NULL)
+{
+  i_s_tables= get_schema_table(m_thd, ::get_schema_table(SCH_TABLES));
+  if (!i_s_tables)
+  {
+    report_error(ER_BACKUP_LIST_TABLES);
+    m_state= ERROR;
+  }
+}
+
+Backup_info::~Backup_info()
+{
+  close();
+  m_state= DONE;
+
+  Item_iterator it(*this);
+  Item *i;
+
+  while ((i=it++))
+    delete i;
+
+  m_items= NULL;
+}
+
+/**
+  Close the structure after populating it with items.
+
+  When meta-data is written, we need to open archive's tables to be able
+  to read their definitions. Tables are opened here so that the
+  structure is ready for creation of the archive.
+
+  FIXME: opening all tables at once consumes too much resources when there
+  is a lot of tables (opened file descriptors!). Find a better solution.
+ */
+bool Backup_info::close()
+{
+  bool ok= is_valid();
+
+  if(i_s_tables)
+    ::free_tmp_table(m_thd,i_s_tables);
+  i_s_tables= NULL;
+
+  if (m_state == INIT)
+    m_state= READY;
+
+  return ok;
+}
+
+int Backup_info::save(OStream &s)
+{
+  if (OK != Archive_info::save(s))
+  {
+    report_error(ER_BACKUP_WRITE_HEADER);
+    return ERROR;
+  }
+
+  return 0;
+}
+
+/**
+  Specialization of @c backup::Db_ref with convenient constructors.
+ */
+
+class Backup_info::Db_ref: public backup::Db_ref
+{
+  String m_name;
+
+ public:
+
+  Db_ref(const String &s): backup::Db_ref(m_name), m_name(s)
+  {}
+
+  Db_ref(const LEX_STRING &s):
+    backup::Db_ref(m_name),
+    m_name(s.str,s.length,::system_charset_info)
+  {}
+};
+
+/**
+  Add to archive all databases in the list.
+ */
+int Backup_info::add_dbs(List< ::LEX_STRING > &dbs)
+{
+  List_iterator< ::LEX_STRING > it(dbs);
+  ::LEX_STRING *s;
+  String unknown_dbs; // comma separated list of databases which don't exist
+
+  while ((s= it++))
+  {
+    Db_ref db(*s);
+
+    if (db_exists(db))
+    {
+      if (!unknown_dbs.is_empty()) // we just compose unknown_dbs list
+        continue;
+
+      Db_item *it= add_db(db);
+
+      if (!it)
+        goto error;
+
+      if (add_db_items(*it))
+        goto error;
+    }
+    else
+    {
+      if (!unknown_dbs.is_empty())
+        unknown_dbs.append(",");
+      unknown_dbs.append(db.name());
+    }
+  }
+
+  if (!unknown_dbs.is_empty())
+  {
+    report_error(ER_BAD_DB_ERROR,unknown_dbs.c_ptr());
+    goto error;
+  }
+  
+  return 0;
+  
+ error:
+
+  m_state= ERROR;
+  return backup::ERROR;
+}
+
+/**
+  Add to archive all instance's databases (except the internal ones).
+ */
+
+int Backup_info::add_all_dbs()
+{
+  my_bitmap_map *old_map;
+
+  DBUG_PRINT("backup", ("Reading databases from I_S"));
+
+  ::TABLE *db_table = get_schema_table(m_thd, ::get_schema_table(SCH_SCHEMATA));
+
+  if (!db_table)
+  {
+    report_error(ER_BACKUP_LIST_DBS);
+    return ERROR;
+  }
+
+  ::handler *ha = db_table->file;
+
+  // TODO: locking
+
+  int res= 0;
+
+  old_map= dbug_tmp_use_all_columns(db_table, db_table->read_set);
+
+  if (ha->ha_rnd_init(TRUE))
+  {
+    res= -2;
+    report_error(ER_BACKUP_LIST_DBS);
+    goto finish;
+  }
+
+  while (!ha->rnd_next(db_table->record[0]))
+  {
+    String db_name;
+    
+    db_table->field[1]->val_str(&db_name);
+    
+    // skip internal databases
+    if (db_name == String("information_schema",&::my_charset_bin) 
+        || db_name == String("mysql",&my_charset_bin))
+    {
+      DBUG_PRINT("backup",(" Skipping internal database %s",db_name.ptr()));
+      continue;
+    }
+
+    Db_ref db(db_name);
+
+    DBUG_PRINT("backup", (" Found database %s", db.name().ptr()));
+
+    Db_item *it= add_db(db);
+
+    if (!it)
+    {
+      res= -3;
+      goto finish;
+    }
+
+    if (add_db_items(*it))
+    {
+      res= -4;
+      goto finish;
+    }
+  }
+
+  DBUG_PRINT("backup", ("No more databases in I_S"));
+
+  ha->ha_rnd_end();
+
+ finish:
+
+  if (db_table)
+  {
+    dbug_tmp_restore_column_map(db_table->read_set, old_map);
+    ::free_tmp_table(m_thd, db_table);
+  }
+
+  if (res)
+    m_state= ERROR;
+
+  return res;
+}
+
+/**
+  Insert a @c Db_item into the meta-data items list.
+ */
+Backup_info::Db_item*
+Backup_info::add_db(const backup::Db_ref &db)
+{
+  StringPool::Key key= db_names.add(db.name());
+
+  if (!key.is_valid())
+  {
+    report_error(ER_OUT_OF_RESOURCES);
+    return NULL;
+  }
+
+  Db_item *di= new Db_item(*this,key);
+
+  if (!di)
+  {
+    report_error(ER_OUT_OF_RESOURCES);
+    return NULL;
+  }
+
+  // insert db item before all table items
+
+  if (!m_last_db)
+  {
+    di->next= m_items;
+    m_items= m_last_db= di;
+    if (!m_last_item)
+     m_last_item= m_items;
+  }
+  else
+  {
+    // since m_last_db is not NULL, m_items list can't be empty
+    DBUG_ASSERT(m_items);
+
+    di->next= m_last_db->next;
+    if (m_last_item == m_last_db)
+     m_last_item= di;
+    m_last_db->next= di;
+  }
+
+  return di;
+}
+
+/**
+  Add to archive all items belonging to a given database.
+ */
+int Backup_info::add_db_items(Db_item &db)
+{
+  my_bitmap_map *old_map;
+
+  DBUG_ASSERT(m_state == INIT);  // should be called before structure is closed
+  DBUG_ASSERT(is_valid());       // should be valid
+  DBUG_ASSERT(i_s_tables->file); // i_s_tables should be opened
+
+  // add all tables from db.
+
+  ::handler *ha= i_s_tables->file;
+
+  /*
+    If error debugging is switched on (see debug.h) then I_S.TABLES access
+    error will be triggered when backing up database whose name starts with 'a'.
+   */
+  TEST_ERROR_IF(db.name().ptr()[0]=='a');
+
+  old_map= dbug_tmp_use_all_columns(i_s_tables, i_s_tables->read_set);
+  
+  if (ha->ha_rnd_init(TRUE) || TEST_ERROR)
+  {
+    dbug_tmp_restore_column_map(i_s_tables->read_set, old_map);
+    report_error(ER_BACKUP_LIST_DB_TABLES,db.name().ptr());
+    return ERROR;
+  }
+
+  int res= 0;
+
+  while (!ha->rnd_next(i_s_tables->record[0]))
+  {
+    String db_name;
+    String name;
+    String type;
+    String engine;
+
+    /* 
+      Read info about next table/view
+      
+      Note: this should be synchronized with the definition of 
+      INFORMATION_SCHEMA.TABLES table.
+     */
+    i_s_tables->field[1]->val_str(&db_name);
+    i_s_tables->field[2]->val_str(&name);
+    i_s_tables->field[3]->val_str(&type);
+    i_s_tables->field[4]->val_str(&engine);
+    
+    if (db_name != db.name())
+      continue; // skip tables not from the given database
+    
+    // FIXME: right now, we handle only tables
+    if (type != String("BASE TABLE",&::my_charset_bin))
+    {
+      report_error(log_level::WARNING,ER_BACKUP_SKIP_VIEW,
+                   name.c_ptr(),db_name.c_ptr());
+      continue;
+    }
+
+    Backup_info::Table_ref t(db,name);
+    
+    if (engine.is_empty())
+    {
+      Table_ref::describe_buf buf;
+      report_error(log_level::WARNING,ER_BACKUP_NO_ENGINE,t.describe(buf));
+      continue;                   
+    }
+
+    DBUG_PRINT("backup", ("Found table %s for database %s",
+                           t.name().ptr(), t.db().name().ptr()));
+
+    // We need to open table for add_table() method below
+    if (!t.open(m_thd))
+    {
+      Table_ref::describe_buf buf;
+      report_error(ER_BACKUP_TABLE_OPEN,t.describe(buf));
+      goto error;
+    }
+    // add_table method selects/creates sub-image appropriate for storing given table
+    Table_item *ti= add_table(t);
+    
+    // Close table to free resources
+    t.close();
+    
+    if (!ti)
+      goto error;
+
+    if (ti == skip_table)
+      continue;
+
+    if (add_table_items(*ti))
+      goto error;
+  }
+
+  goto finish;
+  
+ error:
+ 
+  res= res ? res : ERROR;
+ 
+ finish:
+ 
+  ha->ha_rnd_end();
+
+  dbug_tmp_restore_column_map(i_s_tables->read_set, old_map);
+
+  return res;
+}
+
+/* Implementation of Backup_info::Table_ref */
+
+Backup_info::Table_ref::Table_ref(const Db_item &db, const String &name):
+  backup::Table_ref(m_db_name,m_name), m_table(NULL)
+{
+  m_db_name.append(db.name());
+  m_name.append(name);
+}
+
+/**
+ Open table and create corresponding @c TABLE structure.
+ 
+ A pointer to opened @c TABLE structure is stored in @c m_table member. The 
+ structure is owned by @c Table_ref object, to destroy it call @c close() 
+ method. 
+ 
+ This method does nothing if table has been already opened.
+ 
+ @return Pointer to the opened @c TABLE structure or @c NULL if operation was
+ not successful. 
+ */ 
+::TABLE* Backup_info::Table_ref::open(THD *thd)
+{
+  if (is_open())
+    return m_table;
+    
+  char path[FN_REFLEN];
+  const char *db= m_db_name.ptr();
+  const char *name= m_name.ptr();
+  ::build_table_filename(path, sizeof(path), db, name, "", 0);
+  m_table= ::open_temporary_table(
+                thd, path, db, name, 
+                FALSE /* don't link to thd->temporary_tables */,
+                OTM_OPEN);
+  
+  /*
+   Note: If table couldn't be opened (m_table==NULL), open_temporary_table() 
+   doesn't inform us what was the reason. This makes it difficult to give 
+   precise information in the error log. Currently we just say that table 
+   couldn't be opened. When error reporting is improved, we should try to do 
+   better than that.
+   */ 
+  return m_table;
+}
+
+/**
+ Close previously opened table.
+ 
+ Closes table and frees allocated resources. Can be called even when table
+ has not been opened, in which case it does nothing.
+ */ 
+void Backup_info::Table_ref::close()
+{
+  if (m_table)
+  {
+    // TODO: check if ::free_tmp_table() is not better
+    ::intern_close_table(m_table);
+    my_free(m_table, MYF(0));
+  }
+  m_table= NULL;
+  return;
+}
+
+/**
+  Add table to archive's list of meta-data items.
+  
+  @pre Table should be opened.
+ */
+
+Backup_info::Table_item*
+Backup_info::add_table(const Table_ref &t)
+{
+  DBUG_ASSERT(t.is_open());
+    
+  // TODO: skip table if it is a tmp one
+    
+  int no= find_image(t); // Note: find_image reports errors
+
+  /*
+    If error debugging is switched on (see debug.h) then any table whose
+    name starts with 'a' will trigger "no backup driver" error.
+   */
+  TEST_ERROR_IF(t.name().ptr()[0]=='a');
+
+  if (no < 0 || TEST_ERROR)
+    return NULL;
+
+  /*
+    If image was found then images[no] should point at the Image_info
+    object
+   */
+  Image_info *img= images[no];
+  DBUG_ASSERT(img);
+
+  /*
+    If error debugging is switched on (see debug.h) then any table whose
+    name starts with 'b' will trigger error when added to backup image.
+   */
+  TEST_ERROR_IF(t.name().ptr()[0]=='b');
+
+  int tno= img->tables.add(t);
+  if (tno < 0 || TEST_ERROR)
+  {
+    Table_ref::describe_buf buf;
+    report_error(ER_BACKUP_NOT_ACCEPTED,img->name(),t.describe(buf));
+    return NULL;
+  }
+
+  table_count++;
+
+  Table_item *ti= new Table_item(*this,no,tno);
+
+  if (!ti)
+  {
+    report_error(ER_OUT_OF_RESOURCES);
+    return NULL;
+  }
+
+  // add to the end of items list
+
+  ti->next= NULL;
+  if (m_last_item)
+   m_last_item->next= ti;
+  m_last_item= ti;
+  if (!m_items)
+   m_items= ti;
+
+  return ti;
+}
+
+/**
+  Add to archive all items belonging to a given table.
+ */
+int Backup_info::add_table_items(Table_item&)
+{
+  // TODO: Implement when we handle per-table meta-data.
+  return 0;
+}
+
+} // backup namespace
+
+
+/*************************************************
+
+                   RESTORE
+
+ *************************************************/
+
+// Declarations of functions used in restore operation
+
+namespace backup {
+
+// defined in meta_backup.cc
+int restore_meta_data(THD*, Restore_info&, IStream&);
+
+// defined in data_backup.cc
+int restore_table_data(THD*, Restore_info&, IStream&);
+
+} // backup namespace
+
+
+/**
+  Restore items saved in backup archive.
+
+  @pre archive info has been already read and the stream is positioned
+        at archive's meta-data
+*/
+int mysql_restore(THD *thd, backup::Restore_info &info, backup::IStream &s)
+{
+  DBUG_ENTER("mysql_restore");
+
+  size_t start_bytes= s.bytes;
+
+  DBUG_PRINT("restore",("Restoring meta-data"));
+
+  if (backup::restore_meta_data(thd, info, s))
+    DBUG_RETURN(backup::ERROR);
+
+  DBUG_PRINT("restore",("Restoring table data"));
+
+  if (backup::restore_table_data(thd,info,s))
+    DBUG_RETURN(backup::ERROR);
+
+  DBUG_PRINT("restore",("Done."));
+
+  info.total_size= s.bytes - start_bytes;
+
+  DBUG_RETURN(0);
+}
+
+/*************************************************
+
+               BACKUP LOCATIONS
+
+ *************************************************/
+
+namespace backup {
+
+struct File_loc: public Location
+{
+  String path;
+
+  enum_type type() const
+  { return SERVER_FILE; }
+
+  File_loc(const char *p)
+  { path.append(p); }
+
+  const char* describe()
+  { return path.c_ptr(); }
+
+};
+
+
+template<class S>
+S* open_stream(const Location &loc)
+{
+  switch (loc.type()) {
+
+  case Location::SERVER_FILE:
+  {
+    const File_loc &f= static_cast<const File_loc&>(loc);
+    S *s= new S(f.path);
+
+    if (s && s->open())
+      return s;
+
+    delete s;
+    return NULL;
+  }
+
+  default:
+    return NULL;
+
+  }
+}
+
+template IStream* open_stream<IStream>(const Location&);
+template OStream* open_stream<OStream>(const Location&);
+
+IStream* open_for_read(const Location &loc)
+{
+  return open_stream<IStream>(loc);
+}
+
+
+OStream* open_for_write(const Location &loc)
+{
+  return open_stream<OStream>(loc);
+}
+
+/**
+  Find location described by a string.
+
+  The string is taken from the "TO ..." clause of BACKUP/RESTORE commands.
+  This function parses the string and creates instance of @c Location class
+  describing the location or NULL if string doesn't describe any valid location.
+
+  Currently the only supported type of location is a file on the server host.
+  The string is supposed to contain a path to such file.
+
+  @note No checks on the location are made at this stage. In particular the
+  location might not physically exist. In the future methods performing such
+  checks can be added to @Location class.
+ */
+Location*
+Location::find(const LEX_STRING &where)
+{
+  return where.str && where.length ? new File_loc(where.str) : NULL;
+}
+
+} // backup namespace
+
+
+/*************************************************
+
+                 Helper functions
+
+ *************************************************/
+
+TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); // defined in sql_show.cc
+
+namespace backup {
+
+/**
+  Report errors.
+
+  Current implementation reports the last error saved in the logger if it exist.
+  Otherwise it reports error given by @c error_code.
+ */
+int report_errors(THD *thd, Logger &log, int error_code, ...)
+{
+  MYSQL_ERROR *error= log.last_saved_error();
+
+  if (error && !util::report_mysql_error(thd,error,error_code))
+  {  
+    if (error->code)
+      error_code= error->code;
+  }
+  else // there are no error information in the logger - report error_code
+  {
+    char buf[ERRMSGSIZE + 20];
+    va_list args;
+    va_start(args,error_code);
+
+    my_vsnprintf(buf,sizeof(buf),ER_SAFE(error_code),args);
+    my_printf_error(error_code,buf,MYF(0));
+
+    va_end(args);
+  }
+
+  return error_code;
+}
+
+inline
+int check_info(THD *thd, Backup_info &info)
+{ 
+  return info.is_valid() ? OK : report_errors(thd,info,ER_BACKUP_BACKUP_PREPARE); 
+}
+
+inline
+int check_info(THD *thd, Restore_info &info)
+{ 
+  return info.is_valid() ? OK : report_errors(thd,info,ER_BACKUP_RESTORE_PREPARE); 
+}
+
+/**
+  Send a summary of the backup/restore operation to the client.
+
+  The data about the operation is taken from filled @c Archive_info
+  structure. Parameter @c backup determines if this was backup or
+  restore operation.
+
+  TODO: Add list of databases in the output.
+*/
+
+static
+bool send_summary(THD *thd, const Archive_info &info, bool backup)
+{
+  Protocol   *protocol= ::current_thd->protocol; // client comms
+  List<Item> field_list;                         // list of fields to send
+  Item       *item;                              // field item
+  char       buf[255];                           // buffer for data
+  String     op_str;                             // operations string
+  String     print_str;                          // string to print
+
+  DBUG_ENTER("backup::send_summary");
+
+  DBUG_PRINT(backup?"backup":"restore", ("sending summary"));
+
+  op_str.length(0);
+  if (backup)
+    op_str.append("Backup Summary");
+  else
+    op_str.append("Restore Summary");
+
+
+  field_list.push_back(item= new Item_empty_string(op_str.c_ptr(),255)); // TODO: use varchar field
+  item->maybe_null= TRUE;
+  protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+
+  my_snprintf(buf,sizeof(buf)," header     = %-8lu bytes",(unsigned long)info.header_size);
+  protocol->prepare_for_resend();
+  protocol->store(buf,system_charset_info);
+  protocol->write();
+
+  my_snprintf(buf,sizeof(buf)," meta-data  = %-8lu bytes",(unsigned long)info.meta_size);
+  protocol->prepare_for_resend();
+  protocol->store(buf,system_charset_info);
+  protocol->write();
+
+  my_snprintf(buf,sizeof(buf)," data       = %-8lu bytes",(unsigned long)info.data_size);
+  protocol->prepare_for_resend();
+  protocol->store(buf,system_charset_info);
+  protocol->write();
+
+  my_snprintf(buf,sizeof(buf),"              --------------");
+  protocol->prepare_for_resend();
+  protocol->store(buf,system_charset_info);
+  protocol->write();
+
+  my_snprintf(buf,sizeof(buf)," total        %-8lu bytes", (unsigned long)info.total_size);
+  protocol->prepare_for_resend();
+  protocol->store(buf,system_charset_info);
+  protocol->write();
+
+  send_eof(thd);
+  DBUG_RETURN(0);
+}
+
+inline
+bool send_summary(THD *thd, const Backup_info &info)
+{ return send_summary(thd,info,TRUE); }
+
+inline
+bool send_summary(THD *thd, const Restore_info &info)
+{ return send_summary(thd,info,FALSE); }
+
+
+/// Open given table in @c INFORMATION_SCHEMA database.
+TABLE* get_schema_table(THD *thd, ST_SCHEMA_TABLE *st)
+{
+  TABLE *t;
+  TABLE_LIST arg;
+  my_bitmap_map *old_map;
+
+  bzero( &arg, sizeof(TABLE_LIST) );
+
+  // set context for create_schema_table call
+  arg.schema_table= st;
+  arg.alias=        NULL;
+  arg.select_lex=   NULL;
+
+  t= ::create_schema_table(thd,&arg); // Note: callers must free t.
+
+  if( !t ) return NULL; // error!
+
+  /*
+   Temporarily set thd->lex->wild to NULL to keep st->fill_table
+   happy.
+  */
+  String *wild= thd->lex->wild;
+  ::enum_sql_command command= thd->lex->sql_command;
+  
+  thd->lex->wild = NULL;
+  thd->lex->sql_command = enum_sql_command(0);
+
+  // context for fill_table
+  arg.table= t;
+
+  old_map= tmp_use_all_columns(t, t->read_set);
+  
+  /*
+    Question: is it correct to fill I_S table each time we use it or should it
+    be filled only once?
+   */
+  st->fill_table(thd,&arg,NULL);  // NULL = no select condition
+
+  tmp_restore_column_map(t->read_set, old_map);
+  
+  // undo changes to thd->lex
+  thd->lex->wild= wild;
+  thd->lex->sql_command= command;
+
+  return t;
+}
+
+/// Build linked @c TABLE_LIST list from a list stored in @c Table_list object.
+
+/*
+  FIXME: build list with the same order as in input
+  Actually, should work fine with reversed list as long as we use the reversed
+  list both in table writing and reading.
+ */
+TABLE_LIST *build_table_list(const Table_list &tables, thr_lock_type lock)
+{
+  TABLE_LIST *tl= NULL;
+
+  for( uint tno=0; tno < tables.count() ; tno++ )
+  {
+    TABLE_LIST *ptr= (TABLE_LIST*)my_malloc(sizeof(TABLE_LIST), MYF(MY_WME));
+    DBUG_ASSERT(ptr);  // FIXME: report error instead
+    bzero(ptr,sizeof(TABLE_LIST));
+
+    Table_ref tbl= tables[tno];
+
+    ptr->alias= ptr->table_name= const_cast<char*>(tbl.name().ptr());
+    ptr->db= const_cast<char*>(tbl.db().name().ptr());
+    ptr->lock_type= lock;
+
+    // and add it to the list
+
+    ptr->next_global= ptr->next_local=
+      ptr->next_name_resolution_table= tl;
+    tl= ptr;
+    tl->table= ptr->table;
+  }
+
+  return tl;
+}
+
+} // backup namespace
diff -Nrup a/sql/backup/stream.cc b/sql/backup/stream.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/stream.cc	2007-11-06 11:11:50 -05:00
@@ -0,0 +1,330 @@
+#include "../mysql_priv.h"
+
+#include "stream.h"
+
+/*
+  TODO
+
+  - blocking of OStream output when data window is allocated.
+  - use my_read instead of read - need to know how to detect EOF.
+  - remove fixed chunk size limit (backup::Window::buf_size)
+  - better file buffering (in case of small data chunks)
+ */
+
+
+// Instantiate templates used in backup stream classes
+template class util::IStream< backup::Window >;
+template class util::OStream< backup::Window >;
+
+namespace backup {
+
+/************** Window *************************/
+
+Window::Result Window::set_length(const size_t len)
+{
+  DBUG_ASSERT(!m_blocked);
+
+  m_end= m_head+len;
+
+  if (m_end <= last_byte)
+    return stream_result::OK;
+
+  m_end= last_byte;
+  return out_of_bounds();
+}
+
+Window::Result Window::move(const off_t offset)
+{
+  DBUG_ASSERT(!m_blocked);
+
+  m_head+= offset;
+
+  if (m_head > m_end)
+    m_end= m_head;
+
+  if (m_head <= last_byte)
+    return stream_result::OK;
+
+  m_head= m_end= last_byte;
+  return out_of_bounds();
+}
+
+/************** Stream *************************/
+
+bool Stream::open()
+{
+  close();

Thread
bk commit into 6.0 tree (cbell:1.2647)cbell6 Nov