MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Chuck Bell Date:December 4 2009 10:31pm
Subject:bzr commit into mysql-5.6-next-mr branch (charles.bell:2995) WL#5101
View as plain text  
#At file:///Users/cbell/source/bzr/backport/ based on revid:kostja@stripped1zzcxiswqo

 2995 Chuck Bell	2009-12-04
      WL#5101 : Backporting MySQL Backup to 5.1 tree...
      
      This is only a test, please ignore.
      
      This patch includes porting work to date and includes most of 
      the supporting server code for backup excluding the following:
      
      ./scripts changes
      si_objects (doesn't compile)
      changes to sql_parse.cc (there are many)
      parser changes

    added:
      sql/bml.cc
      sql/bml.h
      sql/si_logs.cc
      sql/si_logs.h
      sql/si_objects.cc
      sql/si_objects.h
    modified:
      include/my_base.h
      include/my_dbug.h
      include/my_dir.h
      include/my_sys.h
      include/mysql_com.h
      mysql-test/r/events_grant.result
      mysql-test/r/grant.result
      mysql-test/r/lowercase_table_grant.result
      mysql-test/r/ps.result
      mysql-test/r/system_mysql_db.result
      mysql-test/suite/funcs_1/r/is_column_privileges.result
      mysql-test/suite/funcs_1/r/is_schema_privileges.result
      mysql-test/suite/funcs_1/r/is_schema_privileges_is_mysql_test.result
      mysql-test/suite/funcs_1/r/is_user_privileges.result
      mysys/mf_format.c
      mysys/mf_pack.c
      mysys/my_init.c
      mysys/my_lib.c
      mysys/my_static.c
      scripts/mysql_system_tables.sql
      scripts/mysql_system_tables_data.sql
      scripts/mysql_system_tables_fix.sql
      sql/CMakeLists.txt
      sql/Makefile.am
      sql/handler.cc
      sql/log.cc
      sql/log.h
      sql/mysql_priv.h
      sql/mysqld.cc
      sql/set_var.cc
      sql/set_var.h
      sql/share/errmsg-utf8.txt
      sql/share/errmsg.txt
      sql/sp_head.cc
      sql/sql_acl.cc
      sql/sql_acl.h
      sql/sql_array.h
      sql/sql_class.h
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_show.cc
      sql/table.cc
      storage/csv/ha_tina.cc
      storage/csv/ha_tina.h
=== modified file 'include/my_base.h'
--- a/include/my_base.h	2009-12-02 23:09:22 +0000
+++ b/include/my_base.h	2009-12-04 22:31:25 +0000
@@ -196,7 +196,8 @@ enum ha_extra_function {
   HA_EXTRA_ADD_CHILDREN_LIST,
   HA_EXTRA_ATTACH_CHILDREN,
   HA_EXTRA_IS_ATTACHED_CHILDREN,
-  HA_EXTRA_DETACH_CHILDREN
+  HA_EXTRA_DETACH_CHILDREN,
+  HA_EXTRA_ALLOW_LOG_DELETE  
 };
 
 /* Compatible option, to be deleted in 6.0 */

=== modified file 'include/my_dbug.h'
--- a/include/my_dbug.h	2009-12-01 19:07:18 +0000
+++ b/include/my_dbug.h	2009-12-04 22:31:25 +0000
@@ -88,6 +88,7 @@ extern  const char* _db_get_func_(void);
 #define DBUG_EXPLAIN_INITIAL(buf,len) _db_explain_init_((buf),(len))
 #define DEBUGGER_OFF                    do { _dbug_on_= 0; } while(0)
 #define DEBUGGER_ON                     do { _dbug_on_= 1; } while(0)
+#define IF_DBUG(A) A
 #ifndef __WIN__
 #define DBUG_ABORT()                    (_db_flush_(), abort())
 #else
@@ -139,6 +140,7 @@ extern  const char* _db_get_func_(void);
 #define DBUG_EXPLAIN_INITIAL(buf,len)
 #define DEBUGGER_OFF                    do { } while(0)
 #define DEBUGGER_ON                     do { } while(0)
+#define IF_DBUG(A)
 #define DBUG_ABORT()                    abort()
 #define DBUG_CRASH_ENTER(func)
 #define DBUG_CRASH_RETURN(val)          do { return(val); } while(0)

=== modified file 'include/my_dir.h'
--- a/include/my_dir.h	2009-09-11 20:26:35 +0000
+++ b/include/my_dir.h	2009-12-04 22:31:25 +0000
@@ -100,6 +100,7 @@ extern MY_DIR *my_dir(const char *path,m
 extern void my_dirend(MY_DIR *buffer);
 extern MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags);
 extern int my_fstat(int filenr, MY_STAT *stat_area, myf MyFlags);
+extern my_bool test_if_directory(const char* path);
 
 #endif /* MY_DIR_H */
 

=== modified file 'include/my_sys.h'
--- a/include/my_sys.h	2009-12-03 18:59:52 +0000
+++ b/include/my_sys.h	2009-12-04 22:31:25 +0000
@@ -84,6 +84,7 @@ extern int NEAR my_errno;		/* Last error
 #define MY_GIVE_INFO	2	/* Give time info about process*/
 #define MY_DONT_FREE_DBUG 4     /* Do not call DBUG_END() in my_end() */
 
+#define MY_RETURN_SYMLINK 4096  /* my_stat(); Use lstat(2), don't follow link */
 #define MY_REMOVE_NONE    0     /* Params for modify_defaults_file */
 #define MY_REMOVE_OPTION  1
 #define MY_REMOVE_SECTION 2
@@ -191,6 +192,18 @@ extern void (*debug_sync_C_callback_ptr)
 #define DEBUG_SYNC_C(_sync_point_name_)
 #endif /* defined(ENABLED_DEBUG_SYNC) */
 
+/*
+  ERROR INJECTION: Non-thread-safe global variable to request error inject.
+  Set this variable to non-zero to request the next my_malloc() to fail.
+  This works with my_malloc.c:my_malloc() and safemalloc.c:_mymalloc().
+  If using this in tests, note that the error messages produced by
+  my_malloc and safemalloc are different. You may need to modify the
+  results with --replace_regex. You may find examples in
+  client/backup_stream.c and backup_client_coverage.test.
+  The global variable is defined in my_static.c.
+*/
+IF_DBUG(extern int my_malloc_error_inject);
+
 #ifdef HAVE_LARGE_PAGES
 extern uint my_get_large_page_size(void);
 extern uchar * my_large_malloc(size_t size, myf my_flags);
@@ -227,6 +240,7 @@ extern int errno;			/* declare errno */
 #endif
 #endif					/* #ifndef errno */
 extern char *home_dir;			/* Home directory for user */
+extern size_t home_dir_len;             /* Length of home_dir value. */
 extern const char *my_progname;		/* program-name (printed in errors) */
 extern char NEAR curr_dir[];		/* Current directory for user */
 extern void (*error_handler_hook)(uint my_err, const char *str,myf MyFlags);
@@ -731,10 +745,12 @@ extern char * fn_same(char * toname,cons
 extern char * fn_format(char * to,const char *name,const char *dir,
 			   const char *form, uint flag);
 extern size_t strlength(const char *str);
+extern char *cut_print_path(const char* path, size_t length_limit);
 extern void pack_dirname(char * to,const char *from);
 extern size_t normalize_dirname(char * to, const char *from);
 extern size_t unpack_dirname(char * to,const char *from);
 extern size_t cleanup_dirname(char * to,const char *from);
+extern size_t safe_cleanup_cat_path(char *to, size_t to_size, ...);
 extern size_t system_filename(char * to,const char *from);
 extern size_t unpack_filename(char * to,const char *from);
 extern char * intern_filename(char * to,const char *from);

=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h	2009-11-11 20:19:41 +0000
+++ b/include/mysql_com.h	2009-12-04 22:31:25 +0000
@@ -127,6 +127,7 @@ enum enum_server_command
 #define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */
 #define REFRESH_DES_KEY_FILE	0x40000L
 #define REFRESH_USER_RESOURCES	0x80000L
+#define REFRESH_BACKUP_LOG  0x200000L
 
 #define CLIENT_LONG_PASSWORD	1	/* new more secure passwords */
 #define CLIENT_FOUND_ROWS	2	/* Found instead of affected rows */

=== modified file 'mysql-test/r/events_grant.result'
--- a/mysql-test/r/events_grant.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/r/events_grant.result	2009-12-04 22:31:25 +0000
@@ -22,7 +22,7 @@ SHOW GRANTS;
 Grants for ev_test@localhost
 GRANT USAGE ON *.* TO 'ev_test'@'localhost'
 GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost'
-GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER ON `events_test2`.* TO 'ev_test'@'localhost'
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER, BACKUP, RESTORE ON `events_test2`.* TO 'ev_test'@'localhost'
 "Here comes an error:";
 SHOW EVENTS;
 ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2'

=== modified file 'mysql-test/r/grant.result'
--- a/mysql-test/r/grant.result	2009-11-06 14:20:27 +0000
+++ b/mysql-test/r/grant.result	2009-12-04 22:31:25 +0000
@@ -13,8 +13,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'loc
 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
 grant delete on mysqltest.* to mysqltest_1@localhost;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	SPECIFIED	EDH-RSA-DES-CBC3-SHA			0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	SPECIFIED	EDH-RSA-DES-CBC3-SHA			0	0	0	0
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -44,15 +44,15 @@ delete from mysql.user where user='mysql
 flush privileges;
 grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	0	0	0
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
 grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
 select * from mysql.user where user="mysqltest_1";
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	20	30	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	mysqltest_1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					10	20	30	0
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
@@ -87,7 +87,7 @@ revoke LOCK TABLES, ALTER on mysqltest.*
 show grants for mysqltest_1@localhost;
 Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
-GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER, BACKUP, RESTORE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
 revoke all privileges on mysqltest.* from mysqltest_1@localhost;
 delete from mysql.user where user='mysqltest_1';
 flush privileges;
@@ -486,6 +486,8 @@ Trigger	Tables	To use triggers
 Create tablespace	Server Admin	To create/alter/drop tablespaces
 Update	Tables	To update existing rows
 Usage	Server Admin	No privileges - allow connect only
+Backup	Server Admin	To execute BACKUP commands.
+Restore	Server Admin	To execute RESTORE commands.
 create database mysqltest;
 create table mysqltest.t1 (a int,b int,c int);
 grant all on mysqltest.t1 to mysqltest_1@localhost;
@@ -614,7 +616,7 @@ flush privileges;
 use test;
 set @user123="non-existent";
 select * from mysql.db where user=@user123;
-Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv
+Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv	Backup_priv	Restore_priv
 set names koi8r;
 create database �;
 grant select on �.* to root@localhost;

=== modified file 'mysql-test/r/lowercase_table_grant.result'
--- a/mysql-test/r/lowercase_table_grant.result	2006-02-01 10:28:45 +0000
+++ b/mysql-test/r/lowercase_table_grant.result	2009-12-04 22:31:25 +0000
@@ -6,8 +6,8 @@ Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
 GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
 select * from db where user = 'mysqltest_1';
-Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv
-localhost	mysqltest	mysqltest_1	Y	Y	Y	Y	Y	Y	N	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y
+Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv	Backup_priv	Restore_priv
+localhost	mysqltest	mysqltest_1	Y	Y	Y	Y	Y	Y	N	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y
 update db set db = 'MYSQLtest' where db = 'mysqltest' and user = 'mysqltest_1' and host = 'localhost';
 flush privileges;
 show grants for mysqltest_1@localhost;
@@ -15,8 +15,8 @@ Grants for mysqltest_1@localhost
 GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
 GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
 select * from db where user = 'mysqltest_1';
-Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv
-localhost	MYSQLtest	mysqltest_1	Y	Y	Y	Y	Y	Y	N	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y
+Host	Db	User	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Create_tmp_table_priv	Lock_tables_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Execute_priv	Event_priv	Trigger_priv	Backup_priv	Restore_priv
+localhost	MYSQLtest	mysqltest_1	Y	Y	Y	Y	Y	Y	N	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y	Y
 delete from db where db = 'MYSQLtest' and user = 'mysqltest_1' and host = 'localhost';
 flush privileges;
 drop user mysqltest_1@localhost;

=== modified file 'mysql-test/r/ps.result'
--- a/mysql-test/r/ps.result	2009-10-21 20:02:06 +0000
+++ b/mysql-test/r/ps.result	2009-12-04 22:31:25 +0000
@@ -1194,13 +1194,13 @@ SET @aux= "SELECT COUNT(*)
 prepare my_stmt from @aux;
 execute my_stmt;
 COUNT(*)
-40
+42
 execute my_stmt;
 COUNT(*)
-40
+42
 execute my_stmt;
 COUNT(*)
-40
+42
 deallocate prepare my_stmt;
 drop procedure if exists p1|
 drop table if exists t1|

=== modified file 'mysql-test/r/system_mysql_db.result'
--- a/mysql-test/r/system_mysql_db.result	2009-11-06 14:20:27 +0000
+++ b/mysql-test/r/system_mysql_db.result	2009-12-04 22:31:25 +0000
@@ -48,6 +48,8 @@ db	CREATE TABLE `db` (
   `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+  `Backup_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+  `Restore_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   PRIMARY KEY (`Host`,`Db`,`User`),
   KEY `User` (`User`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'
@@ -111,6 +113,8 @@ user	CREATE TABLE `user` (
   `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   `Create_tablespace_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+  `Backup_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+  `Restore_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
   `ssl_type` enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '',
   `ssl_cipher` blob NOT NULL,
   `x509_issuer` blob NOT NULL,

=== modified file 'mysql-test/suite/funcs_1/r/is_column_privileges.result'
--- a/mysql-test/suite/funcs_1/r/is_column_privileges.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_column_privileges.result	2009-12-04 22:31:25 +0000
@@ -135,6 +135,7 @@ ORDER BY grantee,table_schema,privilege_
 GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVILEGE_TYPE	IS_GRANTABLE
 'testuser3'@'localhost'	def	db_datadict	ALTER	NO
 'testuser3'@'localhost'	def	db_datadict	ALTER ROUTINE	NO
+'testuser3'@'localhost'	def	db_datadict	BACKUP	NO
 'testuser3'@'localhost'	def	db_datadict	CREATE	NO
 'testuser3'@'localhost'	def	db_datadict	CREATE ROUTINE	NO
 'testuser3'@'localhost'	def	db_datadict	CREATE TEMPORARY TABLES	NO
@@ -147,6 +148,7 @@ GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVI
 'testuser3'@'localhost'	def	db_datadict	INSERT	NO
 'testuser3'@'localhost'	def	db_datadict	LOCK TABLES	NO
 'testuser3'@'localhost'	def	db_datadict	REFERENCES	NO
+'testuser3'@'localhost'	def	db_datadict	RESTORE	NO
 'testuser3'@'localhost'	def	db_datadict	SELECT	NO
 'testuser3'@'localhost'	def	db_datadict	SHOW VIEW	NO
 'testuser3'@'localhost'	def	db_datadict	TRIGGER	NO

=== modified file 'mysql-test/suite/funcs_1/r/is_schema_privileges.result'
--- a/mysql-test/suite/funcs_1/r/is_schema_privileges.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_schema_privileges.result	2009-12-04 22:31:25 +0000
@@ -68,6 +68,8 @@ GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVI
 ''@'%'	def	test	CREATE ROUTINE
 ''@'%'	def	test	EVENT
 ''@'%'	def	test	TRIGGER
+''@'%'	def	test	BACKUP
+''@'%'	def	test	RESTORE
 ''@'%'	def	test\_%	SELECT
 ''@'%'	def	test\_%	INSERT
 ''@'%'	def	test\_%	UPDATE
@@ -84,6 +86,8 @@ GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVI
 ''@'%'	def	test\_%	CREATE ROUTINE
 ''@'%'	def	test\_%	EVENT
 ''@'%'	def	test\_%	TRIGGER
+''@'%'	def	test\_%	BACKUP
+''@'%'	def	test\_%	RESTORE
 ###############################################################################
 # Testcase 3.2.15.2-3.2.15.4 INFORMATION_SCHEMA.SCHEMA_PRIVILEGES accessibility
 ###############################################################################

=== modified file 'mysql-test/suite/funcs_1/r/is_schema_privileges_is_mysql_test.result'
--- a/mysql-test/suite/funcs_1/r/is_schema_privileges_is_mysql_test.result	2009-10-23 11:02:20 +0000
+++ b/mysql-test/suite/funcs_1/r/is_schema_privileges_is_mysql_test.result	2009-12-04 22:31:25 +0000
@@ -11,6 +11,7 @@ WHERE table_schema IN ('information_sche
 ORDER BY grantee, table_schema, privilege_type;
 GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVILEGE_TYPE	IS_GRANTABLE
 ''@'%'	def	test	ALTER	NO
+''@'%'	def	test	BACKUP	NO
 ''@'%'	def	test	CREATE	NO
 ''@'%'	def	test	CREATE ROUTINE	NO
 ''@'%'	def	test	CREATE TEMPORARY TABLES	NO
@@ -22,6 +23,7 @@ GRANTEE	TABLE_CATALOG	TABLE_SCHEMA	PRIVI
 ''@'%'	def	test	INSERT	NO
 ''@'%'	def	test	LOCK TABLES	NO
 ''@'%'	def	test	REFERENCES	NO
+''@'%'	def	test	RESTORE	NO
 ''@'%'	def	test	SELECT	NO
 ''@'%'	def	test	SHOW VIEW	NO
 ''@'%'	def	test	TRIGGER	NO

=== modified file 'mysql-test/suite/funcs_1/r/is_user_privileges.result'
--- a/mysql-test/suite/funcs_1/r/is_user_privileges.result	2009-11-02 11:10:04 +0000
+++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result	2009-12-04 22:31:25 +0000
@@ -76,10 +76,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 #
 # Add GRANT OPTION db_datadict.* to testuser1;
 GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
@@ -93,10 +93,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 # Establish connection testuser1 (user=testuser1)
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
@@ -105,10 +105,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser1'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -130,10 +130,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
 #
 # Here <SELECT YES> is shown correctly for testuser1;
@@ -147,10 +147,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
@@ -159,10 +159,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser1'@'localhost'	def	SELECT	YES
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		Y	N	N	N	N	N	N	N	N	N	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION
@@ -207,10 +207,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
@@ -253,10 +253,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''
@@ -265,10 +265,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser1'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -284,10 +284,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser1'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 SHOW GRANTS;
 Grants for testuser1@localhost
 GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -309,10 +309,10 @@ GRANTEE	TABLE_CATALOG	PRIVILEGE_TYPE	IS_
 'testuser3'@'localhost'	def	USAGE	NO
 SELECT * FROM mysql.user
 WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
-localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
-localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+Host	User	Password	Select_priv	Insert_priv	Update_priv	Delete_priv	Create_priv	Drop_priv	Reload_priv	Shutdown_priv	Process_priv	File_priv	Grant_priv	References_priv	Index_priv	Alter_priv	Show_db_priv	Super_priv	Create_tmp_table_priv	Lock_tables_priv	Execute_priv	Repl_slave_priv	Repl_client_priv	Create_view_priv	Show_view_priv	Create_routine_priv	Alter_routine_priv	Create_user_priv	Event_priv	Trigger_priv	Create_tablespace_priv	Backup_priv	Restore_priv	ssl_type	ssl_cipher	x509_issuer	x509_subject	max_questions	max_updates	max_connections	max_user_connections
+localhost	testuser1		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser2		N	Y	Y	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
+localhost	testuser3		N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N	N					0	0	0	0
 # Switch to connection testuser1
 SELECT * FROM information_schema.user_privileges
 WHERE grantee LIKE '''testuser%'''

=== modified file 'mysys/mf_format.c'
--- a/mysys/mf_format.c	2009-04-19 01:21:33 +0000
+++ b/mysys/mf_format.c	2009-12-04 22:31:25 +0000
@@ -21,6 +21,31 @@
   Function can handle the case where 'to' == 'name'
   For a description of the flag values, consult my_sys.h
   The arguments should be in unix format.
+
+  Warning: The flag MY_UNPACK_FILENAME is evil. It makes a nice, clean
+  path with no ./ and ../. But if a path element is a symbolic link and
+  sufficient ../ are present to "step backwards" over it, the resulting
+  path will be based on the symlink itself, not on its target:
+    Assume symlink: /path/with/symlink -> /target/place
+    fn_format(..., /path/with/symlink/../myfile, ...MY_UNPACK_FILENAME)
+    results in /path/with/myfile
+    OS functions would affect /target/myfile with the original path.
+  MY_RETURN_REAL_PATH does a better job, but returns all symlinks
+  resolved, which is normally not wanted. Also, it does not resolve
+  the path, if the final target does not exist. Also, it cuts the
+  resulting path name to FN_REFLEN if it is longer than that. Also,
+  on Windows it does not clean the path.
+
+  A correct algorithm has been tried with safe_cleanup_cat_path() in
+  mf_pack.c. It calls my_is_symlink() on each path element from the
+  beginning. If the element is not a symlink, it can be cleaned out. If
+  a symlink is encountered, the part of the path behind it is treated as
+  a relative path in itself. It is cleaned in itself like the above, but
+  my_is_symlink() takes the concatenated previous path segments
+  prepended. If another symlink is found, the algorithm repeats. That
+  way we might end up with no ../ except possibly after symlinks:
+    Input:  /path/with/symlink/up/../../yet/another/symlink/up/../end
+    Output: /path/with/symlink/../yet/another/symlink/end
 */
 
 char * fn_format(char * to, const char *name, const char *dir,
@@ -140,3 +165,52 @@ size_t strlength(const char *str)
   }
   DBUG_RETURN((size_t) (found - str));
 } /* strlength */
+
+
+/**
+  Cut path to a printable length.
+
+  This can be used to put a path name into error/warning/info messages,
+  which contain a conversion flag with length limit like %.64s.
+
+  If 'path' is longer than 'length_limit', the end of the path is
+  returned with prepended "... ". Often the end of a path is of
+  more interest in error messages than the begin.
+
+  @note A pointer to static storage is returned. It is assumed that
+  only one path per error message needs to be cut.
+
+  @param[in]    path            path name
+  @param[in]    length_limit    length limit from format string
+
+  @return       pointer to static string
+*/
+
+char *cut_print_path(const char* path, size_t length_limit)
+{
+  static char path_buff[FN_REFLEN];
+  size_t plen= strlen(path);
+  size_t offset= 0;
+
+  if (length_limit >= FN_REFLEN)
+    length_limit= FN_REFLEN - 1; /* Room for '\0'. */ /* purecov: inspected */
+
+  if (plen > length_limit)
+  {
+    offset= plen - length_limit;
+    plen= length_limit;
+  }
+
+  memcpy(path_buff, path + offset, plen + 1); /* Include '\0'. */
+
+  if (offset)
+  {
+    path_buff[0]= '.';
+    path_buff[1]= '.';
+    path_buff[2]= '.';
+    path_buff[3]= ' ';
+  }
+
+  return path_buff;
+}
+

=== modified file 'mysys/mf_pack.c'
--- a/mysys/mf_pack.c	2009-08-28 16:21:54 +0000
+++ b/mysys/mf_pack.c	2009-12-04 22:31:25 +0000
@@ -15,6 +15,7 @@
 
 #include "mysys_priv.h"
 #include <m_string.h>
+#include <my_dir.h>
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
@@ -23,6 +24,8 @@
 #include <iodef.h>
 #include <descrip.h>
 #endif /* VMS */
+#include <stdarg.h>
+
 
 static char * NEAR_F expand_tilde(char * *path);
 
@@ -64,7 +67,7 @@ void pack_dirname(char * to, const char 
     length=0;
     if (home_dir)
     {
-      length= strlen(home_dir);
+      length= home_dir_len;
       if (home_dir[length-1] == FN_LIBCHAR)
 	length--;				/* Don't test last '/' */
     }
@@ -232,6 +235,337 @@ size_t cleanup_dirname(register char *to
 
 
 /*
+  Concatenate paths and remove unwanted chars, taking symlinks into account.
+
+  The function takes a variable length list of path names and
+  concatenates them. An FN_LIBCHAR is inserted, if not already present.
+  Empty arguments ("") are ignored. No double FN_LIBCHAR is inserted.
+
+  "./" at begin is removed, possibly multiple times.
+  "/./" is replaced by "/" if previous path element is a real directory.
+  "/." at end is removed if previous path element is a real directory.
+  "../" at begin is retained.
+  "/../" removes previous path element, if it is a real directory.
+  "/../" at begin is replaced by "/".
+  "/.." at end removes previous path element, if it is a real directory.
+  "~/" at path begin replaces ~ by home directory.
+  "//" is replaced by "/", except on Win32 at begin.
+  "\\.\" is preserved for use on Windows for named pipes.
+
+  If the cleanup process would end up with an empty path, "." is
+  returned.
+
+  The resulting path name has no trailing FN_LIBCHAR.
+
+  If the resulting path exceeds 'to_size', zero is returned, but 'to'
+  can contain a partial path.
+
+  The 'to' buffer is used during the cleanup. It must not overlap with
+  any of the 'from' buffers.
+
+  No attempt is made to create an absolute path, except for "~/" if
+  $HOME is an absolute path. If 'path' is absolute, 'to' will be
+  absolute too.
+
+  For each path element from the beginning, test_if_real_directory() is
+  called. If the element is a real, existent (non-symlinked) directory,
+  it can be cleaned out by a following ../, and a following ./ can be
+  dropped. If a symlink, non-directory object, or non-existent object is
+  encountered, the part of the path behind it is treated as a relative
+  path in itself. It is cleaned in itself like the above, with the
+  exception that leading ./ and ~/ are retained, but
+  test_if_real_directory() takes the concatenated previous path segments
+  prepended. If another non-real-directory is found, the algorithm
+  repeats. That way we might end up with no ../ or ./ except possibly
+  after symlinks or other non-real-directory objects:
+
+    Input:  /path/with/symlink/up/../../yet/another/symlink/up/../end
+    Output: /path/with/symlink/../yet/another/symlink/end
+
+  After a path element references a non-existent file system object, no
+  further cleaning can be done. Otherwise we might create a path to a
+  wrong object.
+
+    Input:  dir1/../dir2/non-existent-file.txt/../existent-file.txt
+    Output: dir2/non-existent-file.txt/../existent-file.txt
+
+  @param[out]   to              address of destination buffer
+  @param[in]    to_size         size of destination buffer
+  @param[in]    ...             Multiple paths (!= to), terminated by NullS
+
+  @return       length of new path name
+    @retval     0               destination buffer size too small
+*/
+
+size_t safe_cleanup_cat_path(char *to, size_t to_size, ...)
+{
+  va_list args;
+  size_t length;
+  size_t parent_len;
+  char   *from_ptr;
+  char   *start;
+  char   *pos;
+  char   *end;
+  my_bool non_exist_found= FALSE;
+  char   parent[5];     /* for "FN_PARENTDIR" */
+  DBUG_ENTER("safe_cleanup_cat_path");
+
+  /* Prepare for variable argument list. */
+  va_start(args, to_size);
+
+  /* Prepare "/.." for comparisons. */
+  parent[0]= FN_LIBCHAR;
+  parent_len= (size_t) (strmov(parent + 1, FN_PARENTDIR) - parent);
+
+  /* Get first non-empty path name. */
+  do
+  {
+    /* Get first (next) argument. */
+    from_ptr= va_arg(args, char*);
+    /* Skip empty strings. */
+  } while (from_ptr && !*from_ptr);
+  /* One path should be present. But if not, just return zero. */
+  if (!from_ptr)
+  {
+    /* purecov: begin tested */ /* unittest */
+    pos= to;
+    goto end;
+    /* purecov: end */
+  }
+  DBUG_PRINT("scc_path", ("from: '%s'", from_ptr));
+  /* Get other start values. */
+  start= to;
+  end= to + to_size;
+
+  IF_DBUG({uint idx; for(idx=0; idx<to_size; idx++) {to[idx]= '0'+idx%10;}});
+
+  /* Handle special conditions. */
+#ifdef FN_DEVCHAR
+  if ((pos= strrchr(from_ptr, FN_DEVCHAR)))
+  {
+    /* Skip device part */
+    length= (size_t) (pos - from_ptr) + 1;
+    if (length >= to_size)
+    {
+      pos= to; /* Return zero length (== error). */
+      goto end;
+    }
+    memcpy(to, from_ptr, length);
+    start= to + length;
+    from_ptr+= length;
+  }
+#endif
+  /* If path starts with ~/ replace by $HOME. */
+  if ((from_ptr[0] == FN_HOMELIB) &&
+      ((from_ptr[1] == '/') || (from_ptr[1] == FN_LIBCHAR)) &&
+      home_dir)
+  {
+    length= home_dir_len;
+    /* Do not copy trailing FN_LIBCHAR. */
+    if (length && (home_dir[length - 1] == FN_LIBCHAR))
+      length--; /* purecov: tested */ /* unittest */
+    if (length >= to_size)
+    {
+      /* purecov: begin inspected */
+      pos= to; /* Return zero length (== error). */
+      goto end;
+      /* purecov: end */
+    }
+    memcpy(to, home_dir, length);
+    /*
+      Do not allow $HOME path members to be eaten by ../
+      Otherwise we must check every $HOME path element for real directory.
+    */
+    start= to + length;
+    /* Skip ~ and point at / */
+    from_ptr++;
+  }
+
+  /* Copy every character from from to to. Check at every FN_LIBCHAR. */
+  for (pos= start; pos < end; pos++)
+  {
+    /* Copy a character from 'from' to 'to'. */
+    *pos= *from_ptr++;
+
+    /* If at end of path name, turn to next path name. */
+    if (!*pos)
+    {
+      do
+      {
+        /* Get next argument. */
+        from_ptr= va_arg(args, char*);
+        /* Skip empty strings. */
+      } while (from_ptr && !*from_ptr);
+      DBUG_PRINT("scc_path", ("from: '%s'", from_ptr ? from_ptr : "[NullS]"));
+
+      /*
+        Do not stop here, even if no argument follows.
+        At the end of the resulting path name we must pretend to have a
+        FN_LIBCHAR to handle trailing /. or /..
+        Insert FN_LIBCHAR after every from-path. If it is duplicate or
+        at end, it is removed below.
+      */
+      *pos= FN_LIBCHAR;
+    }
+
+    /* Convert internally used slashes to FN_LIBCHAR. */
+    if (*pos == '/')
+      *pos= FN_LIBCHAR;
+
+    /* At every FN_LIBCHAR check for additional changes. */
+    if (*pos == FN_LIBCHAR)
+    {
+      /* Check if we are at a FN_LIBCHAR behind /.. */
+      if ((size_t) (pos - start) >= parent_len &&
+          bcmp(pos - parent_len, parent, parent_len) == 0)
+      {
+        /* If .../../ skip previous path element. Backstep over /.. first. */
+        pos-= parent_len;
+        /* Now at the / of /.. */
+        if (pos != start)
+        {
+          /* Not at root. Step back over FN_LIBCHAR. */
+          pos--;
+          /*
+            Step back over the previous path element.
+            This might bring pos below to (there is no valid character left).
+          */
+          while ((pos >= start) && (*pos != FN_LIBCHAR))
+            pos--;
+        }
+        /*
+          At root, we are back at the beginning.
+          After a non-real-directory, start points to the first dot of /..
+          so we won't enter this branch, but the ../ branch below.
+        */
+      }
+      /*
+        If path starts as ../ or has ../ after a non-real-directory,
+        set a new start to avoid its removal.
+      */
+      else if (((size_t) (pos - start) == (parent_len - 1)) &&
+               !bcmp(start, parent + 1, parent_len - 1))
+      {
+        start= pos + 1;
+      }
+      /* Remove duplicate '/' */
+      else if ((pos > to) && (pos[-1] == FN_LIBCHAR))
+      {
+#ifdef FN_NETWORK_DRIVES
+        /* Keep // at path begin. */
+        if ((pos - to) != 1)
+#endif
+          pos--;
+      }
+      /*
+        Skip /./ which could also be at path end.
+        If no non existent path elemnt has been found yet,
+        skip ./ This can happen after a leading ../
+        In Windows case leave //./ in the path intact.
+      */
+      else if (((((pos - start) > 1) &&
+                 (pos[-1] == FN_CURLIB) &&
+                 (pos[-2] == FN_LIBCHAR)) ||
+                (!non_exist_found &&
+                 ((pos - start) > 0) &&
+                 (pos[-1] == FN_CURLIB)))
+#ifdef FN_NETWORK_DRIVES
+                && !(((pos - to) == 3) && (pos[-3] == FN_LIBCHAR))
+#endif
+               )

+      {
+        pos-= 2; /* purecov: tested */ /* unittest */
+      }
+      /* Skip ./ at path begin. */
+      else if (((pos - to) == 1) &&
+               (pos[-1] == FN_CURLIB))
+      {
+        /* This brings pos below to (there is no valid character left). */
+        pos-= 2; /* purecov: tested */ /* unittest */
+      }
+      /* No cleanup done. Check if path element is a real directory. */
+      else
+      {
+#if !defined(__WIN__)
+        /* ^- Windows cleans up ../ and ./ regardless if directory or not. */
+        if (pos > start)
+        {
+          /* Temporarily terminate the path name. */
+          *pos= '\0';
+          if (!test_if_real_directory(to))
+          {
+            /* Prevent backstep over non-real-directory. */
+            start= pos + 1;
+            non_exist_found= TRUE;
+          }
+        }
+#endif
+        /* Restore FN_LIBCHAR if not at last argument. */
+        *pos= from_ptr ? FN_LIBCHAR : '\0';
+      }
+      /*
+        Stop if NullS found (end of arguments). Note that the path name is
+        not terminated with '\0' if a cleanup action has been done in this
+        iteration. Pos points at a terminating FN_LIBCHAR instead or is <=
+        to.
+      */
+      if (!from_ptr)
+        break;
+    } /* end if (*pos == FN_LIBCHAR) */
+  } /* end for(pos < end) */
+
+  /*
+    The loop ends either if the 'to' buffer is full or the end of the
+    argument list is reached.
+    If the 'to' buffer is full, return zero length (== error).
+  */
+  if (pos >= end)
+  {
+    /* purecov: begin tested */ /* unittest */
+    pos= to;
+    goto end;
+    /* purecov: end */
+  }
+
+  /*
+    The end of the argument list is reached. This means that the last
+    character received from the last argument was a terminating '\0',
+    which has been replaces by a FN_LIBCHAR and not necessarily been
+    changed back. There was space in the buffer for the terminator.
+    Whatever cleanup action has been done in between can only make more
+    room in the buffer. So we can safely restore the terminator, if not
+    already present. However, we do not want to remove a single
+    FN_LIBCHAR. It means the file system root directory "/".
+    If pos < to, the path is cleaned out completely, leaving it empty.
+  */
+  if ((pos < to) || (*pos == FN_LIBCHAR))
+  {
+    if (pos <= to)
+      pos++; /* purecov: tested */ /* unittest */
+    *pos= '\0';
+  }
+
+  /* Trim trailing FN_LIBCHAR. But leave a single FN_LIBCHAR at begin. */
+  while ((pos > to + 1) && (pos[-1] == FN_LIBCHAR))
+    *(--pos)= '\0'; /* purecov: tested */ /* unittest */
+
+  /* We do not want to deliver an empty path. It would be invalid. */
+  if ((pos == to) && !*pos && (to_size > 1))
+  {
+    /* purecov: begin tested */ /* unittest */
+    *(pos++)= '.';
+    *pos= '\0';
+    /* purecov: end */
+  }
+
+end:
+  DBUG_PRINT("scc_path", ("to: '%.*s'", (int) (pos - to), to));
+  va_end(args);
+  DBUG_RETURN((size_t) (pos - to));
+} /* safe_cleanup_cat_path */
+
+
+/*
   On system where you don't have symbolic links, the following
   code will allow you to create a file: 
   directory-name.sym that should contain the real path
@@ -337,7 +671,7 @@ size_t normalize_dirname(char *to, const
    Length of new directory name (= length of to)
 */
 
-size_t unpack_dirname(char * to, const char *from)
+size_t unpack_dirname(char *to, const char *from)
 {
   size_t length, h_length;
   char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion;

=== modified file 'mysys/my_init.c'
--- a/mysys/my_init.c	2009-09-30 01:39:37 +0000
+++ b/mysys/my_init.c	2009-12-04 22:31:25 +0000
@@ -27,7 +27,7 @@
 #ifdef _MSC_VER
 #include <locale.h>
 #include <crtdbg.h>
-/* WSAStartup needs winsock library*/
+/* WSAStartup needs winsock library*/
 #pragma comment(lib, "ws2_32")
 #endif
 my_bool have_tcpip=0;
@@ -104,8 +104,13 @@ my_bool my_init(void)
     if (!home_dir)
     {					/* Don't initialize twice */
       my_win_init();
-      if ((home_dir=getenv("HOME")) != 0)
-	home_dir=intern_filename(home_dir_buff,home_dir);
+      if ((home_dir= getenv("HOME")) != NULL)
+      {
+        home_dir= intern_filename(home_dir_buff, home_dir);
+        home_dir_len= strlen(home_dir);
+      }
+      else
+        home_dir_len= 0; /* purecov: inspected */
 #ifndef VMS
       /* Default creation of new files */
       if ((str=getenv("UMASK")) != 0)

=== modified file 'mysys/my_lib.c'
--- a/mysys/my_lib.c	2009-11-24 13:54:59 +0000
+++ b/mysys/my_lib.c	2009-12-04 22:31:25 +0000
@@ -508,6 +508,54 @@ error:
 
 #endif /* _WIN32 */
 
+
+/**
+  Test if path is a directory.
+
+  This test follows the symlink, if the path references one.
+
+  @param[in]    path            path name
+
+  @return       test result
+    @retval     TRUE            path is directory
+    @retval     FALSE           path is not directory
+*/
+
+my_bool test_if_directory(const char* path)
+{
+  MY_STAT statbuf;
+
+  if (my_stat(path, &statbuf, MYF(0)) &&
+      MY_S_ISDIR(statbuf.st_mode))
+    return TRUE;
+  return FALSE;
+}
+
+
+/**
+  Test if path is a real (non-symlinked) directory.
+
+  This test does not follow the symlink, if the path references one.
+  It returns FALSE, if the path references a symlink.
+
+  @param[in]    path            path name
+
+  @return       test result
+    @retval     TRUE            path is directory
+    @retval     FALSE           path is not directory
+*/
+
+my_bool test_if_real_directory(const char* path)
+{
+  MY_STAT statbuf;
+
+  if (my_stat(path, &statbuf, MYF(MY_RETURN_SYMLINK)) &&
+      MY_S_ISDIR(statbuf.st_mode))
+    return TRUE;
+  return FALSE;
+}
+
+
 /****************************************************************************
 ** File status
 ** Note that MY_STAT is assumed to be same as struct stat
@@ -530,20 +578,26 @@ int my_fstat(File Filedes, MY_STAT *stat
 MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags)
 {
   int m_used;
+  int res;
   DBUG_ENTER("my_stat");
-  DBUG_PRINT("my", ("path: '%s'  stat_area: 0x%lx  MyFlags: %d", path,
-                    (long) stat_area, my_flags));
+  DBUG_PRINT("my", ("path: '%s'  stat_area: %p  MyFlags: %d", path,
+                    stat_area, my_flags));
 
   if ((m_used= (stat_area == NULL)))
     if (!(stat_area= (MY_STAT *) my_malloc(sizeof(MY_STAT), my_flags)))
       goto error;
 #ifndef _WIN32
-    if (! stat((char *) path, (struct stat *) stat_area) )
-      DBUG_RETURN(stat_area);
+#if defined (HAVE_LSTAT) && defined (S_ISLNK)
+  if (my_flags & MY_RETURN_SYMLINK)
+    res= lstat((char *) path, (struct stat *) stat_area);
+  else
+#endif
+    res= stat((char *) path, (struct stat *) stat_area);
 #else
-    if (! my_win_stat(path, stat_area) )
-      DBUG_RETURN(stat_area);
+  res= my_win_stat(path, stat_area);
 #endif
+  if (!res)
+    DBUG_RETURN(stat_area);
   DBUG_PRINT("error",("Got errno: %d from stat", errno));
   my_errno= errno;
   if (m_used)					/* Free if new area */

=== modified file 'mysys/my_static.c'
--- a/mysys/my_static.c	2009-12-03 18:59:52 +0000
+++ b/mysys/my_static.c	2009-12-04 22:31:25 +0000
@@ -25,7 +25,8 @@
 my_bool timed_mutexes= 0;
 
 	/* from my_init */
-char *	home_dir=0;
+char *home_dir= NULL;
+size_t home_dir_len= 0;
 const char      *my_progname=0;
 char		NEAR curr_dir[FN_REFLEN]= {0},
 		NEAR home_dir_buff[FN_REFLEN]= {0};

=== modified file 'scripts/mysql_system_tables.sql'
--- a/scripts/mysql_system_tables.sql	2009-12-01 19:07:18 +0000
+++ b/scripts/mysql_system_tables.sql	2009-12-04 22:31:25 +0000
@@ -5,7 +5,7 @@
 set sql_mode='';
 set storage_engine=myisam;
 
-CREATE TABLE IF NOT EXISTS db (   Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db,User), KEY User (User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Database privileges';
+CREATE TABLE IF NOT EXISTS db (   Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db,User), KEY User (User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Database privileges';
 
 -- Remember for later if db table already existed
 set @had_db_table= @@warning_count != 0;
@@ -13,7 +13,7 @@ set @had_db_table= @@warning_count != 0;
 CREATE TABLE IF NOT EXISTS host (  Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges;  Merged with database privileges';
 
 
-CREATE TABLE IF NOT EXISTS user (   Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0  NOT NULL, max_updates int(11) unsigned DEFAULT 0  NOT NULL, max_connections int(11) unsigned DEFAULT 0  NOT NULL, max_user_connections int(11) unsigned DEFAULT 0  NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+CREATE TABLE IF NOT EXISTS user (   Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0  NOT NULL, max_updates int(11) unsigned DEFAULT 0  NOT NULL, max_connections int(11) unsigned DEFAULT 0  NOT NULL, max_user_connections int(11) unsigned DEFAULT 0  NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
 
 -- Remember for later if user table already existed
 set @had_user_table= @@warning_count != 0;

=== modified file 'scripts/mysql_system_tables_data.sql'
--- a/scripts/mysql_system_tables_data.sql	2009-11-25 10:53:23 +0000
+++ b/scripts/mysql_system_tables_data.sql	2009-12-04 22:31:25 +0000
@@ -11,8 +11,8 @@ set @current_hostname= @@hostname;
 -- Fill "db" table with default grants for anyone to
 -- access database 'test' and 'test_%' if "db" table didn't exist
 CREATE TEMPORARY TABLE tmp_db LIKE db;
-INSERT INTO tmp_db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y','Y');
-INSERT INTO tmp_db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y','Y');
+INSERT INTO tmp_db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y','Y','Y','Y');
+INSERT INTO tmp_db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N','Y','Y','Y','Y');
 INSERT INTO db SELECT * FROM tmp_db WHERE @had_db_table=0;
 DROP TABLE tmp_db;
 
@@ -21,10 +21,10 @@ DROP TABLE tmp_db;
 -- from local machine if "users" table didn't exist before
 CREATE TEMPORARY TABLE tmp_user LIKE user;
 set @current_hostname= @@hostname;
-INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
-REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
 INSERT INTO tmp_user (host,user) VALUES ('localhost','');
 INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
 INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;

=== modified file 'scripts/mysql_system_tables_fix.sql'
--- a/scripts/mysql_system_tables_fix.sql	2009-12-01 19:07:18 +0000
+++ b/scripts/mysql_system_tables_fix.sql	2009-12-04 22:31:25 +0000
@@ -561,6 +561,44 @@ ALTER TABLE user MODIFY Create_tablespac
 
 UPDATE user SET Create_tablespace_priv = Super_priv WHERE @hadCreateTablespacePriv = 0;
 
+#
+# user.Backup_priv and user.Restore.priv
+#
+SET @hadBackupPriv := 0;
+SELECT @hadBackupPriv :=1 FROM db WHERE Backup_priv LIKE '%';
+
+ALTER TABLE db ADD Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Trigger_priv;
+ALTER TABLE db MODIFY Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Trigger_priv;
+
+UPDATE user SET Backup_priv = Super_priv WHERE @hadBackupPriv = 0;
+
+SET @hadRestorePriv := 0;
+SELECT @hadRestorePriv :=1 FROM user WHERE Restore_priv LIKE '%';
+
+ALTER TABLE db ADD Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Backup_priv;
+ALTER TABLE db MODIFY Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Backup_priv;
+
+UPDATE db SET Restore_priv = Super_priv WHERE @hadRestorePriv = 0;
+
+#
+# user.Backup_priv and user.Restore.priv
+#
+SET @hadBackupPriv := 0;
+SELECT @hadBackupPriv :=1 FROM user WHERE Backup_priv LIKE '%';
+
+ALTER TABLE user ADD Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Create_tablespace_priv;
+ALTER TABLE user MODIFY Backup_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Create_tablespace_priv;
+
+UPDATE user SET Backup_priv = Super_priv WHERE @hadBackupPriv = 0;
+
+SET @hadRestorePriv := 0;
+SELECT @hadRestorePriv :=1 FROM user WHERE Restore_priv LIKE '%';
+
+ALTER TABLE user ADD Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Backup_priv;
+ALTER TABLE user MODIFY Restore_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL AFTER Backup_priv;
+
+UPDATE user SET Restore_priv = Super_priv WHERE @hadRestorePriv = 0;
+
 # Activate the new, possible modified privilege tables
 # This should not be needed, but gives us some extra testing that the above
 # changes was correct

=== modified file 'sql/CMakeLists.txt'
--- a/sql/CMakeLists.txt	2009-12-03 18:37:38 +0000
+++ b/sql/CMakeLists.txt	2009-12-04 22:31:25 +0000
@@ -74,7 +74,8 @@ SET (SQL_SOURCE
                sql_tablespace.cc events.cc ../sql-common/my_user.c 
                partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
                rpl_rli.cc rpl_mi.cc sql_servers.cc
-               sql_connect.cc scheduler.cc 
+               sql_connect.cc scheduler.cc
+               bml.cc si_objects.cc si_logs.cc 
                sql_profile.cc event_parse_data.cc
                sql_signal.cc rpl_handler.cc mdl.cc
                transaction.cc

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2009-12-03 18:37:38 +0000
+++ b/sql/Makefile.am	2009-12-04 22:31:25 +0000
@@ -111,7 +111,8 @@ noinst_HEADERS =	item.h item_func.h item
 			sql_plugin.h authors.h event_parse_data.h \
 			event_data_objects.h event_scheduler.h \
 			sql_partition.h partition_info.h partition_element.h \
-			contributors.h sql_servers.h sql_signal.h records.h \
+			contributors.h sql_servers.h bml.h \
+      si_logs.h sql_signal.h records.h \
 			sql_prepare.h rpl_handler.h replication.h mdl.h \
 			sql_plist.h transaction.h 
 
@@ -158,6 +159,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
                         event_queue.cc event_db_repository.cc events.cc \
 			sql_plugin.cc sql_binlog.cc \
 			sql_builtin.cc sql_tablespace.cc partition_info.cc \
+      bml.cc si_logs.cc \
 			sql_servers.cc event_parse_data.cc sql_signal.cc \
 			rpl_handler.cc mdl.cc transaction.cc
 

=== added file 'sql/bml.cc'
--- a/sql/bml.cc	1970-01-01 00:00:00 +0000
+++ b/sql/bml.cc	2009-12-04 22:31:25 +0000
@@ -0,0 +1,321 @@
+/* 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 methods to implement the Backup Shared/Exclusive
+   Locks service.
+
+   This file contains methods that allow getting and releasing Shared
+   and Exclusive locks. The service follows the standard lock
+   compatibility matrix for Shared/Exclusive locking:
+
+     | S  X
+   --+-----
+   S | Y  N
+   X | N  N
+
+   Where S=Shared lock and X=Exclusive lock
+
+   The following properties are enforced:
+
+   * A thread requesting an S lock gets the lock immediately unless 
+     - another thread already got the X lock, or 
+     - another thread has requested the X lock and is waiting for the release
+       of S locks to get it
+
+     In both these, the request is blocked until the X lock has been
+     released or a timeout occurs
+
+   * A thread requesting an X lock gets the lock immediately unless 
+     - one or more threads has S locks, in which case the request is
+       blocked until all existing S locks have been released, or
+     - another thread has (requested) the X lock. Since only one
+       BACKUP/RESTORE process can exist at any time, X lock requests
+       return with an error if the X lock has already been set.
+
+
+   The Backup Shared/Exclusive Lock service is used by BACKUP for two things:
+    - Backup Metadata Locking
+    - Backup Commit Blocker
+
+   Hence, two Backup_sx_lock objects are defined at MySQL server startup:
+
+   Backup_sx_lock *BML_instance
+   Backup_sx_lock *BCB_instance
+
+   BACKUP METADATA LOCKING
+   -----------------------
+   For the duration of BACKUP/RESTORE, most DDL statements are
+   disallowed. This is implemented by making BACKUP acquire an exclusive
+   lock for the duration that must be protected, and making DDL
+   statements aquire Shared locks.
+   
+   The list of statements that should obey BML is as follows:
+
+    DROP   DATABASE/TABLE/VIEW/FUNCTION/PROCEDURE/EVENT/TRIGGER/INDEX
+    DROP   USER/TABLESPACE
+    CREATE DATABASE/TABLE/VIEW/FUNCTION/PROCEDURE/EVENT/TRIGGER/INDEX
+    ALTER  DATABASE/TABLE/VIEW/FUNCTION/PROCEDURE/EVENT/TABLESPACE
+    RENAME TABLE/USER
+    GRANT/REVOKE
+    TRUNCATE/OPTIMIZE/REPAIR TABLE
+
+   The parser (mysql_execute_command() in sql_parse.cc) arranges for calls to
+   get_shared_lock() and release_shared_lock() for these statements.
+
+   BACKUP COMMIT BLOCKER
+   ---------------------
+
+   To get a transaction consistent image of all tables in all
+   databases being backed up, BACKUP needs to disallow commits for a
+   short period of time to synchronize data from all storage
+   engines. This is implemented by making BACKUP acquire an exclusive lock
+   for the duration of the synchronization phase, and by making all
+   commits of transactions that have modified data acquire Shared locks.
+
+   ha_commit_trans in handler.cc arranges for calls to get_shared_lock()
+   and release_shared_lock().
+
+*/
+
+#include "bml.h"
+#include "debug_sync.h"
+
+Backup_sx_lock::Backup_sx_lock()
+{
+  pthread_mutex_init(&LOCK_mutex, MY_MUTEX_INIT_FAST);
+  pthread_cond_init(&COND_no_slock, NULL);
+  pthread_cond_init(&COND_no_xlock, NULL);
+  xlock_set= NULL;
+  slock_count= 0;
+}
+
+Backup_sx_lock::~Backup_sx_lock()
+{
+  pthread_mutex_destroy(&LOCK_mutex);
+  pthread_cond_destroy(&COND_no_slock);
+  pthread_cond_destroy(&COND_no_xlock);
+}
+
+/**
+   Check if the exclusive lock has been set. If so, wait until it has
+   been released. When the exclusive lock has been released, acquire
+   a Shared lock by increasing the Shared Lock Counter (slock_count).
+
+   @param[in] thd        The THD object from the caller.
+
+   @note: A successful call to get_shared_lock() must be matched by
+   release_shared_lock().
+
+   @return Operation status
+     @retval TRUE if a Shared Lock was successfully acquired
+     @retval FALSE if timeout occurred while waiting for the exclusive
+             lock to be released
+*/
+my_bool Backup_sx_lock::get_shared_lock(THD *thd)
+{
+  int ret = 0;
+  struct timespec ddl_timeout;
+  DBUG_ENTER("Backup_sx_lock::get_shared_lock()");
+
+  set_timespec(ddl_timeout, thd->backup_wait_timeout);
+
+  /*
+    Check whether an exclusive lock is set. If yes, wait for release
+    which is signalled with COND_no_xlock.
+  */
+  pthread_mutex_lock(&LOCK_mutex);
+  thd->enter_cond(&COND_no_xlock, &LOCK_mutex,
+                  "Backup_sx_lock: waiting until exclusive lock is released");
+  while (xlock_set && xlock_set!=thd && !thd->killed && (ret == 0))
+  {
+    if (thd->backup_wait_timeout == 0)
+    {
+      ret = -1;
+    }
+    else
+    {
+      /*
+        The below sync point fires if the exclusive lock is set.
+        
+        WARNING: Be careful when using WAIT_FOR with this sync point.
+        We hold LOCK_mutex here.
+      */
+      DEBUG_SYNC(thd, "wait_if_backup_exclusive_locked");
+      ret= pthread_cond_timedwait(&COND_no_xlock, &LOCK_mutex,
+                                  &ddl_timeout);
+    }
+  }
+
+  if (thd->killed)
+  {
+    /* Releases LOCK_mutex */
+    thd->exit_cond("Backup_sx_lock: Thread was killed");
+    DBUG_RETURN(FALSE);
+  }
+
+  if (ret == 0)
+    slock_count++;
+  else
+    my_error(ER_DDL_TIMEOUT, MYF(0), thd->query());
+
+  thd->exit_cond("Backup_sx_lock: entered");
+
+  DEBUG_SYNC(thd, "backup_got_shared_lock");
+  DBUG_RETURN(ret == 0);
+}
+
+/**
+   Release the Shared lock by decreasing slock_count
+*/
+void Backup_sx_lock::release_shared_lock()
+{
+  DBUG_ENTER("Backup_sx_lock::release_shared_lock()");
+  pthread_mutex_lock(&LOCK_mutex);
+  
+  DBUG_ASSERT(slock_count > 0);
+  slock_count--;
+
+  if (slock_count == 0 && xlock_set) 
+    pthread_cond_signal(&COND_no_slock);
+
+  pthread_mutex_unlock(&LOCK_mutex);
+  DBUG_VOID_RETURN;
+}
+
+/**
+   Block new shared lock requests by setting the exclusive lock. Wait
+   until all currently active shared locks have been released and then
+   return.
+
+   @param[in] thd        The THD object from the caller.
+
+   @note: A successful call to get_exclusive_lock() must be matched by
+   release_exclusive_lock().
+
+   @return Operation status
+     @retval TRUE if an exclusive lock was successfully acquired
+     @retval FALSE otherwise
+*/
+my_bool Backup_sx_lock::get_exclusive_lock(THD *thd)
+{
+  DBUG_ENTER("Backup_sx_lock::get_exclusive_lock()");
+
+  //part 1 - get the exclusive lock
+  pthread_mutex_lock(&LOCK_mutex);
+  if (unlikely(xlock_set != NULL)) 
+  {
+    /* 
+       Should never happen since only one backup/restore operation is
+       allowed at any time 
+    */
+    thd->exit_cond("Backup_sx_lock: An exclusive lock has already been set");
+    DBUG_RETURN(FALSE);
+  }
+
+  xlock_set= thd; // Blocks new attempts at getting a shared lock
+
+  //part 2 - wait for all shared locks to be released
+  thd->enter_cond(&COND_no_slock, &LOCK_mutex,
+                  "Backup_sx_lock: waiting for release of all Shared locks");
+  while (slock_count != 0 && !thd->killed) 
+  {
+    /*
+      The below sync point fires if a shared lock is set.
+      
+      WARNING: Be careful when using WAIT_FOR with this sync point.
+      We hold LOCK_mutex here.
+    */
+    DEBUG_SYNC(thd, "wait_backup_exclusive_lock");
+    /* wait in queue that is activated when slock_count drops to 0 */
+    pthread_cond_wait(&COND_no_slock, &LOCK_mutex); 
+  }
+  thd->exit_cond("Backup_sx_lock: Exclusive lock set (or thread killed)");
+
+  if (thd->killed)
+  {
+    /* This thread alread got X lock; release it */
+    release_exclusive_lock();
+    DBUG_RETURN(FALSE);
+  }
+
+  DEBUG_SYNC(thd, "after_backup_exclusivelock_set");
+  DBUG_RETURN(TRUE);
+}
+
+/**
+   Release the Exclusive lock by setting xlock_set= NULL. Since there
+   cannot be more than one BACKUP/RESTORE thread at any time, it is
+   safe for BACKUP/RESTORE to call this even if get_exclusive_lock()
+   failed.
+
+   @todo Take THD *thd as argument and verify that xlock_set==thd
+         before doing xlock_set= NULL
+*/
+void Backup_sx_lock::release_exclusive_lock()
+{
+  DBUG_ENTER("Backup_sx_lock::release_exclusive_lock()");
+  pthread_mutex_lock(&LOCK_mutex);
+  xlock_set= NULL;
+  pthread_cond_broadcast(&COND_no_xlock);
+  pthread_mutex_unlock(&LOCK_mutex);
+  DBUG_VOID_RETURN;
+}
+
+Backup_sx_lock *Backup_sx_lock::bml_instance= NULL;
+Backup_sx_lock *Backup_sx_lock::bcb_instance= NULL;
+
+/**
+  Singleton method to get the Backup Metadata Lock instance
+*/
+Backup_sx_lock *Backup_sx_lock::get_BML_instance()
+{
+  if (bml_instance == NULL)
+    bml_instance = new Backup_sx_lock();
+  return bml_instance;
+}
+
+/**
+  Singleton method to destroy the Backup Metadata Lock instance
+*/
+void Backup_sx_lock::destroy_BML_instance()
+{
+  delete bml_instance;
+  bml_instance= NULL;
+}
+
+
+/**
+  Singleton method to get the Backup Commit Blocker instance
+*/
+Backup_sx_lock *Backup_sx_lock::get_BCB_instance()
+{
+  if (bcb_instance == NULL)
+    bcb_instance = new Backup_sx_lock();
+  return bcb_instance;
+}
+
+/**
+  Singleton method to destroy the Backup Commit Blocker instance
+*/
+void Backup_sx_lock::destroy_BCB_instance()
+{
+  delete bcb_instance;
+  bcb_instance= NULL;
+}
+

=== added file 'sql/bml.h'
--- a/sql/bml.h	1970-01-01 00:00:00 +0000
+++ b/sql/bml.h	2009-12-04 22:31:25 +0000
@@ -0,0 +1,111 @@
+#ifndef BML_INCLUDED
+#define BML_INCLUDED
+
+/**
+  @file
+
+  Header file for Backup Metadata Lock.
+ */
+
+#include "mysql_priv.h"
+
+/**
+   @class Backup_sx_lock
+ 
+   @brief Implements a simple Backup Shared/Exclusive Lock service
+   used by BML (backup metadata locking) and Backup Commit Blocker.
+
+   Two singleton instances are created of this class:
+     * BML_instance, which is used by Backup to block concurrent 
+       DDL statements
+     * bcb_instance, which is used by Backup to block concurrent
+       transactions from committing while backup performs the
+       synchronization stage.
+
+   Backup Metadata Locking works as follows:
+     * A transaction that wants to modify the database metada by
+       performing a DDL operation first needs to acquire a shared lock
+       in the BML_instance. It does so by calling get_shared_lock(). A
+       transaction that has taken a shared lock has to release it
+       by calling release_shared_lock() before committing.
+     * When backup want's to block DDL statements so that the database
+       metadata remains unchanged for the duration of backup, it has
+       to acquire the exclusive lock in the BML_instance. It does so
+       by calling get_exclusive_lock(). Once metadata changes no
+       longer need to be blocked, backup will release the exlusive
+       lock by calling release_exclusive_lock().
+
+   Backup Commit Blocking works as follows:
+     * A transaction that wants to commit has to acquire the shared
+       lock in the bcb_instance first. Once it has committed, the
+       shared lock must be released.
+     * When backup reaches the synchronization phase and needs to
+       block commits, it acquires the exclusive lock. Once
+       synchronization is completed, it releases the lock.
+
+
+   Singleton Methods
+   The creation of the locking instances is accomplished using the
+   get_BML_instance() and get_BCB_instance() methods. These methods
+   are called from mysqld.cc and creates and initializes all of the
+   private mutex, condition, and controlling variables. The methods
+   destroy_BML_instance() and destroy_BCB_instance() destroys the
+   mutex and condition variables.
+
+   Calling the Singletons
+
+   To call the BML or BCB singletons, you must declare external
+   variables to the global variables BML_instance and BCB_instance as
+   shown below.
+
+   @c extern Backup_sx_lock *BML_instance;
+   @c extern Backup_sx_lock *BCB_instance;
+
+
+   Calling methods on the singleton is accomplished using the
+   BML_instance or BCB_instance variable such as: 
+
+   @c BML_instance->get_shared_lock().
+
+   @note: This class is currently only used in MySQL backup. If you would
+   like to use it elsewhere and have questions, please contact
+   Chuck Bell (cbell@stripped) for more details and how to setup
+   a test case to test the Backup_sx_lock mechanism for your use.
+*/
+class Backup_sx_lock
+{
+ public:
+  
+  static Backup_sx_lock *get_BML_instance();
+  static void destroy_BML_instance();
+  static Backup_sx_lock *get_BCB_instance();
+  static void destroy_BCB_instance();
+  
+  my_bool get_shared_lock(THD *thd);
+  my_bool get_exclusive_lock(THD *thd);
+  
+  void release_shared_lock();
+  void release_exclusive_lock();
+  
+ private:
+  Backup_sx_lock();
+  ~Backup_sx_lock();
+  
+  /// Mutex for protecting xlock_set and slock_count
+  pthread_mutex_t LOCK_mutex;
+  /// Signals THR_LOCK_lock mutex was released and slock_count dropped to zero
+  pthread_cond_t COND_no_slock;
+  /// Signals wlock was released
+  pthread_cond_t COND_no_xlock;
+  
+  /// THD that has the X lock, or NULL if X lock is not set
+  THD *xlock_set;
+  /// Number of shared locks currently set
+  int slock_count; 
+
+  
+  static Backup_sx_lock *bml_instance; ///< instance var for singleton 
+  static Backup_sx_lock *bcb_instance; ///< instance var for singleton 
+  
+};
+#endif /* BML_INCLUDED */

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2009-12-03 18:37:38 +0000
+++ b/sql/handler.cc	2009-12-04 22:31:25 +0000
@@ -33,6 +33,7 @@
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 #include "ha_partition.h"
+#include "bml.h"
 #endif
 
 /*
@@ -89,6 +90,8 @@ static plugin_ref ha_default_plugin(THD 
 }
 
 
+extern Backup_sx_lock *BCB_instance;
+
 /** @brief
   Return the default storage engine handlerton for thread
 
@@ -1148,6 +1151,8 @@ int ha_commit_trans(THD *thd, bool all)
     DBUG_RETURN(2);
   }
 
+  bool bcb_registered= FALSE;
+
   if (ha_info)
   {
     uint rw_ha_count;
@@ -1164,7 +1169,8 @@ int ha_commit_trans(THD *thd, bool all)
     rw_trans= is_real_trans && (rw_ha_count > 0);
 
     if (rw_trans &&
-        wait_if_global_read_lock(thd, 0, 0))
+        (wait_if_global_read_lock(thd, 0, 0) ||
+         !(bcb_registered= BCB_instance->get_shared_lock(thd))))
     {
       ha_rollback_trans(thd, all);
       DBUG_RETURN(1);
@@ -1176,6 +1182,11 @@ int ha_commit_trans(THD *thd, bool all)
         !thd->slave_thread)
     {
       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+      if (bcb_registered) 
+      {
+        BCB_instance->release_shared_lock();
+        bcb_registered=FALSE;
+      }
       ha_rollback_trans(thd, all);
       error= 1;
       goto end;
@@ -1228,6 +1239,7 @@ end:
   /* Free resources and perform other cleanup even for 'empty' transactions. */
   else if (is_real_trans)
     thd->transaction.cleanup();
+  if (bcb_registered) BCB_instance->release_shared_lock();
   DBUG_RETURN(error);
 }
 

=== modified file 'sql/log.cc'
--- a/sql/log.cc	2009-12-03 22:46:14 +0000
+++ b/sql/log.cc	2009-12-04 22:31:25 +0000
@@ -28,6 +28,8 @@
 #include "sql_repl.h"
 #include "rpl_filter.h"
 #include "rpl_rli.h"
+#include "si_objects.h"
+#include "si_logs.h"
 
 #include <my_dir.h>
 #include <stdarg.h>
@@ -153,6 +155,25 @@ char *make_default_log_name(char *buff,c
                    MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT));
 }
 
+/**
+  Create the name of the backup log specified.
+
+  This method forms a new path + file name for the backup
+  log specified in @c name.
+
+  @param[IN] buff    Location for building new string.
+  @param[IN] name    Name of the backup log.
+  @param[IN] log_ext The extension for the log (e.g. .log).
+
+  @returns Pointer to new string containing the name.
+*/
+char *make_backup_log_name(char *buff, const char *name, const char* log_ext)
+{
+  strmake(buff, name, FN_REFLEN-5);
+  return fn_format(buff, buff, mysql_real_data_home, log_ext,
+                   MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT));
+}
+
 /*
   Helper class to hold a mutex for the duration of the
   block.
@@ -740,16 +761,40 @@ int Log_to_csv_event_handler::
 
   bzero(& table_list, sizeof(TABLE_LIST));
 
-  if (log_table_type == QUERY_LOG_GENERAL)
+  /*
+    Code changed to use a switch now that there are 4 logs.
+  */
+  switch (log_table_type) {
+  case QUERY_LOG_GENERAL:
   {
     table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str;
     table_list.table_name_length= GENERAL_LOG_NAME.length;
+    break;
   }
-  else
+  case QUERY_LOG_SLOW:
   {
     DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW);
+
     table_list.alias= table_list.table_name= SLOW_LOG_NAME.str;
     table_list.table_name_length= SLOW_LOG_NAME.length;
+    break;
+  }
+  case BACKUP_HISTORY_LOG:
+  {
+    DBUG_ASSERT(log_table_type == BACKUP_HISTORY_LOG);
+
+    table_list.alias= table_list.table_name= BACKUP_HISTORY_LOG_NAME.str;
+    table_list.table_name_length= BACKUP_HISTORY_LOG_NAME.length;
+    break;
+  }
+  case BACKUP_PROGRESS_LOG:
+  {
+    DBUG_ASSERT(log_table_type == BACKUP_PROGRESS_LOG);
+
+    table_list.alias= table_list.table_name= BACKUP_PROGRESS_LOG_NAME.str;
+    table_list.table_name_length= BACKUP_PROGRESS_LOG_NAME.length;
+    break;
+  }
   }
 
   table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
@@ -770,1242 +815,2921 @@ int Log_to_csv_event_handler::
   DBUG_RETURN(result);
 }
 
-bool Log_to_csv_event_handler::
-  log_error(enum loglevel level, const char *format, va_list args)
-{
-  /* No log table is implemented */
-  DBUG_ASSERT(0);
-  return FALSE;
-}
-
-bool Log_to_file_event_handler::
-  log_error(enum loglevel level, const char *format,
-            va_list args)
-{
-  return vprint_msg_to_log(level, format, args);
-}
+/**
+  Write the backup log entry for the backup history log to a table.
 
-void Log_to_file_event_handler::init_pthread_objects()
-{
-  mysql_log.init_pthread_objects();
-  mysql_slow_log.init_pthread_objects();
-}
+  This method creates a new row in the backup history log with the
+  information provided.
 
+  @param[IN]   thd   The current thread
+  @param[IN]   st_backup_history   Data to write to log.
 
-/** Wrapper around MYSQL_LOG::write() for slow log. */
+  @retval TRUE if error.
 
-bool Log_to_file_event_handler::
-  log_slow(THD *thd, time_t current_time, time_t query_start_arg,
-           const char *user_host, uint user_host_len,
-           ulonglong query_utime, ulonglong lock_utime, bool is_command,
-           const char *sql_text, uint sql_text_len)
+  @todo Add internal error handler to handle errors that occur on
+        open. See  thd->push_internal_handler(&error_handler).
+*/
+bool Log_to_csv_event_handler::
+  log_backup_history(THD *thd, 
+                     st_backup_history *history_data)
 {
-  Silence_log_table_errors error_handler;
-  thd->push_internal_handler(&error_handler);
-  bool retval= mysql_slow_log.write(thd, current_time, query_start_arg,
-                                    user_host, user_host_len,
-                                    query_utime, lock_utime, is_command,
-                                    sql_text, sql_text_len);
-  thd->pop_internal_handler();
-  return retval;
-}
+  TABLE_LIST table_list;
+  TABLE *table= NULL;
+  bool result= TRUE;
+  bool need_close= FALSE;
+  bool need_rnd_end= FALSE;
+  Open_tables_state open_tables_backup;
+  ulonglong save_thd_options;
+  bool save_time_zone_used;
+  char *host= current_thd->security_ctx->host; // host name
+  char *user= current_thd->security_ctx->user; // user name
+  THD::killed_state saved_killed_state= THD::NOT_KILLED;
 
+  /*
+    Turn the binlog off and don't replicate the
+    updates to the backup logs.
+  */
+  save_thd_options= thd->options;
+  thd->options&= ~OPTION_BIN_LOG;
 
-/**
-   Wrapper around MYSQL_LOG::write() for general log. We need it since we
-   want all log event handlers to have the same signature.
-*/
+  save_time_zone_used= thd->time_zone_used;
 
-bool Log_to_file_event_handler::
-  log_general(THD *thd, time_t event_time, const char *user_host,
-              uint user_host_len, int thread_id,
-              const char *command_type, uint command_type_len,
-              const char *sql_text, uint sql_text_len,
-              CHARSET_INFO *client_cs)
-{
-  Silence_log_table_errors error_handler;
-  thd->push_internal_handler(&error_handler);
-  bool retval= mysql_log.write(event_time, user_host, user_host_len,
-                               thread_id, command_type, command_type_len,
-                               sql_text, sql_text_len);
-  thd->pop_internal_handler();
-  return retval;
-}
+  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
+                            BACKUP_HISTORY_LOG_NAME.str,
+                            BACKUP_HISTORY_LOG_NAME.length,
+                            BACKUP_HISTORY_LOG_NAME.str,
+                            TL_WRITE_CONCURRENT_INSERT);
 
+  /*
+    We need to override checking for a killed thread in the
+    open_performance_schema_table() to allow the backup log
+    to be written even if backup is killed in the middle of
+    execution.
+  */
+  saved_killed_state= thd->killed;
+  if (thd->killed)
+    thd->killed= THD::NOT_KILLED;
 
-bool Log_to_file_event_handler::init()
-{
-  if (!is_initialized)
-  {
-    if (opt_slow_log)
-      mysql_slow_log.open_slow_log(sys_var_slow_log_path.value);
+  if (!(table= open_performance_schema_table(thd, & table_list,
+                                             & open_tables_backup)))
+    goto err;
 
-    if (opt_log)
-      mysql_log.open_query_log(sys_var_general_log_path.value);
+  if (!thd->killed)
+    thd->killed= saved_killed_state;
 
-    is_initialized= TRUE;
-  }
+  need_close= TRUE;
 
-  return FALSE;
-}
+  if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
+      table->file->ha_rnd_init(0))
+    goto err;
 
+  need_rnd_end= TRUE;
 
-void Log_to_file_event_handler::cleanup()
-{
-  mysql_log.cleanup();
-  mysql_slow_log.cleanup();
-}
+  /*
+    Get defaults for new record.
+  */
+  restore_record(table, s->default_values); 
 
-void Log_to_file_event_handler::flush()
-{
-  /* reopen log files */
-  if (opt_log)
-    mysql_log.reopen_file();
-  if (opt_slow_log)
-    mysql_slow_log.reopen_file();
-}
+  /* check that all columns exist */
+  if (table->s->fields < ET_OBH_FIELD_COUNT)
+    goto err;
 
-/*
-  Log error with all enabled log event handlers
+  /*
+    Fill in the data.
+  */
+  table->field[ET_OBH_FIELD_BACKUP_ID]->store(history_data->backup_id, TRUE);
+  table->field[ET_OBH_FIELD_BACKUP_ID]->set_notnull();
+  table->field[ET_OBH_FIELD_PROCESS_ID]->store(history_data->process_id, TRUE);
+  table->field[ET_OBH_FIELD_PROCESS_ID]->set_notnull();
+  table->field[ET_OBH_FIELD_BINLOG_START_POS]->
+    store(history_data->binlog_start_pos, TRUE);
+  table->field[ET_OBH_FIELD_BINLOG_START_POS]->set_notnull();
+
+  if (history_data->binlog_file)
+  {
+    if (table->field[ET_OBH_FIELD_BINLOG_FILE]->
+        store(history_data->binlog_file, 
+              strlen(history_data->binlog_file), 
+              system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_BINLOG_FILE]->set_notnull();
+  }
 
-  SYNOPSIS
-    error_log_print()
+  table->field[ET_OBH_FIELD_BACKUP_STATE]->store(history_data->state, TRUE);
+  table->field[ET_OBH_FIELD_BACKUP_STATE]->set_notnull();
+  table->field[ET_OBH_FIELD_OPER]->store(history_data->operation, TRUE);
+  table->field[ET_OBH_FIELD_OPER]->set_notnull();
+  table->field[ET_OBH_FIELD_ERROR_NUM]->store(history_data->error_num, TRUE);
+  table->field[ET_OBH_FIELD_ERROR_NUM]->set_notnull();
+  table->field[ET_OBH_FIELD_NUM_OBJ]->store(history_data->num_objects, TRUE);
+  table->field[ET_OBH_FIELD_NUM_OBJ]->set_notnull();
+  table->field[ET_OBH_FIELD_TOTAL_BYTES]->store(history_data->size, TRUE);
+  table->field[ET_OBH_FIELD_TOTAL_BYTES]->set_notnull();
 
-    level             The level of the error significance: NOTE,
-                      WARNING or ERROR.
-    format            format string for the error message
-    args              list of arguments for the format string
+  if (history_data->vp_time)
+  {
+    MYSQL_TIME time;
+    my_tz_OFFSET0->gmt_sec_to_TIME(&time, (my_time_t)history_data->vp_time);
 
-  RETURN
-    FALSE - OK
-    TRUE - error occured
-*/
+    table->field[ET_OBH_FIELD_VP]->set_notnull();
+    table->field[ET_OBH_FIELD_VP]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
+  }
 
-bool LOGGER::error_log_print(enum loglevel level, const char *format,
-                             va_list args)
-{
-  bool error= FALSE;
-  Log_event_handler **current_handler;
+  if (history_data->start)
+  {
+    MYSQL_TIME time;
+    /*
+      Set time ahead a few hours to allow backup purge test to test
+      PURGE BACKUP LOGS BEFORE command.
+    */
+    DBUG_EXECUTE_IF("set_log_time", history_data->start= my_time(0) + 100000;);
+    my_tz_OFFSET0->gmt_sec_to_TIME(&time, (my_time_t)history_data->start);
 
-  /* currently we don't need locking here as there is no error_log table */
-  for (current_handler= error_log_handler_list ; *current_handler ;)
-    error= (*current_handler++)->log_error(level, format, args) || error;
+    table->field[ET_OBH_FIELD_START_TIME]->set_notnull();
+    table->field[ET_OBH_FIELD_START_TIME]->store_time(&time, 
+      MYSQL_TIMESTAMP_DATETIME);
+  }
 
-  return error;
-}
+  if (history_data->stop)
+  {
+    MYSQL_TIME time;
+    my_tz_OFFSET0->gmt_sec_to_TIME(&time, (my_time_t)history_data->stop);
 
+    table->field[ET_OBH_FIELD_STOP_TIME]->set_notnull();
+    table->field[ET_OBH_FIELD_STOP_TIME]->store_time(&time, 
+      MYSQL_TIMESTAMP_DATETIME);
+  }
 
-void LOGGER::cleanup_base()
-{
-  DBUG_ASSERT(inited == 1);
-  rwlock_destroy(&LOCK_logger);
-  if (table_log_handler)
+  if (host)
   {
-    table_log_handler->cleanup();
-    delete table_log_handler;
-    table_log_handler= NULL;
+    if(table->field[ET_OBH_FIELD_HOST_OR_SERVER]->store(host, 
+       strlen(host), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_HOST_OR_SERVER]->set_notnull();
   }
-  if (file_log_handler)
-    file_log_handler->cleanup();
-}
 
+  if (user)
+  {
+    if (table->field[ET_OBH_FIELD_USERNAME]->store(user,
+        strlen(user), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_USERNAME]->set_notnull();
+  }
 
-void LOGGER::cleanup_end()
-{
-  DBUG_ASSERT(inited == 1);
-  if (file_log_handler)
+  if (history_data->backup_file)
   {
-    delete file_log_handler;
-    file_log_handler=NULL;
+    if (table->field[ET_OBH_FIELD_BACKUP_FILE]->store(
+        history_data->backup_file, 
+        strlen(history_data->backup_file), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_BACKUP_FILE]->set_notnull();
   }
-  inited= 0;
-}
 
+  if (history_data->backup_file_path)
+  {
+    if (table->field[ET_OBH_FIELD_BACKUP_FILE_PATH]->store(
+        history_data->backup_file_path, 
+        strlen(history_data->backup_file_path), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_BACKUP_FILE_PATH]->set_notnull();
+  }
 
-/**
-  Perform basic log initialization: create file-based log handler and
-  init error log.
-*/
-void LOGGER::init_base()
-{
-  DBUG_ASSERT(inited == 0);
-  inited= 1;
+  if (history_data->user_comment)
+  {
+    if (table->field[ET_OBH_FIELD_COMMENT]->store(history_data->user_comment,
+        strlen(history_data->user_comment), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_COMMENT]->set_notnull();
+  }
 
-  /*
-    Here we create file log handler. We don't do it for the table log handler
-    here as it cannot be created so early. The reason is THD initialization,
-    which depends on the system variables (parsed later).
-  */
-  if (!file_log_handler)
-    file_log_handler= new Log_to_file_event_handler;
+  if (history_data->command)
+  {
+    if (table->field[ET_OBH_FIELD_COMMAND]->store(history_data->command,
+        strlen(history_data->command), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_COMMAND]->set_notnull();
+  }
 
-  /* by default we use traditional error log */
-  init_error_log(LOG_FILE);
+  if (history_data->driver_name.length())
+  {
+    if (table->field[ET_OBH_FIELD_DRIVERS]->store(
+        history_data->driver_name.c_ptr(),
+        history_data->driver_name.length(), system_charset_info))
+      goto err;
+    table->field[ET_OBH_FIELD_DRIVERS]->set_notnull();
+  }
 
-  file_log_handler->init_pthread_objects();
-  my_rwlock_init(&LOCK_logger, NULL);
-}
+  /* log table entries are not replicated */
+  if (table->file->ha_write_row(table->record[0]))
+    goto err;
 
+  result= FALSE;
 
-void LOGGER::init_log_tables()
-{
-  if (!table_log_handler)
-    table_log_handler= new Log_to_csv_event_handler;
+  // Error code insertion for ER_BACKUP_LOG_WRITE_ERROR.
+  DBUG_EXECUTE_IF("ER_BACKUP_LOG_WRITE_ERROR_H", result= TRUE;);
 
-  if (!is_log_tables_initialized &&
-      !table_log_handler->init() && !file_log_handler->init())
-    is_log_tables_initialized= TRUE;
-}
+err:
+  if (result && !thd->killed)
+    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                        ER_BACKUP_LOG_WRITE_ERROR,
+                        ER(ER_BACKUP_LOG_WRITE_ERROR),
+                        "mysql.backup_history");
 
+  if (need_rnd_end)
+    table->file->ha_rnd_end();
+  if (need_close)
+    close_performance_schema_table(thd, & open_tables_backup);
 
-bool LOGGER::flush_logs(THD *thd)
-{
-  int rc= 0;
+  thd->time_zone_used= save_time_zone_used;
 
   /*
-    Now we lock logger, as nobody should be able to use logging routines while
-    log tables are closed
+    Turn binlog back on if disengaged.
   */
-  logger.lock_exclusive();
+  thd->options= save_thd_options;
 
-  /* reopen log files */
-  file_log_handler->flush();
-
-  /* end of log flush */
-  logger.unlock();
-  return rc;
-}
+  return result;
+}
 
+/**
+  Write the backup log entry for the backup progress log to a table.
 
-/*
-  Log slow query with all enabled log event handlers
+  This method creates a new row in the backup progress log with the
+  information provided.
 
-  SYNOPSIS
-    slow_log_print()
+  @param[IN]   thd         The current thread
+  @param[OUT]  backup_id   The id of the backup/restore operation for
+                           the progress information
+  @param[IN]   object      The name of the object processed
+  @param[IN]   start       Start datetime
+  @param[IN]   stop        Stop datetime
+  @param[IN]   size        Size value
+  @param[IN]   progress    Progress (percent)
+  @param[IN]   error_num   Error number (should be 0 if success)
+  @param[IN]   notes       Misc data from engine
 
-    thd                 THD of the query being logged
-    query               The query being logged
-    query_length        The length of the query string
-    current_utime       Current time in microseconds (from undefined start)
+  @retval TRUE if error.
 
-  RETURN
-    FALSE   OK
-    TRUE    error occured
+  @todo Add internal error handler to handle errors that occur on
+        open. See  thd->push_internal_handler(&error_handler).
 */
+bool Log_to_csv_event_handler::
+  log_backup_progress(THD *thd,
+                      ulonglong backup_id,
+                      const char *object,
+                      time_t start,
+                      time_t stop,
+                      longlong size,
+                      longlong progress,
+                      int error_num,
+                      const char *notes)
+{
+  TABLE_LIST table_list;
+  TABLE *table;
+  bool result= TRUE;
+  bool need_close= FALSE;
+  bool need_rnd_end= FALSE;
+  Open_tables_state open_tables_backup;
+  bool save_time_zone_used;
+  THD::killed_state saved_killed_state= THD::NOT_KILLED;
 
-bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
-                            ulonglong current_utime)
+  save_time_zone_used= thd->time_zone_used;
 
-{
-  bool error= FALSE;
-  Log_event_handler **current_handler;
-  bool is_command= FALSE;
-  char user_host_buff[MAX_USER_HOST_SIZE + 1];
-  Security_context *sctx= thd->security_ctx;
-  uint user_host_len= 0;
-  ulonglong query_utime, lock_utime;
+  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
+                            BACKUP_PROGRESS_LOG_NAME.str,
+                            BACKUP_PROGRESS_LOG_NAME.length,
+                            BACKUP_PROGRESS_LOG_NAME.str,
+                            TL_WRITE_CONCURRENT_INSERT);
 
-  DBUG_ASSERT(thd->enable_slow_log);
   /*
-    Print the message to the buffer if we have slow log enabled
-  */
+    We need to override checking for a killed thread in the
+    open_performance_schema_table() to allow the backup log
+    to be written even if backup is killed in the middle of
+    execution.
+  */
+  saved_killed_state= thd->killed;
+  if (thd->killed)
+    thd->killed= THD::NOT_KILLED;
 
-  if (*slow_log_handler_list)
-  {
-    time_t current_time;
+  if (!(table= open_performance_schema_table(thd, & table_list,
+                                             & open_tables_backup)))
+    goto err;
 
-    /* do not log slow queries from replication threads */
-    if (thd->slave_thread && !opt_log_slow_slave_statements)
-      return 0;
+  if (!thd->killed)
+    thd->killed= saved_killed_state;
 
-    lock_shared();
-    if (!opt_slow_log)
-    {
-      unlock();
-      return 0;
-    }
+  need_close= TRUE;
 
-    /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
-    user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
-                             sctx->priv_user ? sctx->priv_user : "", "[",
-                             sctx->user ? sctx->user : "", "] @ ",
-                             sctx->host ? sctx->host : "", " [",
-                             sctx->ip ? sctx->ip : "", "]", NullS) -
-                    user_host_buff);
+  if (table->file->extra(HA_EXTRA_MARK_AS_LOG_TABLE) ||
+      table->file->ha_rnd_init(0))
+    goto err;
 
-    current_time= my_time_possible_from_micro(current_utime);
-    if (thd->start_utime)
-    {
-      query_utime= (current_utime - thd->start_utime);
-      lock_utime=  (thd->utime_after_lock - thd->start_utime);
-    }
-    else
-    {
-      query_utime= lock_utime= 0;
-    }
+  need_rnd_end= TRUE;
 
-    if (!query)
-    {
-      is_command= TRUE;
-      query= command_name[thd->command].str;
-      query_length= command_name[thd->command].length;
-    }
+  /*
+    Get defaults for new record.
+  */
+  restore_record(table, s->default_values); 
 
-    for (current_handler= slow_log_handler_list; *current_handler ;)
-      error= (*current_handler++)->log_slow(thd, current_time, thd->start_time,
-                                            user_host_buff, user_host_len,
-                                            query_utime, lock_utime, is_command,
-                                            query, query_length) || error;
+  /* check that all columns exist */
+  if (table->s->fields < ET_OBP_FIELD_PROG_COUNT)
+    goto err;
 
-    unlock();
-  }
-  return error;
-}
+  /*
+    Fill in the data.
+  */
+  table->field[ET_OBP_FIELD_BACKUP_ID_FK]->store(backup_id, TRUE);
+  table->field[ET_OBP_FIELD_BACKUP_ID_FK]->set_notnull();
 
-bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
-                               const char *query, uint query_length)
-{
-  bool error= FALSE;
-  Log_event_handler **current_handler= general_log_handler_list;
-  char user_host_buff[MAX_USER_HOST_SIZE + 1];
-  Security_context *sctx= thd->security_ctx;
-  uint user_host_len= 0;
-  time_t current_time;
+  if (object)
+  {
+    if (table->field[ET_OBP_FIELD_PROG_OBJECT]->store(object,
+        strlen(object), system_charset_info))
+      goto err;
+    table->field[ET_OBP_FIELD_PROG_OBJECT]->set_notnull();
+  }
 
-  DBUG_ASSERT(thd);
+  table->field[ET_OBP_FIELD_PROG_ERROR_NUM]->store(error_num, TRUE);
+  table->field[ET_OBP_FIELD_PROG_ERROR_NUM]->set_notnull();
 
-  lock_shared();
-  if (!opt_log)
+  if (notes)
   {
-    unlock();
-    return 0;
+    if (table->field[ET_OBP_FIELD_PROG_NOTES]->store(notes,
+        strlen(notes), system_charset_info))
+      goto err;
+    table->field[ET_OBP_FIELD_PROG_NOTES]->set_notnull();
   }
-  user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
-                          sctx->priv_user ? sctx->priv_user : "", "[",
-                          sctx->user ? sctx->user : "", "] @ ",
-                          sctx->host ? sctx->host : "", " [",
-                          sctx->ip ? sctx->ip : "", "]", NullS) -
-                                                          user_host_buff;
 
-  current_time= my_time(0);
-  while (*current_handler)
-    error|= (*current_handler++)->
-      log_general(thd, current_time, user_host_buff,
-                  user_host_len, thd->thread_id,
-                  command_name[(uint) command].str,
-                  command_name[(uint) command].length,
-                  query, query_length,
-                  thd->variables.character_set_client) || error;
-  unlock();
+  /* log table entries are not replicated */
+  if (table->file->ha_write_row(table->record[0]))
+    goto err;
 
-  return error;
-}
+  result= FALSE;
 
-bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
-                               const char *format, va_list args)
-{
-  uint message_buff_len= 0;
-  char message_buff[MAX_LOG_BUFFER_SIZE];
+  // Error code insertion for ER_BACKUP_LOG_WRITE_ERROR.
+  DBUG_EXECUTE_IF("ER_BACKUP_LOG_WRITE_ERROR_P", result= TRUE;);
 
-  /* prepare message */
-  if (format)
-    message_buff_len= my_vsnprintf(message_buff, sizeof(message_buff),
-                                   format, args);
-  else
-    message_buff[0]= '\0';
+err:
+  if (result && !thd->killed)
+    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                        ER_BACKUP_LOG_WRITE_ERROR,
+                        ER(ER_BACKUP_LOG_WRITE_ERROR),
+                        "mysql.backup_progress");
 
-  return general_log_write(thd, command, message_buff, message_buff_len);
+  if (need_rnd_end)
+    table->file->ha_rnd_end();
+  if (need_close)
+    close_performance_schema_table(thd, & open_tables_backup);
+
+  thd->time_zone_used= save_time_zone_used;
+  return result;
 }
 
-void LOGGER::init_error_log(uint error_log_printer)
+/**
+  Delete the current row from a log table.
+
+  This method is used to delete a row from a log that is being
+  accessed as a table. The row to be deleted is the current
+  row that the handler is pointing to via tbl->record[0].
+
+  Note: The handler must be positioned correctly and the
+        tbl->record[0] must be fetched by the appropriate
+        method (e.g., hdl->next()). 
+
+  @param[IN]  hdl   Table handler.
+  @param[IN]  tbl   Table class.
+
+  @returns Result of delete operation
+*/
+static bool delete_current_log_row(handler *hdl, TABLE *tbl)
 {
-  if (error_log_printer & LOG_NONE)
-  {
-    error_log_handler_list[0]= 0;
-    return;
-  }
+  bool result= 0;
 
-  switch (error_log_printer) {
-  case LOG_FILE:
-    error_log_handler_list[0]= file_log_handler;
-    error_log_handler_list[1]= 0;
-    break;
-    /* these two are disabled for now */
-  case LOG_TABLE:
-    DBUG_ASSERT(0);
-    break;
-  case LOG_TABLE|LOG_FILE:
-    DBUG_ASSERT(0);
-    break;
-  }
+  /*
+    Tell the handler to allow deletes for this log table
+    by turning off log table flag.
+  */
+  hdl->extra(HA_EXTRA_ALLOW_LOG_DELETE);
+  result= hdl->ha_delete_row(tbl->record[0]);
+  hdl->extra(HA_EXTRA_MARK_AS_LOG_TABLE);
+  return result;
 }
 
-void LOGGER::init_slow_log(uint slow_log_printer)
+/**
+  Perform a table scan of the backup log tables for deleting rows.
+
+  This method is a helper method designed to delete rows from the
+  backup logs when the destination includes writing to tables.
+
+  The method is designed to work with either the backup_history or
+  backup_progress log. The log is specified using the @c log_name
+  parameter and should be either BACKUP_HISTORY_LOG_NAME or
+  BACKUP_PROGRESS_LOG_NAME.
+
+  There are three ways this method can be used. The choice of which of 
+  the operations to run depends on the values of the parameters as stated.
+  1. Delete rows whose backup_id is < a given value. This is used
+     for @c PURGE BACKUP LOGS TO <@c backup_id>.
+     To use this method, set @c backup_id to the value passed from the
+     command, @c delete_exact_row = FALSE, and @c datetime_val = 0. 
+  2. Delete rows in the log where backuip_id is == to a given value.
+     This is used when cascading deletes from the backup_history log 
+     to the backup_progress log. 
+     To use this method, set @c backup_id to the value of the rows needing
+     deletion, @c delete_exact_row = TRUE, and @c datetime_val = 0. 
+  3. Delete rows whose start_time < a given value. This is used for
+     @c PURGE BACKUP LOGS BEFORE <datetimeval>.
+     To use this method, set @c backup_id = 0, @c delete_exact_row = FALSE, 
+     and @c datetime_val = value passed from the command. 
+
+  @note Deleting rows by date is limited to the backup_history table.
+
+  @param[IN]  THD                 The current thread
+  @param[IN]  log_name            is used to determine which log to process 
+                                  (backup_history or backup_progress).
+  @param[IN]  backup_id           value specified on the command for deleting 
+                                  rows by id or value for exact match 
+  @param[IN]  datetime_val        value specified on the command for deleting 
+                                  rows by date
+  @param[IN]  delete_exact_row    if TRUE, use the comparison for the log 
+                                  column backup_id_in_row == @c backup_id else 
+                                  use the comparison backup_id_in_row < 
+                                  @c backup_id.
+  @param[OUT] num                 Number of rows affected.
+
+  @returns TRUE if error
+*/
+static bool del_bup_log_row(THD *thd, 
+                            LEX_STRING log_name, 
+                            ulonglong backup_id, 
+                            my_time_t datetime_val,
+                            bool delete_exact_row,
+                            int *rows)
 {
-  if (slow_log_printer & LOG_NONE)
+  TABLE_LIST table_list;
+  handler *hdl;
+  TABLE *tbl;
+  uint result= 0;
+  int num_rows= 0;
+  Open_tables_state open_tables_backup;
+  MYSQL_TIME tmp;
+
+  DBUG_ASSERT((my_strcasecmp(system_charset_info, log_name.str, 
+               BACKUP_HISTORY_LOG_NAME.str) == 0) ||
+              (my_strcasecmp(system_charset_info, log_name.str, 
+               BACKUP_PROGRESS_LOG_NAME.str) == 0));
+
+  /*
+    Setup table list for open.
+  */
+  table_list.init_one_table(MYSQL_SCHEMA_NAME.str, MYSQL_SCHEMA_NAME.length,
+                            log_name.str, log_name.length, log_name.str,
+                            TL_WRITE_CONCURRENT_INSERT);
+
+  tbl= open_performance_schema_table(thd, &table_list,
+                                       &open_tables_backup);
+  hdl= tbl->file;
+  hdl->extra(HA_EXTRA_MARK_AS_LOG_TABLE);
+
+  /*
+    Get time zone conversion of value to compare for
+    PURGE BACKUP LOGS BEFORE <datetime>.
+  */
+  bzero(&tmp, sizeof(MYSQL_TIME));
+  thd->variables.time_zone->gmt_sec_to_TIME(&tmp, datetime_val);
+
+  /*
+    Begin table scan.
+  */
+  result= hdl->ha_rnd_init(1);
+  result= hdl->rnd_next(tbl->record[0]);
+  while (result != HA_ERR_END_OF_FILE)
   {
-    slow_log_handler_list[0]= 0;
-    return;
-  }
+    // backup_id is field number 0 in both logs.
+    ulonglong id= tbl->field[0]->val_int();
+    /*
+      Delete by id.
+    */
+    if (!datetime_val)
+    {
+      /*
+        Here we delete a specific row. For example, a specific
+        row in the backup_history log as the result of a 
+        cascade delete initiated from backup_history log.
+      */
+      if (delete_exact_row)
+      {
+        if (id == backup_id) // delete log row
+        {
+          result= delete_current_log_row(hdl, tbl);
+          if (result)
+            goto error;
+        }
+      }
+      /*
+        Here we execute the delete all rows 'to' a specific
+        id, e.g. PURGE BACKUP LOGS TO 17 -- we delete id < 17.
+      */
+      else if (id < backup_id)
+      {
+        result= delete_current_log_row(hdl, tbl);
+        if (result)
+          goto error;
+        num_rows++;
+      }
+    }
+    /*
+      Delete by date
+    */
+    else  
+    {
+      MYSQL_TIME time;      
 
-  switch (slow_log_printer) {
-  case LOG_FILE:
-    slow_log_handler_list[0]= file_log_handler;
-    slow_log_handler_list[1]= 0;
-    break;
-  case LOG_TABLE:
-    slow_log_handler_list[0]= table_log_handler;
-    slow_log_handler_list[1]= 0;
-    break;
-  case LOG_TABLE|LOG_FILE:
-    slow_log_handler_list[0]= file_log_handler;
-    slow_log_handler_list[1]= table_log_handler;
-    slow_log_handler_list[2]= 0;
-    break;
+      DBUG_ASSERT(my_strcasecmp(system_charset_info, log_name.str, 
+                  BACKUP_HISTORY_LOG_NAME.str) == 0);
+
+      tbl->field[ET_OBH_FIELD_START_TIME]->get_date(&time, TIME_NO_ZERO_DATE);
+      if (my_time_compare(&time, &tmp) < 0)
+      {
+        /*
+          When deleting by date for the backup_history table,
+          we must also delete rows from the backup_progress table
+          for this backup id.
+        */
+        if ((my_strcasecmp(system_charset_info, log_name.str, 
+             BACKUP_HISTORY_LOG_NAME.str) == 0) &&
+            opt_backup_progress_log && 
+            (log_backup_output_options & LOG_TABLE))
+        {
+          int r= 0;
+
+          del_bup_log_row(thd, BACKUP_PROGRESS_LOG_NAME, 
+                          id, 0, TRUE, &r);
+        }
+        result= delete_current_log_row(hdl, tbl);
+        if (result)
+          goto error;
+        num_rows++;
+      }
+    }
+    result= hdl->rnd_next(tbl->record[0]);
   }
+error:
+  int res_end= hdl->ha_rnd_end();
+  *rows= num_rows;
+  close_performance_schema_table(thd, &open_tables_backup);
+  return (result != HA_ERR_END_OF_FILE) ? result : res_end;
 }
 
-void LOGGER::init_general_log(uint general_log_printer)
+/**
+  Remove all rows from the backup logs.
+
+  This method removes (via truncate) all data from the backup logs. It checks
+  if the backup logs are turned on and if the log_backup_output_option is set
+  to include writing to tables. If these conditions are true, each log 
+  (backup_history, backup_progress) is truncated.
+
+  @param[IN] THD  current thread
+
+  @returns TRUE = success, FALSE = failed
+*/
+bool Log_to_csv_event_handler::purge_backup_logs(THD *thd)
 {
-  if (general_log_printer & LOG_NONE)
+  TABLE_LIST tables;
+  bool res= FALSE;
+
+  if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
   {
-    general_log_handler_list[0]= 0;
-    return;
+    // need to truncate the table.
+    tables.init_one_table("mysql", strlen("mysql"), 
+                          "backup_history", strlen("backup_history"),
+                          "backup_history", TL_READ);
+    res= mysql_truncate(thd, &tables, 1);
+    close_thread_tables(thd);
+    if (res)
+      goto err;
   }
-
-  switch (general_log_printer) {
-  case LOG_FILE:
-    general_log_handler_list[0]= file_log_handler;
-    general_log_handler_list[1]= 0;
-    break;
-  case LOG_TABLE:
-    general_log_handler_list[0]= table_log_handler;
-    general_log_handler_list[1]= 0;
-    break;
-  case LOG_TABLE|LOG_FILE:
-    general_log_handler_list[0]= file_log_handler;
-    general_log_handler_list[1]= table_log_handler;
-    general_log_handler_list[2]= 0;
-    break;
+  if (opt_backup_progress_log && (log_backup_output_options & LOG_TABLE))
+  {
+    // need to truncate the table.
+    tables.init_one_table("mysql", strlen("mysql"), 
+                          "backup_progress", strlen("backup_progress"),
+                          "backup_progress", TL_READ);
+    res= mysql_truncate(thd, &tables, 1);
+    close_thread_tables(thd);
   }
+err:
+  return res;
 }
 
+/**
+  Purge backup logs of rows less than backup_id passed.
 
-bool LOGGER::activate_log_handler(THD* thd, uint log_type)
+  This method walks the backup log tables deleting all rows whose
+  backup id is less than @c backup_id passed. 
+
+  @param[IN]  THD                 The current thread
+  @param[IN]  backup_id           Value for backup id
+  @param[OUT] num                 Number of rows affected.
+
+  @retval num  Number of rows affected.
+
+  @returns TRUE if error
+*/
+bool 
+Log_to_csv_event_handler::purge_backup_logs_before_id(THD *thd, 
+                                                      ulonglong backup_id,
+                                                      int *rows)
 {
-  MYSQL_QUERY_LOG *file_log;
   bool res= FALSE;
-  lock_exclusive();
-  switch (log_type) {
-  case QUERY_LOG_SLOW:
-    if (!opt_slow_log)
-    {
-      file_log= file_log_handler->get_mysql_slow_log();
 
-      file_log->open_slow_log(sys_var_slow_log_path.value);
-      if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW))
-      {
-        /* Error printed by open table in activate_log() */
-        res= TRUE;
-        file_log->close(0);
-      }
-      else
-      {
-        init_slow_log(log_output_options);
-        opt_slow_log= TRUE;
-      }
-    }
-    break;
-  case QUERY_LOG_GENERAL:
-    if (!opt_log)
-    {
-      file_log= file_log_handler->get_mysql_log();
+  if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
+  { 
+    res= del_bup_log_row(thd, BACKUP_HISTORY_LOG_NAME, 
+                         backup_id, 0, FALSE, rows);
+    if (res)
+      goto err;
+  }
+  if (opt_backup_progress_log && (log_backup_output_options & LOG_TABLE))
+  {
+    int r= 0;   //ignore this for progress log
 
-      file_log->open_query_log(sys_var_general_log_path.value);
-      if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL))
-      {
-        /* Error printed by open table in activate_log() */
-        res= TRUE;
-        file_log->close(0);
-      }
-      else
-      {
-        init_general_log(log_output_options);
-        opt_log= TRUE;
-      }
-    }
-    break;
-  default:
-    DBUG_ASSERT(0);
+    res= del_bup_log_row(thd, BACKUP_PROGRESS_LOG_NAME, 
+                         backup_id, 0, FALSE, &r);
   }
-  unlock();
+err:
   return res;
 }
 
+/**
+  Purge backup logs of rows previous to start date passed.
 
-void LOGGER::deactivate_log_handler(THD *thd, uint log_type)
-{
-  my_bool *tmp_opt= 0;
-  MYSQL_LOG *file_log;
+  This method walks the backup log tables deleting all rows whose
+  start column value is less than @c t passed. 
 
-  switch (log_type) {
-  case QUERY_LOG_SLOW:
-    tmp_opt= &opt_slow_log;
-    file_log= file_log_handler->get_mysql_slow_log();
-    break;
-  case QUERY_LOG_GENERAL:
-    tmp_opt= &opt_log;
-    file_log= file_log_handler->get_mysql_log();
-    break;
-  default:
-    assert(0);                                  // Impossible
-  }
+  Implementation Description: This method uses cascading delete 
+  functionality to remove rows from the backup_progress log when 
+  rows from the backup_history log are removed. Thus, only one 
+  call to delete rows from the logs is necessary.
 
-  if (!(*tmp_opt))
-    return;
+  @param[IN]  THD                 The current thread
+  @param[IN]  t                   Value for start datetime
+  @param[OUT] num                 Number of rows affected.
 
-  lock_exclusive();
-  file_log->close(0);
-  *tmp_opt= FALSE;
-  unlock();
+  @retval num  Number of rows affected.
+
+  @returns TRUE if error
+*/
+bool 
+Log_to_csv_event_handler::purge_backup_logs_before_date(THD *thd, 
+                                                        my_time_t t,
+                                                        int *rows)
+{
+  bool res= FALSE;
+
+  if (opt_backup_history_log && (log_backup_output_options & LOG_TABLE))
+    res= del_bup_log_row(thd, BACKUP_HISTORY_LOG_NAME, 
+                         0, t, FALSE, rows);
+  return res;
 }
 
+bool Log_to_csv_event_handler::
+  log_error(enum loglevel level, const char *format, va_list args)
+{
+  /* No log table is implemented */
+  DBUG_ASSERT(0);
+  return FALSE;
+}
 
-/* the parameters are unused for the log tables */
-bool Log_to_csv_event_handler::init()
+bool Log_to_file_event_handler::
+  log_error(enum loglevel level, const char *format,
+            va_list args)
 {
-  return 0;
+  return vprint_msg_to_log(level, format, args);
 }
 
-int LOGGER::set_handlers(uint error_log_printer,
-                         uint slow_log_printer,
-                         uint general_log_printer)
+void Log_to_file_event_handler::init_pthread_objects()
 {
-  /* error log table is not supported yet */
-  DBUG_ASSERT(error_log_printer < LOG_TABLE);
+  mysql_log.init_pthread_objects();
+  mysql_slow_log.init_pthread_objects();
+  mysql_backup_history_log.init_pthread_objects();
+  mysql_backup_progress_log.init_pthread_objects();
+}
 
-  lock_exclusive();
 
-  if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) &&
-      !is_log_tables_initialized)
-  {
-    slow_log_printer= (slow_log_printer & ~LOG_TABLE) | LOG_FILE;
-    general_log_printer= (general_log_printer & ~LOG_TABLE) | LOG_FILE;
+/** Wrapper around MYSQL_LOG::write() for slow log. */
 
-    sql_print_error("Failed to initialize log tables. "
-                    "Falling back to the old-fashioned logs");
-  }
+bool Log_to_file_event_handler::
+  log_slow(THD *thd, time_t current_time, time_t query_start_arg,
+           const char *user_host, uint user_host_len,
+           ulonglong query_utime, ulonglong lock_utime, bool is_command,
+           const char *sql_text, uint sql_text_len)
+{
+  Silence_log_table_errors error_handler;
+  thd->push_internal_handler(&error_handler);
+  bool retval= mysql_slow_log.write(thd, current_time, query_start_arg,
+                                    user_host, user_host_len,
+                                    query_utime, lock_utime, is_command,
+                                    sql_text, sql_text_len);
+  thd->pop_internal_handler();
+  return retval;
+}
 
-  init_error_log(error_log_printer);
-  init_slow_log(slow_log_printer);
-  init_general_log(general_log_printer);
 
-  unlock();
+/**
+   Wrapper around MYSQL_LOG::write() for general log. We need it since we
+   want all log event handlers to have the same signature.
+*/
 
-  return 0;
+bool Log_to_file_event_handler::
+  log_general(THD *thd, time_t event_time, const char *user_host,
+              uint user_host_len, int thread_id,
+              const char *command_type, uint command_type_len,
+              const char *sql_text, uint sql_text_len,
+              CHARSET_INFO *client_cs)
+{
+  Silence_log_table_errors error_handler;
+  thd->push_internal_handler(&error_handler);
+  bool retval= mysql_log.write(event_time, user_host, user_host_len,
+                               thread_id, command_type, command_type_len,
+                               sql_text, sql_text_len);
+  thd->pop_internal_handler();
+  return retval;
 }
 
-/** 
-    This function checks if a transactional talbe was updated by the
-    current statement.
 
-    @param thd The client thread that executed the current statement.
-    @return
-      @c true if a transactional table was updated, @false otherwise.
-*/
-static bool stmt_has_updated_trans_table(THD *thd)
-{
-  Ha_trx_info *ha_info;
+/**
+  Write the history data to a file.
 
-  for (ha_info= thd->transaction.stmt.ha_list; ha_info && ha_info->is_started(); ha_info= ha_info->next())
-  {
-    if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
-      return (TRUE);
-  }
-  return (FALSE);
-}
+  This method calls the write method for the backup log
+  class to write the history data to the file.
 
- /*
-  Save position of binary log transaction cache.
+  @param[IN]   thd   The current thread
+  @param[IN]   st_backup_history   Data to write to log.
 
-  SYNPOSIS
-    binlog_trans_log_savepos()
+  @returns TRUE if error.
+*/
+bool Log_to_file_event_handler::
+  log_backup_history(THD *thd, 
+                     st_backup_history *history_data)
+{
+  Silence_log_table_errors error_handler;
+  thd->push_internal_handler(&error_handler);
+  bool retval= mysql_backup_history_log.write(thd, history_data);
+  thd->pop_internal_handler();
+  return retval;
+}
 
-    thd      The thread to take the binlog data from
-    pos      Pointer to variable where the position will be stored
+/**
+  Write the progress data to a file.
 
-  DESCRIPTION
+  This method calls the write method for the backup log
+  class to write the progress data to the file.
 
-    Save the current position in the binary log transaction cache into
-    the variable pointed to by 'pos'
- */
+  @param[IN]   thd         The current thread
+  @param[OUT]  backup_id   The new row id for the backup history
+  @param[IN]   object      The name of the object processed
+  @param[IN]   start       Start datetime
+  @param[IN]   stop        Stop datetime
+  @param[IN]   size        Size value
+  @param[IN]   progress    Progress (percent)
+  @param[IN]   error_num   Error number (should be 0 is success)
+  @param[IN]   notes       Misc data from engine
 
-static void
-binlog_trans_log_savepos(THD *thd, my_off_t *pos)
+  @returns TRUE if error.
+*/
+bool Log_to_file_event_handler::
+  log_backup_progress(THD *thd,
+                      ulonglong backup_id,
+                      const char *object,
+                      time_t start,
+                      time_t stop,
+                      longlong size,
+                      longlong progress,
+                      int error_num,
+                      const char *notes)
 {
-  DBUG_ENTER("binlog_trans_log_savepos");
-  DBUG_ASSERT(pos != NULL);
-  if (thd_get_ha_data(thd, binlog_hton) == NULL)
-    thd->binlog_setup_trx_data();
-  binlog_trx_data *const trx_data=
-    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-  DBUG_ASSERT(mysql_bin_log.is_open());
-  *pos= trx_data->position();
-  DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
-  DBUG_VOID_RETURN;
+  Silence_log_table_errors error_handler;
+  thd->push_internal_handler(&error_handler);
+  bool retval= mysql_backup_progress_log.write(thd, backup_id, object, start,
+                 stop, size, progress, error_num, notes);
+  thd->pop_internal_handler();
+  return retval;
 }
 
 
-/*
-  Truncate the binary log transaction cache.
+bool Log_to_file_event_handler::init()
+{
+  if (!is_initialized)
+  {
+    if (opt_slow_log)
+      mysql_slow_log.open_slow_log(sys_var_slow_log_path.value);
 
-  SYNPOSIS
-    binlog_trans_log_truncate()
+    if (opt_log)
+      mysql_log.open_query_log(sys_var_general_log_path.value);
 
-    thd      The thread to take the binlog data from
-    pos      Position to truncate to
+    /*
+      Check the backup log options and open if they are turned on.
+    */
+    if (opt_backup_history_log)
+      mysql_backup_history_log.open_backup_history_log(
+        sys_var_backup_history_log_path.value);
+
+    if (opt_backup_progress_log)
+      mysql_backup_progress_log.open_backup_progress_log(
+        sys_var_backup_progress_log_path.value);
 
-  DESCRIPTION
+    is_initialized= TRUE;
+  }
 
-    Truncate the binary log to the given position. Will not change
-    anything else.
+  return FALSE;
+}
 
- */
-static void
-binlog_trans_log_truncate(THD *thd, my_off_t pos)
+/**
+  Close and reopen the backup logs.
+*/
+void Log_to_file_event_handler::flush_backup_logs()
 {
-  DBUG_ENTER("binlog_trans_log_truncate");
-  DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
-
-  DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
-  /* Only true if binlog_trans_log_savepos() wasn't called before */
-  DBUG_ASSERT(pos != ~(my_off_t) 0);
+  /* 
+    reopen log files 
 
-  binlog_trx_data *const trx_data=
-    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-  trx_data->truncate(pos);
-  DBUG_VOID_RETURN;
+    Where TRUE means perform open on history file (backup_history) and
+    FALSE means perform open on the progress file (backup_progress).
+  */
+  mysql_backup_history_log.reopen_file(TRUE);
+  mysql_backup_progress_log.reopen_file(FALSE);
 }
 
+/**
+  Purge the backup logs of all data.
 
-/*
-  this function is mostly a placeholder.
-  conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
-  should be moved here.
+  This method truncates the backup log files. It will only do so if the
+  log_backup_output_options includes logging to FILE. It also checks to
+  see if each log is turned on (backup_history and backup_progress) and
+  if so, truncates it. Truncate in this case means resetting the size
+  to 0 bytes using my_chsize().
+
+  @returns TRUE = success, FALSE = failed.
 */
+bool Log_to_file_event_handler::purge_backup_logs()
+{
+  bool res= FALSE;
 
-int binlog_init(void *p)
+  if (opt_backup_history_log && (log_backup_output_options & LOG_FILE))
+  {
+    MYSQL_BACKUP_LOG *backup_log= logger.get_backup_history_log_file_handler();
+
+    pthread_mutex_lock(backup_log->get_log_lock());
+    res= my_sync(backup_log->get_file(), MYF(MY_WME));
+    if (!res)
+      res= my_chsize(backup_log->get_file(), 0, 0, MYF(MY_WME));
+    pthread_mutex_unlock(backup_log->get_log_lock());
+    if (res)
+      goto err;
+  }
+  if (opt_backup_progress_log && (log_backup_output_options & LOG_FILE))
+  {
+    MYSQL_BACKUP_LOG *backup_log= logger.get_backup_progress_log_file_handler();
+
+    pthread_mutex_lock(backup_log->get_log_lock());
+    res= my_sync(backup_log->get_file(), MYF(MY_WME));
+    if (!res)
+      res= my_chsize(backup_log->get_file(), 0, 0, MYF(MY_WME));
+    pthread_mutex_unlock(backup_log->get_log_lock());
+    if (res)
+      goto err;
+  }
+err:
+  return res;
+}
+
+void Log_to_file_event_handler::cleanup()
 {
-  binlog_hton= (handlerton *)p;
-  binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
-  binlog_hton->db_type=DB_TYPE_BINLOG;
-  binlog_hton->savepoint_offset= sizeof(my_off_t);
-  binlog_hton->close_connection= binlog_close_connection;
-  binlog_hton->savepoint_set= binlog_savepoint_set;
-  binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
-  binlog_hton->commit= binlog_commit;
-  binlog_hton->rollback= binlog_rollback;
-  binlog_hton->prepare= binlog_prepare;
-  binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
-  return 0;
+  mysql_log.cleanup();
+  mysql_slow_log.cleanup();
+  mysql_backup_history_log.cleanup();
+  mysql_backup_progress_log.cleanup();
 }
 
-static int binlog_close_connection(handlerton *hton, THD *thd)
+void Log_to_file_event_handler::flush()
 {
-  binlog_trx_data *const trx_data=
-    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-  DBUG_ASSERT(trx_data->empty());
-  thd_set_ha_data(thd, binlog_hton, NULL);
-  trx_data->~binlog_trx_data();
-  my_free((uchar*)trx_data, MYF(0));
-  return 0;
+  /* reopen log files */
+  if (opt_log)
+    mysql_log.reopen_file();
+  if (opt_slow_log)
+    mysql_slow_log.reopen_file();
 }
 
 /*
-  End a transaction.
+  Log error with all enabled log event handlers
 
   SYNOPSIS
-    binlog_end_trans()
-
-    thd      The thread whose transaction should be ended
-    trx_data Pointer to the transaction data to use
-    end_ev   The end event to use, or NULL
-    all      True if the entire transaction should be ended, false if
-             only the statement transaction should be ended.
+    error_log_print()
 
-  DESCRIPTION
+    level             The level of the error significance: NOTE,
+                      WARNING or ERROR.
+    format            format string for the error message
+    args              list of arguments for the format string
 
-    End the currently open transaction. The transaction can be either
-    a real transaction (if 'all' is true) or a statement transaction
-    (if 'all' is false).
+  RETURN
+    FALSE - OK
+    TRUE - error occured
+*/
 
-    If 'end_ev' is NULL, the transaction is a rollback of only
-    transactional tables, so the transaction cache will be truncated
-    to either just before the last opened statement transaction (if
-    'all' is false), or reset completely (if 'all' is true).
- */
-static int
-binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
-                 Log_event *end_ev, bool all)
+bool LOGGER::error_log_print(enum loglevel level, const char *format,
+                             va_list args)
 {
-  DBUG_ENTER("binlog_end_trans");
-  int error=0;
-  IO_CACHE *trans_log= &trx_data->trans_log;
-  DBUG_PRINT("enter", ("transaction: %s  end_ev: 0x%lx",
-                       all ? "all" : "stmt", (long) end_ev));
-  DBUG_PRINT("info", ("thd->options={ %s%s}",
-                      FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
-                      FLAGSTR(thd->options, OPTION_BEGIN)));
+  bool error= FALSE;
+  Log_event_handler **current_handler;
 
-  /*
-    NULL denotes ROLLBACK with nothing to replicate: i.e., rollback of
-    only transactional tables.  If the transaction contain changes to
-    any non-transactiona tables, we need write the transaction and log
-    a ROLLBACK last.
-  */
-  if (end_ev != NULL)
-  {
-    if (thd->binlog_flush_pending_rows_event(TRUE))
-      DBUG_RETURN(1);
-    /*
-      Doing a commit or a rollback including non-transactional tables,
-      i.e., ending a transaction where we might write the transaction
-      cache to the binary log.
+  /* currently we don't need locking here as there is no error_log table */
+  for (current_handler= error_log_handler_list ; *current_handler ;)
+    error= (*current_handler++)->log_error(level, format, args) || error;
 
-      We can always end the statement when ending a transaction since
-      transactions are not allowed inside stored functions.  If they
-      were, we would have to ensure that we're not ending a statement
-      inside a stored function.
-     */
-    error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev,
-                               trx_data->has_incident());
-    trx_data->reset();
+  return error;
+}
 
-    /*
-      We need to step the table map version after writing the
-      transaction cache to disk.
-    */
-    mysql_bin_log.update_table_map_version();
-    statistic_increment(binlog_cache_use, &LOCK_status);
-    if (trans_log->disk_writes != 0)
-    {
-      statistic_increment(binlog_cache_disk_use, &LOCK_status);
-      trans_log->disk_writes= 0;
-    }
-  }
-  else
+
+void LOGGER::cleanup_base()
+{
+  DBUG_ASSERT(inited == 1);
+  rwlock_destroy(&LOCK_logger);
+  if (table_log_handler)
   {
-    /*
-      If rolling back an entire transaction or a single statement not
-      inside a transaction, we reset the transaction cache.
+    table_log_handler->cleanup();
+    delete table_log_handler;
+    table_log_handler= NULL;
+  }
+  if (file_log_handler)
+    file_log_handler->cleanup();
+}
 
-      If rolling back a statement in a transaction, we truncate the
-      transaction cache to remove the statement.
-     */
-    thd->binlog_remove_pending_rows_event(TRUE);
-    if (all || !thd->in_multi_stmt_transaction())
-    {
-      if (trx_data->has_incident())
-        mysql_bin_log.write_incident(thd, TRUE);
-      trx_data->reset();
-    }
-    else                                        // ...statement
-      trx_data->truncate(trx_data->before_stmt_pos);
 
-    /*
-      We need to step the table map version on a rollback to ensure
-      that a new table map event is generated instead of the one that
-      was written to the thrown-away transaction cache.
-    */
-    mysql_bin_log.update_table_map_version();
+void LOGGER::cleanup_end()
+{
+  DBUG_ASSERT(inited == 1);
+  if (file_log_handler)
+  {
+    delete file_log_handler;
+    file_log_handler=NULL;
   }
-
-  DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
-  DBUG_RETURN(error);
+  inited= 0;
 }
 
-static int binlog_prepare(handlerton *hton, THD *thd, bool all)
+
+/**
+  Perform basic log initialization: create file-based log handler and
+  init error log.
+*/
+void LOGGER::init_base()
 {
+  DBUG_ASSERT(inited == 0);
+  inited= 1;
+
   /*
-    do nothing.
-    just pretend we can do 2pc, so that MySQL won't
-    switch to 1pc.
-    real work will be done in MYSQL_BIN_LOG::log_xid()
+    Here we create file log handler. We don't do it for the table log handler
+    here as it cannot be created so early. The reason is THD initialization,
+    which depends on the system variables (parsed later).
   */
-  return 0;
-}
+  if (!file_log_handler)
+    file_log_handler= new Log_to_file_event_handler;
 
-/**
-  This function is called once after each statement.
+  /* by default we use traditional error log */
+  init_error_log(LOG_FILE);
 
-  It has the responsibility to flush the transaction cache to the
-  binlog file on commits.
+  file_log_handler->init_pthread_objects();
+  my_rwlock_init(&LOCK_logger, NULL);
+}
 
-  @param hton  The binlog handlerton.
-  @param thd   The client thread that executes the transaction.
-  @param all   This is @c true if this is a real transaction commit, and
-               @false otherwise.
 
-  @see handlerton::commit
-*/
-static int binlog_commit(handlerton *hton, THD *thd, bool all)
+void LOGGER::init_log_tables()
 {
-  int error= 0;
-  DBUG_ENTER("binlog_commit");
-  binlog_trx_data *const trx_data=
-    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-
-  if (trx_data->empty())
-  {
-    // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid()
-    trx_data->reset();
-    DBUG_RETURN(0);
-  }
+  if (!table_log_handler)
+    table_log_handler= new Log_to_csv_event_handler;
 
-  /*
-    We commit the transaction if:
+  if (!is_log_tables_initialized &&
+      !table_log_handler->init() && !file_log_handler->init())
+    is_log_tables_initialized= TRUE;
+}
 
-     - We are not in a transaction and committing a statement, or
 
-     - We are in a transaction and a full transaction is committed
+bool LOGGER::flush_logs(THD *thd)
+{
+  int rc= 0;
 
-    Otherwise, we accumulate the statement
+  /*
+    Now we lock logger, as nobody should be able to use logging routines while
+    log tables are closed
   */
-  bool const in_transaction= thd->in_multi_stmt_transaction();
-  DBUG_PRINT("debug",
-             ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
-              all,
-              YESNO(trx_data->empty()),
-              YESNO(in_transaction),
-              YESNO(thd->transaction.all.modified_non_trans_table),
-              YESNO(thd->transaction.stmt.modified_non_trans_table)));
-  if (!in_transaction || all ||
-      (!all && !trx_data->at_least_one_stmt_committed &&
-      !stmt_has_updated_trans_table(thd) &&
-      thd->transaction.stmt.modified_non_trans_table))
-  {
-    Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, 0);
-    error= binlog_end_trans(thd, trx_data, &qev, all);
-  }
+  logger.lock_exclusive();
 
-  trx_data->at_least_one_stmt_committed = my_b_tell(&trx_data->trans_log) > 0;
+  /* reopen log files */
+  file_log_handler->flush();
 
-  if (!all)
-    trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit
-  DBUG_RETURN(error);
+  /* end of log flush */
+  logger.unlock();
+  return rc;
 }
 
-/**
-  This function is called when a transaction involving a transactional
-  table is rolled back.
 
-  It has the responsibility to flush the transaction cache to the
-  binlog file. However, if the transaction does not involve
-  non-transactional tables, nothing needs to be logged.
+/**
+  Close and reopen the backup logs (with locks).
 
-  @param hton  The binlog handlerton.
-  @param thd   The client thread that executes the transaction.
-  @param all   This is @c true if this is a real transaction rollback, and
-               @false otherwise.
+  @param[IN]  thd   The current thread.
 
-  @see handlerton::rollback
+  @returns FALSE.
 */
-static int binlog_rollback(handlerton *hton, THD *thd, bool all)
+bool LOGGER::flush_backup_logs(THD *thd)
 {
-  DBUG_ENTER("binlog_rollback");
-  int error=0;
-  binlog_trx_data *const trx_data=
-    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+  int rc= 0;
 
-  if (trx_data->empty()) {
-    trx_data->reset();
-    DBUG_RETURN(0);
-  }
+  /*
+    Now we lock logger, as nobody should be able to use logging routines while
+    log tables are closed
+  */
+  logger.lock_exclusive();
 
-  DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
-                       YESNO(all),
-                       YESNO(thd->transaction.all.modified_non_trans_table),
-                       YESNO(thd->transaction.stmt.modified_non_trans_table)));
-  if (mysql_bin_log.check_write_error(thd))
-  {
-    /*
-      "all == true" means that a "rollback statement" triggered the error and
-      this function was called. However, this must not happen as a rollback
-      is written directly to the binary log. And in auto-commit mode, a single
-      statement that is rolled back has the flag all == false.
-    */
-    DBUG_ASSERT(!all);
-    /*
-      We reach this point if either only transactional tables were modified or
-      the effect of a statement that did not get into the binlog needs to be
-      rolled back. In the latter case, if a statement changed non-transactional
-      tables or had the OPTION_KEEP_LOG associated, we write an incident event
-      to the binlog in order to stop slaves and notify users that some changes
-      on the master did not get into the binlog and slaves will be inconsistent.
-      On the other hand, if a statement is transactional, we just safely roll it
-      back.
-    */
-    if ((thd->transaction.stmt.modified_non_trans_table ||
-        (thd->options & OPTION_KEEP_LOG)) &&
-        mysql_bin_log.check_write_error(thd))
-      trx_data->set_incident();
-    error= binlog_end_trans(thd, trx_data, 0, all);
-  }
-  else
-  {
-   /*
-      We flush the cache with a rollback, wrapped in a beging/rollback if:
-        . aborting a transaction that modified a non-transactional table;
-        . aborting a statement that modified both transactional and
-          non-transactional tables but which is not in the boundaries of any
-          transaction or there was no early change;
-        . the OPTION_KEEP_LOG is activate.
-    */
-    if ((all && thd->transaction.all.modified_non_trans_table) ||
-        (!all && thd->transaction.stmt.modified_non_trans_table &&
-         !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) ||
-        (!all && thd->transaction.stmt.modified_non_trans_table &&
-         !trx_data->at_least_one_stmt_committed &&
-         thd->current_stmt_binlog_row_based) ||
-        ((thd->options & OPTION_KEEP_LOG)))
-    {
-      Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
-      error= binlog_end_trans(thd, trx_data, &qev, all);
-    }
-    /*
-      Otherwise, we simply truncate the cache as there is no change on
-      non-transactional tables as follows.
-    */
-    else if ((all && !thd->transaction.all.modified_non_trans_table) ||
-          (!all && !thd->transaction.stmt.modified_non_trans_table))
-      error= binlog_end_trans(thd, trx_data, 0, all);
-  }
-  if (!all)
-    trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
-  DBUG_RETURN(error);
+  /* reopen log files */
+  file_log_handler->flush_backup_logs();
+
+  /* end of log flush */
+  logger.unlock();
+  return rc;
 }
 
-void MYSQL_BIN_LOG::set_write_error(THD *thd)
-{
-  DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
+/**
+  Delete contents of backup logs.
 
-  write_error= 1;
+  This method deletes the data from the backup logs. This applies to both
+  log file and table destinations.
 
-  if (check_write_error(thd))
-    DBUG_VOID_RETURN;
+  @param[IN]  thd  The current thread.
 
-  if (my_errno == EFBIG)
-    my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
-  else
-    my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
+  @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs(THD *thd)
+{
+  my_bool res= FALSE;
 
-  DBUG_VOID_RETURN;
+  DBUG_ENTER("LOGGER::purge_backup_logs");
+  /*
+    Here we need to truncate the files if writing to files.
+  */
+  res= file_log_handler->purge_backup_logs();
+  if (res)
+    goto err;
+
+  /*
+    We also need to truncate the table if writing to tables.
+  */
+  res= table_log_handler->purge_backup_logs(thd);
+
+err:
+  DBUG_RETURN(res);
 }
 
-bool MYSQL_BIN_LOG::check_write_error(THD *thd)
-{
-  DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
+/**
+  Delete contents of backup logs where backup id is less than a given id.
 
-  bool checked= FALSE;
+  This method deletes the data from the backup logs where a backup id is 
+  less than the backup id specified. This applies only to table destinations.
 
-  if (!thd->is_error())
-    DBUG_RETURN(checked);
+  @param[IN]  thd        The current thread.
+  @param[IN]  backup_id  The backup id to compare rows to.
+  @param[OUT] rows       The number of rows affected.
 
-  switch (thd->stmt_da->sql_errno())
-  {
-    case ER_TRANS_CACHE_FULL:
-    case ER_ERROR_ON_WRITE:
-    case ER_BINLOG_LOGGING_IMPOSSIBLE:
-      checked= TRUE;
-    break;
-  }
+  @retval  num  The number of rows affected.
 
-  DBUG_RETURN(checked);
+  @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs_before_id(THD *thd, 
+                                         ulonglong backup_id, 
+                                         int *rows)
+{
+  my_bool res= FALSE;
+
+  DBUG_ENTER("LOGGER::purge_backup_logs_before_id");
+
+  res= table_log_handler->purge_backup_logs_before_id(thd, backup_id, rows);
+
+  DBUG_RETURN(res);
 }
 
 /**
-  @note
-  How do we handle this (unlikely but legal) case:
-  @verbatim
-    [transaction] + [update to non-trans table] + [rollback to savepoint] ?
-  @endverbatim
-  The problem occurs when a savepoint is before the update to the
-  non-transactional table. Then when there's a rollback to the savepoint, if we
-  simply truncate the binlog cache, we lose the part of the binlog cache where
-  the update is. If we want to not lose it, we need to write the SAVEPOINT
-  command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
-  is easy: it's just write at the end of the binlog cache, but the former
-  should be *inserted* to the place where the user called SAVEPOINT. The
-  solution is that when the user calls SAVEPOINT, we write it to the binlog
-  cache (so no need to later insert it). As transactions are never intermixed
-  in the binary log (i.e. they are serialized), we won't have conflicts with
-  savepoint names when using mysqlbinlog or in the slave SQL thread.
-  Then when ROLLBACK TO SAVEPOINT is called, if we updated some
-  non-transactional table, we don't truncate the binlog cache but instead write
-  ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
-  will chop the SAVEPOINT command from the binlog cache, which is good as in
-  that case there is no need to have it in the binlog).
-*/
+  Delete backup rows less than a certain date.

 
-static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
-{
-  DBUG_ENTER("binlog_savepoint_set");
+  This method scans the backup history log and deletes the rows for those
+  operations that were created (start column) previous to the date specified in 
+  @c t.
 
-  binlog_trans_log_savepos(thd, (my_off_t*) sv);
-  /* Write it to the binary log */
+  @param[IN]  thd        The current thread.
+  @param[IN]  t          The date to compare rows to.
+  @param[OUT] rows       The number of rows affected.
 
-  int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
-  int const error=
-    thd->binlog_query(THD::STMT_QUERY_TYPE,
-                      thd->query(), thd->query_length(), TRUE, FALSE, errcode);
-  DBUG_RETURN(error);
-}
+  @retval  rows  The number of rows affected.
 
-static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
+  @returns TRUE if error.
+*/
+bool LOGGER::purge_backup_logs_before_date(THD *thd, 
+                                           my_time_t t, 
+                                           int *rows)
 {
-  DBUG_ENTER("binlog_savepoint_rollback");
+  my_bool res= FALSE;
 
-  /*
-    Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
-    non-transactional table. Otherwise, truncate the binlog cache starting
-    from the SAVEPOINT command.
-  */
-  if (unlikely(thd->transaction.all.modified_non_trans_table || 
-               (thd->options & OPTION_KEEP_LOG)))
-  {
-    int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
-    int error=
-      thd->binlog_query(THD::STMT_QUERY_TYPE,
-                        thd->query(), thd->query_length(), TRUE, FALSE, errcode);
-    DBUG_RETURN(error);
-  }
-  binlog_trans_log_truncate(thd, *(my_off_t*)sv);
-  DBUG_RETURN(0);
+  DBUG_ENTER("LOGGER::purge_backup_logs_before_date");
+
+  res= table_log_handler->purge_backup_logs_before_date(thd, t, rows);
+
+  DBUG_RETURN(res);
 }
 
+/*
+  Log slow query with all enabled log event handlers
 
-int check_binlog_magic(IO_CACHE* log, const char** errmsg)
-{
-  char magic[4];
-  DBUG_ASSERT(my_b_tell(log) == 0);
+  SYNOPSIS
+    slow_log_print()
 
-  if (my_b_read(log, (uchar*) magic, sizeof(magic)))
-  {
-    *errmsg = "I/O error reading the header from the binary log";
-    sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
-		    log->error);
-    return 1;
-  }
-  if (memcmp(magic, BINLOG_MAGIC, sizeof(magic)))
-  {
-    *errmsg = "Binlog has bad magic number;  It's not a binary log file that can be used by this version of MySQL";
-    return 1;
-  }
-  return 0;
-}
+    thd                 THD of the query being logged
+    query               The query being logged
+    query_length        The length of the query string
+    current_utime       Current time in microseconds (from undefined start)
 
+  RETURN
+    FALSE   OK
+    TRUE    error occured
+*/
 
-File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
-{
-  File file;
-  DBUG_ENTER("open_binlog");
+bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
+                            ulonglong current_utime)
 
-  if ((file = my_open(log_file_name, O_RDONLY | O_BINARY | O_SHARE, 
-                      MYF(MY_WME))) < 0)
-  {
-    sql_print_error("Failed to open log (file '%s', errno %d)",
-                    log_file_name, my_errno);
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler;
+  bool is_command= FALSE;
+  char user_host_buff[MAX_USER_HOST_SIZE + 1];
+  Security_context *sctx= thd->security_ctx;
+  uint user_host_len= 0;
+  ulonglong query_utime, lock_utime;
+
+  DBUG_ASSERT(thd->enable_slow_log);
+  /*
+    Print the message to the buffer if we have slow log enabled
+  */
+
+  if (*slow_log_handler_list)
+  {
+    time_t current_time;
+
+    /* do not log slow queries from replication threads */
+    if (thd->slave_thread && !opt_log_slow_slave_statements)
+      return 0;
+
+    lock_shared();
+    if (!opt_slow_log)
+    {
+      unlock();
+      return 0;
+    }
+
+    /* fill in user_host value: the format is "%s[%s] @ %s [%s]" */
+    user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
+                             sctx->priv_user ? sctx->priv_user : "", "[",
+                             sctx->user ? sctx->user : "", "] @ ",
+                             sctx->host ? sctx->host : "", " [",
+                             sctx->ip ? sctx->ip : "", "]", NullS) -
+                    user_host_buff);
+
+    current_time= my_time_possible_from_micro(current_utime);
+    if (thd->start_utime)
+    {
+      query_utime= (current_utime - thd->start_utime);
+      lock_utime=  (thd->utime_after_lock - thd->start_utime);
+    }
+    else
+    {
+      query_utime= lock_utime= 0;
+    }
+
+    if (!query)
+    {
+      is_command= TRUE;
+      query= command_name[thd->command].str;
+      query_length= command_name[thd->command].length;
+    }
+
+    for (current_handler= slow_log_handler_list; *current_handler ;)
+      error= (*current_handler++)->log_slow(thd, current_time, thd->start_time,
+                                            user_host_buff, user_host_len,
+                                            query_utime, lock_utime, is_command,
+                                            query, query_length) || error;
+
+    unlock();
+  }
+  return error;
+}
+
+bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
+                               const char *query, uint query_length)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= general_log_handler_list;
+  char user_host_buff[MAX_USER_HOST_SIZE + 1];
+  Security_context *sctx= thd->security_ctx;
+  uint user_host_len= 0;
+  time_t current_time;
+
+  DBUG_ASSERT(thd);
+
+  lock_shared();
+  if (!opt_log)
+  {
+    unlock();
+    return 0;
+  }
+  user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
+                          sctx->priv_user ? sctx->priv_user : "", "[",
+                          sctx->user ? sctx->user : "", "] @ ",
+                          sctx->host ? sctx->host : "", " [",
+                          sctx->ip ? sctx->ip : "", "]", NullS) -
+                                                          user_host_buff;
+
+  current_time= my_time(0);
+  while (*current_handler)
+    error|= (*current_handler++)->
+      log_general(thd, current_time, user_host_buff,
+                  user_host_len, thd->thread_id,
+                  command_name[(uint) command].str,
+                  command_name[(uint) command].length,
+                  query, query_length,
+                  thd->variables.character_set_client) || error;
+  unlock();
+
+  return error;
+}
+
+bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
+                               const char *format, va_list args)
+{
+  uint message_buff_len= 0;
+  char message_buff[MAX_LOG_BUFFER_SIZE];
+
+  /* prepare message */
+  if (format)
+    message_buff_len= my_vsnprintf(message_buff, sizeof(message_buff),
+                                   format, args);
+  else
+    message_buff[0]= '\0';
+
+  return general_log_write(thd, command, message_buff, message_buff_len);
+}
+
+/**
+  Write the backup log entry for the backup history logs (file or table).
+
+  This method creates a new row in the backup history log with the
+  information provided. It is a high-level wrapper for writing to any
+  of the log types (e.g., FILE or TABLE) as specified by --log-option.
+
+  @Note The backup logs currently only write to tables.
+
+  @param[IN]   thd   The current thread
+  @param[IN]   st_backup_history   Data to write to log.
+  
+  @returns TRUE if error.
+*/
+bool LOGGER::backup_history_log_write(THD *thd, 
+                                      st_backup_history *history_data)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= backup_history_log_handler_list;
+  ulong id;
+
+  /*
+    Don't write if log is turned off.
+  */
+  if (!opt_backup_history_log)
+    return 0;
+
+  if (thd)
+    id= thd->thread_id;                 /* Normal thread */
+  else
+    id= 0;                              /* Log from connect handler */
+
+  lock_shared();
+  while (*current_handler)
+    error|= (*current_handler++)->
+      log_backup_history(thd, history_data) || error;
+  unlock();
+
+  return error;
+}
+
+/**
+  Write the backup log entry for the backup progress logs (file or table).
+
+  This method creates a new row in the backup progress log with the
+  information provided. It is a high-level wrapper for writing to any
+  of the log types (e.g., FILE or TABLE) as specified by --log-option.
+
+  @Note The backup logs currently only write to tables.
+
+  @param[IN]   thd         The current thread
+  @param[OUT]  backup_id   The new row id for the backup history
+  @param[IN]   object      The name of the object processed
+  @param[IN]   start       Start datetime
+  @param[IN]   stop        Stop datetime
+  @param[IN]   size        Size value
+  @param[IN]   progress    Progress (percent)
+  @param[IN]   error_num   Error number (should be 0 is success)
+  @param[IN]   notes       Misc data from engine
+
+  @returns TRUE if error.
+*/
+bool LOGGER::backup_progress_log_write(THD *thd,
+                                       ulonglong backup_id,
+                                       const char *object,
+                                       time_t start,
+                                       time_t stop,
+                                       longlong size,
+                                       longlong progress,
+                                       int error_num,
+                                       const char *notes)
+{
+  bool error= FALSE;
+  Log_event_handler **current_handler= backup_progress_log_handler_list;
+  ulong id;
+
+  /*
+    Don't write if log is turned off.
+  */
+  if (!opt_backup_progress_log)
+    return 0;
+
+  if (thd)
+    id= thd->thread_id;                 /* Normal thread */
+  else
+    id= 0;                              /* Log from connect handler */
+
+
+  lock_shared();
+  while (*current_handler)
+    error|= (*current_handler++)->
+      log_backup_progress(thd, backup_id, object, start, 
+                             stop, size, progress, error_num, notes) || error;
+  unlock();
+
+  return error;
+}
+
+
+void LOGGER::init_error_log(uint error_log_printer)
+{
+  if (error_log_printer & LOG_NONE)
+  {
+    error_log_handler_list[0]= 0;
+    return;
+  }
+
+  switch (error_log_printer) {
+  case LOG_FILE:
+    error_log_handler_list[0]= file_log_handler;
+    error_log_handler_list[1]= 0;
+    break;
+    /* these two are disabled for now */
+  case LOG_TABLE:
+    DBUG_ASSERT(0);
+    break;
+  case LOG_TABLE|LOG_FILE:
+    DBUG_ASSERT(0);
+    break;
+  }
+}
+
+void LOGGER::init_slow_log(uint slow_log_printer)
+{
+  if (slow_log_printer & LOG_NONE)
+  {
+    slow_log_handler_list[0]= 0;
+    return;
+  }
+
+  switch (slow_log_printer) {
+  case LOG_FILE:
+    slow_log_handler_list[0]= file_log_handler;
+    slow_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE:
+    slow_log_handler_list[0]= table_log_handler;
+    slow_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE|LOG_FILE:
+    slow_log_handler_list[0]= file_log_handler;
+    slow_log_handler_list[1]= table_log_handler;
+    slow_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+void LOGGER::init_general_log(uint general_log_printer)
+{
+  if (general_log_printer & LOG_NONE)
+  {
+    general_log_handler_list[0]= 0;
+    return;
+  }
+
+  switch (general_log_printer) {
+  case LOG_FILE:
+    general_log_handler_list[0]= file_log_handler;
+    general_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE:
+    general_log_handler_list[0]= table_log_handler;
+    general_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE|LOG_FILE:
+    general_log_handler_list[0]= file_log_handler;
+    general_log_handler_list[1]= table_log_handler;
+    general_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+
+/**
+  Initialize the backup history log.
+
+  This method initializes the backup log handlers. Currently, only
+  log to table is supported.
+
+  @param[IN]   backup_history_log_printer  The output type for log.
+*/
+void LOGGER::init_backup_history_log(uint backup_history_log_printer)
+{
+  if (backup_history_log_printer & LOG_NONE)
+  {
+    backup_history_log_handler_list[0]= 0;
+    return;
+  }
+
+  switch (backup_history_log_printer) {
+  case LOG_FILE:
+    backup_history_log_handler_list[0]= file_log_handler;
+    backup_history_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE:
+    backup_history_log_handler_list[0]= table_log_handler;
+    backup_history_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE|LOG_FILE:
+    backup_history_log_handler_list[0]= file_log_handler;
+    backup_history_log_handler_list[1]= table_log_handler;
+    backup_history_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+/**
+  Initialize the backup progress log.
+
+  This method initializes the backup log handlers. Currently, only
+  log to table is supported.
+
+  @param[IN]   backup_history_log_printer  The output type for log.
+*/
+void LOGGER::init_backup_progress_log(uint backup_progress_log_printer)
+{
+  if (backup_progress_log_printer & LOG_NONE)
+  {
+    backup_progress_log_handler_list[0]= 0;
+    return;
+  }
+
+  switch (backup_progress_log_printer) {
+  case LOG_FILE:
+    backup_progress_log_handler_list[0]= file_log_handler;
+    backup_progress_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE:
+    backup_progress_log_handler_list[0]= table_log_handler;
+    backup_progress_log_handler_list[1]= 0;
+    break;
+  case LOG_TABLE|LOG_FILE:
+    backup_progress_log_handler_list[0]= file_log_handler;
+    backup_progress_log_handler_list[1]= table_log_handler;
+    backup_progress_log_handler_list[2]= 0;
+    break;
+  }
+}
+
+
+bool LOGGER::activate_log_handler(THD* thd, uint log_type)
+{
+  MYSQL_QUERY_LOG *file_log;
+  MYSQL_BACKUP_LOG *backup_log;
+  bool res= FALSE;
+  lock_exclusive();
+  switch (log_type) {
+  case QUERY_LOG_SLOW:
+    if (!opt_slow_log)
+    {
+      file_log= file_log_handler->get_mysql_slow_log();
+
+      file_log->open_slow_log(sys_var_slow_log_path.value);
+      if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW))
+      {
+        /* Error printed by open table in activate_log() */
+        res= TRUE;
+        file_log->close(0);
+      }
+      else
+      {
+        init_slow_log(log_output_options);
+        opt_slow_log= TRUE;
+      }
+    }
+    break;
+  case QUERY_LOG_GENERAL:
+    if (!opt_log)
+    {
+      file_log= file_log_handler->get_mysql_log();
+
+      file_log->open_query_log(sys_var_general_log_path.value);
+      if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL))
+      {
+        /* Error printed by open table in activate_log() */
+        res= TRUE;
+        file_log->close(0);
+      }
+      else
+      {
+        init_general_log(log_output_options);
+        opt_log= TRUE;
+      }
+    }
+    break;
+ /*
+    Check the backup history and progress logs for activation.
+  */
+  case BACKUP_HISTORY_LOG:
+  {
+    if (!opt_backup_history_log)
+    {
+      backup_log= file_log_handler->get_backup_history_log();
+
+      backup_log->open_backup_history_log(sys_var_backup_history_log_path.value);
+      if (table_log_handler->activate_log(thd, BACKUP_HISTORY_LOG))
+      {
+        /* Error printed by open table in activate_log() */
+        res= TRUE;
+        backup_log->close(0);
+      }
+      else
+      {
+        init_backup_history_log(log_backup_output_options);
+        opt_backup_history_log= TRUE;
+      }
+    }
+    break;
+  }
+  case BACKUP_PROGRESS_LOG:
+  {
+    if (!opt_backup_progress_log)
+    {
+      backup_log= file_log_handler->get_backup_progress_log();
+
+      backup_log->open_backup_progress_log(sys_var_backup_progress_log_path.value);
+      if (table_log_handler->activate_log(thd, BACKUP_PROGRESS_LOG))
+      {
+        /* Error printed by open table in activate_log() */
+        res= TRUE;
+        backup_log->close(0);
+      }
+      else
+      {
+        init_backup_progress_log(log_backup_output_options);
+        opt_backup_progress_log= TRUE;
+      }
+    }
+    break;
+  }
+  default:
+    DBUG_ASSERT(0);
+  }
+  unlock();
+  return res;
+}
+
+
+void LOGGER::deactivate_log_handler(THD *thd, uint log_type)
+{
+  my_bool *tmp_opt= 0;
+  MYSQL_LOG *file_log;
+
+  switch (log_type) {
+  case QUERY_LOG_SLOW:
+    tmp_opt= &opt_slow_log;
+    file_log= file_log_handler->get_mysql_slow_log();
+    break;
+  case QUERY_LOG_GENERAL:
+    tmp_opt= &opt_log;
+    file_log= file_log_handler->get_mysql_log();
+    break;
+  /*
+    Deactivate the backup history and progress logs on request.
+  */
+  case BACKUP_HISTORY_LOG:
+    tmp_opt= &opt_backup_history_log;
+    file_log= file_log_handler->get_backup_history_log();
+    break;
+  case BACKUP_PROGRESS_LOG:
+    tmp_opt= &opt_backup_progress_log;
+    file_log= file_log_handler->get_backup_progress_log();
+    break;
+  default:
+    assert(0);                                  // Impossible
+  }
+
+  if (!(*tmp_opt))
+    return;
+
+  lock_exclusive();
+  file_log->close(0);
+  *tmp_opt= FALSE;
+  unlock();
+}
+
+
+/* the parameters are unused for the log tables */
+bool Log_to_csv_event_handler::init()
+{
+  return 0;
+}
+
+int LOGGER::set_handlers(uint error_log_printer,
+                         uint slow_log_printer,
+                         uint general_log_printer)
+{
+  /* error log table is not supported yet */
+  DBUG_ASSERT(error_log_printer < LOG_TABLE);
+
+  lock_exclusive();
+
+  if ((slow_log_printer & LOG_TABLE || general_log_printer & LOG_TABLE) &&
+      !is_log_tables_initialized)
+  {
+    slow_log_printer= (slow_log_printer & ~LOG_TABLE) | LOG_FILE;
+    general_log_printer= (general_log_printer & ~LOG_TABLE) | LOG_FILE;
+
+    sql_print_error("Failed to initialize log tables. "
+                    "Falling back to the old-fashioned logs");
+  }
+
+  init_error_log(error_log_printer);
+  init_slow_log(slow_log_printer);
+  init_general_log(general_log_printer);
+
+  unlock();
+
+  return 0;
+}
+
+/** 
+    This function checks if a transactional talbe was updated by the
+    current statement.
+
+    @param thd The client thread that executed the current statement.
+    @return
+      @c true if a transactional table was updated, @false otherwise.
+*/
+static bool stmt_has_updated_trans_table(THD *thd)
+{
+  Ha_trx_info *ha_info;
+
+  for (ha_info= thd->transaction.stmt.ha_list; ha_info && ha_info->is_started(); ha_info= ha_info->next())
+  {
+    if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
+      return (TRUE);
+  }
+  return (FALSE);
+}
+
+/**
+  Set the logging handlers for operation on the backup logs.
+
+  This method allows the caller to set the method of logging
+  for the backup logs. Values for these variables equate
+  to the {FILE, TABLE, NONE} definitions.
+
+  @param[IN] backup_history_log_printer  The type of output.
+  @param[IN] backup_progress_log_printer The type of output.
+
+  @returns 0
+*/
+int LOGGER::set_backup_handlers(uint backup_history_log_printer,
+                                uint backup_progress_log_printer)
+{
+  lock_exclusive();
+
+  init_backup_history_log(backup_history_log_printer);
+  init_backup_progress_log(backup_progress_log_printer);
+
+  unlock();
+
+  return 0;
+}
+
+ /*
+  Save position of binary log transaction cache.
+
+  SYNPOSIS
+    binlog_trans_log_savepos()
+
+    thd      The thread to take the binlog data from
+    pos      Pointer to variable where the position will be stored
+
+  DESCRIPTION
+
+    Save the current position in the binary log transaction cache into
+    the variable pointed to by 'pos'
+ */
+
+static void
+binlog_trans_log_savepos(THD *thd, my_off_t *pos)
+{
+  DBUG_ENTER("binlog_trans_log_savepos");
+  DBUG_ASSERT(pos != NULL);
+  if (thd_get_ha_data(thd, binlog_hton) == NULL)
+    thd->binlog_setup_trx_data();
+  binlog_trx_data *const trx_data=
+    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+  DBUG_ASSERT(mysql_bin_log.is_open());
+  *pos= trx_data->position();
+  DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
+  DBUG_VOID_RETURN;
+}
+
+
+/*
+  Truncate the binary log transaction cache.
+
+  SYNPOSIS
+    binlog_trans_log_truncate()
+
+    thd      The thread to take the binlog data from
+    pos      Position to truncate to
+
+  DESCRIPTION
+
+    Truncate the binary log to the given position. Will not change
+    anything else.
+
+ */
+static void
+binlog_trans_log_truncate(THD *thd, my_off_t pos)
+{
+  DBUG_ENTER("binlog_trans_log_truncate");
+  DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
+
+  DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
+  /* Only true if binlog_trans_log_savepos() wasn't called before */
+  DBUG_ASSERT(pos != ~(my_off_t) 0);
+
+  binlog_trx_data *const trx_data=
+    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+  trx_data->truncate(pos);
+  DBUG_VOID_RETURN;
+}
+
+
+/*
+  this function is mostly a placeholder.
+  conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
+  should be moved here.
+*/
+
+int binlog_init(void *p)
+{
+  binlog_hton= (handlerton *)p;
+  binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
+  binlog_hton->db_type=DB_TYPE_BINLOG;
+  binlog_hton->savepoint_offset= sizeof(my_off_t);
+  binlog_hton->close_connection= binlog_close_connection;
+  binlog_hton->savepoint_set= binlog_savepoint_set;
+  binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
+  binlog_hton->commit= binlog_commit;
+  binlog_hton->rollback= binlog_rollback;
+  binlog_hton->prepare= binlog_prepare;
+  binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;

+  return 0;
+}
+
+static int binlog_close_connection(handlerton *hton, THD *thd)
+{
+  binlog_trx_data *const trx_data=
+    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+  DBUG_ASSERT(trx_data->empty());
+  thd_set_ha_data(thd, binlog_hton, NULL);
+  trx_data->~binlog_trx_data();
+  my_free((uchar*)trx_data, MYF(0));
+  return 0;
+}
+
+/*
+  End a transaction.
+
+  SYNOPSIS
+    binlog_end_trans()
+
+    thd      The thread whose transaction should be ended
+    trx_data Pointer to the transaction data to use
+    end_ev   The end event to use, or NULL
+    all      True if the entire transaction should be ended, false if
+             only the statement transaction should be ended.
+
+  DESCRIPTION
+
+    End the currently open transaction. The transaction can be either
+    a real transaction (if 'all' is true) or a statement transaction
+    (if 'all' is false).
+
+    If 'end_ev' is NULL, the transaction is a rollback of only
+    transactional tables, so the transaction cache will be truncated
+    to either just before the last opened statement transaction (if
+    'all' is false), or reset completely (if 'all' is true).
+ */
+static int
+binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
+                 Log_event *end_ev, bool all)
+{
+  DBUG_ENTER("binlog_end_trans");
+  int error=0;
+  IO_CACHE *trans_log= &trx_data->trans_log;
+  DBUG_PRINT("enter", ("transaction: %s  end_ev: 0x%lx",
+                       all ? "all" : "stmt", (long) end_ev));
+  DBUG_PRINT("info", ("thd->options={ %s%s}",
+                      FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
+                      FLAGSTR(thd->options, OPTION_BEGIN)));
+
+  /*
+    NULL denotes ROLLBACK with nothing to replicate: i.e., rollback of
+    only transactional tables.  If the transaction contain changes to
+    any non-transactiona tables, we need write the transaction and log
+    a ROLLBACK last.
+  */
+  if (end_ev != NULL)
+  {
+    if (thd->binlog_flush_pending_rows_event(TRUE))
+      DBUG_RETURN(1);
+    /*
+      Doing a commit or a rollback including non-transactional tables,
+      i.e., ending a transaction where we might write the transaction
+      cache to the binary log.
+
+      We can always end the statement when ending a transaction since
+      transactions are not allowed inside stored functions.  If they
+      were, we would have to ensure that we're not ending a statement
+      inside a stored function.
+     */
+    error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev,
+                               trx_data->has_incident());
+    trx_data->reset();
+
+    /*
+      We need to step the table map version after writing the
+      transaction cache to disk.
+    */
+    mysql_bin_log.update_table_map_version();
+    statistic_increment(binlog_cache_use, &LOCK_status);
+    if (trans_log->disk_writes != 0)
+    {
+      statistic_increment(binlog_cache_disk_use, &LOCK_status);
+      trans_log->disk_writes= 0;
+    }
+  }
+  else
+  {
+    /*
+      If rolling back an entire transaction or a single statement not
+      inside a transaction, we reset the transaction cache.
+
+      If rolling back a statement in a transaction, we truncate the
+      transaction cache to remove the statement.
+     */
+    thd->binlog_remove_pending_rows_event(TRUE);
+    if (all || !thd->in_multi_stmt_transaction())
+    {
+      if (trx_data->has_incident())
+        mysql_bin_log.write_incident(thd, TRUE);
+      trx_data->reset();
+    }
+    else                                        // ...statement
+      trx_data->truncate(trx_data->before_stmt_pos);
+
+    /*
+      We need to step the table map version on a rollback to ensure
+      that a new table map event is generated instead of the one that
+      was written to the thrown-away transaction cache.
+    */
+    mysql_bin_log.update_table_map_version();
+  }
+
+  DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
+  DBUG_RETURN(error);
+}
+
+static int binlog_prepare(handlerton *hton, THD *thd, bool all)
+{
+  /*
+    do nothing.
+    just pretend we can do 2pc, so that MySQL won't
+    switch to 1pc.
+    real work will be done in MYSQL_BIN_LOG::log_xid()
+  */
+  return 0;
+}
+
+/**
+  This function is called once after each statement.
+
+  It has the responsibility to flush the transaction cache to the
+  binlog file on commits.
+
+  @param hton  The binlog handlerton.
+  @param thd   The client thread that executes the transaction.
+  @param all   This is @c true if this is a real transaction commit, and
+               @false otherwise.
+
+  @see handlerton::commit
+*/
+static int binlog_commit(handlerton *hton, THD *thd, bool all)
+{
+  int error= 0;
+  DBUG_ENTER("binlog_commit");
+  binlog_trx_data *const trx_data=
+    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+
+  if (trx_data->empty())
+  {
+    // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid()
+    trx_data->reset();
+    DBUG_RETURN(0);
+  }
+
+  /*
+    We commit the transaction if:
+
+     - We are not in a transaction and committing a statement, or
+
+     - We are in a transaction and a full transaction is committed
+
+    Otherwise, we accumulate the statement
+  */
+  bool const in_transaction= thd->in_multi_stmt_transaction();
+  DBUG_PRINT("debug",
+             ("all: %d, empty: %s, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+              all,
+              YESNO(trx_data->empty()),
+              YESNO(in_transaction),
+              YESNO(thd->transaction.all.modified_non_trans_table),
+              YESNO(thd->transaction.stmt.modified_non_trans_table)));
+  if (!in_transaction || all ||
+      (!all && !trx_data->at_least_one_stmt_committed &&
+      !stmt_has_updated_trans_table(thd) &&
+      thd->transaction.stmt.modified_non_trans_table))
+  {
+    Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, 0);
+    error= binlog_end_trans(thd, trx_data, &qev, all);
+  }
+
+  trx_data->at_least_one_stmt_committed = my_b_tell(&trx_data->trans_log) > 0;
+
+  if (!all)
+    trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit
+  DBUG_RETURN(error);
+}
+
+/**
+  This function is called when a transaction involving a transactional
+  table is rolled back.
+
+  It has the responsibility to flush the transaction cache to the
+  binlog file. However, if the transaction does not involve
+  non-transactional tables, nothing needs to be logged.
+
+  @param hton  The binlog handlerton.
+  @param thd   The client thread that executes the transaction.
+  @param all   This is @c true if this is a real transaction rollback, and
+               @false otherwise.
+
+  @see handlerton::rollback
+*/
+static int binlog_rollback(handlerton *hton, THD *thd, bool all)
+{
+  DBUG_ENTER("binlog_rollback");
+  int error=0;
+  binlog_trx_data *const trx_data=
+    (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+
+  if (trx_data->empty()) {
+    trx_data->reset();
+    DBUG_RETURN(0);
+  }
+
+  DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+                       YESNO(all),
+                       YESNO(thd->transaction.all.modified_non_trans_table),
+                       YESNO(thd->transaction.stmt.modified_non_trans_table)));
+  if (mysql_bin_log.check_write_error(thd))
+  {
+    /*
+      "all == true" means that a "rollback statement" triggered the error and
+      this function was called. However, this must not happen as a rollback
+      is written directly to the binary log. And in auto-commit mode, a single
+      statement that is rolled back has the flag all == false.
+    */
+    DBUG_ASSERT(!all);
+    /*
+      We reach this point if either only transactional tables were modified or
+      the effect of a statement that did not get into the binlog needs to be
+      rolled back. In the latter case, if a statement changed non-transactional
+      tables or had the OPTION_KEEP_LOG associated, we write an incident event
+      to the binlog in order to stop slaves and notify users that some changes
+      on the master did not get into the binlog and slaves will be inconsistent.
+      On the other hand, if a statement is transactional, we just safely roll it
+      back.
+    */
+    if ((thd->transaction.stmt.modified_non_trans_table ||
+        (thd->options & OPTION_KEEP_LOG)) &&
+        mysql_bin_log.check_write_error(thd))
+      trx_data->set_incident();
+    error= binlog_end_trans(thd, trx_data, 0, all);
+  }
+  else
+  {
+   /*
+      We flush the cache with a rollback, wrapped in a beging/rollback if:
+        . aborting a transaction that modified a non-transactional table;
+        . aborting a statement that modified both transactional and
+          non-transactional tables but which is not in the boundaries of any
+          transaction or there was no early change;
+        . the OPTION_KEEP_LOG is activate.
+    */
+    if ((all && thd->transaction.all.modified_non_trans_table) ||
+        (!all && thd->transaction.stmt.modified_non_trans_table &&
+         !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))) ||
+        (!all && thd->transaction.stmt.modified_non_trans_table &&
+         !trx_data->at_least_one_stmt_committed &&
+         thd->current_stmt_binlog_row_based) ||
+        ((thd->options & OPTION_KEEP_LOG)))
+    {
+      Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
+      error= binlog_end_trans(thd, trx_data, &qev, all);
+    }
+    /*
+      Otherwise, we simply truncate the cache as there is no change on
+      non-transactional tables as follows.
+    */
+    else if ((all && !thd->transaction.all.modified_non_trans_table) ||
+          (!all && !thd->transaction.stmt.modified_non_trans_table))
+      error= binlog_end_trans(thd, trx_data, 0, all);
+  }
+  if (!all)
+    trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
+  DBUG_RETURN(error);
+}
+
+void MYSQL_BIN_LOG::set_write_error(THD *thd)
+{
+  DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
+
+  write_error= 1;
+
+  if (check_write_error(thd))
+    DBUG_VOID_RETURN;
+
+  if (my_errno == EFBIG)
+    my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
+  else
+    my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
+
+  DBUG_VOID_RETURN;
+}
+
+bool MYSQL_BIN_LOG::check_write_error(THD *thd)
+{
+  DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
+
+  bool checked= FALSE;
+
+  if (!thd->is_error())
+    DBUG_RETURN(checked);
+
+  switch (thd->stmt_da->sql_errno())
+  {
+    case ER_TRANS_CACHE_FULL:
+    case ER_ERROR_ON_WRITE:
+    case ER_BINLOG_LOGGING_IMPOSSIBLE:
+      checked= TRUE;
+    break;
+  }
+
+  DBUG_RETURN(checked);
+}
+
+/**
+  @note
+  How do we handle this (unlikely but legal) case:
+  @verbatim
+    [transaction] + [update to non-trans table] + [rollback to savepoint] ?
+  @endverbatim
+  The problem occurs when a savepoint is before the update to the
+  non-transactional table. Then when there's a rollback to the savepoint, if we
+  simply truncate the binlog cache, we lose the part of the binlog cache where
+  the update is. If we want to not lose it, we need to write the SAVEPOINT
+  command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
+  is easy: it's just write at the end of the binlog cache, but the former
+  should be *inserted* to the place where the user called SAVEPOINT. The
+  solution is that when the user calls SAVEPOINT, we write it to the binlog
+  cache (so no need to later insert it). As transactions are never intermixed
+  in the binary log (i.e. they are serialized), we won't have conflicts with
+  savepoint names when using mysqlbinlog or in the slave SQL thread.
+  Then when ROLLBACK TO SAVEPOINT is called, if we updated some
+  non-transactional table, we don't truncate the binlog cache but instead write
+  ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
+  will chop the SAVEPOINT command from the binlog cache, which is good as in
+  that case there is no need to have it in the binlog).
+*/
+
+static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
+{
+  DBUG_ENTER("binlog_savepoint_set");
+
+  binlog_trans_log_savepos(thd, (my_off_t*) sv);
+  /* Write it to the binary log */
+
+  int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+  int const error=
+    thd->binlog_query(THD::STMT_QUERY_TYPE,
+                      thd->query(), thd->query_length(), TRUE, FALSE, errcode);
+  DBUG_RETURN(error);
+}
+
+static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
+{
+  DBUG_ENTER("binlog_savepoint_rollback");

+
+  /*
+    Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
+    non-transactional table. Otherwise, truncate the binlog cache starting
+    from the SAVEPOINT command.
+  */
+  if (unlikely(thd->transaction.all.modified_non_trans_table || 
+               (thd->options & OPTION_KEEP_LOG)))
+  {
+    int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+    int error=
+      thd->binlog_query(THD::STMT_QUERY_TYPE,
+                        thd->query(), thd->query_length(), TRUE, FALSE, errcode);
+    DBUG_RETURN(error);
+  }
+  binlog_trans_log_truncate(thd, *(my_off_t*)sv);
+  DBUG_RETURN(0);
+}
+
+
+int check_binlog_magic(IO_CACHE* log, const char** errmsg)
+{
+  char magic[4];
+  DBUG_ASSERT(my_b_tell(log) == 0);
+
+  if (my_b_read(log, (uchar*) magic, sizeof(magic)))
+  {
+    *errmsg = "I/O error reading the header from the binary log";
+    sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
+		    log->error);
+    return 1;
+  }
+  if (memcmp(magic, BINLOG_MAGIC, sizeof(magic)))
+  {
+    *errmsg = "Binlog has bad magic number;  It's not a binary log file that can be used by this version of MySQL";
+    return 1;
+  }
+  return 0;
+}
+
+
+File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
+{
+  File file;
+  DBUG_ENTER("open_binlog");
+
+  if ((file = my_open(log_file_name, O_RDONLY | O_BINARY | O_SHARE, 
+                      MYF(MY_WME))) < 0)
+  {
+    sql_print_error("Failed to open log (file '%s', errno %d)",
+                    log_file_name, my_errno);
+    *errmsg = "Could not open log file";
+    goto err;
+  }
+  if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
+                    MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
+  {
+    sql_print_error("Failed to create a cache on log (file '%s')",
+                    log_file_name);
     *errmsg = "Could not open log file";
     goto err;
   }
-  if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
-                    MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
+  if (check_binlog_magic(log,errmsg))
+    goto err;
+  DBUG_RETURN(file);
+
+err:
+  if (file >= 0)
+  {
+    my_close(file,MYF(0));
+    end_io_cache(log);
+  }
+  DBUG_RETURN(-1);
+}
+
+#ifdef _WIN32
+static int eventSource = 0;
+
+static void setup_windows_event_source()
+{
+  HKEY    hRegKey= NULL;
+  DWORD   dwError= 0;
+  TCHAR   szPath[MAX_PATH];
+  DWORD dwTypes;
+
+  if (eventSource)               // Ensure that we are only called once
+    return;
+  eventSource= 1;
+
+  // Create the event source registry key
+  dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
+                          "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL", 
+                          &hRegKey);
+
+  /* Name of the PE module that contains the message resource */
+  GetModuleFileName(NULL, szPath, MAX_PATH);
+
+  /* Register EventMessageFile */
+  dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
+                          (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
+
+  /* Register supported event types */
+  dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
+            EVENTLOG_INFORMATION_TYPE);
+  dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
+                         (LPBYTE) &dwTypes, sizeof dwTypes);
+
+  RegCloseKey(hRegKey);
+}
+
+#endif /* _WIN32 */
+
+
+/**
+  Find a unique filename for 'filename.#'.
+
+  Set '#' to the number next to the maximum found in the most
+  recent log file extension.
+
+  This function will return nonzero if: (i) the generated name
+  exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
+  or (iii) some other error happened while examining the filesystem.
+
+  @return
+    nonzero if not possible to get unique filename.
+*/
+
+static int find_uniq_filename(char *name)
+{
+  uint                  i;
+  char                  buff[FN_REFLEN], ext_buf[FN_REFLEN];
+  struct st_my_dir     *dir_info;
+  reg1 struct fileinfo *file_info;
+  ulong                 max_found= 0, next= 0, number= 0;
+  size_t		buf_length, length;
+  char			*start, *end;
+  int                   error= 0;
+  DBUG_ENTER("find_uniq_filename");
+
+  length= dirname_part(buff, name, &buf_length);
+  start=  name + length;
+  end=    strend(start);
+
+  *end='.';
+  length= (size_t) (end - start + 1);
+
+  if (!(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))
+  {						// This shouldn't happen
+    strmov(end,".1");				// use name+1
+    DBUG_RETURN(1);
+  }
+  file_info= dir_info->dir_entry;
+  for (i= dir_info->number_off_files ; i-- ; file_info++)
+  {
+    if (bcmp((uchar*) file_info->name, (uchar*) start, length) == 0 &&
+	test_if_number(file_info->name+length, &number,0))
+    {
+      set_if_bigger(max_found,(ulong) number);
+    }
+  }
+  my_dirend(dir_info);
+
+  /* check if reached the maximum possible extension number */
+  if ((max_found == MAX_LOG_UNIQUE_FN_EXT))
+  {
+    sql_print_error("Log filename extension number exhausted: %06lu. \
+Please fix this by archiving old logs and \
+updating the index files.", max_found);
+    error= 1;
+    goto end;
+  }
+
+  next= max_found + 1;
+  sprintf(ext_buf, "%06lu", next);
+  *end++='.';
+
+  /* 
+    Check if the generated extension size + the file name exceeds the
+    buffer size used. If one did not check this, then the filename might be
+    truncated, resulting in error.
+   */
+  if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
+  {
+    sql_print_error("Log filename too large: %s%s (%lu). \
+Please fix this by archiving old logs and updating the \
+index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
+    error= 1;
+    goto end;
+  }
+
+  sprintf(end, "%06lu", next);
+
+  /* print warning if reaching the end of available extensions. */
+  if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
+    sql_print_warning("Next log extension: %lu. \
+Remaining log filename extensions: %lu. \
+Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
+
+end:
+  DBUG_RETURN(error);
+}
+
+
+void MYSQL_LOG::init(enum_log_type log_type_arg,
+                     enum cache_type io_cache_type_arg)
+{
+  DBUG_ENTER("MYSQL_LOG::init");
+  log_type= log_type_arg;
+  io_cache_type= io_cache_type_arg;
+  DBUG_PRINT("info",("log_type: %d", log_type));
+  DBUG_VOID_RETURN;
+}
+
+
+/*
+  Open a (new) log file.
+
+  SYNOPSIS
+    open()
+
+    log_name            The name of the log to open
+    log_type_arg        The type of the log. E.g. LOG_NORMAL
+    new_name            The new name for the logfile. This is only needed
+                        when the method is used to open the binlog file.
+    io_cache_type_arg   The type of the IO_CACHE to use for this log file
+
+  DESCRIPTION
+    Open the logfile, init IO_CACHE and write startup messages
+    (in case of general and slow query logs).
+
+  RETURN VALUES
+    0   ok
+    1   error
+*/
+
+bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
+                     const char *new_name, enum cache_type io_cache_type_arg)
+{
+  char buff[FN_REFLEN];
+  File file= -1;
+  int open_flags= O_CREAT | O_BINARY;
+  DBUG_ENTER("MYSQL_LOG::open");
+  DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
+
+  write_error= 0;
+
+  init(log_type_arg, io_cache_type_arg);
+
+  if (!(name= my_strdup(log_name, MYF(MY_WME))))
+  {
+    name= (char *)log_name; // for the error message
+    goto err;
+  }
+
+  if (new_name)
+    strmov(log_file_name, new_name);
+  else if (generate_new_name(log_file_name, name))
+    goto err;
+
+  if (io_cache_type == SEQ_READ_APPEND)
+    open_flags |= O_RDWR | O_APPEND;
+  else
+    open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
+
+  db[0]= 0;
+
+  if ((file= my_open(log_file_name, open_flags,
+                     MYF(MY_WME | ME_WAITTANG))) < 0 ||
+      init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
+                    my_tell(file, MYF(MY_WME)), 0,
+                    MYF(MY_WME | MY_NABP |
+                        ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
+    goto err;
+
+  if (log_type == LOG_NORMAL)
+  {
+    char *end;
+    int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
+#ifdef EMBEDDED_LIBRARY
+                        "embedded library\n",
+                        my_progname, server_version, MYSQL_COMPILATION_COMMENT
+#elif _WIN32
+			"started with:\nTCP Port: %d, Named Pipe: %s\n",
+                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+                        mysqld_port, mysqld_unix_port
+#else
+			"started with:\nTcp port: %d  Unix socket: %s\n",
+                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
+                        mysqld_port, mysqld_unix_port
+#endif
+                       );
+    end= strnmov(buff + len, "Time                 Id Command    Argument\n",
+                 sizeof(buff) - len);
+    if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
+	flush_io_cache(&log_file))
+      goto err;
+  }
+
+  log_state= LOG_OPENED;
+  DBUG_RETURN(0);
+
+err:
+  sql_print_error("Could not use %s for logging (error %d). \
+Turning logging off for the whole duration of the MySQL server process. \
+To turn it on again: fix the cause, \
+shutdown the MySQL server and restart it.", name, errno);
+  if (file >= 0)
+    my_close(file, MYF(0));
+  end_io_cache(&log_file);
+  safeFree(name);
+  log_state= LOG_CLOSED;
+  DBUG_RETURN(1);
+}
+
+MYSQL_LOG::MYSQL_LOG()
+  : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
+    log_state(LOG_CLOSED)
+{
+  /*
+    We don't want to initialize LOCK_Log here as such initialization depends on
+    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
+    called only in main(). Doing initialization here would make it happen
+    before main().
+  */
+  bzero((char*) &log_file, sizeof(log_file));
+}
+
+void MYSQL_LOG::init_pthread_objects()
+{
+  DBUG_ASSERT(inited == 0);
+  inited= 1;

+  (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
+}
+
+/*
+  Close the log file
+
+  SYNOPSIS
+    close()
+    exiting     Bitmask. For the slow and general logs the only used bit is
+                LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call
+                open at once after close.
+
+  NOTES
+    One can do an open on the object at once after doing a close.
+    The internal structures are not freed until cleanup() is called
+*/
+
+void MYSQL_LOG::close(uint exiting)
+{					// One can't set log_type here!
+  DBUG_ENTER("MYSQL_LOG::close");
+  DBUG_PRINT("enter",("exiting: %d", (int) exiting));
+  if (log_state == LOG_OPENED)
+  {
+    end_io_cache(&log_file);
+
+    if (my_sync(log_file.file, MYF(MY_WME)) && ! write_error)
+    {
+      write_error= 1;
+      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+    }
+
+    if (my_close(log_file.file, MYF(MY_WME)) && ! write_error)
+    {
+      write_error= 1;
+      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+    }
+  }
+
+  log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
+  safeFree(name);
+  DBUG_VOID_RETURN;
+}
+
+/** This is called only once. */
+
+void MYSQL_LOG::cleanup()
+{
+  DBUG_ENTER("cleanup");
+  if (inited)
   {
-    sql_print_error("Failed to create a cache on log (file '%s')",
-                    log_file_name);
-    *errmsg = "Could not open log file";
-    goto err;
+    inited= 0;
+    (void) pthread_mutex_destroy(&LOCK_log);
+    close(0);
   }
-  if (check_binlog_magic(log,errmsg))
-    goto err;
-  DBUG_RETURN(file);
+  DBUG_VOID_RETURN;
+}
 
-err:
-  if (file >= 0)
+
+int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
+{
+  fn_format(new_name, log_name, mysql_data_home, "", 4);
+  if (log_type == LOG_BIN)
   {
-    my_close(file,MYF(0));
-    end_io_cache(log);
+    if (!fn_ext(log_name)[0])
+    {
+      if (find_uniq_filename(new_name))
+      {
+        /* 
+          This should be treated as error once propagation of error further
+          up in the stack gets proper handling.
+        */
+        push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
+                            ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
+                            log_name);
+	sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
+	return 1;
+      }
+    }
   }
-  DBUG_RETURN(-1);
+  return 0;
 }
 
-#ifdef _WIN32
-static int eventSource = 0;
 
-static void setup_windows_event_source()
+/*
+  Reopen the log file
+
+  SYNOPSIS
+    reopen_file()
+
+  DESCRIPTION
+    Reopen the log file. The method is used during FLUSH LOGS
+    and locks LOCK_log mutex
+*/
+
+
+void MYSQL_QUERY_LOG::reopen_file()
 {
-  HKEY    hRegKey= NULL;
-  DWORD   dwError= 0;
-  TCHAR   szPath[MAX_PATH];
-  DWORD dwTypes;
+  char *save_name;
 
-  if (eventSource)               // Ensure that we are only called once
-    return;
-  eventSource= 1;
+  DBUG_ENTER("MYSQL_LOG::reopen_file");
+  if (!is_open())
+  {
+    DBUG_PRINT("info",("log is closed"));
+    DBUG_VOID_RETURN;
+  }
 
-  // Create the event source registry key
-  dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
-                          "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL", 
-                          &hRegKey);
+  pthread_mutex_lock(&LOCK_log);
 
-  /* Name of the PE module that contains the message resource */
-  GetModuleFileName(NULL, szPath, MAX_PATH);
+  save_name= name;
+  name= 0;				// Don't free name
+  close(LOG_CLOSE_TO_BE_OPENED);
 
-  /* Register EventMessageFile */
-  dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
-                          (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
+  /*
+     Note that at this point, log_state != LOG_CLOSED (important for is_open()).
+  */
 
-  /* Register supported event types */
-  dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
-            EVENTLOG_INFORMATION_TYPE);
-  dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
-                         (LPBYTE) &dwTypes, sizeof dwTypes);
+  open(save_name, log_type, 0, io_cache_type);
+  my_free(save_name, MYF(0));
 
-  RegCloseKey(hRegKey);
+  pthread_mutex_unlock(&LOCK_log);
+
+  DBUG_VOID_RETURN;
 }
 
-#endif /* _WIN32 */
 
+/*
+  Write a command to traditional general log file
 
-/**
-  Find a unique filename for 'filename.#'.
+  SYNOPSIS
+    write()
 
-  Set '#' to the number next to the maximum found in the most
-  recent log file extension.
+    event_time        command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log  event handlers
+    thread_id         Id of the thread, issued a query
+    command_type      the type of the command being logged
+    command_type_len  the length of the string above
+    sql_text          the very text of the query being executed
+    sql_text_len      the length of sql_text string
 
-  This function will return nonzero if: (i) the generated name
-  exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
-  or (iii) some other error happened while examining the filesystem.
+  DESCRIPTION
+
+   Log given command to to normal (not rotable) log file
+
+  RETURN
+    FASE - OK
+    TRUE - error occured
+*/
+
+bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
+                            uint user_host_len, int thread_id,
+                            const char *command_type, uint command_type_len,
+                            const char *sql_text, uint sql_text_len)
+{
+  char buff[32];
+  uint length= 0;
+  char local_time_buff[MAX_TIME_SIZE];
+  struct tm start;
+  uint time_buff_len= 0;
+
+  (void) pthread_mutex_lock(&LOCK_log);
+
+  /* Test if someone closed between the is_open test and lock */
+  if (is_open())
+  {
+    /* for testing output of timestamp and thread id */
+    DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
+
+    /* Note that my_b_write() assumes it knows the length for this */
+      if (event_time != last_time)
+      {
+        last_time= event_time;
+
+        localtime_r(&event_time, &start);
+
+        time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
+                                   "%02d%02d%02d %2d:%02d:%02d\t",
+                                   start.tm_year % 100, start.tm_mon + 1,
+                                   start.tm_mday, start.tm_hour,
+                                   start.tm_min, start.tm_sec);
+
+        if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
+          goto err;
+      }
+      else
+        if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
+          goto err;
+
+      /* command_type, thread_id */
+      length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
+
+    if (my_b_write(&log_file, (uchar*) buff, length))
+      goto err;
+
+    if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
+      goto err;
+
+    if (my_b_write(&log_file, (uchar*) "\t", 1))
+      goto err;
+
+    /* sql_text */
+    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
+      goto err;
+
+    if (my_b_write(&log_file, (uchar*) "\n", 1) ||
+        flush_io_cache(&log_file))
+      goto err;
+  }
+
+  (void) pthread_mutex_unlock(&LOCK_log);
+  return FALSE;
+err:
+
+  if (!write_error)
+  {
+    write_error= 1;
+    sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+  }
+  (void) pthread_mutex_unlock(&LOCK_log);
+  return TRUE;
+}
+
+
+/*
+  Log a query to the traditional slow log file
+
+  SYNOPSIS
+    write()
+
+    thd               THD of the query
+    current_time      current timestamp
+    query_start_arg   command start timestamp
+    user_host         the pointer to the string with user@host info
+    user_host_len     length of the user_host string. this is computed once
+                      and passed to all general log event handlers
+    query_utime       Amount of time the query took to execute (in microseconds)
+    lock_utime        Amount of time the query was locked (in microseconds)
+    is_command        The flag, which determines, whether the sql_text is a
+                      query or an administrator command.
+    sql_text          the very text of the query or administrator command
+                      processed
+    sql_text_len      the length of sql_text string
+
+  DESCRIPTION
 
-  @return
-    nonzero if not possible to get unique filename.
+   Log a query to the slow log file.
+
+  RETURN
+    FALSE - OK
+    TRUE - error occured
 */
 
-static int find_uniq_filename(char *name)
+bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
+                            time_t query_start_arg, const char *user_host,
+                            uint user_host_len, ulonglong query_utime,
+                            ulonglong lock_utime, bool is_command,
+                            const char *sql_text, uint sql_text_len)
 {
-  uint                  i;
-  char                  buff[FN_REFLEN], ext_buf[FN_REFLEN];
-  struct st_my_dir     *dir_info;
-  reg1 struct fileinfo *file_info;
-  ulong                 max_found= 0, next= 0, number= 0;
-  size_t		buf_length, length;
-  char			*start, *end;
-  int                   error= 0;
-  DBUG_ENTER("find_uniq_filename");
-
-  length= dirname_part(buff, name, &buf_length);
-  start=  name + length;
-  end=    strend(start);
+  bool error= 0;
+  DBUG_ENTER("MYSQL_QUERY_LOG::write");
 
-  *end='.';
-  length= (size_t) (end - start + 1);
+  (void) pthread_mutex_lock(&LOCK_log);

 
-  if (!(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))
-  {						// This shouldn't happen
-    strmov(end,".1");				// use name+1
-    DBUG_RETURN(1);
-  }
-  file_info= dir_info->dir_entry;
-  for (i= dir_info->number_off_files ; i-- ; file_info++)
+  if (!is_open())
   {
-    if (bcmp((uchar*) file_info->name, (uchar*) start, length) == 0 &&
-	test_if_number(file_info->name+length, &number,0))
-    {
-      set_if_bigger(max_found,(ulong) number);
-    }
+    (void) pthread_mutex_unlock(&LOCK_log);
+    DBUG_RETURN(0);
   }
-  my_dirend(dir_info);
 
-  /* check if reached the maximum possible extension number */
-  if ((max_found == MAX_LOG_UNIQUE_FN_EXT))
-  {
-    sql_print_error("Log filename extension number exhausted: %06lu. \
-Please fix this by archiving old logs and \
-updating the index files.", max_found);
-    error= 1;
-    goto end;
-  }
+  if (is_open())
+  {						// Safety agains reopen
+    int tmp_errno= 0;
+    char buff[80], *end;
+    char query_time_buff[22+7], lock_time_buff[22+7];
+    uint buff_len;
+    end= buff;
 
-  next= max_found + 1;
-  sprintf(ext_buf, "%06lu", next);
-  *end++='.';
+    if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+    {
+      if (current_time != last_time)
+      {
+        last_time= current_time;
+        struct tm start;
+        localtime_r(&current_time, &start);
 
-  /* 
-    Check if the generated extension size + the file name exceeds the
-    buffer size used. If one did not check this, then the filename might be
-    truncated, resulting in error.
-   */
-  if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
-  {
-    sql_print_error("Log filename too large: %s%s (%lu). \
-Please fix this by archiving old logs and updating the \
-index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
-    error= 1;
-    goto end;
-  }
+        buff_len= my_snprintf(buff, sizeof buff,
+                              "# Time: %02d%02d%02d %2d:%02d:%02d\n",
+                              start.tm_year % 100, start.tm_mon + 1,
+                              start.tm_mday, start.tm_hour,
+                              start.tm_min, start.tm_sec);
 
-  sprintf(end, "%06lu", next);
+        /* Note that my_b_write() assumes it knows the length for this */
+        if (my_b_write(&log_file, (uchar*) buff, buff_len))
+          tmp_errno= errno;
+      }
+      const uchar uh[]= "# User@Host: ";
+      if (my_b_write(&log_file, uh, sizeof(uh) - 1))
+        tmp_errno= errno;
+      if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
+        tmp_errno= errno;
+      if (my_b_write(&log_file, (uchar*) "\n", 1))
+        tmp_errno= errno;
+    }
+    /* For slow query log */
+    sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
+    sprintf(lock_time_buff,  "%.6f", ulonglong2double(lock_utime)/1000000.0);
+    if (my_b_printf(&log_file,
+                    "# Query_time: %s  Lock_time: %s"
+                    " Rows_sent: %lu  Rows_examined: %lu\n",
+                    query_time_buff, lock_time_buff,
+                    (ulong) thd->sent_row_count,
+                    (ulong) thd->examined_row_count) == (uint) -1)
+      tmp_errno= errno;
+    if (thd->db && strcmp(thd->db, db))
+    {						// Database changed
+      if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
+        tmp_errno= errno;
+      strmov(db,thd->db);
+    }
+    if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
+    {
+      end=strmov(end, ",last_insert_id=");
+      end=longlong10_to_str((longlong)
+                            thd->first_successful_insert_id_in_prev_stmt_for_binlog,
+                            end, -10);
+    }
+    // Save value if we do an insert.
+    if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
+    {
+      if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+      {
+        end=strmov(end,",insert_id=");
+        end=longlong10_to_str((longlong)
+                              thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
+                              end, -10);
+      }
+    }
 
-  /* print warning if reaching the end of available extensions. */
-  if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
-    sql_print_warning("Next log extension: %lu. \
-Remaining log filename extensions: %lu. \
-Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
+    /*
+      This info used to show up randomly, depending on whether the query
+      checked the query start time or not. now we always write current
+      timestamp to the slow log
+    */
+    end= strmov(end, ",timestamp=");
+    end= int10_to_str((long) current_time, end, 10);
 
-end:
+    if (end != buff)
+    {
+      *end++=';';
+      *end='\n';
+      if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
+          my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
+        tmp_errno= errno;
+    }
+    if (is_command)
+    {
+      end= strxmov(buff, "# administrator command: ", NullS);
+      buff_len= (ulong) (end - buff);
+      my_b_write(&log_file, (uchar*) buff, buff_len);
+    }
+    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
+        my_b_write(&log_file, (uchar*) ";\n",2) ||
+        flush_io_cache(&log_file))
+      tmp_errno= errno;
+    if (tmp_errno)
+    {
+      error= 1;
+      if (! write_error)
+      {
+        write_error= 1;
+        sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
+      }
+    }
+  }
+  (void) pthread_mutex_unlock(&LOCK_log);
   DBUG_RETURN(error);
 }
 
 
-void MYSQL_LOG::init(enum_log_type log_type_arg,
-                     enum cache_type io_cache_type_arg)
+MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
+  :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
+   need_start_event(TRUE), m_table_map_version(0),
+   sync_period_ptr(sync_period),
+   is_relay_log(0), signal_cnt(0),
+   description_event_for_exec(0), description_event_for_queue(0)
 {
-  DBUG_ENTER("MYSQL_LOG::init");
-  log_type= log_type_arg;
-  io_cache_type= io_cache_type_arg;
-  DBUG_PRINT("info",("log_type: %d", log_type));
-  DBUG_VOID_RETURN;
+  /*
+    We don't want to initialize locks here as such initialization depends on
+    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
+    called only in main(). Doing initialization here would make it happen
+    before main().
+  */
+  index_file_name[0] = 0;
+  bzero((char*) &index_file, sizeof(index_file));
+  bzero((char*) &purge_temp, sizeof(purge_temp));
 }
 
+/**
+  Open a (new) backup log file.
 
-/*
-  Open a (new) log file.
-
-  SYNOPSIS
-    open()
-
-    log_name            The name of the log to open
-    log_type_arg        The type of the log. E.g. LOG_NORMAL
-    new_name            The new name for the logfile. This is only needed
-                        when the method is used to open the binlog file.
-    io_cache_type_arg   The type of the IO_CACHE to use for this log file
-
-  DESCRIPTION
-    Open the logfile, init IO_CACHE and write startup messages
-    (in case of general and slow query logs).
-
-  RETURN VALUES
-    0   ok
-    1   error
-*/
+  Open the backup log file, init IO_CACHE and write startup messages.
 
-bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
-                     const char *new_name, enum cache_type io_cache_type_arg)
+  @param[IN] log_name          The name of the log to open
+  @param[IN] log_type_arg      The type of the log. E.g. LOG_NORMAL
+  @param[IN] new_name          The new name for the logfile.
+  @param[IN] io_cache_type_arg The type of the IO_CACHE to use for this loge
+  @param[IN] history           If TRUE, process history log headeer else do
+                               progress header
+
+  @returns 0 success, 1 error
+*/
+bool MYSQL_BACKUP_LOG::open(const char *log_name, 
+                            enum_log_type log_type_arg,
+                            const char *new_name, 
+                            enum cache_type io_cache_type_arg,
+                            bool history)
 {
   char buff[FN_REFLEN];
   File file= -1;
   int open_flags= O_CREAT | O_BINARY;
-  DBUG_ENTER("MYSQL_LOG::open");
+  DBUG_ENTER("MYSQL_BACKUP_LOG::open");
   DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
 
   write_error= 0;
@@ -2038,38 +3762,40 @@ bool MYSQL_LOG::open(const char *log_nam
                         ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
     goto err;
 
-  if (log_type == LOG_NORMAL)
+  /*
+    Write header of column names if this is the first time the log
+    has been opened.
+  */
+  if (!headers_written && (log_type == LOG_NORMAL))
   {
     char *end;
-    int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
-#ifdef EMBEDDED_LIBRARY
-                        "embedded library\n",
-                        my_progname, server_version, MYSQL_COMPILATION_COMMENT
-#elif _WIN32
-			"started with:\nTCP Port: %d, Named Pipe: %s\n",
-                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
-                        mysqld_port, mysqld_unix_port
-#else
-			"started with:\nTcp port: %d  Unix socket: %s\n",
-                        my_progname, server_version, MYSQL_COMPILATION_COMMENT,
-                        mysqld_port, mysqld_unix_port
-#endif
-                       );
-    end= strnmov(buff + len, "Time                 Id Command    Argument\n",
-                 sizeof(buff) - len);
+
+    int len=my_snprintf(buff, sizeof(buff), "Columns for this log:\n");
+    if (history)
+      end= strnmov(buff + len, "backup_id \tprocess_id \tbinlog_start_pos "
+                   "\tbinlog_file \tbackup_state \toperation "
+                   "\terror_num \tnum_objects \ttotal_bytes "
+                   "\tvalidity_point_time \tstart_time \tstop_time "
+                   "\thost_or_server_name \tusername \tbackup_file "
+                   "\tbackup_file_path \tuser_comment \tcommand \tdrivers\n",
+                   sizeof(buff) - len);
+    else
+      end= strnmov(buff + len, "\nbackup_id \tobject \tstart_time \tstop_time "
+                   "\ttotal_bytes \tprogress \terror_num \tnotes \tbackup_id "
+                   "\tobject \tstart_time \tstop_time \ttotal_bytes "
+                   "\tprogress \terror_num \tnotes\n",
+                   sizeof(buff) - len);
     if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
-	flush_io_cache(&log_file))
+        flush_io_cache(&log_file))
       goto err;
+    headers_written= TRUE;
   }
 
   log_state= LOG_OPENED;
   DBUG_RETURN(0);
-
-err:
-  sql_print_error("Could not use %s for logging (error %d). \
-Turning logging off for the whole duration of the MySQL server process. \
-To turn it on again: fix the cause, \
-shutdown the MySQL server and restart it.", name, errno);
+
+err:
+  sql_print_error("Could not use %s for backup logging (error %d).", name, errno);
   if (file >= 0)
     my_close(file, MYF(0));
   end_io_cache(&log_file);
@@ -2078,226 +3804,311 @@ shutdown the MySQL server and restart it
   DBUG_RETURN(1);
 }
 
-MYSQL_LOG::MYSQL_LOG()
-  : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
-    log_state(LOG_CLOSED)
+/**
+  Write an unsigned integer value to the log file.
+
+  This method writes the data passed and appends a tab character.
+
+  @param[IN]   thd   The current thread
+  @param[IN]   num   Data to write to log.
+
+  @returns TRUE if error.
+*/
+bool MYSQL_BACKUP_LOG::write_int(uint num)
 {
+  char buff[32];
+  uint length= 0;
+
   /*
-    We don't want to initialize LOCK_Log here as such initialization depends on
-    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
-    called only in main(). Doing initialization here would make it happen
-    before main().
+    This field is wide to allow ulonglong fields.
+    We don't want to truncate any large backup id values.
   */
-  bzero((char*) &log_file, sizeof(log_file));
+  length= my_snprintf(buff, 32, "%10lu ", (ulong)num);
+  if (my_b_write(&log_file, (uchar*) buff, length))
+    return TRUE;
+  if (my_b_write(&log_file, (uchar*) "\t", 1))
+    return TRUE;
+  return FALSE;
 }
 
-void MYSQL_LOG::init_pthread_objects()
-{
-  DBUG_ASSERT(inited == 0);
-  inited= 1;
-  (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
-}
+/**
+  Write an unsigned longlong value to the log file.
 
-/*
-  Close the log file
+  This method writes the data passed and appends a tab character.
 
-  SYNOPSIS
-    close()
-    exiting     Bitmask. For the slow and general logs the only used bit is
-                LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call
-                open at once after close.
+  @param[IN]   thd   The current thread
+  @param[IN]   num   Data to write to log.
 
-  NOTES
-    One can do an open on the object at once after doing a close.
-    The internal structures are not freed until cleanup() is called
+  @returns TRUE if error.
 */
+bool MYSQL_BACKUP_LOG::write_long(ulonglong num)
+{
+  char buff[32];
+  uint length= 0;
 
-void MYSQL_LOG::close(uint exiting)
-{					// One can't set log_type here!
-  DBUG_ENTER("MYSQL_LOG::close");
-  DBUG_PRINT("enter",("exiting: %d", (int) exiting));
-  if (log_state == LOG_OPENED)
-  {
-    end_io_cache(&log_file);
-
-    if (my_sync(log_file.file, MYF(MY_WME)) && ! write_error)
-    {
-      write_error= 1;
-      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
-    }
+  /*
+    This field is wide to allow ulonglong fields.
+    We don't want to truncate any large backup id values.
+  */
+  length= my_snprintf(buff, 32, "%10lu ", (ulong)num);
+  if (my_b_write(&log_file, (uchar*) buff, length))
+    return TRUE;
+  if (my_b_write(&log_file, (uchar*) "\t", 1))
+    return TRUE;
+  return FALSE;
+}
 
-    if (my_close(log_file.file, MYF(MY_WME)) && ! write_error)
-    {
-      write_error= 1;
-      sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
-    }
-  }
+/**
+  Write a datetime value to the log file.
 
-  log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
-  safeFree(name);
-  DBUG_VOID_RETURN;
-}
+  This method writes the data passed (if not null) and appends a tab character.
 
-/** This is called only once. */
+  @param[IN]   thd       The current thread
+  @param[IN]   time_val  Data to write to log.
 
-void MYSQL_LOG::cleanup()
+  @returns TRUE if error.
+*/
+bool MYSQL_BACKUP_LOG::write_datetime(time_t time_val)
 {
-  DBUG_ENTER("cleanup");
-  if (inited)
+  char local_time_buff[MAX_TIME_SIZE];
+  uint time_buff_len= 0;
+
+  if (time_val)
   {
-    inited= 0;
-    (void) pthread_mutex_destroy(&LOCK_log);
-    close(0);
-  }
-  DBUG_VOID_RETURN;
-}
+    MYSQL_TIME time;
+    my_tz_OFFSET0->gmt_sec_to_TIME(&time, (my_time_t)time_val);
 
+    time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
+                               "%02d%02d%02d %2d:%02d:%02d",
+                               time.year % 100, time.month + 1,
+                               time.day, time.hour,
+                               time.minute, time.second);
 
-int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
-{
-  fn_format(new_name, log_name, mysql_data_home, "", 4);
-  if (log_type == LOG_BIN)
-  {
-    if (!fn_ext(log_name)[0])
-    {
-      if (find_uniq_filename(new_name))
-      {
-        /* 
-          This should be treated as error once propagation of error further
-          up in the stack gets proper handling.
-        */
-        push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
-                            ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
-                            log_name);
-	sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
-	return 1;
-      }
-    }
+    if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
+      return TRUE;
+    if (my_b_write(&log_file, (uchar*) "\t", 1))
+      return TRUE;
   }
-  return 0;
+  return FALSE;
 }
 
+/**
+  Write a character string value to the log file.
 
-/*
-  Reopen the log file
+  This method writes the data passed (if not null) and appends a tab character.
 
-  SYNOPSIS
-    reopen_file()
+  @param[IN]   thd       The current thread
+  @param[IN]   str       Data to write to log.
 
-  DESCRIPTION
-    Reopen the log file. The method is used during FLUSH LOGS
-    and locks LOCK_log mutex
+  @returns TRUE if error.
 */
-
-
-void MYSQL_QUERY_LOG::reopen_file()
+bool MYSQL_BACKUP_LOG::write_str(const char *str)
 {
-  char *save_name;
-
-  DBUG_ENTER("MYSQL_LOG::reopen_file");
-  if (!is_open())
+  if (str)
   {
-    DBUG_PRINT("info",("log is closed"));
-    DBUG_VOID_RETURN;
+    if (my_b_write(&log_file, (uchar*)str, strlen(str)))
+      return TRUE;
+    if (my_b_write(&log_file, (uchar*) "\t", 1))
+      return TRUE;
   }
+  return FALSE;
+}
 
-  pthread_mutex_lock(&LOCK_log);
-
-  save_name= name;
-  name= 0;				// Don't free name
-  close(LOG_CLOSE_TO_BE_OPENED);
-
-  /*
-     Note that at this point, log_state != LOG_CLOSED (important for is_open()).
-  */
-
-  open(save_name, log_type, 0, io_cache_type);
-  my_free(save_name, MYF(0));
-
-  pthread_mutex_unlock(&LOCK_log);
+/**
+  Write the backup log entry for the backup history log to a file.
 
-  DBUG_VOID_RETURN;
-}
+  This method creates a new row in the backup history log with the
+  information provided.
 
+  @param[IN]   thd   The current thread
+  @param[IN]   st_backup_history   Data to write to log.
 
-/*
-  Write a command to traditional general log file
+  @returns TRUE if error.
+*/
+bool MYSQL_BACKUP_LOG::write(THD *thd, st_backup_history *history_data)
+{
+  char *host= current_thd->security_ctx->host; // host name
+  char *user= current_thd->security_ctx->user; // user name
+  bool save_time_zone_used;
 
-  SYNOPSIS
-    write()
+  save_time_zone_used= thd->time_zone_used;
 
-    event_time        command start timestamp
-    user_host         the pointer to the string with user@host info
-    user_host_len     length of the user_host string. this is computed once
-                      and passed to all general log  event handlers
-    thread_id         Id of the thread, issued a query
-    command_type      the type of the command being logged
-    command_type_len  the length of the string above
-    sql_text          the very text of the query being executed
-    sql_text_len      the length of sql_text string
+  (void) pthread_mutex_lock(&LOCK_log);
 
-  DESCRIPTION
+  /* 
+    Test if someone closed between the is_open test and lock 
+  */
+  if (is_open())
+  {
+    /*
+      Write log data.
+    */
+    if (write_long(history_data->backup_id))
+      goto err;
+    if (write_int(history_data->process_id))
+      goto err;
+    if (write_int(history_data->binlog_start_pos))
+      goto err;
+    if (write_str(history_data->binlog_file))
+      goto err;
 
-   Log given command to to normal (not rotable) log file
+    /*
+      Print the enumerated values to match the columns in the
+      table.
+    */
+    switch (history_data->state) {
+    case BUP_COMPLETE:
+      {
+        if (write_str("complete"))
+          goto err;
+        break;
+      }
+    case BUP_STARTING:
+      {
+        if (write_str("starting"))
+          goto err;
+        break;
+      }
+    case BUP_VALIDITY_POINT:
+      {
+        if (write_str("validity point"))
+          goto err;
+        break;
+      }
+    case BUP_RUNNING:
+      {
+        if (write_str("running"))
+          goto err;
+        break;
+      }
+    case BUP_ERRORS:
+      {
+        if (write_str("error"))
+          goto err;
+        break;
+      }
+    case BUP_CANCEL:
+      {
+        if (write_str("cancel"))
+          goto err;
+        break;
+      }
+    default:   // Should be no other values.
+      goto err;
+    }
+      
+    /*
+      Print the enumerated values to match the columns in the
+      table.
+    */
+    switch (history_data->operation) {
+    case OP_BACKUP:
+      {
+        if (write_str("backup"))
+          goto err;
+        break;
+      }
+    case OP_RESTORE:
+      {
+        if (write_str("restore"))
+          goto err;
+        break;
+      }
+    default:   // Should be no other values.
+      goto err;
+    }
+    
+    if (write_int(history_data->error_num))
+      goto err;
+    if (write_int(history_data->num_objects))
+      goto err;
+    if (write_long(history_data->size))
+      goto err;
+    if (write_datetime(history_data->vp_time))
+      goto err;
+    if (write_datetime(history_data->start))
+      goto err;
+    if (write_datetime(history_data->stop))
+      goto err;
+    if (write_str(host))
+      goto err;
+    if (write_str(user))
+      goto err;
+    if (write_str(history_data->backup_file))
+      goto err;
+    if (write_str(history_data->backup_file_path))
+      goto err;
+    if (write_str(history_data->user_comment))
+      goto err;
+    if (write_str(history_data->command))
+      goto err;
+    if (write_str(history_data->driver_name.ptr()))
+      goto err;
 
-  RETURN
-    FASE - OK
-    TRUE - error occured
-*/
+    if (my_b_write(&log_file, (uchar*) "\n", 1) ||
+        flush_io_cache(&log_file))
+      goto err;
+  }
 
-bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
-                            uint user_host_len, int thread_id,
-                            const char *command_type, uint command_type_len,
-                            const char *sql_text, uint sql_text_len)
-{
-  char buff[32];
-  uint length= 0;
-  char local_time_buff[MAX_TIME_SIZE];
-  struct tm start;
-  uint time_buff_len= 0;
+  (void) pthread_mutex_unlock(&LOCK_log);
+  thd->time_zone_used= save_time_zone_used;
+  return FALSE;
+err:
 
-  (void) pthread_mutex_lock(&LOCK_log);
+  if (!write_error)
+  {
+    write_error= 1;
+    sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+  }
+  (void) pthread_mutex_unlock(&LOCK_log);
+  thd->time_zone_used= save_time_zone_used;
+  return TRUE;
+}
 
-  /* Test if someone closed between the is_open test and lock */
-  if (is_open())
-  {
-    /* for testing output of timestamp and thread id */
-    DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
 
-    /* Note that my_b_write() assumes it knows the length for this */
-      if (event_time != last_time)
-      {
-        last_time= event_time;
+/**
+  Write the backup log entry for the backup progress log to a file.
 
-        localtime_r(&event_time, &start);
+  This method creates a new row in the backup progress log with the
+  information provided.
 
-        time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
-                                   "%02d%02d%02d %2d:%02d:%02d\t",
-                                   start.tm_year % 100, start.tm_mon + 1,
-                                   start.tm_mday, start.tm_hour,
-                                   start.tm_min, start.tm_sec);
+  @param[IN]   thd         The current thread
+  @param[OUT]  backup_id   The new row id for the backup history
+  @param[IN]   object      The name of the object processed
+  @param[IN]   start       Start datetime
+  @param[IN]   stop        Stop datetime
+  @param[IN]   size        Size value
+  @param[IN]   progress    Progress (percent)
+  @param[IN]   error_num   Error number (should be 0 is success)
+  @param[IN]   notes       Misc data from engine
+
+  @returns TRUE if error.
+*/
+bool MYSQL_BACKUP_LOG::write(THD *thd, ulonglong backup_id, const char *object, 
+                             time_t start, time_t stop, longlong size,
+                             longlong progress, int error_num, const char *notes)
+{
+  bool save_time_zone_used;
 
-        if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
-          goto err;
-      }
-      else
-        if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
-          goto err;
+  save_time_zone_used= thd->time_zone_used;
 
-      /* command_type, thread_id */
-      length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
+  (void) pthread_mutex_lock(&LOCK_log);
 
-    if (my_b_write(&log_file, (uchar*) buff, length))
+  /* 
+    Test if someone closed between the is_open test and lock 
+  */
+  if (is_open())
+  {
+    /*
+      Write log data.
+    */
+    if (write_long(backup_id))
       goto err;
-
-    if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
+    if (write_str(object))
       goto err;
-
-    if (my_b_write(&log_file, (uchar*) "\t", 1))
+    if (write_int(error_num))
       goto err;
-
-    /* sql_text */
-    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
+    if (write_str(notes))
       goto err;
 
     if (my_b_write(&log_file, (uchar*) "\n", 1) ||
@@ -2306,6 +4117,7 @@ bool MYSQL_QUERY_LOG::write(time_t event
   }
 
   (void) pthread_mutex_unlock(&LOCK_log);
+  thd->time_zone_used= save_time_zone_used;
   return FALSE;
 err:
 
@@ -2315,163 +4127,234 @@ err:
     sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
   }
   (void) pthread_mutex_unlock(&LOCK_log);
+  thd->time_zone_used= save_time_zone_used;
   return TRUE;
 }
 
 
-/*
-  Log a query to the traditional slow log file
-
-  SYNOPSIS
-    write()
-
-    thd               THD of the query
-    current_time      current timestamp
-    query_start_arg   command start timestamp
-    user_host         the pointer to the string with user@host info
-    user_host_len     length of the user_host string. this is computed once
-                      and passed to all general log event handlers
-    query_utime       Amount of time the query took to execute (in microseconds)
-    lock_utime        Amount of time the query was locked (in microseconds)
-    is_command        The flag, which determines, whether the sql_text is a
-                      query or an administrator command.
-    sql_text          the very text of the query or administrator command
-                      processed
-    sql_text_len      the length of sql_text string
+/**
+  Check backup history logs.
+  
+  This method attempts to open the backup logs. It returns
+  an error if either log is not present or cannot be opened.
 
-  DESCRIPTION
+  @param[in] THD * The current thread.
 
-   Log a query to the slow log file.
+  @returns Information whether backup logs can be used.
 
-  RETURN
-    FALSE - OK
-    TRUE - error occured
+  @retval FALSE  success
+  @retval TRUE  failed to open one of the logs
 */
 
-bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
-                            time_t query_start_arg, const char *user_host,
-                            uint user_host_len, ulonglong query_utime,
-                            ulonglong lock_utime, bool is_command,
-                            const char *sql_text, uint sql_text_len)
+my_bool MYSQL_BACKUP_LOG::check_backup_logs(THD *thd)
 {
-  bool error= 0;
-  DBUG_ENTER("MYSQL_QUERY_LOG::write");
+  TABLE_LIST tables;
+  my_bool ret= FALSE;
 
-  (void) pthread_mutex_lock(&LOCK_log);
+  DBUG_ENTER("check_backup_logs");
 
-  if (!is_open())
+  /*
+     ADD TO THIS AREA!!!
+     CHECK TO SEE IF LOG TO TABLE OR LOG TO FILE! 
+     ONLY DO CHECK IF WE ARE LOGGING TO TABLE!
+  */
+
+  /* Check mysql.backup_history */
+  tables.init_one_table("mysql", strlen("mysql"), 
+                        "backup_history", strlen("backup_history"),
+                        "backup_history", TL_READ);
+  if (simple_open_n_lock_tables(thd, &tables))
   {
-    (void) pthread_mutex_unlock(&LOCK_log);
-    DBUG_RETURN(0);
+    close_thread_tables(thd);
+    /*
+      Here we wish to change the error that is generated by the open method,
+      "table does not exist" to a specific error message for missing
+      backup logs. In this case, we reset the old error and issue the new one.
+    */
+    ret= TRUE;
+    sql_print_error("%s", ER(ER_BACKUP_PROGRESS_TABLES));
+    thd->stmt_da->reset_diagnostics_area();
+    thd->stmt_da->set_error_status(thd,
+                                   ER_BACKUP_PROGRESS_TABLES,
+                                   ER(ER_BACKUP_PROGRESS_TABLES),
+                                   NULL);
+    DBUG_RETURN(ret);
+  }
+  close_thread_tables(thd);
+
+  /* Check mysql.backup_progress */
+  tables.init_one_table("mysql", strlen("mysql"), 
+                        "backup_progress", strlen("backup_progress"),
+                        "backup_progress", TL_READ);
+  if (simple_open_n_lock_tables(thd, &tables))

+  {
+    close_thread_tables(thd);
+    /*
+      Here we wish to change the error that is generated by the open method,
+      "table does not exist" to a specific error message for missing
+      backup logs. In this case, we reset the old error and issue the new one.
+    */
+    ret= TRUE;
+    sql_print_error("%s", ER(ER_BACKUP_PROGRESS_TABLES));
+    thd->stmt_da->reset_diagnostics_area();
+    thd->stmt_da->set_error_status(thd,
+                                   ER_BACKUP_PROGRESS_TABLES,
+                                   ER(ER_BACKUP_PROGRESS_TABLES),
+                                   NULL);
+    DBUG_RETURN(ret);
   }
+  close_thread_tables(thd);
+  DBUG_RETURN(ret);
+}
 
-  if (is_open())
-  {						// Safety agains reopen
-    int tmp_errno= 0;
-    char buff[80], *end;
-    char query_time_buff[22+7], lock_time_buff[22+7];
-    uint buff_len;
-    end= buff;
-
-    if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+/**
+  Generate the next backup id.
+  
+  Since autoincrement columns are not permitted in CSV files, an alternative 
+  mechanism has been developed to create monotonically increasing values. When 
+  a server that does not have any logs written (no backup logs), the system 
+  starts at backup_id = 0. This value is stored in a binary file in the data 
+  directory named backup_settings.obx. Each time a new backup_id is needed, 
+  this value is read, incremented, then the file rewritten. This ensures a 
+  monotonically increasing backup_id. If the backup logs exist and the 
+  backup_settings.obx file does not, the system uses the backup log file size 
+  as the starting backup_id. 
+
+  @todo Do we need a mutex to protect this call internally?
+  @todo Do we need to version the file?
+
+  @returns next backup id or 0 if failure
+*/
+ulonglong MYSQL_BACKUP_LOG::get_next_backup_id()
+{
+  ulonglong id= 0;
+  uchar *read_id= 0;
+  uchar *write_id= 0;
+  char buff[FN_REFLEN], *file_path;
+  File file= 0;
+
+  /*
+    This code attempts to generate a new backup_id. The process is:
+    1) Attempt to read from backup settings file.
+    2) If file exists, read value, increment, write new values.
+    3) If file does not exist, create it and set the first backup_id
+       equal to the filesize of the backup history log file.
+
+    Notes:
+    m_next_id == 0 means we need to read the next id from the file (on startup).
+    m_next_id > 0 means use this value
+  */
+  pthread_mutex_lock(&LOCK_backupid);
+  if (!m_next_id)
+  {
+    file_path= make_backup_log_name(buff, BACKUP_SETTINGS_NAME.str, ".obx");
+    MY_STAT state;  
+  
+    file= my_open(file_path, O_RDWR|O_BINARY|O_CREAT, MYF(MY_WME));
+    if (!file)
+      goto err_end; 
+    if (my_fstat(file, &state, MYF(0)))
+      goto err;
+    /*
+      Check to see if the file size is 0. If it is, we need to pick a 
+      new backup_id to start from. 
+    */
+    if (state.st_size == 0)
     {
-      if (current_time != last_time)
+      MY_STAT fstate;
+      File hist_file= my_open(sys_var_backup_history_log_path.value,
+        O_RDONLY|O_BINARY, MYF(MY_WME));
+      if (hist_file > 0)
       {
-        last_time= current_time;
-        struct tm start;
-        localtime_r(&current_time, &start);
-
-        buff_len= my_snprintf(buff, sizeof buff,
-                              "# Time: %02d%02d%02d %2d:%02d:%02d\n",
-                              start.tm_year % 100, start.tm_mon + 1,
-                              start.tm_mday, start.tm_hour,
-                              start.tm_min, start.tm_sec);
-
-        /* Note that my_b_write() assumes it knows the length for this */
-        if (my_b_write(&log_file, (uchar*) buff, buff_len))
-          tmp_errno= errno;
+         my_fstat(hist_file, &fstate, MYF(0));
+         my_close(hist_file, MYF(MY_WME));
+        id= fstate.st_size + 1;
       }
-      const uchar uh[]= "# User@Host: ";
-      if (my_b_write(&log_file, uh, sizeof(uh) - 1))
-        tmp_errno= errno;
-      if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
-        tmp_errno= errno;
-      if (my_b_write(&log_file, (uchar*) "\n", 1))
-        tmp_errno= errno;
-    }
-    /* For slow query log */
-    sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
-    sprintf(lock_time_buff,  "%.6f", ulonglong2double(lock_utime)/1000000.0);
-    if (my_b_printf(&log_file,
-                    "# Query_time: %s  Lock_time: %s"
-                    " Rows_sent: %lu  Rows_examined: %lu\n",
-                    query_time_buff, lock_time_buff,
-                    (ulong) thd->sent_row_count,
-                    (ulong) thd->examined_row_count) == (uint) -1)
-      tmp_errno= errno;
-    if (thd->db && strcmp(thd->db, db))
-    {						// Database changed
-      if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
-        tmp_errno= errno;
-      strmov(db,thd->db);
-    }
-    if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
-    {
-      end=strmov(end, ",last_insert_id=");
-      end=longlong10_to_str((longlong)
-                            thd->first_successful_insert_id_in_prev_stmt_for_binlog,
-                            end, -10);
+      else
+        id= 1;
     }
-    // Save value if we do an insert.
-    if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
+    // else .... we read the next value in the file!
+    else
     {
-      if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
-      {
-        end=strmov(end,",insert_id=");
-        end=longlong10_to_str((longlong)
-                              thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
-                              end, -10);
-      }
+      my_seek(file, 0, 0, MYF(MY_WME));
+      read_id= (uchar *)my_malloc(sizeof(ulonglong), MYF(MY_ZEROFILL));
+      my_read(file, read_id, sizeof(ulonglong), MYF(MY_WME|MY_NABP));
+      id= uint8korr(read_id);
+      id++;
     }
+  }
+  else  // increment the counter
+    id= m_next_id + 1;
 
-    /*
-      This info used to show up randomly, depending on whether the query
-      checked the query start time or not. now we always write current
-      timestamp to the slow log
-    */
-    end= strmov(end, ",timestamp=");
-    end= int10_to_str((long) current_time, end, 10);
+//  DBUG_EXECUTE_IF("set_backup_id", id= obs::is_slave() ? 600 : 500;);
 
-    if (end != buff)
-    {
-      *end++=';';
-      *end='\n';
-      if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
-          my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
-        tmp_errno= errno;
-    }
-    if (is_command)
-    {
-      end= strxmov(buff, "# administrator command: ", NullS);
-      buff_len= (ulong) (end - buff);
-      my_b_write(&log_file, (uchar*) buff, buff_len);
-    }
-    if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
-        my_b_write(&log_file, (uchar*) ";\n",2) ||
-        flush_io_cache(&log_file))
-      tmp_errno= errno;
-    if (tmp_errno)
+  /* 
+    Write the new value to the file
+  */
+  if ((m_next_id != id) && id)
+  {
+    if (!file)
     {
-      error= 1;
-      if (! write_error)
-      {
-        write_error= 1;
-        sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
-      }
+      file_path= make_backup_log_name(buff, BACKUP_SETTINGS_NAME.str, ".obx");
+      file= my_open(file_path, O_RDWR|O_BINARY|O_CREAT, MYF(MY_WME));
+      if (!file)
+        goto err_end; 
     }
+    my_seek(file, 0, 0, MYF(MY_WME));
+    write_id= (uchar *)my_malloc(sizeof(ulonglong), MYF(MY_ZEROFILL));
+    int8store(write_id, id);
+    my_write(file, write_id, sizeof(ulonglong), MYF(MY_WME));
   }
-  (void) pthread_mutex_unlock(&LOCK_log);
-  DBUG_RETURN(error);
+err:
+  if (file > 0)
+    my_close(file,MYF(MY_WME));
+  
+err_end:
+  m_next_id= id;
+  pthread_mutex_unlock(&LOCK_backupid);
+  DBUG_PRINT("backup_log",("The next id is %lu.\n", (ulong)id));
+  if(read_id)
+    my_free(read_id, MYF(0));
+  if(write_id)
+    my_free(write_id, MYF(0));
+  return id;
+}
+
+
+/**
+  Reopen the log file.
+
+  This method opens the log file.
+
+  @param[IN] history Process as history log if TRUE else progress log
+*/
+void MYSQL_BACKUP_LOG::reopen_file(bool history)
+{
+  char *save_name;
+
+  DBUG_ENTER("MYSQL_BACKUP_LOG::reopen_file");
+  if (!is_open())
+  {
+    DBUG_PRINT("info",("log is closed"));
+    DBUG_VOID_RETURN;
+  }
+
+  pthread_mutex_lock(&LOCK_log);
+
+  save_name= name;
+  name= 0;            // Don't free name
+  close(LOG_CLOSE_TO_BE_OPENED);
+
+  /*
+     Note that at this point, log_state != LOG_CLOSED (important for is_open()).
+  */
+
+  open(save_name, log_type, 0, io_cache_type, history);
+  my_free(save_name, MYF(0));
+
+  pthread_mutex_unlock(&LOCK_log);
+
+  DBUG_VOID_RETURN;
 }
 
 
@@ -2502,25 +4385,6 @@ const char *MYSQL_LOG::generate_name(con
 }
 
 
-
-MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
-  :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
-   need_start_event(TRUE), m_table_map_version(0),
-   sync_period_ptr(sync_period),
-   is_relay_log(0), signal_cnt(0),
-   description_event_for_exec(0), description_event_for_queue(0)
-{
-  /*
-    We don't want to initialize locks here as such initialization depends on
-    safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
-    called only in main(). Doing initialization here would make it happen
-    before main().
-  */
-  index_file_name[0] = 0;
-  bzero((char*) &index_file, sizeof(index_file));
-  bzero((char*) &purge_temp, sizeof(purge_temp));
-}
-
 /* this is called only once */
 
 void MYSQL_BIN_LOG::cleanup()

=== modified file 'sql/log.h'
--- a/sql/log.h	2009-11-22 03:59:48 +0000
+++ b/sql/log.h	2009-12-04 22:31:25 +0000
@@ -16,6 +16,47 @@
 #ifndef LOG_H
 #define LOG_H
 
+struct st_backup_history;
+
+/**
+  List of fields for online backup table.
+*/
+enum enum_backup_history_log_field
+{
+  ET_OBH_FIELD_BACKUP_ID = 0, /* start from 0 to correspond with field array */
+  ET_OBH_FIELD_PROCESS_ID,
+  ET_OBH_FIELD_BINLOG_START_POS,
+  ET_OBH_FIELD_BINLOG_FILE,
+  ET_OBH_FIELD_BACKUP_STATE,
+  ET_OBH_FIELD_OPER,
+  ET_OBH_FIELD_ERROR_NUM,
+  ET_OBH_FIELD_NUM_OBJ,
+  ET_OBH_FIELD_TOTAL_BYTES,
+  ET_OBH_FIELD_VP,
+  ET_OBH_FIELD_START_TIME,
+  ET_OBH_FIELD_STOP_TIME,
+  ET_OBH_FIELD_HOST_OR_SERVER,
+  ET_OBH_FIELD_USERNAME,
+  ET_OBH_FIELD_BACKUP_FILE,
+  ET_OBH_FIELD_BACKUP_FILE_PATH,
+  ET_OBH_FIELD_COMMENT,
+  ET_OBH_FIELD_COMMAND,
+  ET_OBH_FIELD_DRIVERS,
+  ET_OBH_FIELD_COUNT /* a cool trick to count the number of fields :) */
+};
+
+/**
+  List of fields for online backup progress table.
+*/
+enum enum_backup_progress_log_field
+{
+  ET_OBP_FIELD_BACKUP_ID_FK = 0, /* start from 0 to correspond with field array */
+  ET_OBP_FIELD_PROG_OBJECT,
+  ET_OBP_FIELD_PROG_ERROR_NUM,
+  ET_OBP_FIELD_PROG_NOTES,
+  ET_OBP_FIELD_PROG_COUNT /* a cool trick to count the number of fields :) */
+};
+
 class Relay_log_info;
 
 class Format_description_log_event;
@@ -171,6 +212,14 @@ enum enum_log_type { LOG_UNKNOWN, LOG_NO
 enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
 
 /*
+  Values for the type enum. This reflects the order of the enum 
+  declaration in the PURGE BACKUP LOGS command.
+*/
+#define TYPE_ENUM_PURGE_BACKUP_LOGS       1
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_ID    2
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_DATE  3
+
+/*
   TODO use mmap instead of IO_CACHE for binlog
   (mmap+fsync is two times faster than write+fsync)
 */
@@ -236,6 +285,67 @@ private:
   time_t last_time;
 };
 
+/*
+  Values for the type enum. This reflects the order of the enum 
+  declaration in the PURGE BACKUP LOGS command.
+*/
+#define TYPE_ENUM_PURGE_BACKUP_LOGS       1
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_ID    2
+#define TYPE_ENUM_PURGE_BACKUP_LOGS_DATE  3
+
+class MYSQL_BACKUP_LOG: public MYSQL_LOG
+{
+public:
+  MYSQL_BACKUP_LOG () 
+  { 
+    headers_written= FALSE; 
+    pthread_mutex_init(&LOCK_backupid, MY_MUTEX_INIT_FAST);
+    m_next_id= 0;
+  }
+  ~MYSQL_BACKUP_LOG()
+  {
+    pthread_mutex_destroy(&LOCK_backupid);
+  }
+  void reopen_file(bool history);
+  bool write(THD *thd, st_backup_history *history_data);
+  bool write(THD *thd, ulonglong backup_id, const char *object, 
+             time_t start, time_t stop, longlong size,
+             longlong progress, int error_num, const char *notes);
+
+  bool open(const char *log_name,
+            enum_log_type log_type,
+            const char *new_name,
+            enum cache_type io_cache_type_arg,
+            bool history);
+
+  bool open_backup_history_log(const char *log_name)
+  {
+    char buf[FN_REFLEN];
+    return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0,
+                WRITE_CACHE, TRUE);
+  }
+  bool open_backup_progress_log(const char *log_name)
+  {
+    char buf[FN_REFLEN];
+    return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0,
+                WRITE_CACHE, FALSE);
+  }
+  ulonglong get_next_backup_id();
+  my_bool check_backup_logs(THD *thd);
+  File get_file() { return log_file.file; }
+  inline pthread_mutex_t* get_log_lock() { return &LOCK_log; }
+
+private:
+  bool write_int(uint num);
+  bool write_long(ulonglong num);
+  bool write_datetime(time_t time_val);
+  bool write_str(const char *str);
+  bool headers_written;
+  ulonglong m_next_id;       ///< the next available backup_id
+  pthread_mutex_t LOCK_backupid; ///< mutex for backupid generation
+};
+
+
 class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
 {
  private:
@@ -464,6 +574,20 @@ public:
                            const char *command_type, uint command_type_len,
                            const char *sql_text, uint sql_text_len,
                            CHARSET_INFO *client_cs)= 0;
+
+  virtual bool log_backup_history(THD *thd, 
+                                  st_backup_history *history_data)= 0;
+
+  virtual bool log_backup_progress(THD *thd,
+                                   ulonglong backup_id,
+                                   const char *object,
+                                   time_t start,
+                                   time_t stop,
+                                   longlong size,
+                                   longlong progress,
+                                   int error_num,
+                                   const char *notes)= 0;
+
   virtual ~Log_event_handler() {}
 };
 
@@ -494,6 +618,27 @@ public:
                            const char *sql_text, uint sql_text_len,
                            CHARSET_INFO *client_cs);
 
+
+  virtual bool log_backup_history(THD *thd, 
+                                  st_backup_history *history_data);
+
+  virtual bool log_backup_progress(THD *thd,
+                                   ulonglong backup_id,
+                                   const char *object,
+                                   time_t start,
+                                   time_t stop,
+                                   longlong size,
+                                   longlong progress,
+                                   int error_num,
+                                   const char *notes);
+  bool purge_backup_logs(THD *thd);
+  bool purge_backup_logs_before_id(THD *thd, 
+                                   ulonglong backup_id, 
+                                   int *rows);
+  bool purge_backup_logs_before_date(THD *thd, 
+                                     my_time_t t, 
+                                     int *rows);
+
   int activate_log(THD *thd, uint log_type);
 };
 
@@ -501,11 +646,15 @@ public:
 /* type of the log table */
 #define QUERY_LOG_SLOW 1
 #define QUERY_LOG_GENERAL 2
+#define BACKUP_HISTORY_LOG 3
+#define BACKUP_PROGRESS_LOG 4
 
 class Log_to_file_event_handler: public Log_event_handler
 {
   MYSQL_QUERY_LOG mysql_log;
   MYSQL_QUERY_LOG mysql_slow_log;
+  MYSQL_BACKUP_LOG mysql_backup_history_log;
+  MYSQL_BACKUP_LOG mysql_backup_progress_log;
   bool is_initialized;
 public:
   Log_to_file_event_handler(): is_initialized(FALSE)
@@ -525,14 +674,37 @@ public:
                            const char *command_type, uint command_type_len,
                            const char *sql_text, uint sql_text_len,
                            CHARSET_INFO *client_cs);
+
+  virtual bool log_backup_history(THD *thd, 
+                                   st_backup_history *history_data);
+
+  virtual bool log_backup_progress(THD *thd,
+                                   ulonglong backup_id,
+                                   const char *object,
+                                   time_t start,
+                                   time_t stop,
+                                   longlong size,
+                                   longlong progress,
+                                   int error_num,
+                                   const char *notes);
+
   void flush();
+  void flush_backup_logs();
+  bool purge_backup_logs();
   void init_pthread_objects();
   MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
   MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
+  MYSQL_BACKUP_LOG *get_backup_history_log()
+  { return &mysql_backup_history_log; }
+  MYSQL_BACKUP_LOG *get_backup_progress_log() 
+  { return &mysql_backup_progress_log; }
 };
 
 
-/* Class which manages slow, general and error log event handlers */
+/*
+   Class which manages slow, general, error log, backup history, and
+   backup progress event handlers.
+*/
 class LOGGER
 {
   rw_lock_t LOCK_logger;
@@ -547,6 +719,8 @@ class LOGGER
   Log_event_handler *error_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
   Log_event_handler *slow_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
   Log_event_handler *general_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+  Log_event_handler *backup_history_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
+  Log_event_handler *backup_progress_log_handler_list[MAX_LOG_HANDLERS_NUM + 1];
 
 public:
 
@@ -570,6 +744,14 @@ public:
   void init_base();
   void init_log_tables();
   bool flush_logs(THD *thd);
+  bool flush_backup_logs(THD *thd);
+  bool purge_backup_logs(THD *thd);
+  bool purge_backup_logs_before_id(THD *thd, 
+                                   ulonglong backup_id, 
+                                   int *rows);
+  bool purge_backup_logs_before_date(THD *thd, 
+                                     my_time_t t, 
+                                     int *rows);
   /* Perform basic logger cleanup. this will leave e.g. error log open. */
   void cleanup_base();
   /* Free memory. Nothing could be logged after this function is called */
@@ -583,13 +765,30 @@ public:
   bool general_log_write(THD *thd, enum enum_server_command command,
                          const char *query, uint query_length);
 
+
+  bool backup_history_log_write(THD *thd, 
+                                st_backup_history *history_data);
+  bool backup_progress_log_write(THD *thd,
+                                 ulonglong backup_id,
+                                 const char *object,
+                                 time_t start,
+                                 time_t stop,
+                                 longlong size,
+                                 longlong progress,
+                                 int error_num,
+                                 const char *notes);
+
   /* we use this function to setup all enabled log event handlers */
   int set_handlers(uint error_log_printer,
                    uint slow_log_printer,
                    uint general_log_printer);
+  int set_backup_handlers(uint backup_history_log_printer,
+                          uint backup_progress_log_printer);
   void init_error_log(uint error_log_printer);
   void init_slow_log(uint slow_log_printer);
   void init_general_log(uint general_log_printer);
+  void init_backup_history_log(uint backup_history_log_printer);
+  void init_backup_progress_log(uint backup_progress_log_printer);
   void deactivate_log_handler(THD* thd, uint log_type);
   bool activate_log_handler(THD* thd, uint log_type);
   MYSQL_QUERY_LOG *get_slow_log_file_handler()
@@ -604,6 +803,18 @@ public:
       return file_log_handler->get_mysql_log();
     return NULL;
   }
+  MYSQL_BACKUP_LOG *get_backup_history_log_file_handler()
+  { 
+    if (file_log_handler)
+      return file_log_handler->get_backup_history_log();
+    return NULL;
+  }
+  MYSQL_BACKUP_LOG *get_backup_progress_log_file_handler()
+  { 
+    if (file_log_handler)
+      return file_log_handler->get_backup_progress_log();
+    return NULL;
+  }
 };
 
 enum enum_binlog_format {

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2009-12-03 18:37:38 +0000
+++ b/sql/mysql_priv.h	2009-12-04 22:31:25 +0000
@@ -621,6 +621,7 @@ protected:
 
 /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
 #define UNDEF_POS (-1)
+#define BACKUP_WAIT_TIMEOUT_DEFAULT 50
 #ifdef EXTRA_DEBUG
 /**
   Sync points allow us to force the server to reach a certain line of code
@@ -1335,6 +1336,9 @@ extern LEX_STRING INFORMATION_SCHEMA_NAM
 extern LEX_STRING MYSQL_SCHEMA_NAME;
 extern LEX_STRING GENERAL_LOG_NAME;
 extern LEX_STRING SLOW_LOG_NAME;
+extern LEX_STRING BACKUP_HISTORY_LOG_NAME;
+extern LEX_STRING BACKUP_PROGRESS_LOG_NAME;
+extern LEX_STRING BACKUP_SETTINGS_NAME;
 
 extern const LEX_STRING partition_keywords[];
 ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
@@ -1521,6 +1525,7 @@ bool rename_temporary_table(THD* thd, TA
 void flush_tables();
 bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
 char *make_default_log_name(char *buff,const char* log_ext);
+char *make_backup_log_name(char *buff, const char *name, const char* log_ext);
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 uint fast_alter_partition_table(THD *thd, TABLE *table,
@@ -1819,6 +1824,7 @@ extern time_t server_start_time, flush_s
 #endif /* MYSQL_SERVER */
 #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
 extern uint mysql_data_home_len;
+extern uint mysql_real_data_home_len;
 
 extern MYSQL_PLUGIN_IMPORT char  *mysql_data_home;
 extern char server_version[SERVER_VERSION_LENGTH];
@@ -1919,7 +1925,11 @@ extern MYSQL_PLUGIN_IMPORT bool mysqld_e
 extern bool opt_large_files, server_id_supplied;
 extern bool opt_update_log, opt_bin_log, opt_error_log;
 extern my_bool opt_log, opt_slow_log;
+extern my_bool opt_backup_history_log;
+extern my_bool opt_backup_progress_log;
+extern my_bool opt_mysql_backup;
 extern ulong log_output_options;
+extern ulong log_backup_output_options;
 extern my_bool opt_log_queries_not_using_indexes;
 extern bool opt_disable_networking, opt_skip_show_db;
 extern bool opt_ignore_builtin_innodb;
@@ -1936,6 +1946,8 @@ extern my_bool opt_readonly, lower_case_
 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
 extern my_bool opt_secure_auth;
 extern char* opt_secure_file_priv;
+extern char* opt_secure_backup_file_priv;
+extern size_t opt_secure_backup_file_priv_len;
 extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
 extern my_bool sp_automatic_privileges, opt_noacl;
 extern my_bool opt_old_style_user_limits, trust_function_creators;
@@ -1951,10 +1963,14 @@ extern uint opt_large_page_size;
 #ifdef MYSQL_SERVER
 extern char *opt_logname, *opt_slow_logname;
 extern const char *log_output_str;
+extern char *opt_backup_history_logname, *opt_backup_progress_logname,
+            *opt_backup_settings_name;
+extern const char *log_backup_output_str;
 
 extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
 extern LOGGER logger;
-extern TABLE_LIST general_log, slow_log;
+extern TABLE_LIST general_log, slow_log, 
+       backup_history_log, backup_progress_log;
 extern FILE *bootstrap_file;
 extern int bootstrap_error;
 extern FILE *stderror_file;

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2009-12-03 18:59:52 +0000
+++ b/sql/mysqld.cc	2009-12-04 22:31:25 +0000
@@ -26,6 +26,7 @@
 #include "mysqld_suffix.h"
 #include "mysys_err.h"
 #include "events.h"
+#include "bml.h"
 #include "probes_mysql.h"
 #include "debug_sync.h"
 
@@ -237,6 +238,14 @@ extern "C" int gethostname(char *name, i
 #endif
 
 extern "C" sig_handler handle_segfault(int sig);
+/* 
+  Online backup initialization and shutdown functions - defined in 
+  backup/kernel.cc
+ */
+#ifndef EMBEDDED_LIBRARY
+//int backup_init();
+//void backup_shutdown();
+#endif
 
 #if defined(__linux__)
 #define ENABLE_TEMP_POOL 1
@@ -412,9 +421,17 @@ static pthread_cond_t COND_thread_cache,
 
 /* Global variables */
 
+extern Backup_sx_lock *BML_instance;
+extern Backup_sx_lock *BCB_instance;
 bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
 my_bool opt_log, opt_slow_log;
+my_bool opt_backup_history_log;
+my_bool opt_backup_progress_log;
+my_bool opt_mysql_backup;
 ulong log_output_options;
+ulong log_backup_output_options;
+my_bool opt_backup_elevation= TRUE;
+my_bool opt_restore_elevation= TRUE;
 my_bool opt_log_queries_not_using_indexes= 0;
 bool opt_error_log= IF_WIN(1,0);
 bool opt_disable_networking=0, opt_skip_show_db=0;
@@ -489,6 +506,8 @@ my_bool relay_log_recovery;
 my_bool opt_sync_frm, opt_allow_suspicious_udfs;
 my_bool opt_secure_auth= 0;
 char* opt_secure_file_priv= 0;
+char* opt_secure_backup_file_priv= NULL;
+size_t opt_secure_backup_file_priv_len= 0;
 my_bool opt_log_slow_admin_statements= 0;
 my_bool opt_log_slow_slave_statements= 0;
 my_bool lower_case_file_system= 0;
@@ -571,6 +590,7 @@ uint sync_binlog_period= 0, sync_relaylo
 ulong expire_logs_days = 0;
 ulong rpl_recovery_rank=0;
 const char *log_output_str= "FILE";
+const char *log_backup_output_str= "TABLE";
 
 time_t server_start_time, flush_status_time;
 
@@ -586,6 +606,7 @@ char mysql_real_data_home[FN_REFLEN],
 char err_shared_dir[FN_REFLEN];
 char mysql_unpacked_real_data_home[FN_REFLEN];
 int mysql_unpacked_real_data_home_len;
+uint mysql_real_data_home_len;
 uint reg_ext_length;
 const key_map key_map_empty(0);
 key_map key_map_full(0);                        // Will be initialized later
@@ -685,6 +706,8 @@ char *master_info_file;
 char *relay_log_info_file, *report_user, *report_password, *report_host;
 char *opt_relay_logname = 0, *opt_relaylog_index_name=0;
 char *opt_logname, *opt_slow_logname;
+char *opt_backup_history_logname, *opt_backup_progress_logname,
+     *opt_backup_settings_name;
 
 /* Static variables */
 
@@ -1308,6 +1331,9 @@ void clean_up(bool print_message)
     udf_free();
 #endif
   }
+#ifndef EMBEDDED_LIBRARY
+//  backup_shutdown();
+#endif
   plugin_shutdown();
   ha_end();
   if (tc_log)
@@ -1330,6 +1356,9 @@ void clean_up(bool print_message)
   my_free(sys_init_connect.value, MYF(MY_ALLOW_ZERO_PTR));
   my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR));
   my_free(sys_var_general_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
+  my_free(sys_var_backup_history_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
+  my_free(sys_var_backup_progress_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
+  my_free(sys_var_backupdir.value, MYF(MY_ALLOW_ZERO_PTR));
   my_free(sys_var_slow_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
   free_tmpdir(&mysql_tmpdir_list);
 #ifdef HAVE_REPLICATION
@@ -1338,6 +1367,8 @@ void clean_up(bool print_message)
   x_free(opt_bin_logname);
   x_free(opt_relay_logname);
   x_free(opt_secure_file_priv);
+  x_free(opt_secure_backup_file_priv);
+  opt_secure_backup_file_priv_len= 0;
   bitmap_free(&temp_pool);
   free_max_user_conn();
 #ifdef HAVE_REPLICATION
@@ -1453,6 +1484,8 @@ static void clean_up_mutexes()
   (void) pthread_cond_destroy(&COND_thread_cache);
   (void) pthread_cond_destroy(&COND_flush_thread_cache);
   (void) pthread_cond_destroy(&COND_manager);
+  Backup_sx_lock::destroy_BML_instance();
+  Backup_sx_lock::destroy_BCB_instance();
 }
 
 #endif /*EMBEDDED_LIBRARY*/
@@ -2823,6 +2856,8 @@ pthread_handler_t signal_hand(void *arg 
       /* switch to the old log message processing */
       logger.set_handlers(LOG_FILE, opt_slow_log ? LOG_FILE:LOG_NONE,
                           opt_log ? LOG_FILE:LOG_NONE);
+      logger.set_backup_handlers(opt_backup_history_log ? LOG_FILE : LOG_NONE,
+                                 opt_backup_progress_log ? LOG_FILE : LOG_NONE);
       DBUG_PRINT("info",("Got signal: %d  abort_loop: %d",sig,abort_loop));
       if (!abort_loop)
       {
@@ -2861,6 +2896,12 @@ pthread_handler_t signal_hand(void *arg 
                             opt_slow_log ? log_output_options : LOG_NONE,
                             opt_log ? log_output_options : LOG_NONE);
       }
+      if (log_backup_output_options & LOG_NONE)
+        logger.set_backup_handlers(LOG_NONE, LOG_NONE);
+      else
+        logger.set_backup_handlers(
+          opt_backup_history_log ? log_backup_output_options : LOG_NONE,
+          opt_backup_progress_log ? log_backup_output_options : LOG_NONE);
       break;
 #ifdef USE_ONE_SIGNAL_HAND
     case THR_SERVER_ALARM:
@@ -3029,6 +3070,10 @@ SHOW_VAR com_status_vars[]= {
   {"alter_tablespace",     (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLESPACE]), SHOW_LONG_STATUS},
   {"analyze",              (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS},
   {"begin",                (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BEGIN]), SHOW_LONG_STATUS},
+  {"backup",               (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP]), SHOW_LONG_STATUS},
+#ifdef BACKUP_TEST
+  {"backup_test",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TEST]), SHOW_LONG_STATUS},
+#endif
   {"binlog",               (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BINLOG_BASE64_EVENT]), SHOW_LONG_STATUS},
   {"call_procedure",       (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CALL]), SHOW_LONG_STATUS},
   {"change_db",            (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CHANGE_DB]), SHOW_LONG_STATUS},
@@ -3080,6 +3125,7 @@ SHOW_VAR com_status_vars[]= {
   {"prepare_sql",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PREPARE]), SHOW_LONG_STATUS},
   {"purge",                (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE]), SHOW_LONG_STATUS},
   {"purge_before_date",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BEFORE]), SHOW_LONG_STATUS},
+  {"purge_bup_log",        (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PURGE_BACKUP_LOGS]), SHOW_LONG_STATUS},
   {"release_savepoint",    (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RELEASE_SAVEPOINT]), SHOW_LONG_STATUS},
   {"rename_table",         (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_TABLE]), SHOW_LONG_STATUS},
   {"rename_user",          (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RENAME_USER]), SHOW_LONG_STATUS},
@@ -3088,6 +3134,7 @@ SHOW_VAR com_status_vars[]= {
   {"replace_select",       (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE_SELECT]), SHOW_LONG_STATUS},
   {"reset",                (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESET]), SHOW_LONG_STATUS},
   {"resignal",             (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESIGNAL]), SHOW_LONG_STATUS},
+  {"restore",              (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESTORE]), SHOW_LONG_STATUS},
   {"revoke",               (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS},
   {"revoke_all",           (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
   {"rollback",             (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS},
@@ -3513,10 +3560,39 @@ static int init_common_variables(const c
                       "--log-slow-queries option, log tables are used. "
                       "To enable logging to files use the --log-output=file option.");
 
+  if (opt_backup_history_log && opt_backup_history_logname &&
+      *opt_backup_history_logname &&
+      !(log_backup_output_options & (LOG_FILE | LOG_NONE)))
+    sql_print_warning("Although a path was specified for the "
+                      "--log-backup-history option, log tables are used. "
+                      "To enable logging to files use the --log-backup-output option.");
+
+  if (opt_backup_progress_log && opt_backup_progress_logname &&
+      *opt_backup_progress_logname &&
+      !(log_backup_output_options & (LOG_FILE | LOG_NONE)))
+    sql_print_warning("Although a path was specified for the "
+                      "--log-backup-progress option, log tables are used. "
+                      "To enable logging to files use the --log-backup-output option.");
+
   s= opt_logname ? opt_logname : make_default_log_name(buff, ".log");
   sys_var_general_log_path.value= my_strdup(s, MYF(0));
   sys_var_general_log_path.value_length= strlen(s);
 
+  /*
+    Set defaults for history and progress log paths.
+  */
+  s= (opt_backup_history_logname && *opt_backup_history_logname) ?
+    opt_backup_history_logname : 
+    make_backup_log_name(buff, BACKUP_HISTORY_LOG_NAME.str, ".log");
+  sys_var_backup_history_log_path.value= my_strdup(s, MYF(0));
+  sys_var_backup_history_log_path.value_length= BACKUP_HISTORY_LOG_NAME.length;
+
+  s= (opt_backup_progress_logname && *opt_backup_progress_logname) ?
+    opt_backup_progress_logname : 
+    make_backup_log_name(buff, BACKUP_PROGRESS_LOG_NAME.str, ".log");
+  sys_var_backup_progress_log_path.value= my_strdup(s, MYF(0));
+  sys_var_backup_progress_log_path.value_length= BACKUP_PROGRESS_LOG_NAME.length;
+
   s= opt_slow_logname ? opt_slow_logname : make_default_log_name(buff, "-slow.log");
   sys_var_slow_log_path.value= my_strdup(s, MYF(0));
   sys_var_slow_log_path.value_length= strlen(s);
@@ -3641,6 +3717,13 @@ static int init_thread_environment()
 #endif
   (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
   (void) pthread_cond_init(&COND_server_started,NULL);
+
+  /*
+    Initialize the backup DDL and commit blockers
+  */
+  BML_instance= Backup_sx_lock::get_BML_instance();
+  BCB_instance= Backup_sx_lock::get_BCB_instance();
+
   sp_cache_init();
 #ifdef HAVE_EVENT_SCHEDULER
   Events::init_mutexes();
@@ -3998,6 +4081,14 @@ will be ignored as the --log-bin option 
     plugins_are_initialized= TRUE;  /* Don't separate from init function */
   }
 
+#ifndef EMBEDDED_LIBRARY
+//  if (backup_init())
+//  {
+//    sql_print_error("Failed to initialize MySQL backup.");
+//    unireg_abort(1);
+//  }
+#endif
+
   if (opt_help)
     unireg_abort(0);
 
@@ -4045,7 +4136,10 @@ will be ignored as the --log-bin option 
 
 #ifdef WITH_CSV_STORAGE_ENGINE
   if (opt_bootstrap)
+  {
     log_output_options= LOG_FILE;
+    log_backup_output_options= LOG_FILE;
+  }
   else
     logger.init_log_tables();
 
@@ -4077,9 +4171,40 @@ will be ignored as the --log-bin option 
     logger.set_handlers(LOG_FILE, opt_slow_log ? log_output_options:LOG_NONE,
                         opt_log ? log_output_options:LOG_NONE);
   }
+  if (log_backup_output_options & LOG_NONE)
+  {
+    /*
+      Issue a warining if there were specified additional options to the
+      log-backup-output along with NONE. Probably this wasn't what user wanted.
+    */
+    if ((log_backup_output_options & LOG_NONE) && 
+        (log_backup_output_options & ~LOG_NONE))
+      sql_print_warning("There were other values specified to "
+                        "log-backup-output besides NONE. Disabling "
+                        "backup logs anyway.");
+    logger.set_backup_handlers(LOG_NONE, LOG_NONE);
+  }
+  else
+  {
+    /* fall back to the log files if tables are not present */
+    LEX_STRING csv_name={C_STRING_WITH_LEN("csv")};
+    if (!plugin_is_ready(&csv_name, MYSQL_STORAGE_ENGINE_PLUGIN))
+    {
+      /* purecov: begin inspected */
+      sql_print_error("CSV engine is not present, falling back to the "
+                      "log files");
+      log_backup_output_options= 
+        (log_backup_output_options & ~LOG_TABLE) | LOG_FILE;
+      /* purecov: end */
+    }
+
+    logger.set_backup_handlers(log_backup_output_options,
+      log_backup_output_options);
+  }
 #else
   logger.set_handlers(LOG_FILE, opt_slow_log ? LOG_FILE:LOG_NONE,
                       opt_log ? LOG_FILE:LOG_NONE);
+  logger.set_backup_handlers(LOG_FILE, LOG_FILE);
 #endif
 
   /*
@@ -5724,14 +5849,18 @@ enum options_mysqld
   OPT_PLUGIN_LOAD,
   OPT_PLUGIN_DIR,
   OPT_LOG_OUTPUT,
+  OPT_LOG_BACKUP_OUTPUT,
   OPT_PORT_OPEN_TIMEOUT,
   OPT_PROFILING,
   OPT_KEEP_FILES_ON_CREATE,
   OPT_GENERAL_LOG,
+  OPT_BACKUP_HISTORY_LOG,
+  OPT_BACKUP_PROGRESS_LOG,
   OPT_SLOW_LOG,
   OPT_THREAD_HANDLING,
   OPT_INNODB_ROLLBACK_ON_TIMEOUT,
   OPT_SECURE_FILE_PRIV,
+  OPT_SECURE_BACKUP_FILE_PRIV,
   OPT_MIN_EXAMINED_ROW_LIMIT,
   OPT_LOG_SLOW_SLAVE_STATEMENTS,
 #if defined(ENABLED_DEBUG_SYNC)
@@ -5744,7 +5873,14 @@ enum options_mysqld
   OPT_IGNORE_BUILTIN_INNODB,
   OPT_SYNC_RELAY_LOG,
   OPT_SYNC_RELAY_LOG_INFO,
-  OPT_SYNC_MASTER_INFO
+  OPT_SYNC_MASTER_INFO,
+  OPT_BACKUP_HISTORY_LOG_FILE,
+  OPT_BACKUP_PROGRESS_LOG_FILE,
+  OPT_MYSQL_BACKUP,
+  OPT_BACKUP_ELEVATION,
+  OPT_RESTORE_ELEVATION,
+  OPT_RESTORE_PRECHECK,
+  OPT_RESTORE_DISABLES_EVENTS  
 };
 
 
@@ -5784,6 +5920,20 @@ struct my_option my_long_options[] =
    "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.",
    (uchar**) &sp_automatic_privileges, (uchar**) &sp_automatic_privileges,
    0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+  {"backupdir", 'B', "Path used to store backup data.", (uchar**) &sys_var_backupdir.value,
+   (uchar**) &sys_var_backupdir.value, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"backup-history-log", OPT_BACKUP_HISTORY_LOG,
+   "Enable|disable backup history log", (uchar**) &opt_backup_history_log,
+   (uchar**) &opt_backup_history_log, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+  {"backup-progress-log", OPT_BACKUP_PROGRESS_LOG,
+   "Enable|disable backup progress log", (uchar**) &opt_backup_progress_log,
+   (uchar**) &opt_backup_progress_log, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+  {"mysql-backup", OPT_MYSQL_BACKUP,
+   "Enable|disable MySQL Backup system", (uchar**) &opt_mysql_backup,
+   (uchar**) &opt_mysql_backup, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
+  {"backup-elevation", OPT_BACKUP_ELEVATION,
+   "Enable|disable privilege elevaton for backup", (uchar**) &opt_backup_elevation,
+   (uchar**) &opt_backup_elevation, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
   {"basedir", 'b',
    "Path to installation directory. All paths are usually resolved relative to this.",
    (uchar**) &mysql_home_ptr, (uchar**) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG,
@@ -5995,6 +6145,16 @@ Disable with --skip-super-large-pages.",
   {"general_log_file", OPT_GENERAL_LOG_FILE,
    "Log connections and queries to given file.", (uchar**) &opt_logname,
    (uchar**) &opt_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"backup_history_log_file", OPT_BACKUP_HISTORY_LOG_FILE,
+   "Log backup history to a given file.", 
+   (uchar**) &opt_backup_history_logname,
+   (uchar**) &opt_backup_history_logname, 0, GET_STR, 
+   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"backup_progress_log_file", OPT_BACKUP_PROGRESS_LOG_FILE,
+   "Log backup progress to a given file.", 
+   (uchar**) &opt_backup_progress_logname,
+   (uchar**) &opt_backup_progress_logname, 0, GET_STR, 
+   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"log-bin", OPT_BIN_LOG,
    "Log update queries in binary format. Optional (but strongly recommended "
    "to avoid replication problems if server's hostname changes) argument "
@@ -6045,6 +6205,11 @@ Disable with --skip-super-large-pages.",
    "FILE or NONE.",
    (uchar**) &log_output_str, (uchar**) &log_output_str, 0,
    GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+  {"log-backup-output", OPT_LOG_BACKUP_OUTPUT,
+   "Syntax: log-backup-output[=value[,value...]], where \"value\" could be TABLE, "
+   "FILE or NONE.",
+   (uchar**) &log_backup_output_str, (uchar**) &log_backup_output_str, 0,
+   GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 #endif
   {"log-queries-not-using-indexes", OPT_LOG_QUERIES_NOT_USING_INDEXES,
    "Log queries that are executed without benefit of any index to the slow log if it is open.",
@@ -6288,6 +6453,12 @@ relay logs.",
 thread is in the relay logs.",
    (uchar**) &relay_log_info_file, (uchar**) &relay_log_info_file, 0, GET_STR,
    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"restore-elevation", OPT_RESTORE_ELEVATION,
+   "Enable|disable privilege elevaton for restore", (uchar**) &opt_restore_elevation,
+   (uchar**) &opt_restore_elevation, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
+  {"restore-precheck", OPT_RESTORE_PRECHECK,
+   "Enable|disable privilege prechecking for restore", (uchar**) &global_system_variables.restore_precheck,
+   (uchar**) &global_system_variables.restore_precheck, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
   {"replicate-do-db", OPT_REPLICATE_DO_DB,
    "Tells the slave thread to restrict replication to the specified database. To specify more than one database, use the directive multiple times, once for each database. Note that this will only work if you do not use cross-database queries such as UPDATE some_db.some_table SET foo='bar' while having selected a different or no database. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-do-table=db_name.%.",
    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -6332,6 +6503,9 @@ Can't be set to 1 if --log-slave-updates
    MYSQL_PORT, 0, 0, 0, 0, 0},
   {"report-user", OPT_REPORT_USER, "Undocumented.", (uchar**) &report_user,
    (uchar**) &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"restore-disables-events", OPT_RESTORE_DISABLES_EVENTS,
+   "Enable|disable events on restore", (uchar**) &global_system_variables.restore_disables_events,
+   (uchar**) &global_system_variables.restore_disables_events, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
   {"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented.",
    (uchar**) &rpl_recovery_rank, (uchar**) &rpl_recovery_rank, 0, GET_ULONG,
    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -6356,6 +6530,11 @@ Can't be set to 1 if --log-slave-updates
    "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory",
    (uchar**) &opt_secure_file_priv, (uchar**) &opt_secure_file_priv, 0,
    GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"secure-backup-file-priv", OPT_SECURE_BACKUP_FILE_PRIV,
+   "Limit MySQL Backup operations to files within specified directory",
+   (uchar**) &opt_secure_backup_file_priv,
+   (uchar**) &opt_secure_backup_file_priv, 0,
+   GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"server-id",	OPT_SERVER_ID,
    "Uniquely identifies the server instance in the community of replication partners.",
    (uchar**) &server_id, (uchar**) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, UINT_MAX32,
@@ -7689,6 +7868,7 @@ static int mysql_init_variables(void)
   opt_log= opt_slow_log= 0;
   opt_update_log= 0;
   log_output_options= find_bit_type(log_output_str, &log_output_typelib);
+  log_backup_output_options= find_bit_type(log_backup_output_str, &log_output_typelib);
   opt_bin_log= 0;
   opt_disable_networking= opt_skip_show_db=0;
   opt_ignore_builtin_innodb= 0;
@@ -7696,6 +7876,8 @@ static int mysql_init_variables(void)
   opt_tc_log_file= (char *)"tc.log";      // no hostname in tc_log file name !
   opt_secure_auth= 0;
   opt_secure_file_priv= 0;
+  opt_secure_backup_file_priv= NULL;
+  opt_secure_backup_file_priv_len= 0;
   opt_bootstrap= opt_myisam_log= 0;
   mqh_used= 0;
   segfaulted= kill_in_progress= 0;
@@ -7936,6 +8118,13 @@ mysqld_get_one_option(int optid,
     if (default_collation_name == compiled_default_collation_name)
       default_collation_name= 0;
     break;
+  case 'B':
+  {
+    sys_var_backupdir.value= my_strdup(argument, MYF(MY_WME));
+    sys_var_backupdir.value_length= (sys_var_backupdir.value ?
+                                     strlen(sys_var_backupdir.value) : 0);
+    break;
+  }
   case 'l':
     WARN_DEPRECATED(NULL, 7, 0, "--log", "'--general-log'/'--general-log-file'");
     opt_log=1;
@@ -8132,6 +8321,24 @@ mysqld_get_one_option(int optid,
   }
     break;
   }
+  case  OPT_LOG_BACKUP_OUTPUT:
+  {
+    if (!argument || !argument[0])
+    {
+      log_backup_output_options= LOG_TABLE;
+      log_backup_output_str= log_output_typelib.type_names[1];
+    }
+    else
+    {
+      log_backup_output_str= argument;
+      log_backup_output_options=
+        find_bit_type_or_exit(argument, &log_output_typelib, opt->name,
+                              &error);
+      if (error)
+        return 1;
+  }
+    break;
+  }
 #endif
   case OPT_EVENT_SCHEDULER:
 #ifndef HAVE_EVENT_SCHEDULER
@@ -8693,6 +8900,20 @@ static int fix_paths(void)
   convert_dirname(lc_messages_dir, lc_messages_dir, NullS);
   (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir
   (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home);
+
+  /* Set default backupdir variable if value is not set. */
+  if (!sys_var_backupdir.value)
+    sys_var_backupdir.value= default_backupdir();
+  if (sys_var_backupdir.value)
+  {
+    sys_var_backupdir.value_length= strlen(sys_var_backupdir.value);
+    /* Warn if not a directory. */
+    (void) is_usable_directory(NULL, sys_var_backupdir.name,
+                               sys_var_backupdir.value, mysql_real_data_home);
+  }
+  else
+    sys_var_backupdir.value_length= 0; /* purecov: inspected */
+
   (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home);
   (void) my_load_path(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
                                       get_relative_path(PLUGINDIR), mysql_home);
@@ -8735,6 +8956,17 @@ static int fix_paths(void)
     my_free(opt_secure_file_priv, MYF(0));
     opt_secure_file_priv= my_strdup(buff, MYF(MY_FAE));
   }
+
+  if (opt_secure_backup_file_priv)
+  {
+    opt_secure_backup_file_priv_len= strlen(opt_secure_backup_file_priv);
+    /* Warn if not a directory. */
+    (void) is_usable_directory(NULL, "secure_backup_file_priv",
+                               opt_secure_backup_file_priv,
+                               mysql_real_data_home);
+  }
+  else
+    opt_secure_backup_file_priv_len= 0;
   return 0;
 }
 

=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc	2009-12-03 18:37:38 +0000
+++ b/sql/set_var.cc	2009-12-04 22:31:25 +0000
@@ -76,7 +76,7 @@ extern ulong ndb_report_thresh_binlog_me
 #endif
 
 extern CHARSET_INFO *character_set_filesystem;
-
+extern my_bool disable_slaves;
 
 static HASH system_variable_hash;
 
@@ -148,8 +148,14 @@ static uchar *get_tmpdir(THD *thd);
 static int  sys_check_log_path(THD *thd,  set_var *var);
 static bool sys_update_general_log_path(THD *thd, set_var * var);
 static void sys_default_general_log_path(THD *thd, enum_var_type type);
+static bool sys_update_backup_history_log_path(THD *thd, set_var * var);
+static void sys_default_backup_history_log_path(THD *thd, enum_var_type type);
+static bool sys_update_backup_progress_log_path(THD *thd, set_var * var);
+static void sys_default_backup_progress_log_path(THD *thd, enum_var_type type);
 static bool sys_update_slow_log_path(THD *thd, set_var * var);
 static void sys_default_slow_log_path(THD *thd, enum_var_type type);
+static bool sys_update_backupdir(THD *thd, set_var * var);
+static void sys_default_backupdir(THD *thd, enum_var_type type);
 
 /*
   Variable definition list
@@ -178,6 +184,9 @@ static sys_var_bool_ptr	sys_automatic_sp
 static sys_var_const            sys_back_log(&vars, "back_log",
                                              OPT_GLOBAL, SHOW_LONG,
                                              (uchar*) &back_log);
+static sys_var_const    sys_backup_elevation(&vars, "backup_elevation",
+                                             OPT_GLOBAL, SHOW_BOOL,
+                                             (uchar*) &opt_backup_elevation);
 static sys_var_const_os_str       sys_basedir(&vars, "basedir", mysql_home);
 static sys_var_long_ptr	sys_binlog_cache_size(&vars, "binlog_cache_size",
 					      &binlog_cache_size);
@@ -236,6 +245,7 @@ static sys_var_long_ptr	sys_concurrent_i
 static sys_var_long_ptr	sys_connect_timeout(&vars, "connect_timeout",
 					    &connect_timeout);
 static sys_var_const_os_str       sys_datadir(&vars, "datadir", mysql_real_data_home);
+static sys_var_backup_wait_timeout sys_backup_wait_timeout(&vars, "backup_wait_timeout");
 #ifndef DBUG_OFF
 static sys_var_thd_dbug        sys_dbug(&vars, "debug");
 #endif
@@ -250,6 +260,9 @@ static sys_var_long_ptr	sys_delayed_inse
 static sys_var_long_ptr	sys_delayed_queue_size(&vars, "delayed_queue_size",
 					       &delayed_queue_size);
 
+static sys_var_thd_bool	sys_restore_disables_events(&vars, 
+                                                    "restore_disables_events",
+                                                  &SV::restore_disables_events);
 #ifdef HAVE_EVENT_SCHEDULER
 static sys_var_event_scheduler sys_event_scheduler(&vars, "event_scheduler");
 #endif
@@ -491,6 +504,11 @@ static sys_var_thd_ulong	sys_read_rnd_bu
 					       &SV::read_rnd_buff_size);
 static sys_var_thd_ulong	sys_div_precincrement(&vars, "div_precision_increment",
                                               &SV::div_precincrement);
+static sys_var_const    sys_restore_elevation(&vars, "restore_elevation",
+                                              OPT_GLOBAL, SHOW_BOOL,
+                                              (uchar*) &opt_restore_elevation);
+static sys_var_thd_bool sys_restore_precheck(&vars, "restore_precheck",
+                                              &SV::restore_precheck);
 static sys_var_long_ptr	sys_rpl_recovery_rank(&vars, "rpl_recovery_rank",
 					      &rpl_recovery_rank);
 
@@ -575,6 +593,8 @@ sys_query_cache_wlock_invalidate(&vars, 
 static sys_var_bool_ptr	sys_secure_auth(&vars, "secure_auth", &opt_secure_auth);
 static sys_var_const_str_ptr sys_secure_file_priv(&vars, "secure_file_priv",
                                              &opt_secure_file_priv);
+static sys_var_const_str_ptr sys_secure_backup_file_priv(&vars, "secure_backup_file_priv",
+                                             &opt_secure_backup_file_priv);
 static sys_var_long_ptr	sys_server_id(&vars, "server_id", &server_id, fix_server_id);
 static sys_var_bool_ptr	sys_slave_compressed_protocol(&vars, "slave_compressed_protocol",
 						      &opt_slave_compressed_protocol);
@@ -647,6 +667,8 @@ static sys_var_thd_ulonglong	sys_tmp_tab
 					   &SV::tmp_table_size);
 static sys_var_bool_ptr  sys_timed_mutexes(&vars, "timed_mutexes",
                                     &timed_mutexes);
+//static sys_var_bool_ptr  sys_disable_slaves(&vars, "disable_slave_connections",
+//                                             &disable_slaves);
 static sys_var_const_str	sys_version(&vars, "version", server_version);
 static sys_var_const_str	sys_version_comment(&vars, "version_comment",
                                             MYSQL_COMPILATION_COMMENT);
@@ -884,6 +906,12 @@ static sys_var_const_str	sys_license(&va
 /* Global variables which enable|disable logging */
 static sys_var_log_state sys_var_general_log(&vars, "general_log", &opt_log,
                                       QUERY_LOG_GENERAL);
+static sys_var_log_state sys_var_backup_history_log(&vars, "backup_history_log", 
+                                                    &opt_backup_history_log,
+                                                    BACKUP_HISTORY_LOG);
+static sys_var_log_state sys_var_backup_progress_log(&vars, "backup_progress_log",
+                                                     &opt_backup_progress_log,
+                                                     BACKUP_PROGRESS_LOG);
 /* Synonym of "general_log" for consistency with SHOW VARIABLES output */
 static sys_var_log_state sys_var_log(&vars, "log", &opt_log,
                                       QUERY_LOG_GENERAL);
@@ -896,12 +924,36 @@ sys_var_str sys_var_general_log_path(&va
 				     sys_update_general_log_path,
 				     sys_default_general_log_path,
 				     opt_logname);
+/*
+  Added new variables for backup log file paths.
+*/
+sys_var_str sys_var_backup_history_log_path(&vars, "backup_history_log_file", 
+                                            sys_check_log_path,
+                                            sys_update_backup_history_log_path,
+                                            sys_default_backup_history_log_path,
+                                            opt_logname);
+sys_var_str sys_var_backup_progress_log_path(&vars, "backup_progress_log_file", 
+                                            sys_check_log_path,
+                                            sys_update_backup_progress_log_path,
+                                            sys_default_backup_progress_log_path,
+                                            opt_logname);
+/*
+  Create the backupdir dynamic variable.
+*/
+sys_var_str sys_var_backupdir(&vars, "backupdir", 0, sys_update_backupdir,
+                              sys_default_backupdir, 0);
+
 sys_var_str sys_var_slow_log_path(&vars, "slow_query_log_file", sys_check_log_path,
 				  sys_update_slow_log_path, 
 				  sys_default_slow_log_path,
 				  opt_slow_logname);
 static sys_var_log_output sys_var_log_output_state(&vars, "log_output", &log_output_options,
 					    &log_output_typelib, 0);
+/*
+  Defines variable for specifying the backup log output.
+*/
+static sys_var_log_backup_output sys_var_log_backup_output_state(&vars, "log_backup_output",
+              &log_backup_output_options, &log_output_typelib, 0);
 
 
 bool sys_var::check(THD *thd, set_var *var)
@@ -2523,9 +2575,19 @@ void sys_var_log_state::set_default(THD 
   else if (this == &sys_var_log_slow)
     WARN_DEPRECATED(thd, 7, 0, "@@log_slow_queries", "'@@slow_query_log'");
 
-  pthread_mutex_lock(&LOCK_global_system_variables);
-  logger.deactivate_log_handler(thd, log_type);
-  pthread_mutex_unlock(&LOCK_global_system_variables);
+  /*
+    Default for general and slow log is OFF.
+    Default for backup logs is ON.
+  */
+  if ((this == &sys_var_backup_history_log) ||
+      (this == &sys_var_backup_progress_log))
+    logger.activate_log_handler(thd, log_type);
+  else
+  {
+    pthread_mutex_lock(&LOCK_global_system_variables);
+    logger.deactivate_log_handler(thd, log_type);
+    pthread_mutex_unlock(&LOCK_global_system_variables);
+  }
 }
 
 
@@ -2583,8 +2645,18 @@ static int  sys_check_log_path(THD *thd,
   return 0;
 
 err:
-  my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name, 
-           res ? log_file_str : "NULL");
+  /*
+    If this is one of the backup logs, process the backup specific
+    error message.
+  */
+  if ((my_strcasecmp(system_charset_info, var->var->name, 
+       "backup_history_log_file") == 0) ||
+      (my_strcasecmp(system_charset_info, var->var->name,
+       "backup_progress_log_file") == 0))
+    my_error(ER_BACKUP_LOGPATH_INVALID, MYF(0), var->var->name); 
+  else
+    my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name, 
+             res ? log_file_str : "NULL");
   return 1;
 }
 
@@ -2594,6 +2666,7 @@ bool update_sys_var_str_path(THD *thd, s
 			     bool log_state, uint log_type)
 {
   MYSQL_QUERY_LOG *file_log;
+  MYSQL_BACKUP_LOG *backup_log= 0;
   char buff[FN_REFLEN];
   char *res= 0, *old_value= 0;
   bool result= 0;
@@ -2610,6 +2683,9 @@ bool update_sys_var_str_path(THD *thd, s
   
 
 
+  /*
+    Added support for backup log types.
+  */
   switch (log_type) {
   case QUERY_LOG_SLOW:
     file_log= logger.get_slow_log_file_handler();
@@ -2617,13 +2693,38 @@ bool update_sys_var_str_path(THD *thd, s
   case QUERY_LOG_GENERAL:
     file_log= logger.get_log_file_handler();
     break;
+  /* 
+    Check the backup logs to update their paths.
+  */
+  case BACKUP_HISTORY_LOG:
+    backup_log= logger.get_backup_history_log_file_handler();
+    break;
+  case BACKUP_PROGRESS_LOG:
+    backup_log= logger.get_backup_progress_log_file_handler();
+    break;
   default:
     assert(0);                                  // Impossible
   }
 
   if (!old_value)
   {
-    old_value= make_default_log_name(buff, log_ext);
+    /*
+      Added support for backup log types.
+    */
+    switch (log_type) {
+    case QUERY_LOG_SLOW:
+    case QUERY_LOG_GENERAL:
+      old_value= make_default_log_name(buff, log_ext);
+      break;
+    case BACKUP_HISTORY_LOG:
+      old_value= make_backup_log_name(buff, BACKUP_HISTORY_LOG_NAME.str, log_ext);
+      break;
+    case BACKUP_PROGRESS_LOG:
+      old_value= make_backup_log_name(buff, BACKUP_PROGRESS_LOG_NAME.str, log_ext);
+      break;
+    default:
+      assert(0);                                  // Impossible
+    }
     str_length= strlen(old_value);
   }
   if (!(res= my_strndup(old_value, str_length, MYF(MY_FAE+MY_WME))))
@@ -2635,20 +2736,51 @@ bool update_sys_var_str_path(THD *thd, s
   pthread_mutex_lock(&LOCK_global_system_variables);
   logger.lock_exclusive();
 
-  if (file_log && log_state)
-    file_log->close(0);
+  /*
+    Added support for backup log types.
+  */
+  switch (log_type) {
+  case QUERY_LOG_SLOW:
+  case QUERY_LOG_GENERAL:
+    if (file_log && log_state)
+      file_log->close(0);
+    break;
+  /*
+    Close the backup logs if specified.
+  */
+  case BACKUP_HISTORY_LOG:
+  case BACKUP_PROGRESS_LOG:
+    if (backup_log && log_state)
+      backup_log->close(0);
+    break;
+  default:
+    assert(0);                                  // Impossible
+  }
   old_value= var_str->value;
   var_str->value= res;
   var_str->value_length= str_length;
   my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
-  if (file_log && log_state)
+  if ((file_log && log_state) ||
+      (backup_log && log_state))
   {
+    /*
+      Added support for backup log types.
+    */
     switch (log_type) {
     case QUERY_LOG_SLOW:
-      file_log->open_slow_log(sys_var_slow_log_path.value);
+      file_log->open_slow_log(res);
       break;
     case QUERY_LOG_GENERAL:
-      file_log->open_query_log(sys_var_general_log_path.value);
+      file_log->open_query_log(res);
+      break;
+    /*
+      Open the backup logs if specified.
+    */
+    case BACKUP_HISTORY_LOG:
+      result= backup_log->open_backup_history_log(res);
+      break;
+    case BACKUP_PROGRESS_LOG:
+      result= backup_log->open_backup_progress_log(res);
       break;
     default:
       DBUG_ASSERT(0);
@@ -2677,6 +2809,63 @@ static void sys_default_general_log_path
 }
 
 
+/*
+  Update the backup history log path variable.
+*/
+static bool sys_update_backup_history_log_path(THD *thd, set_var * var)
+{
+  char buff[STRING_BUFFER_USUAL_SIZE];
+  String str(buff,sizeof(buff), system_charset_info), *res;
+
+  res= var->value->val_str(&str);
+  if (my_strcasecmp(system_charset_info, res->c_ptr_safe(), 
+      sys_var_backup_progress_log_path.value) == 0)
+  {
+    my_error(ER_BACKUP_LOGPATHS, MYF(0));
+    return 1;
+  }
+  return update_sys_var_str_path(thd, &sys_var_backup_history_log_path, 
+                                 var, ".log", opt_log, BACKUP_HISTORY_LOG);
+}
+
+/*
+  Set the default for backup history log path variable.
+*/
+static void sys_default_backup_history_log_path(THD *thd, enum_var_type type)
+{
+  (void) update_sys_var_str_path(thd, &sys_var_backup_history_log_path,
+				 0, ".log", opt_log, BACKUP_HISTORY_LOG);
+}
+
+/*
+  Update the backup progress log path variable.
+*/
+static bool sys_update_backup_progress_log_path(THD *thd, set_var * var)
+{
+  char buff[STRING_BUFFER_USUAL_SIZE];
+  String str(buff,sizeof(buff), system_charset_info), *res;
+
+  res= var->value->val_str(&str);
+  if (my_strcasecmp(system_charset_info, res->c_ptr_safe(), 
+      sys_var_backup_history_log_path.value) == 0)
+  {
+    my_error(ER_BACKUP_LOGPATHS, MYF(0));
+    return 1;
+  }
+  return update_sys_var_str_path(thd, &sys_var_backup_progress_log_path, 
+                                 var, ".log", opt_log, BACKUP_PROGRESS_LOG);
+}
+
+/*
+  Set the default for backup progress log path variable.
+*/
+static void sys_default_backup_progress_log_path(THD *thd, enum_var_type type)
+{
+  (void) update_sys_var_str_path(thd, &sys_var_backup_progress_log_path,
+				 0, ".log", opt_log, BACKUP_PROGRESS_LOG);
+}
+
+
 static bool sys_update_slow_log_path(THD *thd, set_var * var)
 {
   return update_sys_var_str_path(thd, &sys_var_slow_log_path,
@@ -2742,6 +2931,295 @@ uchar *sys_var_log_output::value_ptr(THD
   return (uchar*) thd->strmake(tmp.ptr(), length);
 }
 
+/*
+  Allow update of the log-backup-output variable.
+*/
+bool sys_var_log_backup_output::update(THD *thd, set_var *var)
+{
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  logger.lock_exclusive();
+  logger.init_backup_history_log(var->save_result.ulong_value);
+  logger.init_backup_progress_log(var->save_result.ulong_value);
+  *value= var->save_result.ulong_value;
+  logger.unlock();
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+  return 0;
+}
+
+/*
+  Set the default for the log-backup-output variable.
+*/
+void sys_var_log_backup_output::set_default(THD *thd, enum_var_type type)
+{
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  logger.lock_exclusive();
+  logger.init_backup_history_log(LOG_TABLE);
+  logger.init_backup_progress_log(LOG_TABLE);
+  *value= LOG_TABLE;
+  logger.unlock();
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+}
+
+/*
+  Allow reading of the log-backup-output variable.
+*/
+uchar *sys_var_log_backup_output::value_ptr(THD *thd, enum_var_type type,
+                                            LEX_STRING *base)
+{
+  char buff[256];
+  String tmp(buff, sizeof(buff), &my_charset_latin1);
+  ulong length;
+  ulong val= *value;
+
+  tmp.length(0);
+  for (uint i= 0; val; val>>= 1, i++)
+  {
+    if (val & 1)
+    {
+      tmp.append(log_output_typelib.type_names[i],
+                 log_output_typelib.type_lengths[i]);
+      tmp.append(',');
+    }
+  }
+
+  if ((length= tmp.length()))
+    length--;
+  return (uchar*) thd->strmake(tmp.ptr(), length);
+}
+
+
+/*
+  Functions for backupdir variable.
+*/
+
+/**
+  Get the default for the backupdir.
+
+  The resulting path name will not have trailing FN_LIBCHAR. It is
+  allocated with my_malloc(), so needs to be freed after use.
+
+  @returns      pointer
+    @retval     NULL            memory allocation error
+    @retval     != NULL         pointer to path name string
+*/
+
+char *default_backupdir(void)
+{
+  char *path;
+  size_t plen;
+  DBUG_ENTER("default_backupdir");
+
+  path= my_strdup(mysql_real_data_home, MYF(MY_WME));
+  plen= mysql_real_data_home_len;
+
+  /* Strip off trailing FN_LIBCHAR. */
+  while (plen && (path[plen - 1] == FN_LIBCHAR))
+    path[--plen]= '\0';
+
+  DBUG_PRINT("backupdir", ("default: '%s'", path));
+  DBUG_RETURN(path);
+}
+
+
+/**
+  Check if backupdir is bad: not a directory or not writable.
+
+  @param[in]    backupdir       backupdir
+
+  @return       status
+    @retval     TRUE            bad, not directory or not writable
+    @retval     FALSE           good, is directory and is writable
+
+  @note The path name must not have a trailing FN_LIBCHAR.
+*/
+
+bool check_backupdir(char *backupdir)
+{
+  int is_bad;
+  DBUG_ENTER("check_backupdir");
+  DBUG_PRINT("backupdir", ("path: '%s'", backupdir));
+
+  is_bad= (!test_if_directory(backupdir) ||
+           my_access(backupdir, W_OK));
+
+  DBUG_PRINT("backupdir", ("is_bad: %d", is_bad));
+  DBUG_RETURN(is_bad);
+}
+
+
+/**
+  Check if path is a writable directory.
+
+  Internally, the path is prepended with a prefix if it is not an
+  absolute path.
+
+  Issue a warning if the resulting path is not a directory or not
+  writable.
+
+  Throw an error and return FALSE if memory allocation fails.
+
+  @param[in]    thd             thread context, may be NULL
+  @param[in]    varname         name of variable
+  @param[in]    path            path name
+  @param[in]    prefix          path name to prepend if path is relative
+
+  @return       status
+    @retval     TRUE            path is a writable directory
+    @retval     FALSE           not a writable directory or memory error
+*/
+
+bool is_usable_directory(THD *thd, const char *varname,
+                         const char *path, const char *prefix)
+{
+  char          *alloc_path= NULL;
+  const char    *datadir_ptr;
+  size_t        datadir_len;
+  size_t        alen;
+  size_t        plen;
+  bool          is_dir= FALSE;
+  DBUG_ENTER("is_usable_directory");
+
+  if (test_if_hard_path(path))
+  {
+    /* Absolute path. Prepend nothing. */
+    datadir_ptr= "";
+    datadir_len= 0;
+  }
+  else
+  {
+    /* Relative path. Prepend datadir. */
+    datadir_ptr= mysql_real_data_home;
+    datadir_len= mysql_real_data_home_len;
+  }
+  plen= strlen(path);
+
+  /*
+    The resulting path can be a maximum of:
+    Datadir + '/' + path + '\0' + home_dir
+    Home_dir comes into the game if path starts with "~/".
+    The clean path will not be longer than the concatenated one.
+  */
+  alen= datadir_len + 1 + plen + 1 + home_dir_len;
+  alloc_path= (char*) my_malloc(alen, MYF(MY_WME));
+  if (!alloc_path)
+    goto end; /* purecov: inspected */
+
+  plen= safe_cleanup_cat_path(alloc_path, alen, datadir_ptr, path, NullS);
+  if (unlikely(!plen))
+    goto err; /* purecov: inspected */
+
+  /* Check path but don't stop if it's bad, just warn. */
+  if (check_backupdir(alloc_path))
+  {
+    if (!thd)
+    {
+      /*
+        Cannot use ER(ER_NOT_RW_DIR) at this place.
+        The errmesg array may not yet be initialized.
+        We can profit from this by inserting the variable name.
+        secure_backup_file_priv is initialized at server startup only,
+        so it will always take this branch.
+      */
+      sql_print_warning("The path specified for the variable %s is not"
+                        " a directory or cannot be written: '%s'\n",
+                        varname, alloc_path);
+    }
+    else
+    {
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                          ER_NOT_RW_DIR, ER(ER_NOT_RW_DIR), varname,
+                          cut_print_path(alloc_path, ER_NOT_RW_DIR_PATHSIZE));
+    }
+  }
+  else
+    is_dir= TRUE;
+  goto end;
+
+  /* purecov: begin inspected */
+ err:
+  DBUG_PRINT("error", (": path too long"));
+  my_error(ER_PATH_LENGTH, MYF(0), varname);
+  /* purecov: end */
+
+ end:
+  my_free(alloc_path, MYF(MY_ALLOW_ZERO_PTR));
+  DBUG_RETURN(is_dir);
+}
+
+
+/**
+  Update the backupdir variable.
+
+  This method is used to change the backupdir variable.
+
+  @param[in]    thd             thread context
+  @param[in]    var             new value for backupdir in a set_var structure
+
+  @return       error_status
+    @retval     TRUE            error, value not changed
+    @retval     FALSE           ok, value changed
+*/
+static bool sys_update_backupdir(THD *thd, set_var *var)
+{
+  char buff[FN_REFLEN];
+  String str(buff, sizeof(buff), system_charset_info);
+  char *backupdir;
+  uint str_length;
+  bool error_status= TRUE;
+  DBUG_ENTER("sys_update_backupdir");
+
+  if (var)
+  {
+    String *strres;
+
+    if (!(strres= var->value->val_str(&str)))
+    {
+      my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name, "NULL");
+      goto end;
+    }
+    backupdir= my_strndup(strres->ptr(), strres->length(), MYF(MY_WME));
+  }
+  else
+  {
+    backupdir= default_backupdir();
+  }
+  if (unlikely(!backupdir))
+    goto end; /* purecov: inspected */
+  str_length= strlen(backupdir);
+
+  /* Warn if not a directory. */
+  (void) is_usable_directory(thd, "backupdir", backupdir,
+                             mysql_real_data_home);
+
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  logger.lock_exclusive();
+  my_free(sys_var_backupdir.value, MYF(MY_ALLOW_ZERO_PTR));
+  sys_var_backupdir.value= backupdir;
+  sys_var_backupdir.value_length= str_length;
+  logger.unlock();
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+  error_status= FALSE;
+
+ end:
+  DBUG_RETURN(error_status);
+}
+
+
+/**
+  Set the default value for the backupdir variable
+
+  This method is used to reset the backupdir variable to the
+  default by calling the update method without a path.
+
+  @param[in]    thd             thread context
+  @param[in]    type            ignored (needed for api)
+*/
+static void sys_default_backupdir(THD *thd,
+                                  enum_var_type type __attribute__((unused)))
+{
+  sys_update_backupdir(thd, NULL);
+}
+
 
 /*****************************************************************************
   Functions to handle SET NAMES and SET CHARACTER SET
@@ -2821,6 +3299,81 @@ bool sys_var_insert_id::update(THD *thd,
 }
 
 
+/**
+  Get value of backup_wait_timeout.
+
+  Returns the value for the backup_wait_timeout session variable.
+
+  The variable is of type SHOW_LONG.
+
+  @param[IN] thd    Thread object
+  @param[IN] type   Type of variable (unused)
+  @param[IN] base   base name (unused)
+
+  @returns value of variable as address to ulong
+*/
+
+uchar *sys_var_backup_wait_timeout::value_ptr(THD *thd, enum_var_type type
+                                              __attribute__((unused)),
+                                              LEX_STRING *base
+                                              __attribute__((unused)))
+{
+  return (uchar*) &thd->backup_wait_timeout;
+}
+
+
+/**
+  Update value of backup_wait_timeout.
+
+  Set the backup_wait_timeout variable.
+
+  The variable is of type SHOW_LONG.
+
+  @param[IN] thd    Thread object
+  @param[IN] var    Pointer to value from command.
+
+  @returns 0
+*/
+
+bool sys_var_backup_wait_timeout::update(THD *thd, set_var *var)
+{
+  /*
+    The default sys_var::check() method sets ulonglong_value.
+    This can corrupt other values on some platforms.
+    Since we don't redefine check() for backup_wait_timeout,
+    we need to use ulonglong_value. Since we assign to an ulong
+    variable, we better check the value and limit it.
+  */
+  if (var->save_result.ulonglong_value > ULONG_MAX)
+    thd->backup_wait_timeout= ULONG_MAX;
+  else
+  {
+    /*
+      This cast is required for the Windows compiler. The assignment is
+      safe because we checked the range of the value above.
+    */
+    thd->backup_wait_timeout= (ulong) var->save_result.ulonglong_value;
+  }
+  return 0;
+}
+
+
+/**
+  Set default value.
+
+  Set the backup_wait_timeout variable to their default value.
+
+  @param[IN] thd    Thread object
+  @param[IN] type   Type of variable (unused)
+*/
+
+void sys_var_backup_wait_timeout::set_default(THD *thd, enum_var_type type
+                                              __attribute__((unused)))
+{
+  thd->backup_wait_timeout= BACKUP_WAIT_TIMEOUT_DEFAULT;
+}
+
+
 uchar *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
 				   LEX_STRING *base)
 {

=== modified file 'sql/set_var.h'
--- a/sql/set_var.h	2009-12-01 19:07:18 +0000
+++ b/sql/set_var.h	2009-12-04 22:31:25 +0000
@@ -42,6 +42,9 @@ typedef void (*sys_after_update_func)(TH
 typedef void (*sys_set_default_func)(THD *, enum_var_type);
 typedef uchar *(*sys_value_ptr_func)(THD *thd);
 
+extern my_bool opt_backup_elevation;
+extern my_bool opt_restore_elevation;
+
 struct sys_var_chain
 {
   sys_var *first;
@@ -703,6 +706,30 @@ public:
 };
 
 
+/**
+  Backup_wait_timeout system variable class.
+
+  This class consolidates the mechanism to manage the backup_wait_timeout
+  system variable. It is a session only variable thus we check the type for
+  check_type() and check_default() to ensure it isn't accessed as a global.
+
+  A set_default() method is provided to allow the SET command :
+  SET backup_wait_TIMEOUT = DEFAULT; 
+*/
+class sys_var_backup_wait_timeout :public sys_var
+{
+public:
+  sys_var_backup_wait_timeout(sys_var_chain *chain, const char *name_arg)
+    :sys_var(name_arg)
+  { chain_sys_var(chain); }
+  bool update(THD *thd, set_var *var);
+  bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
+  bool check_default(enum_var_type type) { return type == OPT_GLOBAL; }
+  SHOW_TYPE show_type() { return SHOW_LONG; }
+  uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+  void set_default(THD *thd, enum_var_type type);
+};
+
 class sys_var_insert_id :public sys_var
 {
 public:
@@ -983,6 +1010,31 @@ public:
   SHOW_TYPE show_type() { return SHOW_CHAR; }
 };
 
+/*
+  Class used to manage log-backup-output variable.
+*/
+class sys_var_log_backup_output : public sys_var
+{
+  ulong *value;
+  TYPELIB *enum_names;
+public:
+  sys_var_log_backup_output(sys_var_chain *chain, const char *name_arg, ulong *value_arg,
+                     TYPELIB *typelib, sys_after_update_func func)
+    :sys_var(name_arg,func), value(value_arg), enum_names(typelib)
+  {
+    chain_sys_var(chain);
+    set_allow_empty_value(FALSE);
+  }
+  virtual bool check(THD *thd, set_var *var)
+  {
+    return check_set(thd, var, enum_names);
+  }
+  bool update(THD *thd, set_var *var);
+  uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+  bool check_update_type(Item_result type) { return 0; }
+  void set_default(THD *thd, enum_var_type type);
+  SHOW_TYPE show_type() { return SHOW_CHAR; }
+};
 
 /* Variable that you can only read from */
 
@@ -1486,7 +1538,16 @@ CHARSET_INFO *get_old_charset_by_name(co
 uchar* find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
 		NAMED_LIST **found);
 
-extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path;
+/* This must match the path length limit in the ER_NOT_RW_DIR error msg. */
+#define ER_NOT_RW_DIR_PATHSIZE 200
+bool is_usable_directory(THD *thd, const char *varname,
+                         const char *path, const char *prefix);
+bool check_backupdir(char *backupdir);
+char *default_backupdir(void);
+
+extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path,
+       sys_var_backup_history_log_path, sys_var_backup_progress_log_path,
+       sys_var_backupdir;
 
 /* key_cache functions */
 KEY_CACHE *get_key_cache(LEX_STRING *cache_name);

=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt	2009-11-03 11:37:02 +0000
+++ b/sql/share/errmsg-utf8.txt	2009-12-04 22:31:25 +0000
@@ -6201,6 +6201,207 @@ ER_TOO_MANY_CONCURRENT_TRXS
 WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
   eng "Non-ASCII separator arguments are not fully supported"
 
+ER_NO_STORAGE_ENGINE
+   eng "Can't access storage engine of table %-.64s"
+ER_BACKUP_BACKUP_START
+   eng "Starting backup process"
+ER_BACKUP_BACKUP_DONE
+   eng "Backup completed"
+ER_BACKUP_RESTORE_START
+   eng "Starting restore process"
+ER_BACKUP_RESTORE_DONE
+   eng "Restore completed"
+ER_BACKUP_NOTHING_TO_BACKUP
+   eng "Nothing to backup"
+ER_BACKUP_CANNOT_INCLUDE_DB
+   eng "Database '%-.64s' cannot be included in a backup"
+ER_BACKUP_BACKUP
+   eng "Error during backup operation - see SHOW WARNINGS for more information"
+ER_BACKUP_RESTORE
+   eng "Error during restore operation - see SHOW WARNINGS for more information"
+ER_BACKUP_RUNNING
+   eng "Can't execute this command because another BACKUP/RESTORE operation is in progress"
+ER_BACKUP_BACKUP_PREPARE
+   eng "Error when preparing for backup operation"
+ER_BACKUP_RESTORE_PREPARE
+   eng "Error when preparing for restore operation"
+ER_BACKUP_INVALID_LOC
+   eng "Invalid backup location '%-.64s'"
+ER_BACKUP_READ_LOC
+   eng "Can't read backup location '%-.64s'"
+ER_BACKUP_WRITE_LOC
+   eng "Can't write to backup location '%-.64s'"
+ER_BACKUP_LIST_DBS
+   eng "Can't enumerate server databases"
+ER_BACKUP_LIST_TABLES
+   eng "Can't enumerate server tables"
+ER_BACKUP_LIST_DB_TABLES
+   eng "Can't enumerate tables in database %-.64s"
+ER_BACKUP_SKIP_VIEW
+   eng "Skipping view %-.64s in database %-.64s"
+ER_BACKUP_NO_ENGINE
+   eng "Skipping table %-.64s since it has no valid storage engine"
+ER_BACKUP_TABLE_OPEN
+   eng "Can't open table %-.64s"
+ER_BACKUP_READ_HEADER
+   eng "Can't read backup archive preamble"
+ER_BACKUP_WRITE_HEADER
+   eng "Can't write backup archive preamble"
+ER_BACKUP_NO_BACKUP_DRIVER
+   eng "Can't find backup driver for table %-.64s"
+ER_BACKUP_NOT_ACCEPTED
+   eng "%-.64s backup driver was selected for table %-.64s but it rejects to handle this table"
+ER_BACKUP_CREATE_BACKUP_DRIVER
+   eng "Can't create %-.64s backup driver"
+ER_BACKUP_CREATE_RESTORE_DRIVER
+   eng "Can't create %-.64s restore driver"
+ER_BACKUP_TOO_MANY_IMAGES
+   eng "Backup image uses %d backup engines but maximum %d are supported"
+ER_BACKUP_WRITE_META
+   eng "Error when saving metadata of %-.64s"
+ER_BACKUP_READ_META
+   eng "Error when reading metadata"
+ER_BACKUP_CREATE_META
+   eng "Failed to obtain metadata for %-.64s"
+ER_BACKUP_GET_BUF
+   eng "Can't allocate buffer for image data transfer"
+ER_BACKUP_WRITE_DATA
+   eng "Error when writing data from %-.64s backup driver (data block for table #%d)"
+ER_BACKUP_READ_DATA
+   eng "Error when reading data from backup stream"
+ER_BACKUP_NEXT_CHUNK
+   eng "Can't go to the next chunk in backup stream"
+ER_BACKUP_INIT_BACKUP_DRIVER
+   eng "Can't initialize %-.64s backup driver"
+ER_BACKUP_INIT_RESTORE_DRIVER
+   eng "Can't initialize %-.64s restore driver"
+ER_BACKUP_STOP_BACKUP_DRIVER
+   eng "Can't shut down %-.64s backup driver"
+ER_BACKUP_STOP_RESTORE_DRIVERS
+   eng "Can't shut down %-.64s restore driver(s)"
+ER_BACKUP_PREPARE_DRIVER
+   eng "%-.64s backup driver can't prepare for synchronization"
+ER_BACKUP_CREATE_VP
+   eng "%-.64s backup driver can't create its validity point"
+ER_BACKUP_UNLOCK_DRIVER
+   eng "Can't unlock %-.64s backup driver after creating the validity point"
+ER_BACKUP_CANCEL_BACKUP
+   eng "%-.64s backup driver can't cancel its backup operation"
+ER_BACKUP_CANCEL_RESTORE
+   eng "%-.64s restore driver can't cancel its restore operation"
+ER_BACKUP_GET_DATA
+   eng "Error when polling %-.64s backup driver for its data"
+ER_BACKUP_SEND_DATA
+   eng "Error when sending data (for table #%d) to %-.64s restore driver"
+ER_BACKUP_SEND_DATA_RETRY
+   eng "After %d attempts %-.64s restore driver still can't accept next block of data"
+ER_BACKUP_OPEN_TABLES
+   eng "Open and lock tables failed in %-.64s"
+ER_BACKUP_THREAD_INIT
+   eng "Backup driver's table locking thread can not be initialized."
+ER_BACKUP_PROGRESS_TABLES
+   eng "Can't open the backup logs as tables. Check 'mysql.backup_history' and 'mysql.backup_progress' or run mysql_upgrade to repair."
+ER_TABLESPACE_EXIST
+  eng "Tablespace '%-.192s' already exists"
+ER_NO_SUCH_TABLESPACE
+  eng "Tablespace '%-.192s' doesn't exist"
+ER_BACKUP_CANT_FIND_SE
+   eng "Backup image contains data created by a native driver of %-.64s storage engine but this engine can not be found on this server"
+ER_BACKUP_NO_NATIVE_BE
+   eng "Backup image contains data created by a native driver of %-.64s storage engine but it has no native backup support on this server"
+ER_BACKUP_UNKNOWN_BE
+   eng "Backup engine #%d needed for restoring from backup image has unknown type"
+ER_BACKUP_WRONG_TABLE_BE
+   eng "Backup image specifies wrong backup engine #%d for one of the tables"
+ER_BACKUP_CANT_RESTORE_DB
+   eng "Could not restore database %-.64s"
+ER_BACKUP_CANT_RESTORE_TABLE
+   eng "Could not restore table %-.64s"
+ER_BACKUP_CANT_RESTORE_VIEW
+   eng "Could not restore view %-.64s. Please check the view definition for possible missing dependencies."
+ER_BACKUP_CANT_RESTORE_SROUT
+   eng "Could not restore stored routine %-.64s"
+ER_BACKUP_CANT_RESTORE_EVENT
+   eng "Could not restore event %-.64s"
+ER_BACKUP_CANT_RESTORE_TRIGGER
+   eng "Could not restore trigger %-.64s"
+ER_BACKUP_CATALOG_ADD_DB
+   eng "Failed to add database `%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_TABLE
+   eng "Failed to add table `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_VIEW
+   eng "Failed to add view `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_SROUT
+   eng "Failed to add stored routine `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_EVENT
+   eng "Failed to add event `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_TRIGGER
+   eng "Failed to add trigger `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_UNKNOWN_OBJECT
+   eng "Backup image refers to an object which could not be found in the catalog"
+ER_BACKUP_UNKNOWN_OBJECT_TYPE
+   eng "Backup image refers to an object of unknown type"
+ER_BACKUP_OPEN_WR
+   eng "Cannot open backup stream for writing"
+ER_BACKUP_OPEN_RD
+   eng "Cannot open backup stream for reading"
+ER_BACKUP_BAD_MAGIC
+   eng "Could not find correct signature at the beginning of backup stream"
+ER_BACKUP_CONTEXT_CREATE
+   eng "Cannot create backup/restore execution context"
+ER_BACKUP_CONTEXT_REMOVE
+   eng "Error when cleaning up after backup/restore operation"
+ER_BAD_PATH
+   eng "Malformed file path '%-128s'"
+ER_DDL_BLOCK
+   eng "Erorr when attempting to block DDLs"
+ER_BACKUP_LOGGER_INIT
+   eng "Could not initialize logging and reporting services"
+ER_BACKUP_WRITE_SUMMARY
+   eng "Error when writing summary section of backup image"
+ER_BACKUP_READ_SUMMARY
+   eng "Error when reading summary section of backup image"
+ER_BACKUP_GET_META_DB
+   eng "Failed to obtain metadata for database %-.64s"
+ER_BACKUP_GET_META_TABLE
+   eng "Failed to obtain metadata for table %-.64s"
+ER_BACKUP_GET_META_VIEW
+   eng "Failed to obtain metadata for view %-.64s"
+ER_BACKUP_GET_META_SROUT
+   eng "Failed to obtain metadata for stored routine %-.64s"
+ER_BACKUP_GET_META_EVENT
+   eng "Failed to obtain metadata for event %-.64s"
+ER_BACKUP_GET_META_TRIGGER
+   eng "Failed to obtain metadata for trigger %-.64s"
+ER_BACKUP_CREATE_BE
+   eng "Cannot create backup engine for storage engine %-.64s"
+ER_BACKUP_LIST_PERDB
+   eng "Can't enumerate per database objects"
+ER_BACKUP_LIST_DB_VIEWS
+   eng "Can't enumerate views in database `%-.64s`"
+ER_BACKUP_LIST_DB_SROUT
+   eng "Can't enumerate stored routines in database `%-.64s`"
+ER_BACKUP_LIST_DB_EVENTS
+   eng "Can't enumerate events in database `%-.64s`"
+ER_BACKUP_LIST_DB_TRIGGERS
+   eng "Can't enumerate triggers in database `%-.64s`"
+ER_BACKUP_LOG_WRITE_ERROR
+   eng "Can't write to the online backup progress log %-.64s."
+ER_TABLESPACE_NOT_EMPTY
+  eng "Tablespace '%-.192s' not empty"
+ER_BACKUP_CAT_ENUM
+   eng "Could not access the contents of the backup catalog when writing backup image preamble"
+ER_BACKUP_CANT_RESTORE_TS
+   eng "Could not create tablespace %-.64s needed by tables being restored."
+ER_BACKUP_TS_CHANGE
+   eng "Tablespace %-.64s needed by tables being restored, but the current tablespace definition differs from how it was when backup was made."
+ER_BACKUP_GET_META_TS
+   eng "Failed to obtain metadata for tablespace %-.64s."
+ER_TABLESPACE_DATAFILE_EXIST
+  eng "Tablespace data file '%-.192s' already exists"
+ER_BACKUP_CATALOG_ADD_TS
+   eng "Failed to add tablespace `%-.64s` to the catalog"
+
 ER_DEBUG_SYNC_TIMEOUT
   eng "debug sync point wait timed out"
   ger "Debug Sync Point Wartezeit überschritten"
@@ -6208,6 +6409,77 @@ ER_DEBUG_SYNC_HIT_LIMIT
   eng "debug sync point hit limit reached"
   ger "Debug Sync Point Hit Limit erreicht"
 
+ER_BACKUP_FAILED_TO_INIT_COMPRESSION
+  eng "Could not initialize compression of backup image (function deflateInit2 returned error code %d: %-.64s)"
+ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
+  eng "Restore failed to obtain the name locks on the tables."
+ER_BACKUP_RELEASE_NAME_LOCK_FAILED
+  eng "Restore failed to release the name locks on the tables."
+
+# NOTE: If changing the length limit for the path name in this message,
+#       please change ER_NOT_RW_DIR_PATHSIZE in set_var.h too.
+ER_NOT_RW_DIR
+  eng "The path specified for the system variable %s is not a directory or cannot be written: %.200s"
+
+ER_DDL_TIMEOUT
+   eng "The backup wait timeout has expired for query '%-64s'."
+ER_BACKUP_LIST_DB_PRIV
+   eng "Can't enumerate grants in database '%-.64s'."
+ER_BACKUP_CATALOG_ADD_PRIV
+   eng "Failed to add grant '%-.64s' to the catalog"
+ER_BACKUP_GET_META_PRIV
+   eng "Failed to obtain metadata for grant '%-.64s'."
+ER_BACKUP_CANT_RESTORE_PRIV
+   eng "Could not execute grant '%-.64s'."
+ER_BACKUP_GRANT_SKIPPED
+   eng "The grant '%-.64s' for the user %-.64s was skipped because the user does not exist."
+ER_BACKUP_GRANT_WRONG_DB
+   eng "The grant '%-.64s' failed. Database not included in the backup image."
+ER_BACKUP_LOGPATHS
+  eng "The log names for backup_history and backup_progress must be unique."
+ER_BACKUP_LOGPATH_INVALID
+  eng "The path specified for the %-.64s is invalid."
+ER_BACKUP_SEND_REPLY
+   eng "Failed to send reply to client after successful %-.64s operation"
+   nor "Feil ved sending av svar til klient etter %-.64s"
+   norwegian-ny "Feil ved sending av svar til klienten etter %-.64s"
+ER_BACKUP_CLOSE
+   eng "Backup/Restore: Error on close of backup stream"
+   nor "Backup/Restore: Feil ved lukning av backup-strøm"
+   norwegian-ny "Backup/Restore:  Feil ved lukking av backup-straum"
+ER_BACKUP_BINLOG
+   eng "Error on accessing binlog during BACKUP"
+   nor "Lesing av binlog feilet under BACKUP"
+   norwegian-ny "Lesing av binlog feila under BACKUP"
+ER_BACKUP_LOG_OUTPUT
+  eng "Removing backup log entries by date or backup_id requires logging to tables."
+ER_BACKUP_PURGE_DATETIME
+  eng "The datetime specified is invalid for the '%-.64s' command."
+ER_BACKUP_LOGS_DELETED
+  eng "Backup log entries deleted: "
+ER_BACKUP_LOGS_TRUNCATED
+  eng "All backup log entries have been deleted"
+ER_MASTER_BLOCKING_SLAVES
+  eng "The master is not allowing slave connections."
+ER_RESTORE_ON_MASTER
+  eng "A restore operation was initiated on the master."
+ER_RESTORE_ON_SLAVE
+  eng "A restore operation was attempted on a slave during replication. You must stop the slave prior to running a restore."
+ER_RESTORE_DB_EXISTS
+  eng "Database \'%-.64s\' already exists. Use OVERWRITE flag to overwrite."
+
+ER_BACKUP_UNEXPECTED_DATA
+  eng "Backup image contains no tables, but table data was found in it"
+ER_BACKUP_BACKUP_DBS
+  eng "Backing up %u database(s) %.220s"
+ER_BACKUP_RESTORE_DBS
+  eng "Restoring %u database(s) %.220s"
+
+ER_BACKUP_SYNCHRONIZE
+  eng "Backup failed to synchronize table images."
+ER_RESTORE_CANNOT_START_SLAVE
+  eng "Cannot start slave. SLAVE START is blocked by %-.64s."
+
 ER_DUP_SIGNAL_SET 42000
         eng "Duplicate condition information item '%s'"
 
@@ -6235,6 +6507,33 @@ WARN_COND_ITEM_TRUNCATED
 ER_COND_ITEM_TOO_LONG
         eng "Data too long for condition item '%s'"
 
+ER_PATH_LENGTH
+  eng "The path specified for %.64s is too long."
+ER_BACKUP_INTERRUPTED
+  eng "Operation has been interrupted."
+ER_BACKUP_NOT_ENABLED
+  eng "The MySQL Backup system is disabled in this release. Use --mysql-backup or --mysql-backup=1 on server startup to enable."
+ER_BACKUP_NO_NDB
+  eng "NDB tables cannot be used with MySQL Backup. Please see the MySQL Cluster reference manual."
+ER_BACKUP_ACCESS_DENIED_ERROR
+  eng "Insufficient privileges. You must have the BACKUP privilege to backup database '%s'."
+ER_RESTORE_ACCESS_DENIED_ERROR
+  eng "Insufficient privileges. You must have the RESTORE privilege to restore database '%s'."
+ER_BACKUP_ACCESS_DBS_INCOMPLETE
+  eng "Insufficient privileges. You must have access privileges to all databases to execute BACKUP DATABASE *."
+ER_BACKUP_ACCESS_OBJS_INCOMPLETE
+  eng "Insufficient privileges. You do not have privileges to backup database '%s'."
+ER_BACKUP_USER_PRIV_CHECK
+  eng "Failed to perform access privileges check for user '%s'."
+ER_RESTORE_EVENT_DISABLED
+  eng "The event '%s' was disabled during restore."
+ER_RESTORE_ACCESS_OBJS_INCOMPLETE
+  eng "Insufficient privileges. You do not have privileges to restore the object '%s' from this backup image."
+ER_RESTORE_ACCESS_DEFINER
+  eng "Insufficient privileges. You must have the SUPER privilege to restore the object '%s'.'%s'."
+ER_RESTORE_DB_ERROR
+  eng "The database for object '%s' was not found in the catalog."
+
 ER_UNKNOWN_LOCALE                                                                                                                                                                    
         eng "Unknown locale: '%-.64s'"
 

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2009-12-01 19:07:18 +0000
+++ b/sql/share/errmsg.txt	2009-12-04 22:31:25 +0000
@@ -6202,6 +6202,207 @@ ER_TOO_MANY_CONCURRENT_TRXS
 WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
   eng "Non-ASCII separator arguments are not fully supported"
 
+ER_NO_STORAGE_ENGINE
+   eng "Can't access storage engine of table %-.64s"
+ER_BACKUP_BACKUP_START
+   eng "Starting backup process"
+ER_BACKUP_BACKUP_DONE
+   eng "Backup completed"
+ER_BACKUP_RESTORE_START
+   eng "Starting restore process"
+ER_BACKUP_RESTORE_DONE
+   eng "Restore completed"
+ER_BACKUP_NOTHING_TO_BACKUP
+   eng "Nothing to backup"
+ER_BACKUP_CANNOT_INCLUDE_DB
+   eng "Database '%-.64s' cannot be included in a backup"
+ER_BACKUP_BACKUP
+   eng "Error during backup operation - see SHOW WARNINGS for more information"
+ER_BACKUP_RESTORE
+   eng "Error during restore operation - see SHOW WARNINGS for more information"
+ER_BACKUP_RUNNING
+   eng "Can't execute this command because another BACKUP/RESTORE operation is in progress"
+ER_BACKUP_BACKUP_PREPARE
+   eng "Error when preparing for backup operation"
+ER_BACKUP_RESTORE_PREPARE
+   eng "Error when preparing for restore operation"
+ER_BACKUP_INVALID_LOC
+   eng "Invalid backup location '%-.64s'"
+ER_BACKUP_READ_LOC
+   eng "Can't read backup location '%-.64s'"
+ER_BACKUP_WRITE_LOC
+   eng "Can't write to backup location '%-.64s'"
+ER_BACKUP_LIST_DBS
+   eng "Can't enumerate server databases"
+ER_BACKUP_LIST_TABLES
+   eng "Can't enumerate server tables"
+ER_BACKUP_LIST_DB_TABLES
+   eng "Can't enumerate tables in database %-.64s"
+ER_BACKUP_SKIP_VIEW
+   eng "Skipping view %-.64s in database %-.64s"
+ER_BACKUP_NO_ENGINE
+   eng "Skipping table %-.64s since it has no valid storage engine"
+ER_BACKUP_TABLE_OPEN
+   eng "Can't open table %-.64s"
+ER_BACKUP_READ_HEADER
+   eng "Can't read backup archive preamble"
+ER_BACKUP_WRITE_HEADER
+   eng "Can't write backup archive preamble"
+ER_BACKUP_NO_BACKUP_DRIVER
+   eng "Can't find backup driver for table %-.64s"
+ER_BACKUP_NOT_ACCEPTED
+   eng "%-.64s backup driver was selected for table %-.64s but it rejects to handle this table"
+ER_BACKUP_CREATE_BACKUP_DRIVER
+   eng "Can't create %-.64s backup driver"
+ER_BACKUP_CREATE_RESTORE_DRIVER
+   eng "Can't create %-.64s restore driver"
+ER_BACKUP_TOO_MANY_IMAGES
+   eng "Backup image uses %d backup engines but maximum %d are supported"
+ER_BACKUP_WRITE_META
+   eng "Error when saving metadata of %-.64s"
+ER_BACKUP_READ_META
+   eng "Error when reading metadata"
+ER_BACKUP_CREATE_META
+   eng "Failed to obtain metadata for %-.64s"
+ER_BACKUP_GET_BUF
+   eng "Can't allocate buffer for image data transfer"
+ER_BACKUP_WRITE_DATA
+   eng "Error when writing data from %-.64s backup driver (data block for table #%d)"
+ER_BACKUP_READ_DATA
+   eng "Error when reading data from backup stream"
+ER_BACKUP_NEXT_CHUNK
+   eng "Can't go to the next chunk in backup stream"
+ER_BACKUP_INIT_BACKUP_DRIVER
+   eng "Can't initialize %-.64s backup driver"
+ER_BACKUP_INIT_RESTORE_DRIVER
+   eng "Can't initialize %-.64s restore driver"
+ER_BACKUP_STOP_BACKUP_DRIVER
+   eng "Can't shut down %-.64s backup driver"
+ER_BACKUP_STOP_RESTORE_DRIVERS
+   eng "Can't shut down %-.64s restore driver(s)"
+ER_BACKUP_PREPARE_DRIVER
+   eng "%-.64s backup driver can't prepare for synchronization"
+ER_BACKUP_CREATE_VP
+   eng "%-.64s backup driver can't create its validity point"
+ER_BACKUP_UNLOCK_DRIVER
+   eng "Can't unlock %-.64s backup driver after creating the validity point"
+ER_BACKUP_CANCEL_BACKUP
+   eng "%-.64s backup driver can't cancel its backup operation"
+ER_BACKUP_CANCEL_RESTORE
+   eng "%-.64s restore driver can't cancel its restore operation"
+ER_BACKUP_GET_DATA
+   eng "Error when polling %-.64s backup driver for its data"
+ER_BACKUP_SEND_DATA
+   eng "Error when sending data (for table #%d) to %-.64s restore driver"
+ER_BACKUP_SEND_DATA_RETRY
+   eng "After %d attempts %-.64s restore driver still can't accept next block of data"
+ER_BACKUP_OPEN_TABLES
+   eng "Open and lock tables failed in %-.64s"
+ER_BACKUP_THREAD_INIT
+   eng "Backup driver's table locking thread can not be initialized."
+ER_BACKUP_PROGRESS_TABLES
+   eng "Can't open the backup logs as tables. Check 'mysql.backup_history' and 'mysql.backup_progress' or run mysql_upgrade to repair."
+ER_TABLESPACE_EXIST
+  eng "Tablespace '%-.192s' already exists"
+ER_NO_SUCH_TABLESPACE
+  eng "Tablespace '%-.192s' doesn't exist"
+ER_BACKUP_CANT_FIND_SE
+   eng "Backup image contains data created by a native driver of %-.64s storage engine but this engine can not be found on this server"
+ER_BACKUP_NO_NATIVE_BE
+   eng "Backup image contains data created by a native driver of %-.64s storage engine but it has no native backup support on this server"
+ER_BACKUP_UNKNOWN_BE
+   eng "Backup engine #%d needed for restoring from backup image has unknown type"
+ER_BACKUP_WRONG_TABLE_BE
+   eng "Backup image specifies wrong backup engine #%d for one of the tables"
+ER_BACKUP_CANT_RESTORE_DB
+   eng "Could not restore database %-.64s"
+ER_BACKUP_CANT_RESTORE_TABLE
+   eng "Could not restore table %-.64s"
+ER_BACKUP_CANT_RESTORE_VIEW
+   eng "Could not restore view %-.64s. Please check the view definition for possible missing dependencies."
+ER_BACKUP_CANT_RESTORE_SROUT
+   eng "Could not restore stored routine %-.64s"
+ER_BACKUP_CANT_RESTORE_EVENT
+   eng "Could not restore event %-.64s"
+ER_BACKUP_CANT_RESTORE_TRIGGER
+   eng "Could not restore trigger %-.64s"
+ER_BACKUP_CATALOG_ADD_DB
+   eng "Failed to add database `%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_TABLE
+   eng "Failed to add table `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_VIEW
+   eng "Failed to add view `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_SROUT
+   eng "Failed to add stored routine `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_EVENT
+   eng "Failed to add event `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_CATALOG_ADD_TRIGGER
+   eng "Failed to add trigger `%-.64s`.`%-.64s` to the catalog"
+ER_BACKUP_UNKNOWN_OBJECT
+   eng "Backup image refers to an object which could not be found in the catalog"
+ER_BACKUP_UNKNOWN_OBJECT_TYPE
+   eng "Backup image refers to an object of unknown type"
+ER_BACKUP_OPEN_WR
+   eng "Cannot open backup stream for writing"
+ER_BACKUP_OPEN_RD
+   eng "Cannot open backup stream for reading"
+ER_BACKUP_BAD_MAGIC
+   eng "Could not find correct signature at the beginning of backup stream"
+ER_BACKUP_CONTEXT_CREATE
+   eng "Cannot create backup/restore execution context"
+ER_BACKUP_CONTEXT_REMOVE
+   eng "Error when cleaning up after backup/restore operation"
+ER_BAD_PATH
+   eng "Malformed file path '%-128s'"
+ER_DDL_BLOCK
+   eng "Erorr when attempting to block DDLs"
+ER_BACKUP_LOGGER_INIT
+   eng "Could not initialize logging and reporting services"
+ER_BACKUP_WRITE_SUMMARY
+   eng "Error when writing summary section of backup image"
+ER_BACKUP_READ_SUMMARY
+   eng "Error when reading summary section of backup image"
+ER_BACKUP_GET_META_DB
+   eng "Failed to obtain metadata for database %-.64s"
+ER_BACKUP_GET_META_TABLE
+   eng "Failed to obtain metadata for table %-.64s"
+ER_BACKUP_GET_META_VIEW
+   eng "Failed to obtain metadata for view %-.64s"
+ER_BACKUP_GET_META_SROUT
+   eng "Failed to obtain metadata for stored routine %-.64s"
+ER_BACKUP_GET_META_EVENT
+   eng "Failed to obtain metadata for event %-.64s"
+ER_BACKUP_GET_META_TRIGGER
+   eng "Failed to obtain metadata for trigger %-.64s"
+ER_BACKUP_CREATE_BE
+   eng "Cannot create backup engine for storage engine %-.64s"
+ER_BACKUP_LIST_PERDB
+   eng "Can't enumerate per database objects"
+ER_BACKUP_LIST_DB_VIEWS
+   eng "Can't enumerate views in database `%-.64s`"
+ER_BACKUP_LIST_DB_SROUT
+   eng "Can't enumerate stored routines in database `%-.64s`"
+ER_BACKUP_LIST_DB_EVENTS
+   eng "Can't enumerate events in database `%-.64s`"
+ER_BACKUP_LIST_DB_TRIGGERS
+   eng "Can't enumerate triggers in database `%-.64s`"
+ER_BACKUP_LOG_WRITE_ERROR
+   eng "Can't write to the online backup progress log %-.64s."
+ER_TABLESPACE_NOT_EMPTY
+  eng "Tablespace '%-.192s' not empty"
+ER_BACKUP_CAT_ENUM
+   eng "Could not access the contents of the backup catalog when writing backup image preamble"
+ER_BACKUP_CANT_RESTORE_TS
+   eng "Could not create tablespace %-.64s needed by tables being restored."
+ER_BACKUP_TS_CHANGE
+   eng "Tablespace %-.64s needed by tables being restored, but the current tablespace definition differs from how it was when backup was made."
+ER_BACKUP_GET_META_TS
+   eng "Failed to obtain metadata for tablespace %-.64s."
+ER_TABLESPACE_DATAFILE_EXIST
+  eng "Tablespace data file '%-.192s' already exists"
+ER_BACKUP_CATALOG_ADD_TS
+   eng "Failed to add tablespace `%-.64s` to the catalog"
+
 ER_DEBUG_SYNC_TIMEOUT
   eng "debug sync point wait timed out"
   ger "Debug Sync Point Wartezeit �hritten"
@@ -6209,6 +6410,77 @@ ER_DEBUG_SYNC_HIT_LIMIT
   eng "debug sync point hit limit reached"
   ger "Debug Sync Point Hit Limit erreicht"
 
+ER_BACKUP_FAILED_TO_INIT_COMPRESSION
+  eng "Could not initialize compression of backup image (function deflateInit2 returned error code %d: %-.64s)"
+ER_BACKUP_OBTAIN_NAME_LOCK_FAILED
+  eng "Restore failed to obtain the name locks on the tables."
+ER_BACKUP_RELEASE_NAME_LOCK_FAILED
+  eng "Restore failed to release the name locks on the tables."
+
+# NOTE: If changing the length limit for the path name in this message,
+#       please change ER_NOT_RW_DIR_PATHSIZE in set_var.h too.
+ER_NOT_RW_DIR
+  eng "The path specified for the system variable %s is not a directory or cannot be written: %.200s"
+
+ER_DDL_TIMEOUT
+   eng "The backup wait timeout has expired for query '%-64s'."
+ER_BACKUP_LIST_DB_PRIV
+   eng "Can't enumerate grants in database '%-.64s'."
+ER_BACKUP_CATALOG_ADD_PRIV
+   eng "Failed to add grant '%-.64s' to the catalog"
+ER_BACKUP_GET_META_PRIV
+   eng "Failed to obtain metadata for grant '%-.64s'."
+ER_BACKUP_CANT_RESTORE_PRIV
+   eng "Could not execute grant '%-.64s'."
+ER_BACKUP_GRANT_SKIPPED
+   eng "The grant '%-.64s' for the user %-.64s was skipped because the user does not exist."
+ER_BACKUP_GRANT_WRONG_DB
+   eng "The grant '%-.64s' failed. Database not included in the backup image."
+ER_BACKUP_LOGPATHS
+  eng "The log names for backup_history and backup_progress must be unique."
+ER_BACKUP_LOGPATH_INVALID
+  eng "The path specified for the %-.64s is invalid."
+ER_BACKUP_SEND_REPLY
+   eng "Failed to send reply to client after successful %-.64s operation"
+   nor "Feil ved sending av svar til klient etter %-.64s"
+   norwegian-ny "Feil ved sending av svar til klienten etter %-.64s"
+ER_BACKUP_CLOSE
+   eng "Backup/Restore: Error on close of backup stream"
+   nor "Backup/Restore: Feil ved lukning av backup-strøm"
+   norwegian-ny "Backup/Restore:  Feil ved lukking av backup-straum"
+ER_BACKUP_BINLOG
+   eng "Error on accessing binlog during BACKUP"
+   nor "Lesing av binlog feilet under BACKUP"
+   norwegian-ny "Lesing av binlog feila under BACKUP"
+ER_BACKUP_LOG_OUTPUT
+  eng "Removing backup log entries by date or backup_id requires logging to tables."
+ER_BACKUP_PURGE_DATETIME
+  eng "The datetime specified is invalid for the '%-.64s' command."
+ER_BACKUP_LOGS_DELETED
+  eng "Backup log entries deleted: "
+ER_BACKUP_LOGS_TRUNCATED
+  eng "All backup log entries have been deleted"
+ER_MASTER_BLOCKING_SLAVES
+  eng "The master is not allowing slave connections."
+ER_RESTORE_ON_MASTER
+  eng "A restore operation was initiated on the master."
+ER_RESTORE_ON_SLAVE
+  eng "A restore operation was attempted on a slave during replication. You must stop the slave prior to running a restore."
+ER_RESTORE_DB_EXISTS
+  eng "Database \'%-.64s\' already exists. Use OVERWRITE flag to overwrite."
+
+ER_BACKUP_UNEXPECTED_DATA
+  eng "Backup image contains no tables, but table data was found in it"
+ER_BACKUP_BACKUP_DBS
+  eng "Backing up %u database(s) %.220s"
+ER_BACKUP_RESTORE_DBS
+  eng "Restoring %u database(s) %.220s"
+
+ER_BACKUP_SYNCHRONIZE
+  eng "Backup failed to synchronize table images."
+ER_RESTORE_CANNOT_START_SLAVE
+  eng "Cannot start slave. SLAVE START is blocked by %-.64s."
+
 ER_DUP_SIGNAL_SET 42000
         eng "Duplicate condition information item '%s'"
 
@@ -6236,6 +6508,33 @@ WARN_COND_ITEM_TRUNCATED
 ER_COND_ITEM_TOO_LONG
         eng "Data too long for condition item '%s'"
 
+ER_PATH_LENGTH
+  eng "The path specified for %.64s is too long."
+ER_BACKUP_INTERRUPTED
+  eng "Operation has been interrupted."
+ER_BACKUP_NOT_ENABLED
+  eng "The MySQL Backup system is disabled in this release. Use --mysql-backup or --mysql-backup=1 on server startup to enable."
+ER_BACKUP_NO_NDB
+  eng "NDB tables cannot be used with MySQL Backup. Please see the MySQL Cluster reference manual."
+ER_BACKUP_ACCESS_DENIED_ERROR
+  eng "Insufficient privileges. You must have the BACKUP privilege to backup database '%s'."
+ER_RESTORE_ACCESS_DENIED_ERROR
+  eng "Insufficient privileges. You must have the RESTORE privilege to restore database '%s'."
+ER_BACKUP_ACCESS_DBS_INCOMPLETE
+  eng "Insufficient privileges. You must have access privileges to all databases to execute BACKUP DATABASE *."
+ER_BACKUP_ACCESS_OBJS_INCOMPLETE
+  eng "Insufficient privileges. You do not have privileges to backup database '%s'."
+ER_BACKUP_USER_PRIV_CHECK
+  eng "Failed to perform access privileges check for user '%s'."
+ER_RESTORE_EVENT_DISABLED
+  eng "The event '%s' was disabled during restore."
+ER_RESTORE_ACCESS_OBJS_INCOMPLETE
+  eng "Insufficient privileges. You do not have privileges to restore the object '%s' from this backup image."
+ER_RESTORE_ACCESS_DEFINER
+  eng "Insufficient privileges. You must have the SUPER privilege to restore the object '%s'.'%s'."
+ER_RESTORE_DB_ERROR
+  eng "The database for object '%s' was not found in the catalog."
+
 ER_UNKNOWN_LOCALE                                                                                                                                                                    
         eng "Unknown locale: '%-.64s'"
 

=== added file 'sql/si_logs.cc'
--- a/sql/si_logs.cc	1970-01-01 00:00:00 +0000
+++ b/sql/si_logs.cc	2009-12-04 22:31:25 +0000
@@ -0,0 +1,225 @@
+/**
+   @file
+
+   This file defines the API for the following object services:
+     - table|file|both logging services for backup
+
+  The methods defined below are used to provide server functionality to
+  and permitting an isolation layer for the client (caller).
+*/
+
+#include "mysql_priv.h"
+#include "si_logs.h"
+#include "log.h"
+
+/**
+  Initialize the class for logging backup or restore operation.
+  
+  This constructor initializes the m_op_hist structure with the
+  information passed during instantiation.
+
+  @param[in]  THD   current thread
+  @param[in]  type  type of operation (backup or restore)
+  @param[in]  path  location of the backup image
+  @param[in]  query backup or restore query starting the operation
+    
+  @todo Add code to get the user comment from command.
+
+*/ 
+Backup_log::Backup_log(THD *thd, 
+                       enum_backup_operation type, 
+                       const char *query)
+{
+  m_thd= thd;
+
+  bzero(&m_op_hist, sizeof(st_backup_history));
+  m_op_hist.process_id= m_thd->id;
+  m_op_hist.state= BUP_STARTING;
+  m_op_hist.operation= type;
+
+  if (strlen(query) > 0)
+    m_op_hist.command= (char *)query;
+
+  MYSQL_BACKUP_LOG *backup_log= logger.get_backup_history_log_file_handler();
+  m_op_hist.backup_id= backup_log->get_next_backup_id();
+}
+
+/**
+  Report name of a driver used in backup/restore operation.
+
+  This method updates the drivers information in the history data. This method 
+  appends to the those drivers listed in the history data.
+
+  @param[IN] char *            driver_name  The name of the engine to add.
+*/
+void Backup_log::add_driver(const char *driver_name)
+{
+  String str;    // drivers string
+
+  str.length(0);
+  if (m_op_hist.driver_name.length())
+    str.append(m_op_hist.driver_name);
+  if ((str.length() > 0) && (strlen(driver_name) > 0))
+    str.append(", ");
+  if (strlen(driver_name) > 0)
+    str.append(driver_name);
+  m_op_hist.driver_name.copy(str);
+}
+
+/**
+  Report name of a backup image file and path used in backup/restore.
+
+  This method updates the backup_file and backup_file_path information 
+  in the history data. 
+
+  @param[IN] char * full_path  The full path and file name of the backup file.
+*/
+void Backup_log::backup_file(const char *full_path)
+{
+  String str;   
+
+  if (strlen(full_path))
+  {
+    size_t i= 0;
+    size_t j= 0;
+    j= dirname_part(m_op_hist.backup_file_path, full_path, &i);
+    m_op_hist.backup_file = (char *)(full_path + i);
+  }
+}
+
+/**
+  Write history data.
+
+  This method calls the server's logger to write the backup_history log
+  information.
+
+  @returns results of logging function (i.e., TRUE if error)
+
+  @note This method should be called after all data has been set for the
+  history data.
+*/
+bool Backup_log::write_history() 
+{ 
+  return logger.backup_history_log_write(m_thd, &m_op_hist); 
+}
+
+/**
+  Write the backup log entry for the backup progress log.
+
+  This method is a pass-through to allow calling of the logging 
+  functions for the backup history log.
+
+  @param[IN]   object      The name of the object processed
+  @param[IN]   start       Start datetime
+  @param[IN]   stop        Stop datetime
+  @param[IN]   size        Size value
+  @param[IN]   progress    Progress (percent)
+  @param[IN]   error_num   Error number (should be 0 is success)
+  @param[IN]   notes       Misc data from backup kernel
+
+  @returns results of logging function (i.e., TRUE if error)
+*/
+bool Backup_log::write_progress(const char *object,
+                                time_t start,
+                                time_t stop,
+                                longlong size,
+                                longlong progress,
+                                int error_num,

+                                const char *notes)
+{
+  /* Write the message to the backup progress log */
+  return logger.backup_progress_log_write(m_thd, m_op_hist.backup_id, object, 
+                                          start, stop, size, progress, 
+                                          error_num, notes);
+}
+
+/**
+  Write master's binlog file and position to progress log.
+
+  @returns results of logging function (i.e., TRUE if error)
+*/
+bool Backup_log::write_master_binlog_info()
+{
+  char buff[1024];
+  bool ret= FALSE;
+
+  if (m_op_hist.master_binlog_file || 
+      m_op_hist.master_binlog_start_pos)
+  {
+    sprintf(buff, 
+      "Recording master binlog information. binlog file = '%s', position = %d.",
+      m_op_hist.master_binlog_file, m_op_hist.master_binlog_start_pos);
+    ret= write_progress(0, 0, 0, 0, 0, 0, (char *)&buff);
+  }
+  return ret;
+}
+
+/** 
+  Report change of the state of operation
+ 
+  For possible states see definition of @c enum_backup_state 
+
+  @todo Consider reporting state changes in the server error log (as info
+  entries).
+ */
+void Backup_log::set_state(enum_backup_state state)
+{
+  m_op_hist.state= state;
+  logger.backup_progress_log_write(m_thd, m_op_hist.backup_id, "backup kernel", 0, 
+                            0, 0, 0, 0, get_state_string(state));
+}
+
+/** 
+  Get current state of operation.
+ 
+  For possible states see definition of @c enum_backup_state 
+ */
+enum_backup_state Backup_log::get_state()
+{
+  return m_op_hist.state;
+}
+
+
+/**
+  Report validity point creation time.
+
+  This method saves the validation point time in the history data and writes
+  a message to the progress log.
+
+  @param[IN]  when  Time of validity point.
+  @param[IN]  report Determines if an entry should be written to the 
+                     backup_progress log.
+
+  @note If the time is 0|NULL, nothing is saved in the history data.
+*/
+void Backup_log::vp_time(time_t when, bool report)
+{
+  if (when)
+  {
+    m_op_hist.vp_time= when;
+    if (report)
+      logger.backup_progress_log_write(m_thd, m_op_hist.backup_id, "backup kernel", 
+                                       when, 0, 0, 0, 0, "vp time");
+  }
+}
+
+/**
+  Get text string for state.
+
+  @param[IN]  state  The current state of the operation
+
+  @returns char * a text string for state.
+*/
+inline
+const char *Backup_log::get_state_string(enum_backup_state state)
+{
+  switch (state) {
+  case BUP_COMPLETE: return("complete");
+  case BUP_STARTING: return("starting");
+  case BUP_VALIDITY_POINT: return("validity point");
+  case BUP_RUNNING: return("running");
+  case BUP_ERRORS: return("error");
+  case BUP_CANCEL: return("cancel");
+  default: return("unknown");
+  }
+}

=== added file 'sql/si_logs.h'
--- a/sql/si_logs.h	1970-01-01 00:00:00 +0000
+++ b/sql/si_logs.h	2009-12-04 22:31:25 +0000
@@ -0,0 +1,231 @@
+#ifndef SI_LOGS_H_
+#define SI_LOGS_H_
+
+/**
+   @file
+
+   This file defines the API for the following object services:
+     - table|file|both logging services for backup
+*/
+
+/**
+  List of operations for backup history log.
+  
+  @note These must match the definition for the column 'operation'
+  in the backup_history and backup_progress tables.
+*/
+enum enum_backup_operation
+{
+  OP_BACKUP = 1,
+  OP_RESTORE = 2
+};
+
+/**
+  List of states for backup logs.
+  
+  @note These must match the definition for the column 'backup_state'
+  in the backup_history table.
+*/
+enum enum_backup_state
+{
+  BUP_COMPLETE = 1,
+  BUP_STARTING = 2,
+  BUP_VALIDITY_POINT = 3,
+  BUP_RUNNING = 4,
+  BUP_ERRORS = 5,
+  BUP_CANCEL = 6
+};
+
+/**
+  Structure for holding backup history data.
+
+  This structure is used to collect the information needed to write a
+  single row of information to the backup_history log.
+*/
+struct st_backup_history
+{
+  ulonglong backup_id;             ///< the id for this row in the log
+  int process_id;                  ///< the process id of the backup/restore
+  enum_backup_state state;         ///< current state of the operation
+  enum_backup_operation operation; ///< the type of operation (backup, restore)
+  int error_num;                   ///< error number
+  char *user_comment;              ///< user comment from command
+  char *backup_file;               ///< the backup image file
+  char backup_file_path[FN_REFLEN]; ///< the backup image path
+  char *command;                   ///< the command used
+  int binlog_start_pos;            ///< position in the binary log
+  char *binlog_file;               ///< the name of the binary log file
+  int num_objects;                 ///< number of objects in backup
+  longlong size;                   ///< total size of the backup image file
+  time_t start;                    ///< start time of operation
+  time_t stop;                     ///< stop time of operation
+  time_t vp_time;                  ///< point in time validation was assured
+  String driver_name;              ///< list of backup engines used
+  int master_binlog_start_pos;           ///< position in the binary log
+  char *master_binlog_file;        ///< name of the master's binary log file
+};
+
+
+/**
+  Class Backup_log defines the basic set of operations for server logs.
+
+  This class is used to write information to the backup_history and 
+  backup_progress logs. While the log output control is determined by the 
+  server, this class is used as an interface to allow the backup to write 
+  messages to the logs without regard to how they are stored or how the 
+  logging mechanisms of the server behave.
+
+  To use this class, one must instantiate the class. When instantiated, the
+  constructor gets the next backup_id from the server and initializes the 
+  history data stored in m_op_hist.
+
+  Use the set methods below to store the information for the logs. When the
+  caller is ready to write information to the logs, call the write_*() 
+  method for the appropriate log.
+
+  The write_history() method is used to write the history data to the 
+  backup_history log. It should be called at the end of the backup or 
+  restore operation.
+
+  The write_progress() method is used to write miscellaneous messages to
+  the backup_progress log. It may be called at any time.
+
+  The state() method is designed to change the state of the operation and
+  to write a message to the backup_progress log.
+
+  @todo Add method to set the user comment from the command-line.
+*/
+class Backup_log
+{
+public:
+  Backup_log(THD *thd, 
+             enum_backup_operation type, 
+             const char *query); 
+
+  /* 
+    Write the backup history data to the backup_history log. 
+  */
+  bool write_history();
+
+  /* 
+    Write a message to the backup_progress log.
+  */
+  bool write_progress(const char *object,
+                      time_t start,
+                      time_t stop,
+                      longlong size,
+                      longlong progress,
+                      int error_num,
+                      const char *notes);
+
+  /*
+    Write master's binlog position and file if recorded earlier.
+  */
+  bool write_master_binlog_info();
+
+  /*
+    Check the backup logs (as tables).
+  */
+  bool check_logs();
+
+  /*
+    The following get/set methods populate the history data for
+    the backup_history log.
+  */
+  ulonglong get_backup_id() { return m_op_hist.backup_id; }
+  void set_state(enum_backup_state);
+  enum_backup_state get_state();
+  void error_num(int code) { m_op_hist.error_num= code; }
+  void binlog_start_pos(unsigned long int pos) 
+  { 
+    m_op_hist.binlog_start_pos= pos; 
+  }
+  void binlog_file(char *file);
+  void master_binlog_start_pos(unsigned long int pos) 
+  { 
+    m_op_hist.master_binlog_start_pos= pos; 
+  }
+  void master_binlog_file(char *file);
+  void num_objects(int num) { m_op_hist.num_objects= num; }
+  void size(longlong s) { m_op_hist.size= s; }
+  void start(time_t when);
+  void stop(time_t when);
+  void vp_time(time_t when, bool report);
+  void add_driver(const char* driver);
+  void backup_file(const char *full_path);
+
+private:
+  st_backup_history m_op_hist;  ///< history log information
+  THD *m_thd;                   ///< current thread
+
+  /*
+    Helper method to provide string constants for states.
+  */
+  const char *get_state_string(enum_backup_state state);
+};
+
+/**
+  Report start of an operation.
+
+  This method saves the start time in the history data.
+  
+  @param[IN]  when  The start time.
+
+  @note If the time is 0|NULL, nothing is saved in the history data.
+*/
+inline
+void Backup_log::start(time_t when)
+{
+  if (when)
+    m_op_hist.start= when;
+}
+
+/**
+  Report stop of an operation.
+
+  This method saves the stop time in the history data.
+  
+  @param[IN]  when  The stop time.
+
+  @note If the time is 0|NULL, nothing is saved in the history data.
+*/
+inline
+void Backup_log::stop(time_t when)
+{
+  if (when)
+    m_op_hist.stop= when;
+}
+
+/** 
+  Report binlog position at validity point.
+
+  This method saves the binlog file name in the history data.
+
+  @param[IN] file Binlog file name.
+
+  @note If the file name is 0|NULL, nothing is saved in the history data.
+*/
+inline
+void Backup_log::binlog_file(char *file)
+{
+  if (file && strlen(file) > 0)
+    m_op_hist.binlog_file= file;
+}
+
+/** 
+  Report master's binlog position at validity point.
+
+  This method saves the binlog file name in the history data.
+
+  @param[IN] file Binlog file name.
+
+  @note If the file name is 0|NULL, nothing is saved in the history data.
+*/
+inline
+void Backup_log::master_binlog_file(char *file)
+{
+  if (file && strlen(file) > 0)
+    m_op_hist.master_binlog_file= file;
+}
+
+#endif // SI_LOGS_H_

=== added file 'sql/si_objects.cc'
--- a/sql/si_objects.cc	1970-01-01 00:00:00 +0000
+++ b/sql/si_objects.cc	2009-12-04 22:31:25 +0000
@@ -0,0 +1,3945 @@
+/* Copyright (C) 2008 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 Server Service Interface for Backup: the implementation.  */
+
+#include "mysql_priv.h"
+#include "sql_prepare.h"
+
+#include "si_objects.h"
+#include "bml.h"
+#include "sql_show.h"
+#include "sql_trigger.h"
+#include "sp.h"
+#include "sp_head.h" // for sp_add_to_query_tables().
+#include "rpl_mi.h"
+
+#ifdef HAVE_EVENT_SCHEDULER
+#include "events.h"
+#include "event_data_objects.h"
+#include "event_db_repository.h"
+#endif
+
+Backup_sx_lock *BML_instance= NULL;
+Backup_sx_lock *BCB_instance= NULL;
+
+extern my_bool disable_slaves;
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#define STR(x) (int) (x).length(), (x).ptr()
+
+#define LXS_INIT(x) {((char *) (x)), ((size_t) (sizeof (x) - 1))}
+
+///////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Si_session_context defines a way to save/reset/restore session context
+  for SI-operations.
+*/
+
+class Si_session_context
+{
+public:
+  inline Si_session_context()
+  { m_engage_general_log= FALSE; }
+
+public:
+  void save_si_ctx(THD *thd);
+  void reset_si_ctx(THD *thd);
+  void restore_si_ctx(THD *thd);
+
+private:
+  ulong m_sql_mode_saved;
+  CHARSET_INFO *m_client_cs_saved;
+  CHARSET_INFO *m_results_cs_saved;
+  CHARSET_INFO *m_connection_cl_saved;
+  CHARSET_INFO *m_old_db_collation;
+  Time_zone *m_tz_saved;
+  TABLE *m_tmp_tables_saved;
+  bool m_engage_general_log;  
+  String m_db_saved_string;
+
+private:
+  Si_session_context(const Si_session_context &);
+  Si_session_context &operator =(const Si_session_context &);
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Preserve the following session attributes:
+    - sql_mode;
+    - character_set_client;
+    - character_set_results;
+    - collation_connection;
+    - time_zone;
+
+  Remember also session temporary tables.
+*/
+
+void Si_session_context::save_si_ctx(THD *thd)
+{
+  DBUG_ENTER("Si_session_context::save_si_ctx");
+  m_sql_mode_saved= thd->variables.sql_mode;
+  m_client_cs_saved= thd->variables.character_set_client;
+  m_results_cs_saved= thd->variables.character_set_results;
+  m_connection_cl_saved= thd->variables.collation_connection;
+  m_tz_saved= thd->variables.time_zone;
+  m_tmp_tables_saved= thd->temporary_tables;
+  m_old_db_collation= thd->variables.collation_database;
+  
+  /* Saving the default database */
+  if (thd->db)
+    m_db_saved_string.copy(thd->db, thd->db_length, thd->db_charset);
+  /*
+    Clear up and old data in the saved database since there is no default
+    database set.
+  */
+  else
+    if (!m_db_saved_string.is_empty())
+      m_db_saved_string= 0; 
+  /* clean up database info from thd context */
+  thd->set_db(NULL,0);
+  
+  DBUG_VOID_RETURN;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Reset session state to the following:
+    - sql_mode: MODE_NO_AUTO_CREATE_USER | MODE_NO_ENGINE_SUBSTITUTION
+    - character_set_client: utf8
+    - character_set_results: binary (to fetch results w/o conversion)
+    - collation_connection: utf8
+
+  Temporary tables should be ignored while looking for table structures.
+  We want to deal with real tables, not temporary ones.
+*/
+
+void Si_session_context::reset_si_ctx(THD *thd)
+{
+  DBUG_ENTER("Si_session_context::reset_si_ctx");
+
+  thd->variables.sql_mode= 
+    MODE_NO_AUTO_CREATE_USER | MODE_NO_ENGINE_SUBSTITUTION;
+
+  thd->variables.character_set_client= system_charset_info;
+  thd->variables.character_set_results= &my_charset_bin;
+  thd->variables.collation_connection= system_charset_info;
+  thd->update_charset();
+
+  thd->temporary_tables= NULL;
+
+  /*
+   Turn off general log during restore.
+  */
+  if (obs::is_general_log_engaged())
+  {
+    obs::engage_general_log(FALSE);
+    m_engage_general_log= TRUE;
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Restore session state.
+*/
+
+void Si_session_context::restore_si_ctx(THD *thd)
+{
+
+  String query ("USE `",m_db_saved_string.charset());
+  Ed_connection ed_connection(thd);
+  LEX_STRING lex_query;
+
+  
+  DBUG_ENTER("Si_session_context::restore_si_ctx");
+
+ 
+  /* Restoring the default database */
+  if (m_db_saved_string != 0)
+  {
+    query.append(m_db_saved_string);
+    query.append("`");
+    lex_query= query.lex_string(); 
+    ed_connection.execute_direct(lex_query);
+  }
+  thd->variables.sql_mode= m_sql_mode_saved;
+  thd->variables.time_zone= m_tz_saved;
+
+  thd->variables.collation_connection= m_connection_cl_saved;
+  thd->variables.character_set_results= m_results_cs_saved;
+  thd->variables.character_set_client= m_client_cs_saved;
+  thd->update_charset();
+
+  thd->temporary_tables= m_tmp_tables_saved;
+  thd->variables.collation_database= m_old_db_collation;
+
+  /*
+    Turn on general log.
+  */
+  if (m_engage_general_log)
+    obs::engage_general_log(TRUE);
+
+  DBUG_VOID_RETURN;
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Execute one SQL statement in a backup-specific context.
+
+  Some session attributes are preserved and reset to predefined values
+  before query execution (@see Si_session_context).
+
+  @param[in]    thd             thread handle
+  @param[in]    ed_connection   Ed_connection handle
+  @param[in]    query           SQL query to be executed
+
+  @return       status
+    @retval     TRUE            error
+    @retval     FALSE           success
+*/
+
+bool
+run_service_interface_sql(THD *thd, Ed_connection *ed_connection,
+                          const LEX_STRING *query, bool get_warnings)
+{
+  Si_session_context session_context;
+  
+  DBUG_ENTER("run_service_interface_sql");
+  DBUG_PRINT("run_service_interface_sql",
+             ("query: %.*s",
+              (int) query->length, (const char *) query->str));
+
+  session_context.save_si_ctx(thd);
+  session_context.reset_si_ctx(thd);
+
+  bool rc= ed_connection->execute_direct(*query);
+
+  if (get_warnings) 
+  {
+    /* Push warnings on the THD error stack. */
+    thd->warning_info->append_warnings(thd, ed_connection->get_warn_list());
+  }
+
+  session_context.restore_si_ctx(thd);
+
+  DBUG_RETURN(rc);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Table_name_key defines a hash key, which includes database and tables
+  names.
+*/
+
+typedef MDL_key Table_name_key;
+
+///////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+
+static uchar *
+get_table_name_key(const uchar *record,
+                   size_t *key_length,
+                   my_bool not_used __attribute__((unused)))
+{
+  Table_name_key *table_name_key= (Table_name_key *) record;
+  *key_length= table_name_key->length();
+  return (uchar*) table_name_key->ptr();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+static void
+free_table_name_key(void *data)
+{
+  Table_name_key *table_name_key= (Table_name_key *) data;
+  my_free(table_name_key, MYF(0));
+}
+
+} // end of extern "C"
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Int_value is a wrapper for unsigned long type to be used with
+  the String_stream class.
+*/
+
+struct Int_value
+{
+  unsigned long m_value;
+
+  Int_value(unsigned long value_arg)
+    :m_value(value_arg)
+  { }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/*
+  C_str is a wrapper for C-string (const char *) to be used with the
+  String_stream class.
+*/
+
+struct C_str
+{
+  LEX_STRING lex_string;
+
+  inline C_str(const char *str, size_t length)
+  {
+    lex_string.str= (char *) str;
+    lex_string.length= length;
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class String_stream
+
+  This class provides a convenient way to create a dynamic string from
+  C-strings in different forms (LEX_STRING, const char *) and integer
+  constants.
+*/
+
+class String_stream
+{
+public:
+  inline String_stream()
+    : m_buffer(&m_container),
+      m_error(FALSE)
+  { }
+
+  inline String_stream(String *dst)
+    : m_buffer(dst),
+      m_error(FALSE)
+  { }
+
+public:
+  inline String *str() { return m_buffer; }
+
+  inline const LEX_STRING *lex_string()
+  {
+    m_lex_string= m_buffer->lex_string();
+    return &m_lex_string;
+  }
+
+  inline void reset()
+  {
+    m_buffer->length(0);
+    m_error= FALSE;
+  }
+
+  inline bool is_error() const { return m_error; }
+
+public:
+  String_stream &operator <<(const Int_value &v);
+
+  inline String_stream &operator <<(const C_str &v)
+  {
+    m_error= m_error || m_buffer->append(v.lex_string.str, v.lex_string.length);
+    return *this;
+  }
+
+  inline String_stream &operator <<(const LEX_STRING *v)
+  {
+    m_error= m_error || m_buffer->append(v->str, v->length);
+    return *this;
+  }
+
+  inline String_stream &operator <<(const String *v)
+  {
+    m_error= m_error || m_buffer->append(v->ptr(), v->length());
+    return *this;
+  }
+
+  inline String_stream &operator <<(const char *v)
+  {
+    m_error= m_error || m_buffer->append(v);
+    return *this;
+  }
+
+private:
+  String m_container;
+  String *m_buffer;
+  LEX_STRING m_lex_string;
+  bool m_error;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+String_stream &String_stream::operator <<(const Int_value &int_value)
+{
+  char buffer[13];
+  my_snprintf(buffer, sizeof (buffer), "%lu", int_value.m_value);
+
+  m_error= m_error || m_buffer->append(buffer);
+  return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Out_stream
+
+  This class encapsulates the semantic of creating the serialization image.
+  The image is actually a list of strings. Strings may contain any data
+  (including binary and new-line characters). String is length-coded, which
+  means there is string length before the string data.
+
+  The format is as follows:
+    <string length> <space> <string data> \n
+
+  Example:
+    12 Hello,
+    world
+    5 qwerty
+*/
+
+class Out_stream
+{
+public:
+  inline Out_stream(String *image) :
+    m_image(image),
+    m_error(FALSE)
+  { }
+
+public:
+  inline bool is_error() const { return m_error; }
+
+public:
+  Out_stream &operator <<(const LEX_STRING *query);
+
+  inline Out_stream &operator <<(const char *query)
+  {
+    LEX_STRING str= { (char *) query, strlen(query) };
+    return Out_stream::operator <<(&str);
+  }
+
+  inline Out_stream &operator <<(const String *query)
+  {
+    LEX_STRING str= { (char *) query->ptr(), query->length() };
+    return Out_stream::operator <<(&str);
+  }
+
+  inline Out_stream &operator <<(String_stream &s_stream)
+  {
+    return Out_stream::operator <<(s_stream.str());
+  }
+
+private:
+  String *m_image;
+  bool m_error;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+Out_stream &Out_stream::operator <<(const LEX_STRING *query)
+{
+  if (m_error)
+    return *this;
+
+  String_stream s_stream(m_image);
+
+  s_stream <<
+    Int_value(query->length) << " " << query << "\n";
+
+  m_error= s_stream.is_error();
+
+  return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class In_stream
+
+  This class encapsulates the semantic of reading from the serialization image.
+  For the format definition of the serialization image, @see Out_stream.
+*/
+
+class In_stream
+{
+public:
+  inline In_stream(uint image_version, const String *image)
+    : m_image_version(image_version),
+      m_image(image),
+      m_read_ptr(m_image->ptr()),
+      m_end_ptr(m_image->ptr() + m_image->length()),
+      m_error(FALSE)
+  { }
+
+public:
+  inline bool is_error() const { return m_error; }
+
+public:
+  bool next(LEX_STRING *chunk);
+
+private:
+  uint m_image_version;
+  const String *m_image;
+  const char *m_read_ptr;
+  const char *m_end_ptr;
+  bool m_error;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+bool In_stream::next(LEX_STRING *chunk)
+{
+  if (m_error || m_read_ptr >= m_end_ptr)
+    return TRUE;
+
+  const char *delimiter_ptr=
+    my_strchr(system_charset_info, m_read_ptr, m_end_ptr, ' ');
+
+  if (!delimiter_ptr)
+  {
+    m_read_ptr= m_end_ptr;
+    m_error= TRUE;
+    return TRUE;
+  }
+
+  char buffer[STRING_BUFFER_USUAL_SIZE];
+  int n= delimiter_ptr - m_read_ptr;
+
+  memcpy(buffer, m_read_ptr, n);
+  buffer[n]= 0;
+
+  chunk->str= (char *) delimiter_ptr + 1;
+  chunk->length= atoi(buffer);
+
+  if (chunk->length <= 0 ||
+      chunk->length >= (unsigned) (m_end_ptr - m_read_ptr))
+  {
+    m_error= TRUE;
+    return TRUE;
+  }
+
+  m_read_ptr+= n    /* chunk length */
+    + 1             /* delimiter (a space) */
+    + chunk->length /* chunk */
+    + 1;            /* chunk delimiter (\n) */
+
+  return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+} // end of anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+namespace obs {
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Abstract_obj
+
+  This class is a base class for all other Obj implementations.
+*/
+
+class Abstract_obj : public Obj
+{
+public:
+  static Obj *init_obj_from_image(Abstract_obj *obj,
+                                  uint image_version,
+                                  const String *image);
+
+public:
+  virtual inline const String *get_name() const    { return &m_id; }
+  virtual inline const String *get_db_name() const { return NULL; }
+
+public:
+  /**
+    Serialize an object to an image. The serialization image is opaque
+    object for the client.
+
+    @param[in]  thd     Thread context.
+    @param[out] image   Serialization image.
+
+    @return Error status.
+  */
+  virtual bool serialize(THD *thd, String *image);
+
+  /**
+    Create an object persistently in the database.
+
+    @param[in]  thd     Thread context.
+
+    @return Error status.
+  */
+  virtual bool create(THD *thd);
+
+  /**
+    Drop an object in the database.
+
+    @param[in]  thd     Thread context.
+
+    @return Error status.
+  */
+  virtual bool drop(THD *thd);
+
+public:
+  /**
+    Read the object state from a given serialization image and restores
+    object state to the point, where it can be created persistently in the
+    database.
+
+    @param[in] image_version The version of the serialization format.
+    @param[in] image         Buffer contained serialized object.
+
+    @return Error status.
+      @retval FALSE on success.
+      @retval TRUE on error.
+  */
+  virtual bool init_from_image(uint image_version, const String *image);
+
+protected:
+  /**
+    A primitive implementing @c serialize() method.
+  */
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream) = 0;
+
+  /**
+    A primitive implementing @c init_from_image() method.
+  */
+  virtual bool do_init_from_image(In_stream *is);
+
+  virtual void build_drop_statement(String_stream &s_stream) const = 0;
+
+protected:
+  MEM_ROOT m_mem_root; /* This mem-root is for keeping stmt list. */
+  List<LEX_STRING> m_stmt_list;
+  int m_errno; //< Remember last error number of last statement
+
+protected:
+  /* These attributes are to be used only for serialization. */
+  String m_id; //< identify object
+
+protected:
+  inline Abstract_obj(LEX_STRING id);
+
+  virtual inline ~Abstract_obj();
+
+private:
+  Abstract_obj(const Abstract_obj &);
+  Abstract_obj &operator =(const Abstract_obj &);
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *Abstract_obj::init_obj_from_image(Abstract_obj *obj,
+                                       uint image_version,
+                                       const String *image)
+{
+  if (!obj)
+    return NULL;
+
+  if (obj->init_from_image(image_version, image))
+  {
+    delete obj;
+    return NULL;
+  }
+
+  return obj;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+inline Abstract_obj::Abstract_obj(LEX_STRING id)
+{
+  init_sql_alloc(&m_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+
+  if (id.str && id.length)
+    m_id.copy(id.str, id.length, system_charset_info);
+  else
+    m_id.length(0);
+  m_errno= 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+inline Abstract_obj::~Abstract_obj()
+{
+  free_root(&m_mem_root, MYF(0));
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize object state into a buffer. The buffer actually should be a
+  binary buffer. String class is used here just because we don't have
+  convenient primitive for binary buffers.
+
+  Serialization format is opaque to the client, i.e. the client should
+  not make any assumptions about the format or the content of the
+  returned buffer.
+
+  Serialization format can be changed in the future versions. However,
+  the server must be able to materialize objects coded in any previous
+  formats.
+
+  @param[in] thd              Server thread context.
+  @param[in] image Buffer to serialize the object
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+
+  @note The real work is done inside @c do_serialize() primitive which should be
+  defied in derived classes. This method prepares appropriate context and calls
+  the primitive.
+*/
+
+bool Abstract_obj::serialize(THD *thd, String *image)
+{
+  ulong sql_mode_saved= thd->variables.sql_mode;
+  thd->variables.sql_mode= 0;
+
+  Out_stream out_stream(image);
+
+  bool ret= do_serialize(thd, out_stream);
+
+  thd->variables.sql_mode= sql_mode_saved;
+
+  return ret || out_stream.is_error();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Create the object in the database.
+
+  @param[in] thd              Server thread context.
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Abstract_obj::create(THD *thd)
+{
+  bool rc= FALSE;
+  List_iterator_fast<LEX_STRING> it(m_stmt_list);
+  LEX_STRING *sql_text;
+  Si_session_context session_context;
+
+  DBUG_ENTER("Abstract_obj::create");
+
+  /*
+    Drop the object if it exists first of all.
+
+    @note this semantics will be changed in the future.
+    That's why drop() is a public separate operation of Obj.
+  */
+  drop(thd);
+
+  /*
+    Now, proceed with creating the object.
+  */
+  session_context.save_si_ctx(thd);
+  session_context.reset_si_ctx(thd);
+
+  /* Run queries from the serialization image. */
+  while ((sql_text= it++))
+  {
+    Ed_connection ed_connection(thd);
+
+    rc= ed_connection.execute_direct(*sql_text);
+
+    /* Push warnings on the THD error stack. */
+    thd->warning_info->append_warnings(thd, ed_connection.get_warn_list());
+
+    if (rc)
+      break;
+  }
+
+  session_context.restore_si_ctx(thd);
+
+  DBUG_RETURN(rc);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Drop the object in the database.
+
+  @param[in] thd              Server thread context.
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Abstract_obj::drop(THD *thd)
+{
+  String_stream s_stream;
+  const LEX_STRING *sql_text;
+  Ed_connection ed_connection(thd);
+  bool rc;
+
+  DBUG_ENTER("Abstract_obj::drop");
+
+  build_drop_statement(s_stream);
+  sql_text= s_stream.lex_string();
+
+  if (!sql_text->str || !sql_text->length)
+    DBUG_RETURN(FALSE);
+
+  Si_session_context session_context;
+
+  session_context.save_si_ctx(thd);
+  session_context.reset_si_ctx(thd);
+
+  /* Execute DDL operation. */
+  rc= ed_connection.execute_direct(*sql_text);
+
+  /* Save error number. Ed_connection gets destroyed on return. */
+  if (rc)
+    m_errno= ed_connection.get_last_errno();
+
+  session_context.restore_si_ctx(thd);
+
+  DBUG_RETURN(rc);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool Abstract_obj::init_from_image(uint image_version, const String *image)
+{
+  In_stream is(image_version, image);
+
+  return do_init_from_image(&is) || is.is_error();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool Abstract_obj::do_init_from_image(In_stream *is)
+{
+  LEX_STRING sql_text;
+  while (! is->next(&sql_text))
+  {
+    LEX_STRING *sql_text_root= (LEX_STRING *) alloc_root(&m_mem_root,
+                                                         sizeof (LEX_STRING));
+
+    if (!sql_text_root)
+      return TRUE;
+
+    sql_text_root->str= strmake_root(&m_mem_root,
+                                     sql_text.str, sql_text.length);
+    sql_text_root->length= sql_text.length;
+
+    if (!sql_text_root->str || m_stmt_list.push_back(sql_text_root))
+      return TRUE;
+  }
+
+  return is->is_error();
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Database_obj
+
+  This class provides an abstraction to a database object for creation and
+  capture of the creation data.
+*/
+
+class Database_obj : public Abstract_obj
+{
+public:
+  inline Database_obj(const Ed_row &ed_row)
+    : Abstract_obj(ed_row[0] /* database name */)
+  { }
+
+  inline Database_obj(LEX_STRING db_name)
+    : Abstract_obj(db_name)
+  { }
+
+public:
+  virtual inline const String *get_db_name() const { return get_name(); }
+  virtual bool drop(THD *thd);
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+  virtual void build_drop_statement(String_stream &s_stream) const;
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Database_item_obj
+
+  This class is a base class for all classes representing objects, which
+  belong to a Database.
+*/
+
+class Database_item_obj : public Abstract_obj
+{
+public:
+  inline Database_item_obj(LEX_STRING db_name, LEX_STRING object_name)
+    : Abstract_obj(object_name)
+  {
+    if (db_name.str && db_name.length)
+      m_db_name.copy(db_name.str, db_name.length, system_charset_info);
+    else
+      m_db_name.length(0);
+  }
+
+public:
+  virtual inline const String *get_db_name() const { return &m_db_name; }
+
+protected:
+  virtual const LEX_STRING *get_type_name() const = 0;
+  virtual void build_drop_statement(String_stream &s_stream) const;
+
+protected:
+  String m_db_name;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+void Database_item_obj::build_drop_statement(String_stream &s_stream) const
+{
+  s_stream <<
+    "DROP " << get_type_name() << " IF EXISTS `" <<
+    get_db_name() << "`.`" << get_name() << "`";
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Table_obj
+
+  This class provides an abstraction to a table object for creation and
+  capture of the creation data.
+*/
+
+class Table_obj : public Database_item_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline Table_obj(const Ed_row &ed_row)
+    : Database_item_obj(ed_row[0], /* database name */
+                        ed_row[1]) /* table name */
+  { }
+
+  inline Table_obj(LEX_STRING db_name, LEX_STRING table_name)
+    : Database_item_obj(db_name, table_name)
+  { }
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &Table_obj::TYPE_NAME; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Table_obj::TYPE_NAME= LXS_INIT("TABLE");
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class View_obj
+
+  This class provides an abstraction to a view object for creation and
+  capture of the creation data.
+*/
+
+class View_obj : public Database_item_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline View_obj(const Ed_row &ed_row)
+    : Database_item_obj(ed_row[0], /* schema name */
+                        ed_row[1]) /* view name */
+  { }
+
+  inline View_obj(LEX_STRING db_name, LEX_STRING view_name)
+    : Database_item_obj(db_name, view_name)
+  { }
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &View_obj::TYPE_NAME; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING View_obj::TYPE_NAME= LXS_INIT("VIEW");
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Stored_program_obj
+
+  This is a base class for stored program objects: stored procedures,
+  stored functions, triggers, events.
+*/
+
+class Stored_program_obj : public Database_item_obj
+{
+public:
+  inline Stored_program_obj(LEX_STRING db_name, LEX_STRING sp_name)
+    : Database_item_obj(db_name, sp_name)
+  { }
+
+protected:
+  virtual const LEX_STRING *get_create_stmt(Ed_row *row) = 0;
+  virtual void dump_header(Ed_row *row, Out_stream &out_stream) = 0;
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Stored_routine_obj
+
+  This is a base class for stored routine objects: stored procedures,
+  stored functions, triggers.
+*/
+
+class Stored_routine_obj : public Stored_program_obj
+{
+public:
+  inline Stored_routine_obj(LEX_STRING db_name, LEX_STRING sr_name)
+    : Stored_program_obj(db_name, sr_name)
+  { }
+
+protected:
+  virtual const LEX_STRING *get_create_stmt(Ed_row *row);
+  virtual void dump_header(Ed_row *row, Out_stream &out_stream);
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Trigger_obj
+
+  This class provides an abstraction to a trigger object for creation and
+  capture of the creation data.
+*/
+
+class Trigger_obj : public Stored_routine_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline Trigger_obj(const Ed_row &ed_row)
+    : Stored_routine_obj(ed_row[0], ed_row[1])
+  { }
+
+  inline Trigger_obj(LEX_STRING db_name, LEX_STRING trigger_name)
+    : Stored_routine_obj(db_name, trigger_name)
+  { }
+
+private:
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &Trigger_obj::TYPE_NAME; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Trigger_obj::TYPE_NAME= LXS_INIT("TRIGGER");
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Stored_proc_obj
+
+  This class provides an abstraction to a stored procedure object for creation
+  and capture of the creation data.
+*/
+
+class Stored_proc_obj : public Stored_routine_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline Stored_proc_obj(const Ed_row &ed_row)
+    : Stored_routine_obj(ed_row[0], ed_row[1])
+  { }
+
+  inline Stored_proc_obj(LEX_STRING db_name, LEX_STRING sp_name)
+    : Stored_routine_obj(db_name, sp_name)
+  { }
+
+private:
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &Stored_proc_obj::TYPE_NAME; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Stored_proc_obj::TYPE_NAME= LXS_INIT("PROCEDURE");
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Stored_func_obj
+
+  This class provides an abstraction to a stored function object for creation
+  and capture of the creation data.
+*/
+
+class Stored_func_obj : public Stored_routine_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline Stored_func_obj(const Ed_row &ed_row)
+    : Stored_routine_obj(ed_row[0], ed_row[1])
+  { }
+
+  inline Stored_func_obj(LEX_STRING db_name, LEX_STRING sf_name)
+    : Stored_routine_obj(db_name, sf_name)
+  { }
+
+private:
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &Stored_func_obj::TYPE_NAME; }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Stored_func_obj::TYPE_NAME= LXS_INIT("FUNCTION");
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_EVENT_SCHEDULER
+
+/**
+  @class Event_obj
+
+  This class provides an abstraction to a event object for creation and capture
+  of the creation data.
+*/
+
+class Event_obj : public Stored_program_obj
+{
+private:
+  static const LEX_STRING TYPE_NAME;
+
+public:
+  inline Event_obj(const Ed_row &ed_row)
+    : Stored_program_obj(ed_row[0], ed_row[1])
+  { }
+
+  inline Event_obj(LEX_STRING db_name, LEX_STRING event_name)
+    : Stored_program_obj(db_name, event_name)
+  { }
+
+private:
+  virtual inline const LEX_STRING *get_type_name() const
+  { return &Event_obj::TYPE_NAME; }
+
+  virtual inline const LEX_STRING *get_create_stmt(Ed_row *row)
+  { return row->get_column(3); }
+
+  virtual void dump_header(Ed_row *row, Out_stream &out_stream);
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING Event_obj::TYPE_NAME= LXS_INIT("EVENT");
+
+#endif // HAVE_EVENT_SCHEDULER
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Tablespace_obj
+
+  This class provides an abstraction to a user object for creation and
+  capture of the creation data.
+*/
+
+class Tablespace_obj : public Abstract_obj
+{
+public:
+  Tablespace_obj(LEX_STRING ts_name,
+                 LEX_STRING comment,
+                 LEX_STRING data_file_name,
+                 LEX_STRING engine_name);
+
+  Tablespace_obj(LEX_STRING ts_name);
+
+public:
+  const String *get_description();
+
+public:
+  virtual bool init_from_image(uint image_version, const String *image);
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+  virtual void build_drop_statement(String_stream &s_stream) const;
+
+private:
+  /* These attributes are to be used only for serialization. */
+  String m_comment;
+  String m_data_file_name;
+  String m_engine;
+
+private:
+  String m_description;
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Grant_obj
+
+  This class provides an abstraction to grants. This class will permit the
+  recording and replaying of these grants.
+*/
+
+class Grant_obj : public Abstract_obj
+{
+public:
+  static void generate_unique_grant_id(const String *user_name, String *id);
+
+public:
+  Grant_obj(LEX_STRING grant_id);
+  Grant_obj(const Ed_row &ed_row);
+
+public:
+  inline const String *get_user_name() const { return &m_user_name; }
+  inline const String *get_grant_info() const { return &m_grant_info; }
+
+private:
+  virtual bool do_init_from_image(In_stream *is);
+  inline virtual void build_drop_statement(String_stream &s_stream) const
+  { }
+
+private:
+  /* These attributes are to be used only for serialization. */
+  String m_user_name;
+  String m_grant_info; //< contains privilege definition
+
+private:
+  virtual bool do_serialize(THD *thd, Out_stream &out_stream);
+};
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+template <typename Iterator>
+Iterator *create_row_set_iterator(THD *thd, const LEX_STRING *query)
+{
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+  Iterator *it;
+
+  if (run_service_interface_sql(thd, &ed_connection, query, TRUE))
+    /* Query failed with an error */
+    return NULL;
+
+  DBUG_ASSERT(ed_connection.get_field_count());
+
+  /* Use store_result to get ownership of result memory */
+  ed_result_set= ed_connection.store_result_set();
+
+  if (! (it= new Iterator(ed_result_set)))
+  {
+    delete ed_result_set;
+    return NULL;
+  }
+
+  return it;
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Ed_result_set_iterator
+
+  This is an implementation of Obj_iterator, which creates objects from a
+  result set (represented by an instance of Ed_result).
+*/
+
+template <typename Obj_type>
+class Ed_result_set_iterator : public Obj_iterator
+{
+public:
+  inline Ed_result_set_iterator(Ed_result_set *ed_result_set);
+  inline ~Ed_result_set_iterator();
+
+public:
+  virtual Obj *next();
+
+private:
+  Ed_result_set *m_ed_result_set;
+  List_iterator_fast<Ed_row> m_row_it;
+};
+

+///////////////////////////////////////////////////////////////////////////
+
+template <typename Obj_type>
+inline
+Ed_result_set_iterator<Obj_type>::
+Ed_result_set_iterator(Ed_result_set *ed_result_set)
+  : m_ed_result_set(ed_result_set),
+   m_row_it(*ed_result_set)
+{ }
+
+///////////////////////////////////////////////////////////////////////////
+
+template <typename Obj_type>
+inline
+Ed_result_set_iterator<Obj_type>::~Ed_result_set_iterator()
+{
+  delete m_ed_result_set;
+}
+
+
+template <typename Obj_type>
+Obj *
+Ed_result_set_iterator<Obj_type>::next()
+{
+  Ed_row *ed_row= m_row_it++;
+
+  if (!ed_row)
+    return NULL;
+
+  return new Obj_type(*ed_row);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+typedef Ed_result_set_iterator<Database_obj>    Database_iterator;
+typedef Ed_result_set_iterator<Table_obj>       Db_tables_iterator;
+typedef Ed_result_set_iterator<View_obj>        Db_views_iterator;
+typedef Ed_result_set_iterator<Trigger_obj>     Db_trigger_iterator;
+typedef Ed_result_set_iterator<Stored_proc_obj> Db_stored_proc_iterator;
+typedef Ed_result_set_iterator<Stored_func_obj> Db_stored_func_iterator;
+#ifdef HAVE_EVENT_SCHEDULER
+typedef Ed_result_set_iterator<Event_obj>       Db_event_iterator;
+#endif
+typedef Ed_result_set_iterator<Grant_obj>       Grant_iterator;
+
+///////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class Ed_result_set_iterator<Database_obj>;
+template class Ed_result_set_iterator<Table_obj>;
+template class Ed_result_set_iterator<View_obj>;
+template class Ed_result_set_iterator<Trigger_obj>;
+template class Ed_result_set_iterator<Stored_proc_obj>;
+template class Ed_result_set_iterator<Stored_func_obj>;
+#ifdef HAVE_EVENT_SCHEDULER
+template class Ed_result_set_iterator<Event_obj>;
+#endif
+template class Ed_result_set_iterator<Grant_obj>;
+
+template
+Database_iterator *
+create_row_set_iterator<Database_iterator>(THD *thd, const LEX_STRING *query);
+
+template
+Db_tables_iterator *
+create_row_set_iterator<Db_tables_iterator>(THD *thd, const LEX_STRING *query);
+
+template
+Db_views_iterator *
+create_row_set_iterator<Db_views_iterator>(THD *thd, const LEX_STRING *query);
+
+template
+Db_trigger_iterator *
+create_row_set_iterator<Db_trigger_iterator>(THD *thd, const LEX_STRING *query);
+
+template
+Db_stored_proc_iterator *
+create_row_set_iterator<Db_stored_proc_iterator>(THD *thd,
+                                                 const LEX_STRING *query);
+
+template
+Db_stored_func_iterator *
+create_row_set_iterator<Db_stored_func_iterator>(THD *thd,
+                                                 const LEX_STRING *query);
+
+#ifdef HAVE_EVENT_SCHEDULER
+template
+Db_event_iterator *
+create_row_set_iterator<Db_event_iterator>(THD *thd, const LEX_STRING *query);
+#endif
+
+template
+Grant_iterator *
+create_row_set_iterator<Grant_iterator>(THD *thd, const LEX_STRING *query);
+
+#endif // HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class View_base_iterator
+
+  This is a base implementation of Obj_iterator for the
+  view-dependency-object iterators.
+*/
+
+class View_base_obj_iterator : public Obj_iterator
+{
+public:
+  inline View_base_obj_iterator();
+  virtual inline ~View_base_obj_iterator();
+
+public:
+  virtual Obj *next();
+
+  enum enum_base_obj_kind { BASE_TABLE= 0, VIEW };
+
+  virtual enum_base_obj_kind get_base_obj_kind() const= 0;
+
+protected:
+  template <typename Iterator>
+  static Iterator *create(THD *thd,
+                          const String *db_name, const String *view_name);
+
+protected:
+  bool init(THD *thd, const String *db_name, const String *view_name);
+
+  virtual Obj *create_obj(const LEX_STRING *db_name,
+                          const LEX_STRING *obj_name)= 0;
+
+private:
+  HASH m_table_names;
+  uint m_cur_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+template <typename Iterator>
+Iterator *View_base_obj_iterator::create(THD *thd,
+                                         const String *db_name,
+                                         const String *view_name)
+{
+  Iterator *it= new Iterator();
+
+  if (!it)
+    return NULL;
+
+  if (it->init(thd, db_name, view_name))
+  {
+    delete it;
+    return NULL;
+  }
+
+  return it;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+inline View_base_obj_iterator::View_base_obj_iterator()
+  :m_cur_idx(0)
+{
+  my_hash_init(&m_table_names, system_charset_info, 16, 0, 0,
+               get_table_name_key, free_table_name_key,
+               MYF(0));
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+inline View_base_obj_iterator::~View_base_obj_iterator()
+{
+  my_hash_free(&m_table_names);
+}
+
+
+/**
+  Find all base tables or views of a view.
+*/
+
+class Find_view_underlying_tables: public Server_runnable
+{
+public:
+  Find_view_underlying_tables(const View_base_obj_iterator *base_obj_it,
+                              HASH *table_names,
+                              const String *db_name,
+                              const String *view_name);
+
+  bool execute_server_code(THD *thd);
+private:
+  /* Store the resulting unique here */
+  const View_base_obj_iterator *m_base_obj_it;
+  HASH *m_table_names;
+  const String *m_db_name;
+  const String *m_view_name;
+};
+
+
+Find_view_underlying_tables::
+Find_view_underlying_tables(const View_base_obj_iterator *base_obj_it,
+                            HASH *table_names,
+                            const String *db_name,
+                            const String *view_name)
+  :m_base_obj_it(base_obj_it),
+   m_table_names(table_names),
+   m_db_name(db_name),
+   m_view_name(view_name)
+{
+}
+
+
+bool
+Find_view_underlying_tables::execute_server_code(THD *thd)
+{
+  bool res= TRUE;
+  uint counter_not_used; /* Passed to open_tables(). Not used. */
+
+  TABLE_LIST *table_list;
+
+  DBUG_ENTER("Find_view_underlying_tables::execute_server_code");
+
+  lex_start(thd);
+  table_list= sp_add_to_query_tables(thd, thd->lex,
+                                     ((String *) m_db_name)->c_ptr_safe(),
+                                     ((String *) m_view_name)->c_ptr_safe(),
+                                     TL_READ);
+
+  if (table_list == NULL)                      /* out of memory, reported */
+  {
+    lex_end(thd->lex);
+    DBUG_RETURN(TRUE);
+  }
+
+  if (open_tables(thd, &table_list, &counter_not_used,
+                  MYSQL_OPEN_SKIP_TEMPORARY))
+    goto end;
+
+  if (table_list->view_tables)
+  {
+    TABLE_LIST *table;
+    List_iterator_fast<TABLE_LIST> it(*table_list->view_tables);
+
+    /*
+      Iterate over immediate underlying tables.
+      The list doesn't include views or tables referenced indirectly,
+      through other views, or stored functions or triggers.
+    */
+    while ((table= it++))
+    {
+      Table_name_key *table_name_key;
+
+      /* If we expect a view, and it's a table, or vice versa, continue */
+      if ((int) m_base_obj_it->get_base_obj_kind() != test(table->view))
+        continue;
+
+      if (! my_multi_malloc(MYF(MY_WME),
+                            &table_name_key, sizeof(*table_name_key), NullS))
+        goto end;
+
+      table_name_key->mdl_key_init(&table->mdl_request.key);
+
+      if (my_hash_insert(m_table_names, (uchar*) table_name_key))
+      {
+        my_free(table_name_key, MYF(0));
+        goto end;
+      }
+    }
+  }
+  res= FALSE;
+  my_ok(thd);
+
+end:
+  lex_end(thd->lex);
+  DBUG_RETURN(res);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool View_base_obj_iterator::init(THD *thd,
+                                  const String *db_name,
+                                  const String *view_name)
+{
+  Find_view_underlying_tables find_tables(this, &m_table_names,
+                                          db_name, view_name);
+  Ed_connection ed_connection(thd); /* Just to grab OK or ERROR */
+
+  return ed_connection.execute_direct(&find_tables);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *View_base_obj_iterator::next()
+{
+  LEX_STRING db_name;
+  LEX_STRING table_name;
+  if (m_cur_idx >= m_table_names.records)
+    return NULL;
+
+  Table_name_key *table_name_key=
+    (Table_name_key *) my_hash_element(&m_table_names, m_cur_idx);
+
+  ++m_cur_idx;
+
+  db_name.str= (char*) table_name_key->db_name();
+  db_name.length= table_name_key->db_name_length();
+
+  table_name.str= (char*) table_name_key->name();
+  table_name.length= table_name_key->name_length();
+
+  return create_obj(&db_name, &table_name);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class View_base_table_iterator
+
+  This is an iterator over base tables for a view.
+*/
+
+class View_base_table_iterator : public View_base_obj_iterator
+{
+public:
+  static inline View_base_obj_iterator *
+  create(THD *thd, const String *db_name, const String *view_name)
+  {
+    return View_base_obj_iterator::create<View_base_table_iterator>
+      (thd, db_name, view_name);
+  }
+
+protected:
+  virtual inline enum_base_obj_kind get_base_obj_kind() const
+  { return BASE_TABLE; }
+
+  virtual inline Obj *create_obj(const LEX_STRING *db_name,
+                                 const LEX_STRING *obj_name)
+  { return new Table_obj(*db_name, *obj_name); }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+template
+View_base_table_iterator *
+View_base_obj_iterator::
+create<View_base_table_iterator>(THD *thd, const String *db_name,
+                                 const String *view_name);
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class View_base_view_iterator
+
+  This is an iterator over base views for a view.
+*/
+
+class View_base_view_iterator : public View_base_obj_iterator
+{
+public:
+  static inline View_base_obj_iterator *
+  create(THD *thd, const String *db_name, const String *view_name)
+  {
+    return View_base_obj_iterator::create<View_base_view_iterator>
+      (thd, db_name, view_name);
+  }
+
+protected:
+  virtual inline enum_base_obj_kind get_base_obj_kind() const { return VIEW; }
+
+  virtual inline Obj *create_obj(const LEX_STRING *db_name,
+                                 const LEX_STRING *obj_name)
+  { return new View_obj(*db_name, *obj_name); }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+template
+View_base_view_iterator *
+View_base_obj_iterator::
+create<View_base_view_iterator>(THD *thd, const String *db_name,
+                                const String *view_name);
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize the object.
+
+  This method produces the data necessary for materializing the object
+  on restore (creates object).
+
+  @param[in]  thd         Thread context.
+  @param[out] out_stream  Output stream.
+
+  @note this method will return an error if the db_name is either
+  mysql or information_schema or performance_schema as these are not objects 
+  that should be recreated using this interface.
+
+  @returns Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Database_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+
+  DBUG_ENTER("Database_obj::do_serialize");
+  DBUG_PRINT("Database_obj::do_serialize",
+             ("name: %.*s", STR(*get_name())));
+
+  if (is_internal_db_name(get_name()))
+  {
+    DBUG_PRINT("backup",
+               (" Skipping internal database %.*s", STR(*get_name())));
+
+    DBUG_RETURN(TRUE);
+  }
+
+  /* Run 'SHOW CREATE' query. */
+
+  {
+    String_stream s_stream;
+    s_stream <<
+      "SHOW CREATE DATABASE `" << get_name() << "`";
+
+    if (run_service_interface_sql(thd, &ed_connection, 
+				  s_stream.lex_string(), TRUE) ||
+        ed_connection.get_warn_count())
+    {
+      /*
+        There should be no warnings. A warning means that serialization has
+        failed.
+      */
+      DBUG_RETURN(TRUE);
+    }
+  }
+
+  /* Check result. */
+
+  /* The result must contain only one result-set... */
+  DBUG_ASSERT(ed_connection.get_field_count());
+
+  ed_result_set= ed_connection.use_result_set();
+
+  if (ed_result_set->size() != 1)
+    DBUG_RETURN(TRUE);
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* Generate image. */
+  out_stream << row->get_column(1);
+
+  DBUG_RETURN(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void Database_obj::build_drop_statement(String_stream &s_stream) const
+{
+  s_stream <<
+    "DROP DATABASE IF EXISTS `" << get_name() << "`";
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Drop the database.
+
+  If DROP DATABASE fails because of unknown files in the directory,
+  fix that by renaming the directory to oldname-uuid.
+
+  @param[in] thd              Server thread context.
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Database_obj::drop(THD *thd)
+{
+  bool rc;
+  DBUG_ENTER("Database_obj::drop");
+
+  /*
+    If DROP DATABASE fails due to unknown files in the directory,
+    fix that by renaming the directory.
+  */
+  rc= Abstract_obj::drop(thd);
+  if (rc)
+  {
+    DBUG_PRINT("si_objects", ("drop database failed, m_errno: %d", m_errno));
+    if (m_errno == ER_DB_DROP_RMDIR)
+    {
+      size_t len= get_name()->length();
+      char oldname[FN_REFLEN];
+      char newname[FN_REFLEN + MY_UUID_STRING_LENGTH + 1]; /* for '-' */
+      uchar uuid[MY_UUID_SIZE];
+
+      DBUG_PRINT("si_objects", ("Renaming to oldname-uuid"));
+      /*
+        get_name() returns a const String. We cannot use c_ptr*() with it.
+        We need a new buffer to add a terminator. FN_REFLEN should be
+        sufficient, as build_table_filename() works with such buffers
+        internally. If the path is too long, we give up. We let DROP
+        fail and hence RESTORE will fail.
+      */
+      if (len >= FN_REFLEN)
+        goto err; /* purecov: inspected */
+      memcpy(oldname, get_name()->ptr(), len);
+      oldname[len] = '\0';
+      /* Convert database name to a file name. */
+      len= build_table_filename(oldname, sizeof(oldname)-1,
+                                oldname, "", "", 0);
+      /* Remove trailing slash. */
+      if (len && (oldname[len-1] == FN_LIBCHAR))
+        oldname[--len]= '\0';
+      /* Build new name as old + uuid. */
+      memcpy(newname, oldname, len);
+      newname[len++]= '-';
+      my_uuid(uuid);
+      my_uuid2str(uuid, newname + len);
+      newname[len + MY_UUID_STRING_LENGTH]= '\0';
+      DBUG_PRINT("si_objects", ("Rename '%s' to '%s'", oldname, newname));
+      rc= my_rename(oldname, newname, MYF(MY_WME));
+      if (rc)
+      {
+        /* Could not rename directory. Restore will fail. */
+        /* purecov: begin tested */
+        my_error(ER_DB_DROP_RMDIR, MYF(0), oldname, my_errno);
+        /* purecov: end */
+      }
+      else
+      {
+        /* Renamed directory. Add a warning. */
+        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                            WARN_DB_DROP_RENAMED, ER(WARN_DB_DROP_RENAMED),
+                            newname);
+      }
+    }
+  }
+ err:
+  DBUG_RETURN(rc);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize the object.
+
+  This method produces the data necessary for materializing the object
+  on restore (creates object).
+
+  @param[in]  thd         Thread context.
+  @param[out] out_stream  Output stream.
+
+  @returns Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Table_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  Ed_result_set *ed_result_set;
+
+  DBUG_ENTER("Table_obj::do_serialize");
+  DBUG_PRINT("Table_obj::do_serialize",
+             ("name: %.*s.%.*s",
+              STR(m_db_name), STR(m_id)));
+
+  s_stream <<
+    "SHOW CREATE TABLE `" << &m_db_name << "`.`" << &m_id << "`";
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /*
+      There should be no warnings. A warning means that serialization has
+      failed.
+    */
+    DBUG_RETURN(TRUE);
+  }
+
+  /* Check result. */
+
+  ed_result_set= ed_connection.use_result_set();
+
+  if (ed_result_set->size() != 1)
+    DBUG_RETURN(TRUE);
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be two columns: database name and create statement. */
+  DBUG_ASSERT(row->size() == 2);
+
+  /* Generate serialization image. */
+
+  {
+    s_stream.reset();
+    s_stream << "USE `" << &m_db_name << "`";
+    out_stream << s_stream;
+  }
+
+  out_stream << row->get_column(1);
+
+  DBUG_RETURN(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+static bool
+get_view_create_stmt(THD *thd,
+                     View_obj *view,
+                     LEX_STRING *create_stmt,
+                     LEX_STRING *client_cs_name,
+                     LEX_STRING *connection_cl_name)
+{
+  /* Get a create statement for a view. */
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+  String_stream s_stream;
+
+  s_stream <<
+    "SHOW CREATE VIEW `" << view->get_db_name() << "`."
+    "`" << view->get_name() << "`";
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /*
+      There should be no warnings. A warning means that serialization has
+      failed.
+    */
+    return TRUE;
+  }
+
+
+  /* The result must contain only one result-set... */
+
+  ed_result_set= ed_connection.use_result_set();
+
+  if (ed_result_set->size() != 1)
+    return TRUE;
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be four columns. */
+  DBUG_ASSERT(row->size() == 4);
+
+  const LEX_STRING *c1= row->get_column(1);
+  const LEX_STRING *c2= row->get_column(2);
+  const LEX_STRING *c3= row->get_column(3);
+
+  create_stmt->str= thd->strmake(c1->str, c1->length);
+  create_stmt->length= c1->length;
+
+  client_cs_name->str= thd->strmake(c2->str, c2->length);
+  client_cs_name->length= c2->length;
+
+  connection_cl_name->str= thd->strmake(c3->str, c3->length);
+  connection_cl_name->length= c3->length;
+
+  return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize the object.
+
+  This method produces the data necessary for materializing the object
+  on restore (creates object).
+
+  @param[in]  thd         Thread context.
+  @param[out] out_stream  Output stream.
+
+  @returns Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+bool View_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  DBUG_ENTER("View_obj::do_serialize");
+  DBUG_PRINT("View_obj::do_serialize",
+             ("name: %.*s.%.*s",
+              STR(m_db_name), STR(m_id)));
+
+  String_stream s_stream;
+
+  LEX_STRING create_stmt= null_lex_str;
+  LEX_STRING client_cs_name= null_lex_str;
+  LEX_STRING connection_cl_name= null_lex_str;
+
+  if (get_view_create_stmt(thd, this, &create_stmt,
+                           &client_cs_name, &connection_cl_name))
+  {
+    DBUG_RETURN(TRUE);
+  }
+
+  s_stream << "USE `" << &m_db_name << "`";
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream << "SET character_set_client = " << &client_cs_name;
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream << "SET collation_connection = " << &connection_cl_name;
+  out_stream << s_stream;
+
+  out_stream << &create_stmt;
+
+  DBUG_RETURN(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+bool Stored_program_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  String_stream s_stream;
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+
+  DBUG_ENTER("Stored_program_obj::do_serialize");
+  DBUG_PRINT("Stored_program_obj::do_serialize",
+             ("name: %.*s.%.*s",
+              STR(m_db_name), STR(m_id)));
+
+  DBUG_EXECUTE_IF("backup_fail_add_trigger", DBUG_RETURN(TRUE););
+
+  s_stream <<
+    "SHOW CREATE " << get_type_name() <<
+    " `" << &m_db_name << "`.`" << &m_id << "`";
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE))
+    /* Query failed with an error */
+    DBUG_RETURN(TRUE);
+  else if(ed_connection.get_warn_count())
+    /* Push warnings to BACKUP's error stack. Warnings are acceptable
+       when getting serialization string for procedures */
+    thd->warning_info->append_warnings(thd, ed_connection.get_warn_list());
+
+  ed_result_set= ed_connection.use_result_set();
+
+  if (ed_result_set->size() != 1)
+    DBUG_RETURN(TRUE);
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  s_stream.reset();
+  s_stream << "USE `" << &m_db_name << "`";
+  out_stream << s_stream;
+
+  dump_header(row, out_stream);
+  out_stream << get_create_stmt(row);
+
+  DBUG_RETURN(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+const LEX_STRING *Stored_routine_obj::get_create_stmt(Ed_row *row)
+{
+  return row->get_column(2);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void Stored_routine_obj::dump_header(Ed_row *row, Out_stream &out_stream)
+{
+  String_stream s_stream;
+
+  s_stream <<
+    "SET character_set_client = " << row->get_column(3);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET collation_connection = " << row->get_column(4);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET collation_database = " << row->get_column(5);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET sql_mode = '" << row->get_column(1) << "'";
+  out_stream << s_stream;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_EVENT_SCHEDULER
+
+void Event_obj::dump_header(Ed_row *row, Out_stream &out_stream)
+{
+  String_stream s_stream;
+
+  s_stream <<
+    "SET character_set_client = " << row->get_column(4);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET collation_connection = " << row->get_column(5);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET collation_database = " << row->get_column(6);
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET sql_mode = '" << row->get_column(1) << "'";
+  out_stream << s_stream;
+
+  s_stream.reset();
+  s_stream <<
+    "SET time_zone = '" << row->get_column(2) << "'";
+  out_stream << s_stream;
+}
+
+#endif // HAVE_EVENT_SCHEDULER
+
+///////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+Tablespace_obj::
+Tablespace_obj(LEX_STRING ts_name,
+               LEX_STRING comment,
+               LEX_STRING data_file_name,
+               LEX_STRING engine)
+  : Abstract_obj(ts_name)
+{
+  m_comment.copy(comment.str, comment.length, system_charset_info);
+  m_data_file_name.copy(data_file_name.str, data_file_name.length,
+                        system_charset_info);
+  m_engine.copy(engine.str, engine.length, system_charset_info);
+
+  m_description.length(0);
+}
+
+
+Tablespace_obj::Tablespace_obj(LEX_STRING ts_name)
+  : Abstract_obj(ts_name)
+{
+  m_comment.length(0);
+  m_data_file_name.length(0);
+  m_engine.length(0);
+
+  m_description.length(0);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize the object.
+
+  This method produces the data necessary for materializing the object
+  on restore (creates object).
+
+  @param[in]  thd Thread context.
+  @param[out] out_stream  Output stream.
+
+  @returns Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Tablespace_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  DBUG_ENTER("Tablespace_obj::do_serialize");
+
+  out_stream << get_description();
+
+  DBUG_RETURN(FALSE);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool Tablespace_obj::init_from_image(uint image_version, const String *image)
+{
+  if (Abstract_obj::init_from_image(image_version, image))
+    return TRUE;
+
+  List_iterator_fast<LEX_STRING> it(m_stmt_list);
+  LEX_STRING *desc= it++;
+
+  /* Tablespace description must not be NULL. */
+  DBUG_ASSERT(desc);
+
+  m_description.copy(desc->str, desc->length, system_charset_info);
+
+  return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Get a description of the tablespace object.
+
+  This method returns the description of the object which is currently
+  the serialization image.
+
+  @returns Serialization string.
+*/
+
+const String *Tablespace_obj::get_description()
+{
+  DBUG_ENTER("Tablespace_obj::get_description");
+
+  /* Either description or id and data file name must be not empty. */
+  DBUG_ASSERT(m_description.length() ||
+              (m_id.length() && m_data_file_name.length()));
+
+  if (m_description.length())
+    DBUG_RETURN(&m_description);
+
+  /* Construct the CREATE TABLESPACE command from the variables. */
+
+  m_description.length(0);
+
+  String_stream s_stream(&m_description);
+
+  s_stream <<
+    "CREATE TABLESPACE `" << &m_id << "` "
+    "ADD DATAFILE '" << &m_data_file_name << "' ";
+
+  if (m_comment.length())
+    s_stream << "COMMENT = '" << &m_comment << "' ";
+
+  s_stream << "ENGINE = " << &m_engine;
+
+  DBUG_RETURN(&m_description);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void Tablespace_obj::build_drop_statement(String_stream &s_stream) const
+{
+  s_stream <<
+    "DROP TABLESPACE `" << &m_id << "` ENGINE = " << &m_engine;
+}
+
+///////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+void
+Grant_obj::
+generate_unique_grant_id(const String *user_name, String *id)
+{
+  /*
+    @note This code is not MT-safe, but we don't need MT-safety here.
+    Backup has object cache (a hash) for one BACKUP/RESTORE statement.
+    Hash keys are formed from object names (Obj::get_db_name() and
+    Obj::get_name()). So, here unique keys for the object cache are
+    generated. These keys need to be unique only within one backup session
+    (serialization image).
+  */
+
+  static unsigned long id_counter= 0;
+
+  id->length(0);
+
+  String_stream s_stream(id);
+
+  if (user_name->length())
+    s_stream << "<empty>";
+  else
+    s_stream << user_name;
+
+  s_stream << " " << Int_value(++id_counter);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+Grant_obj::Grant_obj(const Ed_row &row)
+  : Abstract_obj(null_lex_str)
+{
+  const LEX_STRING *user_name= row.get_column(0);
+  const LEX_STRING *privilege_type= row.get_column(1);
+  const LEX_STRING *db_name= row.get_column(2);
+  const LEX_STRING *tbl_name= row.get_column(3);
+  const LEX_STRING *col_name= row.get_column(4);
+  const LEX_STRING *routine_type= row.get_column(5);
+
+  LEX_STRING table_name= { C_STRING_WITH_LEN("") };
+  LEX_STRING column_name= { C_STRING_WITH_LEN("") };
+
+  if (tbl_name)
+    table_name= *tbl_name;
+
+  if (col_name)
+    column_name= *col_name;
+
+  m_user_name.copy(user_name->str, user_name->length, system_charset_info);
+
+  /* Grant info. */
+
+  String_stream s_stream(&m_grant_info);
+  s_stream << privilege_type;
+
+  /*
+    Either column_name or routine_type or both are NULL.
+    They are never both non-NULL.
+  */
+  DBUG_ASSERT(!column_name.length || !routine_type->length);
+
+  if (column_name.length)
+    s_stream << "(`" << &column_name << "`)";
+
+  s_stream << " ON ";
+
+  if (routine_type->length)
+    s_stream << routine_type << " ";
+
+  s_stream << "`" << db_name << "`.";
+
+  if (table_name.length)
+    s_stream << "`" << &table_name << "`";
+  else
+    s_stream << "*";
+
+  /* Id. */
+
+  generate_unique_grant_id(&m_user_name, &m_id);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+Grant_obj::Grant_obj(LEX_STRING name)
+  : Abstract_obj(null_lex_str)
+{
+  m_user_name.length(0);
+  m_grant_info.length(0);
+
+  m_id.copy(name.str, name.length, system_charset_info);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+  Serialize the object.
+
+  This method produces the data necessary for materializing the object
+  on restore (creates object).
+
+  @param[in]  thd Thread context.
+  @param[out] out_stream  Output stream.
+
+  @note this method will return an error if the db_name is either
+  mysql or information_schema or performance_schema as these are not objects 
+  that should be recreated using this interface.
+
+  @returns Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+
+bool Grant_obj::do_serialize(THD *thd, Out_stream &out_stream)
+{
+  DBUG_ENTER("Grant_obj::do_serialize");
+
+  out_stream <<
+    &m_user_name <<
+    &m_grant_info <<
+    "SET character_set_client= binary";
+
+  String_stream s_stream;
+  s_stream << "GRANT " << &m_grant_info << " TO " << &m_user_name;
+
+  out_stream << s_stream;
+
+  DBUG_RETURN(FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool Grant_obj::do_init_from_image(In_stream *is)
+{
+  LEX_STRING user_name;
+  LEX_STRING grant_info;
+
+  if (is->next(&user_name))
+    return TRUE; /* Can not decode user name. */
+
+  if (is->next(&grant_info))
+    return TRUE; /* Can not decode grant info. */
+
+  m_user_name.copy(user_name.str, user_name.length, system_charset_info);
+  m_grant_info.copy(grant_info.str, grant_info.length, system_charset_info);
+
+  return Abstract_obj::do_init_from_image(is);
+}
+
+///////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+Obj *get_database_stub(THD *thd, const String *db_name)
+{
+
+  DBUG_EXECUTE_IF("siobj_get_db_stub",  return NULL; );
+  /*
+    The specified db name may have characters in a wrong case. Get the
+    normalized name by reading it from INFORMATION_SCHEMA. Note: if
+    lower_case_table_names=0, the db name must not be converted.
+   */
+
+  if (!lower_case_table_names)
+    return new Database_obj(db_name->lex_string()); 
+
+
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  Ed_result_set *ed_result_set;
+
+  // Prepare SELECT statement.
+
+  s_stream << "SELECT schema_name "
+              "FROM INFORMATION_SCHEMA.SCHEMATA WHERE "
+              "LCASE(schema_name) = LCASE('" << db_name << "')";
+
+  // Execute SELECT.
+
+  if (run_service_interface_sql(thd, &ed_connection, s_stream.lex_string(), 
+				TRUE) || ed_connection.get_warn_count())
+    return NULL;
+
+  // Fetch result.
+
+  ed_result_set= ed_connection.use_result_set();
+
+  // The database did not find the database. Return a Database_obj
+  // with correct name anyway as per method specification
+  if (ed_result_set->size() != 1)
+  {
+    // Need to convert the name to lower case when lctn=1
+    if (lower_case_table_names == 1)
+    {
+      String lcasename;
+      lcasename.copy(db_name->ptr(), db_name->length(), system_charset_info);
+      my_casedn_str(lcasename.charset(), lcasename.c_ptr());
+      return new Database_obj(lcasename.lex_string()); 
+    }
+    else 
+      return new Database_obj(db_name->lex_string()); 
+  }
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  DBUG_ASSERT(row->size() == 1);
+    
+
+  // Create object using normalized name.
+
+  return new Database_obj(*row); 
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_databases(THD *thd)
+{
+  LEX_STRING query=
+  { C_STRING_WITH_LEN(
+                      "SELECT schema_name "
+                      "FROM INFORMATION_SCHEMA.SCHEMATA "
+                      "WHERE LCASE(schema_name) != 'mysql' AND "
+                      "LCASE(schema_name) != 'information_schema' AND "
+                      "LCASE(schema_name) != 'performance_schema'")};
+
+  return create_row_set_iterator<Database_iterator>(thd, &query);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+/**
+  Return a count of databases.
+
+  This method returns the number of databases excluding the internal
+  databases 'mysql', 'information_schema', and 'performance_schema'.
+ 
+  @Note This method executes based on the current context of the THD
+        and thus returns a value based on the accessibility of objects
+        by the user context.
+
+  @param[in] THD     Current thread
+  
+  @return int Number of databases. Return value of -1 indicates failure.
+*/
+int get_num_dbs(THD *thd)
+{
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+  String_stream s_stream;
+
+  s_stream << 
+    "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA " <<
+    "WHERE LCASE(schema_name) != 'mysql' AND " <<
+    "LCASE(schema_name) != 'information_schema' AND " <<
+    "LCASE(schema_name) != 'performance_schema'";
+
+  if (run_service_interface_sql(thd, &ed_connection,
+				s_stream.lex_string(), TRUE))
+    /* Query failed with an error */
+    return -1;
+
+  DBUG_ASSERT(ed_connection.get_field_count());
+
+  /* Use store_result to get ownership of result memory */
+  ed_result_set= ed_connection.store_result_set();
+  uint count= ed_result_set->size();
+  delete ed_result_set;
+  return count;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Return a count of objects belonging to a given database.
+
+  This method returns the number of objects that match the database name
+  specified as @c db_name. 
+
+  @Note This method executes based on the current context of the THD
+        and thus returns a value based on the accessibility of objects
+        by the user context.
+
+  @param[in] THD     Current thread
+  @param[in] String  Database name.
+
+  @return int Number of objects in the database. Value of -1 means failure.
+*/
+int get_num_objects(THD *thd, const String *db_name)
+{
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+  String_stream s_stream;
+
+  /*
+    Build query to select all objects from IS tables.
+  */
+  s_stream <<
+    "SELECT TABLE_NAME "
+    "FROM INFORMATION_SCHEMA.TABLES "
+    "WHERE table_schema COLLATE utf8_bin = '" << db_name << "' UNION " 
+    "SELECT TRIGGER_NAME "
+    "FROM INFORMATION_SCHEMA.TRIGGERS "
+    "WHERE trigger_schema COLLATE utf8_bin = '" << db_name << "' UNION " 
+    "SELECT ROUTINE_NAME "
+    "FROM INFORMATION_SCHEMA.ROUTINES "
+    "WHERE routine_schema COLLATE utf8_bin = '" << db_name << "' UNION " 
+    "SELECT EVENT_NAME "
+    "FROM INFORMATION_SCHEMA.EVENTS "
+    "WHERE event_schema COLLATE utf8_bin = '" << db_name << "'";
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE))
+    /* Query failed with an error */
+    return -1;
+
+  DBUG_ASSERT(ed_connection.get_field_count());
+
+  /* Use store_result to get ownership of result memory */
+  ed_result_set= ed_connection.store_result_set();
+  uint count= ed_result_set->size();
+  delete ed_result_set;
+  return count;
+}
+
+///////////////////////////////////////////////////////////////////////////
+bool check_user_access(THD *thd, const String *db_name, bool *has_access)
+{
+  int elev_count;
+  int user_count;
+
+  DBUG_ASSERT(has_access);
+  *has_access= false;                           /* Reset access status. */
+
+  /*
+    Get value from user context without elevation.
+  */
+  if (!db_name) 
+    user_count= get_num_dbs(thd);
+  else 
+    user_count= get_num_objects(thd, db_name);
+
+  if (user_count < 0)
+    return TRUE;                                /* Return error status */
+
+  /* 
+    Peform global elevation with SELECT to get value of
+    someone with global SELECT (e.g. root).
+  */
+  ulong saved_master_access= thd->security_ctx->master_access; 
+  ulong saved_db_access= thd->security_ctx->db_access; 
+  thd->security_ctx->master_access |= (SELECT_ACL | TRIGGER_ACL | EVENT_ACL);
+
+  if (!db_name) 
+    elev_count= get_num_dbs(thd);
+  else 
+    elev_count= get_num_objects(thd, db_name);
+
+  /*
+    Turn elevation off and restore user access.
+  */
+  thd->security_ctx->master_access= saved_master_access; 
+  thd->security_ctx->db_access= saved_db_access; 
+
+  if (elev_count < 0)
+    return TRUE;                                /* Return error status */
+
+  /* User has proper access if both counts are same. */
+  *has_access= (user_count == elev_count);
+
+  return FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////
+Obj_iterator *get_db_tables(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  /*
+    BUG#45587: Backup of tables using a non-existent storage engine
+               does not give warning/error 
+    BUG#47697: Inconsistencies in the query outputs on
+               INFORMATION_SCHEMA.TABLES 
+
+    Once BUG#47697 is fixed, the query s_stream  should be reverted 
+    to following query:
+    s_stream<<
+      "SELECT '" << db_name << "', table_name "
+      "FROM INFORMATION_SCHEMA.TABLES "
+      "WHERE table_schema = '" << db_name << "' AND "
+      "table_type = 'BASE TABLE'";
+   */
+
+  s_stream <<
+    "SELECT '" << db_name << "', table_name " 

+    "FROM ( SELECT * "
+    "FROM INFORMATION_SCHEMA.TABLES "
+    "WHERE table_schema = '" << db_name << "' AND "
+    "table_type = 'BASE TABLE'"
+    ") AS get_table";
+
+  return create_row_set_iterator<Db_tables_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_db_views(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  s_stream <<
+    "SELECT '" << db_name << "', table_name "
+    "FROM INFORMATION_SCHEMA.TABLES "
+    "WHERE table_schema = '" << db_name << "' AND table_type = 'VIEW'";
+
+  return create_row_set_iterator<Db_views_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_db_triggers(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  s_stream <<
+    "SELECT '" << db_name << "', trigger_name "
+    "FROM INFORMATION_SCHEMA.TRIGGERS "
+    "WHERE trigger_schema COLLATE utf8_bin = '" << db_name << "'";
+
+  return create_row_set_iterator<Db_trigger_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_db_stored_procedures(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  s_stream <<
+    "SELECT '" << db_name << "', routine_name "
+    "FROM INFORMATION_SCHEMA.ROUTINES ";
+  /*
+    We need to use case insensitive compare when LCTN = 2 and on Windows. 
+  */
+  if ((lower_case_table_names == 2) && IF_WIN(1,0))
+    s_stream << "WHERE LOWER(routine_schema) "
+      "COLLATE utf8_bin = LOWER('" << db_name << "') ";
+  else
+    s_stream << "WHERE routine_schema COLLATE utf8_bin = '" << db_name << "' ";
+  s_stream << "AND routine_type = 'PROCEDURE'";
+
+  return create_row_set_iterator<Db_stored_proc_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_db_stored_functions(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  s_stream <<
+    "SELECT '" << db_name << "', routine_name "
+    "FROM INFORMATION_SCHEMA.ROUTINES ";
+  /*
+    We need to use case insensitive compare when LCTN = 2 and on Windows. 
+  */
+  if ((lower_case_table_names == 2) && IF_WIN(1,0))
+    s_stream << "WHERE LOWER(routine_schema) "
+      "COLLATE utf8_bin = LOWER('" << db_name << "') ";
+  else
+    s_stream << "WHERE routine_schema COLLATE utf8_bin = '" << db_name << "' ";
+  s_stream << "AND routine_type = 'FUNCTION'";
+
+  return create_row_set_iterator<Db_stored_func_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_db_events(THD *thd, const String *db_name)
+{
+#ifdef HAVE_EVENT_SCHEDULER
+  String_stream s_stream;
+  s_stream <<
+    "SELECT '" << db_name << "', event_name "
+    "FROM INFORMATION_SCHEMA.EVENTS ";
+  /*
+    We need to use case insensitive compare when LCTN = 2 and on Windows. 
+  */
+  if ((lower_case_table_names == 2) && IF_WIN(1,0))
+    s_stream << "WHERE LOWER(event_schema) "
+      "COLLATE utf8_bin = LOWER('" << db_name << "')";
+  else
+    s_stream << "WHERE event_schema COLLATE utf8_bin = '" << db_name << "'";
+
+  return create_row_set_iterator<Db_event_iterator>(thd, s_stream.lex_string());
+#else
+  return NULL;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator *get_all_db_grants(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  s_stream <<
+    "(SELECT grantee AS c1, "
+    "privilege_type AS c2, "
+    "table_schema AS c3, "
+    "NULL AS c4, "
+    "NULL AS c5, "
+    "NULL AS c6 "
+    "FROM INFORMATION_SCHEMA.SCHEMA_PRIVILEGES "
+    "WHERE table_schema = '" << db_name << "') "
+    "UNION "
+    "(SELECT grantee, "
+    "privilege_type, "
+    "table_schema, "
+    "table_name, "
+    "NULL, "
+    "NULL "
+    "FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES "
+    "WHERE table_schema = '" << db_name << "') "
+    "UNION "
+    "(SELECT grantee, "
+    "privilege_type, "
+    "table_schema, "
+    "table_name, "
+    "column_name, "
+    "NULL "
+    "FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES "
+    "WHERE table_schema = '" << db_name << "') "
+    "UNION "
+    "(SELECT CONCAT('''', User, '''@''', Host, ''''), "
+    "Proc_priv, "
+    "Db, "
+    "Routine_name, "
+    "NULL, "
+    "Routine_type "
+    "FROM mysql.procs_priv "
+    "WHERE Db = '" << db_name << "') "
+    "ORDER BY c1 ASC, c2 ASC, c3 ASC, c4 ASC, c5 ASC, c6 ASC";
+
+  return create_row_set_iterator<Grant_iterator>(thd, s_stream.lex_string());
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator* get_view_base_tables(THD *thd,
+                                   const String *db_name,
+                                   const String *view_name)
+{
+  return View_base_table_iterator::create(thd, db_name, view_name);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj_iterator* get_view_base_views(THD *thd,
+                                  const String *db_name,
+                                  const String *view_name)
+{
+  return View_base_view_iterator::create(thd, db_name, view_name);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_database(const String *db_name,
+                  uint image_version,
+                  const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Database_obj(db_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_table(const String *db_name,
+               const String *table_name,
+               uint image_version,
+               const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Table_obj(db_name->lex_string(), table_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_view(const String *db_name,
+              const String *view_name,
+              uint image_version,
+              const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new View_obj(db_name->lex_string(), view_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_trigger(const String *db_name,
+                 const String *trigger_name,
+                 uint image_version,
+                 const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Trigger_obj(db_name->lex_string(), trigger_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_stored_procedure(const String *db_name,
+                          const String *sp_name,
+                          uint image_version,
+                          const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Stored_proc_obj(db_name->lex_string(), sp_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_stored_function(const String *db_name,
+                         const String *sf_name,
+                         uint image_version,
+                         const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Stored_func_obj(db_name->lex_string(), sf_name->lex_string()),
+    image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_EVENT_SCHEDULER
+
+Obj *get_event(const String *db_name,
+               const String *event_name,
+               uint image_version,
+               const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Event_obj(db_name->lex_string(), event_name->lex_string()),
+    image_version, image);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_tablespace(const String *ts_name,
+                    uint image_version,
+                    const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Tablespace_obj(ts_name->lex_string()), image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_db_grant(const String *db_name,
+                  const String *name,
+                  uint image_version,
+                  const String *image)
+{
+  return Abstract_obj::init_obj_from_image(
+    new Grant_obj(name->lex_string()), image_version, image);
+}
+
+///////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////
+
+bool is_internal_db_name(const String *db_name)
+{
+  return
+    my_strcasecmp(lower_case_table_names ? system_charset_info :
+                  &my_charset_bin, ((String *) db_name)->c_ptr_safe(),
+                  MYSQL_SCHEMA_NAME.str) == 0 ||
+    my_strcasecmp(system_charset_info,
+                  ((String *) db_name)->c_ptr_safe(),
+                  "information_schema") == 0 ||
+    my_strcasecmp(system_charset_info,
+                  ((String *) db_name)->c_ptr_safe(),
+                  "performance_schema") == 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool check_db_existence(THD *thd, const String *db_name)
+{
+  String_stream s_stream;
+  int rc;
+
+  s_stream << "SHOW CREATE DATABASE `" << db_name << "`";
+
+  Ed_connection ed_connection(thd);
+  rc= run_service_interface_sql(thd, 
+				&ed_connection, s_stream.lex_string(), 
+				FALSE);
+
+  /* We're not interested in warnings/errors here. */
+
+  return test(rc);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool check_user_existence(THD *thd, const Obj *obj)
+{
+#ifdef EMBEDDED_LIBRARY
+  return TRUE;
+#else
+  Grant_obj *grant_obj= (Grant_obj *) obj;
+  Ed_connection ed_connection(thd);
+  Ed_result_set *ed_result_set;
+  String_stream s_stream;
+
+  s_stream <<
+    "SELECT 1 "
+    "FROM INFORMATION_SCHEMA.USER_PRIVILEGES "
+    "WHERE grantee = \"" << grant_obj->get_user_name() << "\"";
+
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+    return FALSE;
+  }
+
+  ed_result_set= ed_connection.use_result_set();
+
+  return ed_result_set->size() > 0;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+const String *grant_get_user_name(const Obj *obj)
+{
+  return ((Grant_obj *) obj)->get_user_name();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+const String *grant_get_grant_info(const Obj *obj)
+{
+  return ((Grant_obj *) obj)->get_grant_info();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *find_tablespace(THD *thd, const String *ts_name)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  Ed_result_set *ed_result_set;
+
+  s_stream <<
+    "SELECT t1.tablespace_comment, t2.file_name, t1.engine "
+    "FROM INFORMATION_SCHEMA.TABLESPACES AS t1, "
+    "INFORMATION_SCHEMA.FILES AS t2 "
+    "WHERE t1.tablespace_name = t2.tablespace_name AND "
+    "t1.tablespace_name = '" << ts_name << "'";
+
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+    return NULL;
+  }
+
+  ed_result_set= ed_connection.use_result_set();
+
+  // Return NULL if the tablespace does not exist
+  if (ed_result_set->size() == 0)
+    return NULL;
+
+  // There can only be one tablespace with this name
+  DBUG_ASSERT(ed_result_set->size() == 1);
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be 3 columns. */
+  DBUG_ASSERT(row->size() == 3);
+
+  const LEX_STRING *comment= row->get_column(0);
+  const LEX_STRING *data_file_name= row->get_column(1);
+  const LEX_STRING *engine= row->get_column(2);
+
+  return new Tablespace_obj(ts_name->lex_string(),
+                            *comment, *data_file_name, *engine);
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Retrieve the tablespace for a table if it exists
+
+  This method returns a @c Tablespace_obj object if the table has a tablespace.
+
+  @param[in]  thd         Thread context.
+  @param[in]  db_name     The database name for the table.
+  @param[in]  table_name  The table name.
+
+  @note Caller is responsible for destroying the object.
+
+  @return Tablespace object.
+    @retval Tablespace object if table uses a tablespace.
+    @retval NULL if table does not use a tablespace.
+*/
+
+Obj *find_tablespace_for_table(THD *thd,
+                               const String *db_name,
+                               const String *table_name)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  Ed_result_set *ed_result_set;
+
+  s_stream <<
+    "SELECT t1.tablespace_name, t1.engine, t1.tablespace_comment, t2.file_name "
+    "FROM INFORMATION_SCHEMA.TABLESPACES AS t1, "
+    "INFORMATION_SCHEMA.FILES AS t2, "
+    "INFORMATION_SCHEMA.TABLES AS t3 "
+    "WHERE t1.tablespace_name = t2.tablespace_name AND "
+    "t2.tablespace_name = t3.tablespace_name AND "
+    "t3.table_schema = '" << db_name << "' AND "
+    "t3.table_name = '" << table_name << "'";
+
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+    return NULL;
+  }
+
+  ed_result_set= ed_connection.use_result_set();
+
+  /* The result must contain only one row. */
+  if (ed_result_set->size() != 1)
+    return NULL;
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be 4 columns. */
+  DBUG_ASSERT(row->size() == 4);
+
+  const LEX_STRING *ts_name= row->get_column(0);
+  const LEX_STRING *engine= row->get_column(1);
+  const LEX_STRING *comment= row->get_column(2);
+  const LEX_STRING *data_file_name= row->get_column(3);
+
+  return new Tablespace_obj(*ts_name, *comment, *data_file_name, *engine);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Retrieve the table name for a trigger
+
+  This method returns a @c Table_obj object for the trigger.
+
+  @param[in]  thd          Thread context.
+  @param[in]  db_name      The database name for the trigger.
+  @param[in]  trigger_name The trigger name.
+
+  @return Table_obj
+    @retval Table name for trigger.
+    @retval NULL if no table specified.
+*/
+Obj *find_table_for_trigger(THD *thd,
+                            const String *db_name,
+                            const String *trigger_name)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  Ed_result_set *ed_result_set;
+
+  s_stream <<
+    "SELECT event_object_table "
+    "FROM INFORMATION_SCHEMA.TRIGGERS "
+    "WHERE trigger_schema = '" << db_name << "' AND "
+    "trigger_name COLLATE utf8_bin = '" << trigger_name << "'";
+
+
+  if (run_service_interface_sql(thd, &ed_connection, 
+				s_stream.lex_string(), TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+    return NULL;
+  }
+
+  ed_result_set= ed_connection.use_result_set();
+
+  /* The result must contain only one row. */
+  if (ed_result_set->size() != 1)
+    return NULL;
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be 1 column. */
+  DBUG_ASSERT(row->size() == 1);
+
+  LEX_STRING db;
+  db.str= (char *)db_name->ptr();
+  db.length= db_name->length();
+
+  const LEX_STRING *table_name= row->get_column(0);
+
+  return new Table_obj(db, *table_name);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+bool compare_tablespace_attributes(Obj *ts1, Obj *ts2)
+{
+  DBUG_ENTER("obs::compare_tablespace_attributes");
+
+  Tablespace_obj *o1= (Tablespace_obj *) ts1;
+  Tablespace_obj *o2= (Tablespace_obj *) ts2;
+
+  DBUG_RETURN(my_strcasecmp(system_charset_info,
+                            o1->get_description()->ptr(),
+                            o2->get_description()->ptr()) == 0);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Implementation: Backup Commit Blocker
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/*
+  BCB methods
+*/
+
+bool bcb_get(THD *thd)
+{
+  DBUG_ASSERT(BCB_instance);
+  DBUG_ENTER("obs::bcb_get");
+  if (!BCB_instance->get_exclusive_lock(thd))
+    DBUG_RETURN(TRUE);
+  DBUG_RETURN(FALSE);
+}
+
+
+void bcb_release()
+{
+  DBUG_ASSERT(BCB_instance);
+  DBUG_ENTER("obs::bcb_release");
+  BCB_instance->release_exclusive_lock();
+  DBUG_VOID_RETURN;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Implementation: Backup Metadata Lock.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/*
+  BML methods
+*/
+
+/**
+   Get the backup metadata lock.
+
+   After successful acquiring of the lock, all statements marked as 
+   CF_BLOCKED_BY_BML will be blocked (see @c sql_command_flags[] in 
+   sql_parse.cc).
+
+   @param[in] thd  current thread
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+bool bml_get(THD *thd)
+{
+  DBUG_ENTER("obs::bml_get");
+  if (!BML_instance->get_exclusive_lock(thd))
+    DBUG_RETURN(TRUE);
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Release the backup metadata lock if acquired earlier.
+*/
+void bml_release()
+{
+  DBUG_ENTER("obs::bml_release");
+  BML_instance->release_exclusive_lock();
+  DBUG_VOID_RETURN;
+}
+
+/**
+  Build a table list from a list of tables as class Obj.
+
+  This method creates a TABLE_LIST from a List<> of type Obj.
+
+  param[IN]  tables    The list of tables
+  param[IN]  lock      The desired lock type
+
+  @returns TABLE_LIST *
+
+  @note Caller must free memory.
+*/
+TABLE_LIST *Name_locker::build_table_list(List<Obj> *tables,
+                                          thr_lock_type lock)
+{
+  TABLE_LIST *tl= NULL;
+  Obj *tbl= NULL;
+  DBUG_ENTER("Name_locker::build_table_list");
+
+  List_iterator<Obj> it(*tables);
+  while ((tbl= it++))
+  {
+    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));
+
+    ptr->alias= ptr->table_name= const_cast<char*>(tbl->get_name()->ptr());
+    ptr->db= const_cast<char*>(tbl->get_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;
+  }
+
+  DBUG_RETURN(tl);
+}
+
+
+void Name_locker::free_table_list(TABLE_LIST *table_list)
+{
+  TABLE_LIST *ptr= table_list;
+
+  while (ptr)
+  {
+    table_list= table_list->next_global;
+    my_free(ptr, MYF(0));
+    ptr= table_list;
+  }
+}
+
+/**
+  Gets name locks on table list.
+
+  This method attempts to take an exclusive name lock on each table in the
+  list. It does nothing if the table list is empty.
+
+  @param[IN] tables  The list of tables to lock.
+  @param[IN] lock    The type of lock to take.
+
+  @returns 0 if success, 1 if error
+*/
+int Name_locker::get_name_locks(List<Obj> *tables, thr_lock_type lock)
+{
+  TABLE_LIST *ltable= 0;
+  int ret= 0;
+  DBUG_ENTER("Name_locker::get_name_locks");
+  /*
+    Convert List<Obj> to TABLE_LIST *
+  */
+  m_table_list= build_table_list(tables, lock);
+  if (m_table_list)
+  {
+    if (lock_table_names(m_thd, m_table_list))
+      ret= 1;
+    pthread_mutex_lock(&LOCK_open);
+    for (ltable= m_table_list; ltable; ltable= ltable->next_local)
+      tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, ltable->db,
+                       ltable->table_name);
+  }
+  DBUG_RETURN(ret);
+}
+
+/*
+  Releases name locks on table list.
+
+  This method releases the name locks on the table list. It does nothing if
+  the table list is empty.
+
+  @returns 0 if success, 1 if error
+*/
+int Name_locker::release_name_locks()
+{
+  DBUG_ENTER("Name_locker::release_name_locks");
+  if (m_table_list)
+  {
+    pthread_mutex_unlock(&LOCK_open);
+    unlock_table_names(m_thd);
+  }
+  DBUG_RETURN(0);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Miscellaneous.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+   Acquire locks on a list of tables.

+
+   @param[in]   thd             current thread
+   @param[in]   table_list      list of tables to lock for write
+
+   @return      status
+     @retval    FALSE           success
+     @retval    TRUE            error
+*/
+
+bool lock_tables_for_write(THD *thd, TABLE_LIST *table_list)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  TABLE_LIST *tlist;
+  bool error_status= FALSE;
+  DBUG_ENTER("obs:lock_tables_for_write");
+
+  if (table_list)
+  {
+    s_stream << "LOCK TABLE ";
+    for (tlist= table_list; tlist; tlist= tlist->next_global)
+    {
+      if (tlist != table_list)
+        s_stream << ", ";
+      s_stream << "`" << tlist->db << "`.`" << tlist->table_name << "` WRITE";
+    }
+
+    error_status= run_service_interface_sql(thd, &ed_connection,
+                                            s_stream.lex_string(), TRUE);
+  }
+  DBUG_RETURN(error_status);
+}
+
+
+/**
+   Release all table locks of this thread.
+
+   @param[in]   thd             current thread
+
+   @return      status
+     @retval    FALSE           success
+     @retval    TRUE            error
+*/
+
+bool unlock_tables(THD *thd)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  bool error_status;
+  DBUG_ENTER("obs:unlock_tables");
+
+  s_stream << "UNLOCK TABLES";
+  error_status= run_service_interface_sql(thd, &ed_connection,
+                                          s_stream.lex_string(), TRUE);
+  DBUG_RETURN(error_status);
+}
+
+
+/**
+   Flush a list of tables.
+
+   @param[in]   thd             current thread
+   @param[in]   table_list      list of tables to flush
+
+   @return      status
+     @retval    FALSE           success
+     @retval    TRUE            error
+*/
+
+bool flush_tables(THD *thd, TABLE_LIST *table_list)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  TABLE_LIST *tlist;
+  bool error_status= FALSE;
+  DBUG_ENTER("obs:flush_tables");
+
+  for (tlist= table_list; tlist; tlist= tlist->next_global)
+  {
+    s_stream.reset();
+    s_stream << "FLUSH TABLE ";
+    s_stream << "`" << tlist->db << "`.`" << tlist->table_name << "`";
+    if (run_service_interface_sql(thd, &ed_connection,
+                                  s_stream.lex_string(), TRUE))
+    {
+      error_status= TRUE;
+      DBUG_PRINT("error", ("execute direct: '%s'  errno: %d",
+                           s_stream.lex_string()->str,
+                           ed_connection.get_last_errno()));
+    }
+  }
+
+  DBUG_RETURN(error_status);
+}
+
+
+/**
+   Truncate a list of tables.
+
+   @param[in]   thd             current thread
+   @param[in]   table_list      list of tables to truncate
+
+   @return      status
+     @retval    FALSE           success
+     @retval    TRUE            error
+*/
+
+bool truncate_tables(THD *thd, TABLE_LIST *table_list)
+{
+  Ed_connection ed_connection(thd);
+  String_stream s_stream;
+  TABLE_LIST *tlist;
+  DBUG_ENTER("obs:truncate_tables");
+
+  for (tlist= table_list; tlist; tlist= tlist->next_global)
+  {
+    s_stream.reset();
+    s_stream << "TRUNCATE TABLE ";
+    s_stream << "`" << tlist->db << "`.`" << tlist->table_name << "`";
+    if (run_service_interface_sql(thd, &ed_connection,
+                                  s_stream.lex_string(), TRUE))
+      DBUG_RETURN(TRUE);
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Implementation: Replication methods.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/*
+  Replication methods
+*/
+
+/**
+  Turn on or off the binary log for the current thread. 
+
+  This method can be used to turn binary logging on or off in situations 
+  where it is necessary to prevent some operations from being logged, e.g.
+  BACKUP DATABASE.
+
+  @param[IN] enable  TRUE = turn on, FALSE = turn off
+
+  @returns 0
+*/
+int engage_binlog(bool enable)
+{
+  int ret= 0;
+  THD *thd= current_thd;
+
+  if (enable)
+    thd->options|= OPTION_BIN_LOG;
+  else
+    thd->options&= ~OPTION_BIN_LOG;
+  return ret;
+}
+
+/**
+  Check if binlog is enabled for the current thread.
+
+  This method can be used to check to see if logging is enabled and operate
+  as a gate for those areas of code that are conditional on whether logging is
+  enabled (or disabled).
+
+  @returns   TRUE  if logging is enabled and binlog is open
+  @returns   FALSE if logging is turned off or binlog is closed
+*/
+bool is_binlog_engaged()
+{
+  THD *thd= current_thd;
+
+  return (thd->options & OPTION_BIN_LOG) && mysql_bin_log.is_open();
+}
+
+/**
+  Turn on or off the general log for the current thread.
+
+  This method can be used to turn the general log on or off in situations 
+  where it is necessary to prevent some operations from being logged, e.g.
+  running a restore.
+
+  @param[IN] enable  TRUE = turn on, FALSE = turn off
+
+  @returns 0
+*/
+int engage_general_log(bool enable)
+{
+  int ret= 0;
+  THD *thd= current_thd;
+
+  if (enable)
+    thd->options&= ~OPTION_LOG_OFF;
+  else
+    thd->options|= OPTION_LOG_OFF;
+  return ret;
+}
+
+/**
+  Check if general log is enabled for the current thread.
+
+  This method can be used to check to see if the general log is enabled and 
+  operate as a gate for those areas of code that are conditional on whether
+  the general log is enabled (or disabled).
+
+  @returns   TRUE  if the general log is enabled
+  @returns   FALSE if the general log is turned off
+*/
+bool is_general_log_engaged()
+{
+  THD *thd= current_thd;
+
+  return (thd->options & ~OPTION_LOG_OFF);
+}
+
+/**
+  Check if current server is executing as a slave.
+
+  This method can be used to detect when running as a slave. This means that
+  the server is configured to act as a slave. This can make it easier to
+  organize the code for specialized sections where running as a slave requires
+  additional work, prohibiting backup and restore operations, or error
+  conditions.
+
+  @returns   TRUE  if operating as a slave
+*/
+bool is_slave()
+{
+  bool running= FALSE;
+
+#ifdef HAVE_REPLICATION
+  if (active_mi)
+  {
+    pthread_mutex_lock(&LOCK_active_mi);
+    running= (active_mi->slave_running == MYSQL_SLAVE_RUN_CONNECT);
+    pthread_mutex_unlock(&LOCK_active_mi);
+  }
+#endif
+  return running;
+}
+
+/**
+  Check if any slaves are connected.
+
+  This method can be used to detect whether there are slaves attached to the
+  current server. This will allow the code to know if replication is active.
+
+  @returns  int number of slaves currently attached
+*/
+int num_slaves_attached()
+{
+  THD *thd= current_thd;
+  Ed_row *ed_row;
+  const LEX_STRING *num_slaves_str;
+  Ed_connection ed_connection(thd);
+  LEX_STRING sql_text= LXS_INIT("SELECT CONCAT(COUNT(1)) "
+                                "FROM INFORMATION_SCHEMA.PROCESSLIST"
+                                "WHERE LCASE(command) = LCASE('Binlog Dump')");
+
+  if (run_service_interface_sql(thd, &ed_connection, &sql_text, TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+
+    /*
+      XXX: now zero is returned if there are warnings. Probably, that
+      should be changed and error flag (-1) should be returned.
+    */
+    return 0;
+  }
+
+  Ed_result_set *ed_result_set= ed_connection.use_result_set();
+
+  if (ed_result_set->size() != 1)
+    return 0;
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+
+  ed_row= row_it++;
+  num_slaves_str= ed_row->get_column(0);
+
+  /* Can safely run atoi, strings are always NUL-terminated in Ed interface */
+  return atoi(num_slaves_str->str);
+}
+
+/**
+  Disable or enable connection of new slaves to the master.
+
+  This method can be used to temporarily prevent new slaves from connecting to
+  the master. This can allow operations such as RESTORE to prevent new slaves
+  from attaching during the execution of RESTORE.
+
+  @param[IN] disable  TRUE = turn off, FALSE = turn on
+
+  @returns value of disable_slaves (TRUE | FALSE)
+*/
+int disable_slave_connections(bool disable)
+{
+  // Protect with a mutex?
+  disable_slaves= disable ? 1 : 0;
+  return disable_slaves;
+}
+
+/**
+  Set state where replication is blocked from starting.
+
+  This method tells the server that a process that requires replication
+  to be turned off while the operation is in progress.
+  This is used to prohibit slaves from starting.
+
+  @param[in] block  TRUE = block slave start, FALSE = do not block
+  @param[in] reason  Reason for the block
+*/
+void block_replication(bool block, const char *reason)
+{
+  pthread_mutex_lock(&LOCK_slave_start);
+  allow_slave_start= !block;
+  if (block)
+  {
+    reason_slave_blocked.length= strlen(reason);
+    reason_slave_blocked.str= (char *)reason;
+  }
+  pthread_mutex_unlock(&LOCK_slave_start);
+}
+
+/**
+  Write an incident event in the binary log.
+
+  This method can be used to issue an incident event to inform the slave
+  that an unusual event has occurred on the master. For example an incident
+  event could represent that a restore has been issued on the master. This
+  should force the slave to stop and allow the user to analyze the effect
+  of the restore on the master and take the appropriate steps to correct
+  the slave (e.g. running the same restore on the slave.
+
+  @param[IN] thd            The current thread
+  @param[IN] incident_enum  The indicent being reported
+
+  @retval FALSE on success.
+  @retval TRUE on error.
+*/
+int write_incident_event(THD *thd, incident_events incident_enum)
+{
+  bool res= FALSE;
+
+#ifdef HAVE_REPLICATION
+  Incident incident;
+
+  /*
+    Generate an incident log event (gap event) to signal the slave
+    that a restore has been issued on the master.
+  */
+  switch (incident_enum){
+    case NONE:
+      incident= INCIDENT_NONE;
+      break;
+    case LOST_EVENTS:
+      incident= INCIDENT_LOST_EVENTS;
+      break;
+    case RESTORE_EVENT:
+      incident= INCIDENT_RESTORE_EVENT;
+      break;
+    default:   // fail safe : should not occur
+      incident= INCIDENT_NONE;
+      break;
+  }
+  DBUG_PRINT("write_incident_event()", ("Before write_incident_event()."));
+  /*
+    Don't write this unless binlog is engaged.

+  */
+  if (incident && is_binlog_engaged())
+  {
+    LEX_STRING msg;
+    msg.str= (char *)ER(ER_RESTORE_ON_MASTER);
+    msg.length = strlen(ER(ER_RESTORE_ON_MASTER));
+    Incident_log_event ev(thd, incident, msg);
+    res= mysql_bin_log.write(&ev);
+    mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+  }
+#endif
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Disable or enable an event.
+
+  This method can be used to turn off (disable) a specific event.
+   
+  @param[IN] thd         The current thread
+  @param[IN] db_name     Database name for event
+  @param[IN] event_name  Name of event to disable
+  @param[IN] disable     If TRUE, disable, else enable.
+  
+  @retval FALSE on success.
+  @retval TRUE on error.
+*/
+bool disable_event(THD *thd, 
+                   const char *db_name, 
+                   const char *event_name,
+                   bool disable)
+{
+  int res= FALSE;
+  
+  Ed_connection ed_connection(thd);
+  String sql_text;
+  LEX_STRING sql;
+
+  /*
+    We update the mysql table directly to avoid changing character
+    collation or character set, modified by, etc.
+  */
+  sql_text.length(0);
+  sql_text.append("UPDATE mysql.event SET status = ");
+  if (disable)
+    sql_text.append("'DISABLED' ");
+  else
+    sql_text.append("'ENABLED' ");
+  sql_text.append("WHERE db = '");
+  sql_text.append(db_name);
+  sql_text.append("' AND name = '");
+  sql_text.append(event_name);
+  sql_text.append("' ");
+
+  sql.str= sql_text.c_ptr();
+  sql.length= sql_text.length();
+  
+  Si_session_context session_context;
+
+  session_context.save_si_ctx(thd);
+  session_context.reset_si_ctx(thd);
+
+  res= ed_connection.execute_direct(sql);
+
+  /* Push warnings on the THD error stack. */
+  thd->warning_info->append_warnings(thd, ed_connection.get_warn_list());
+
+  session_context.restore_si_ctx(thd);
+
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Checks to if an event is enabled.
+
+  This method can be used to determine when a specific event is enabled.
+   
+  @param[IN] thd         The current thread
+  @param[IN] db_name     Database name for event
+  @param[IN] event_name  Name of event to disable
+  
+  @retval FALSE event is not enabled.
+  @retval TRUE event is enabled.
+*/
+bool event_enabled(THD *thd, 
+                   const char *db_name, 
+                   const char *event_name)
+{
+  int res= FALSE;
+  
+  Ed_connection ed_connection(thd);
+  String sql_text;
+  LEX_STRING sql;
+  Ed_result_set *ed_result_set;
+
+  /*
+    We update the mysql table directly to avoid changing character
+    collation or character set, modified by, etc.
+  */
+  sql_text.length(0);
+  sql_text.append("SELECT status FROM mysql.event ");
+  sql_text.append("WHERE db = '");
+  sql_text.append(db_name);
+  sql_text.append("' AND name = '");
+  sql_text.append(event_name);
+  sql_text.append("' ");
+
+  sql.str= sql_text.c_ptr();
+  sql.length= sql_text.length();
+
+  if (run_service_interface_sql(thd, &ed_connection, &sql, TRUE) ||
+      ed_connection.get_warn_count())
+  {
+    /* Should be no warnings. */
+    return NULL;
+  }
+
+  ed_result_set= ed_connection.use_result_set();
+
+  /* The result must contain only one row. */
+  if (ed_result_set->size() != 1)
+    return NULL;
+
+  List_iterator_fast<Ed_row> row_it(*ed_result_set);
+  Ed_row *row= row_it++;
+
+  /* There must be 1 column. */
+  DBUG_ASSERT(row->size() == 1);
+
+  const LEX_STRING *status= row->get_column(0);
+  
+  printf ("EVENT %s.%s IS %s.\n", db_name, event_name, status->str);
+  
+  res= (my_strcasecmp(system_charset_info, status->str, "ENABLED") == 0);
+  
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+} // obs namespace

=== added file 'sql/si_objects.h'
--- a/sql/si_objects.h	1970-01-01 00:00:00 +0000
+++ b/sql/si_objects.h	2009-12-04 22:31:25 +0000
@@ -0,0 +1,611 @@
+#ifndef SI_OBJECTS_H_
+#define SI_OBJECTS_H_
+
+/* Copyright (C) 2008 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
+
+  An object in this file refers to SQL language database objects
+  such as tables, views, stored programs, events or users,
+  databases and tablespaces.
+  This file defines an API for the following object services:
+    - serialize object definition (metadata) into a string,
+    - materialize (de-serialize) object from a string,
+    - enumerate all objects of a database,
+    - find dependencies between objects, such as underlying tables of
+    views, tablespace of a table.
+
+  Additionally, the interface provides two helper services
+  for backup:
+    - execute an arbitrary SQL statement
+    - block DDL statements interferring with backup/restore operations - so 
+      called Backup Metadata Lock.
+*/
+
+namespace obs {
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Obj
+
+  This interface defines the basic set of operations for each database object.
+*/
+
+class Obj
+{
+public:
+  /**
+    Return the name of the object.
+
+    @return object name.
+  */
+  virtual const String *get_name() const = 0;
+
+  /**
+    Return the database name of the object.
+
+    @note this is a subject to remove.
+  */
+  virtual const String *get_db_name() const = 0;
+
+public:
+  /**
+    Serialize an object to an image. The serialization image is opaque
+    object for the client. The client should supply a valid string
+    instance, which will contain serialization image after return.
+
+    @param[in]  thd     Thread context.
+    @param[out] image   Serialization image.
+
+    @return Error status.
+  */
+  virtual bool serialize(THD *thd, String *image) = 0;
+
+  /**
+    Create an object persistently in the database.
+
+    @param[in]  thd     Thread context.
+
+    @return Error status.
+  */
+  virtual bool create(THD *thd) = 0;
+
+  /**
+    Drop an object in the database.
+
+    @param[in]  thd     Thread context.
+
+    @return Error status.
+  */
+  virtual bool drop(THD *thd) = 0;
+
+public:
+  virtual ~Obj()
+  { }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  @class Obj_iterator
+
+  This is a basic interface to enumerate objects.
+*/
+
+class Obj_iterator
+{
+public:
+  Obj_iterator()
+  { }
+
+  /**
+    This operation returns a pointer to the next object in an enumeration.
+    It returns NULL if there is no more objects.
+    Results of an attempt to continue iteration when there is no
+    more objects are undefined.
+
+    The client is responsible for destruction of the returned object.
+
+    @return a pointer to the object
+      @retval NULL if there is no more objects in an enumeration.
+  */
+  virtual Obj *next() = 0;
+
+public:
+  virtual ~Obj_iterator()
+  { }
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+// Functions in this section are intended to construct an instance of Obj
+// class for any particular database object. These functions do not interact
+// with the server to validate requested names. So, it is possible to
+// construct instances for non-existing objects.
+//
+// The client is responsible for destroying the returned object.
+
+/**
+  Construct an instance of Obj representing a database.
+
+  No actions are performed in the server. An object can be created
+  even for an invalid database name or for a non-existing database.
+
+  The client is responsible for destruction of the created object.
+
+  @param[in] db_name Database name.
+
+  @return a pointer to an instance of Obj representing the given database.
+*/
+Obj *get_database_stub(THD *thd, const String *db_name);
+
+///////////////////////////////////////////////////////////////////////////
+
+// Functions in this section provide a way to iterate over all objects in
+// the server or in a particular database.
+//
+// The client is responsible for destruction of the returned iterator.
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Create an iterator over the databases in the server that the user
+  can read with his current permissions.
+
+  Includes system databases, such as "mysql" and "information_schema".
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+*/
+Obj_iterator *get_databases(THD *thd);
+
+/**
+  Check user access to see all databases or objects in a given database.
+
+  This method can be used in two ways. 
+
+  1) If db_name = 0, the method shall check the user's visibility for all
+     databases excluding mysql and information_schema. 
+  2) If db_name != 0, the method shall check the user's visibility for all
+     objects in the database specified. 
+ 
+  @param[in]  THD     the current thread
+  @param[in]  String  the database name to check
+  @param[out] bool    set to true/false as described below:
+                      TRUE  : User has the required access permission
+                      FALSE : User does not have the required access permission
+
+  @return boolean value which indicates if an error has occured.
+    @retval TRUE indicates error
+    @retval FALSE indicates success
+*/
+bool check_user_access(THD *thd, const String *db_name, bool *has_access);
+
+/**
+  Create an iterator over all base tables in a particular database.
+  Temporary tables are not included.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_tables(THD *thd, const String *db_name);
+
+/**
+  Create an iterator over all views in a particular database.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_views(THD *thd, const String *db_name);
+
+/**
+  Create an iterator over all triggers in a particular database.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_triggers(THD *thd, const String *db_name);
+
+/**
+  Create an iterator over all stored procedures in a particular database.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_stored_procedures(THD *thd, const String *db_name);
+
+/**
+  Create an iterator over all stored functions in a particular database.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_stored_functions(THD *thd, const String *db_name);
+
+/**
+  Create an iterator over all events in a particular database.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator *get_db_events(THD *thd, const String *db_name);
+
+/*
+  Creates a high-level iterator that iterates over database-, table-,
+  routine-, and column-level- privileges. This allows to retrieve all
+  privileges of a given database using a single iterator.
+*/
+Obj_iterator *get_all_db_grants(THD *thd, const String *db_name);
+
+///////////////////////////////////////////////////////////////////////////
+
+// The functions are intended to enumerate dependent objects.
+//
+// The client is responsible for destruction of the returned iterator.
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Create an iterator over all base tables of a view.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator* get_view_base_tables(THD *thd,
+                                   const String *db_name,
+                                   const String *view_name);
+
+/**
+  Create an iterator over all base views of a particular view.
+
+  The client is responsible for destruction of the returned iterator.
+
+  @return a pointer to an iterator object.
+    @retval NULL in case of error.
+*/
+Obj_iterator* get_view_base_views(THD *thd,
+                                  const String *db_name,
+                                  const String *view_name);
+
+///////////////////////////////////////////////////////////////////////////
+
+// Functions in this section provide a way to materialize objects from their
+// serialized form (serialization image). In order to do that, the client
+// creates an object handle, by means of one of the functions below, and
+// then calls "create()" method on it.
+//
+// The client is responsible for destruction of the returned object handle.
+//
+// The client is responsible for providing valid serialization image. The
+// operations below do not modify serialization image in any way. In
+// particular, the client is responsible to advance in the serialiazation
+// stream after successful object restoration.
+
+///////////////////////////////////////////////////////////////////////////
+
+Obj *get_database(const String *db_name,
+                  uint image_version,
+                  const String *image);
+
+Obj *get_table(const String *db_name,
+               const String *table_name,
+               uint image_version,
+               const String *image);
+
+Obj *get_view(const String *db_name,
+              const String *view_name,
+              uint image_version,
+              const String *image);
+
+Obj *get_trigger(const String *db_name,
+                 const String *trigger_name,
+                 uint image_version,
+                 const String *image);
+
+Obj *get_stored_procedure(const String *db_name,
+                          const String *stored_proc_name,
+                          uint image_version,
+                          const String *image);
+
+Obj *get_stored_function(const String *db_name,
+                         const String *stored_func_name,
+                         uint image_version,
+                         const String *image);
+
+Obj *get_event(const String *db_name,
+               const String *event_name,
+               uint image_version,
+               const String *image);
+
+Obj *get_tablespace(const String *ts_name,
+                    uint image_version,
+                    const String *image);
+
+Obj *get_db_grant(const String *db_name,
+                  const String *name,
+                  uint image_version,
+                  const String *image);
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+  Check if the given database name is reserved for internal use.
+
+  @return
+    @retval TRUE if the given database name is reserved for internal use.
+    @retval FALSE otherwise.
+*/
+bool is_internal_db_name(const String *db_name);
+
+/**
+  Check if the given directory actually exists.
+
+  @return Error status.
+    @retval FALSE on success (the database exists and accessible).
+    @retval TRUE on error (the database either not exists, or not accessible).
+*/
+bool check_db_existence(THD *thd, const String *db_name);
+
+/**
+  Check if the user is defined on the system.
+
+  @return
+    @retval TRUE if user is defined
+    @retval FALSE otherwise
+*/
+bool check_user_existence(THD *thd, const Obj *obj);
+
+/**
+  Return user name of materialized grant object.
+*/
+const String *grant_get_user_name(const Obj *obj);
+
+/**
+  Return grant info of materialized grant object.
+*/
+const String *grant_get_grant_info(const Obj *obj);
+
+/**
+  Determine if the tablespace referenced by name exists on the system.
+
+  @return a Tablespace_obj if it exists or NULL if it doesn't.
+*/
+Obj *find_tablespace(THD *thd, const String *ts_name);
+
+/**
+  This method returns a @c Tablespace_obj object if the table has a
+  tablespace.
+*/
+Obj *find_tablespace_for_table(THD *thd,
+                               const String *db_name,
+                               const String *table_name);
+
+/**
+  This method returns the table name for a given trigger.
+*/
+Obj *find_table_for_trigger(THD *thd,
+                            const String *db_name,
+                            const String *trigger_name);
+
+/**
+  This method determines if a materialized tablespace exists on the system.
+  This compares the name and all saved attributes of the tablespace. A
+  FALSE return would mean either the tablespace does not exist or the
+  tablespace attributes are different.
+*/
+bool compare_tablespace_attributes(Obj *ts1, Obj *ts2);
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Backup Commit Blocker (BCB) methods.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+   Get the backup commit blocker.
+
+   @param[in] thd  current thread
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+bool bcb_get(THD *thd);
+
+/**
+  Release the backup commit blocker if acquired earlier.
+*/
+void bcb_release();
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Backup Metadata Lock (BML) methods.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+   Get the backup metadata lock.
+
+   After successful acquiring of the lock, all statements marked as 
+   CF_BLOCKED_BY_BML will be blocked (see @c sql_command_flags[] in 
+   sql_parse.cc).
+
+   @param[in] thd  current thread
+
+  @return Error status.
+    @retval FALSE on success.
+    @retval TRUE on error.
+*/
+bool bml_get(THD *thd);
+
+/**
+  Release the backup metadata lock if acquired earlier.
+*/
+void bml_release();
+
+/*
+  The following class is used to manage name locks on a list of tables.
+
+  This class uses a list of type List<Obj> to establish the table list
+  that will be used to manage locks on the tables.
+*/
+class Name_locker
+{
+public:
+  Name_locker(THD *thd) { m_thd= thd; }
+  ~Name_locker()
+  {
+    free_table_list(m_table_list);
+    m_table_list= NULL;
+  }
+
+  /*
+    Gets name locks on table list.
+  */
+  int get_name_locks(List<Obj> *tables, thr_lock_type lock);
+
+  /*
+    Releases name locks on table list.
+  */
+  int release_name_locks();
+
+private:
+  TABLE_LIST *m_table_list; ///< The list of tables to obtain locks on.
+  THD *m_thd;               ///< Thread context.
+
+  /*
+    Builds a table list from the list of objects passed to constructor.
+  */
+  TABLE_LIST *build_table_list(List<Obj> *tables, thr_lock_type lock);
+  void free_table_list(TABLE_LIST*);
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Miscellaneous.
+//
+
+///////////////////////////////////////////////////////////////////////////
+
+bool lock_tables_for_write(THD *thd, TABLE_LIST *table_list);
+bool unlock_tables(THD *thd);
+bool flush_tables(THD *thd, TABLE_LIST *table_list);
+bool truncate_tables(THD *thd, TABLE_LIST *table_list);
+
+
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Replication methods.
+//
+
+/*
+  Turn on or off the binary log for the current thread. 
+*/
+int engage_binlog(bool enable);
+
+/*
+  Check if binlog is enabled for the current thread. 
+*/
+bool is_binlog_engaged();
+
+/*
+  Turn on or off the general log for the current thread.
+*/
+int engage_general_log(bool enable);
+
+/*
+  Check if general log is enabled for the current thread. 
+*/
+bool is_general_log_engaged();
+
+/*
+  Check if current server is executing as a slave. 
+*/
+bool is_slave();
+
+/*
+  Check if any slaves are connected. 
+*/
+int num_slaves_attached();
+
+/*
+  Disable or enable connection of new slaves to the master. 
+*/
+int disable_slave_connections(bool disable);
+
+/*
+  Set state where replication is blocked (TRUE) or not blocked (FALSE)
+  from starting. Include reason for feedback to user.
+*/
+void block_replication(bool block, const char *reason);
+
+/**
+  Enumeration of the incidents that can occur on the master.
+*/
+enum incident_events {
+  NONE,          // Indicates there was no incident 
+  LOST_EVENTS,   // Indicates lost events 
+  RESTORE_EVENT, // Indicates a restore has executed on the master
+  COUNT          // Value counts the enumerations
+};
+
+/*
+  Write incident event to signal slave an unusual event has been issued 
+  on the master.
+*/
+int write_incident_event(THD *thd, incident_events incident_enum);
+
+/*
+  Disable or enable an event.
+*/
+bool disable_event(THD *thd, 
+                   const char *db_name, 
+                   const char *event_name,
+                   bool disable);
+
+/*
+  Check if event is enabled.
+*/
+bool event_enabled(THD *thd, 
+                   const char *db_name, 
+                   const char *event_name);
+
+} // obs namespace
+
+#endif // SI_OBJECTS_H_

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2009-12-03 23:52:05 +0000
+++ b/sql/sp_head.cc	2009-12-04 22:31:25 +0000
@@ -166,6 +166,7 @@ sp_get_flags_for_command(LEX *lex)
     }
     /* fallthrough */
   case SQLCOM_ANALYZE:
+  case SQLCOM_BACKUP:
   case SQLCOM_OPTIMIZE:
   case SQLCOM_PRELOAD_KEYS:
   case SQLCOM_ASSIGN_TO_KEYCACHE:

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2009-12-03 23:29:40 +0000
+++ b/sql/sql_acl.cc	2009-12-04 22:31:25 +0000
@@ -143,6 +143,16 @@ TABLE_FIELD_W_TYPE mysql_db_table_fields
     { C_STRING_WITH_LEN("Trigger_priv") },
     { C_STRING_WITH_LEN("enum('N','Y')") },
     { C_STRING_WITH_LEN("utf8") }
+  },
+  {
+    { C_STRING_WITH_LEN("Backup_priv") },
+    { C_STRING_WITH_LEN("enum('N','Y')") },
+    { C_STRING_WITH_LEN("utf8") }
+  },
+  {
+    { C_STRING_WITH_LEN("Restore_priv") },
+    { C_STRING_WITH_LEN("enum('N','Y')") },
+    { C_STRING_WITH_LEN("utf8") }
   }
 };
 
@@ -4658,13 +4668,13 @@ static const char *command_array[]=
   "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
   "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
   "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
-  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
+  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", "BACKUP", "RESTORE"
 };
 
 static uint command_lengths[]=
 {
   6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
-  14, 13, 11, 5, 7, 17
+  14, 13, 11, 5, 7, 17, 6, 7
 };
 
 
@@ -4683,7 +4693,7 @@ static int show_routine_grants(THD *thd,
 bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
 {
   ulong want_access;
-  uint counter,index;
+  ulong counter,index;
   int  error = 0;
   ACL_USER *acl_user;
   ACL_DB *acl_db;

=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h	2009-10-28 07:55:44 +0000
+++ b/sql/sql_acl.h	2009-12-04 22:31:25 +0000
@@ -55,12 +55,15 @@
   4. acl_init() or whatever - to define behaviour for old privilege tables
   5. sql_yacc.yy - for GRANT/REVOKE to work
 */
-#define NO_ACCESS	(1L << 30)
+#define BACKUP_ACL   (1L << 29)
+#define RESTORE_ACL   (1L << 30)
+#define NO_ACCESS	(1L << 31)
 #define DB_ACLS \
 (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
  GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
  LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
- CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL)
+ CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL | \
+ BACKUP_ACL | RESTORE_ACL )
 
 #define TABLE_ACLS \
 (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
@@ -83,7 +86,7 @@
  CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
  EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \
  ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \
- CREATE_TABLESPACE_ACL)
+ CREATE_TABLESPACE_ACL | BACKUP_ACL | RESTORE_ACL )
 
 #define DEFAULT_CREATE_PROC_ACLS \
 (ALTER_PROC_ACL | EXECUTE_ACL)
@@ -107,19 +110,22 @@
 		   CREATE_PROC_ACL | ALTER_PROC_ACL )
 #define DB_CHUNK4 (EXECUTE_ACL)
 #define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL)
+#define DB_CHUNK6 (BACKUP_ACL | RESTORE_ACL)
 
 #define fix_rights_for_db(A)  (((A)       & DB_CHUNK0) | \
 			      (((A) << 4) & DB_CHUNK1) | \
 			      (((A) << 6) & DB_CHUNK2) | \
 			      (((A) << 9) & DB_CHUNK3) | \
 			      (((A) << 2) & DB_CHUNK4))| \
-                              (((A) << 9) & DB_CHUNK5)
+                              (((A) << 9) & DB_CHUNK5) | \
+                              (((A) << 10) & DB_CHUNK6)
 #define get_rights_for_db(A)  (((A) & DB_CHUNK0)       | \
 			      (((A) & DB_CHUNK1) >> 4) | \
 			      (((A) & DB_CHUNK2) >> 6) | \
 			      (((A) & DB_CHUNK3) >> 9) | \
 			      (((A) & DB_CHUNK4) >> 2))| \
-                              (((A) & DB_CHUNK5) >> 9)
+                              (((A) & DB_CHUNK5) >> 9) | \
+                              (((A) & DB_CHUNK6) >> 10)
 #define TBL_CHUNK0 DB_CHUNK0
 #define TBL_CHUNK1 DB_CHUNK1
 #define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
@@ -165,6 +171,8 @@ enum mysql_db_table_field
   MYSQL_DB_FIELD_EXECUTE_PRIV,
   MYSQL_DB_FIELD_EVENT_PRIV,
   MYSQL_DB_FIELD_TRIGGER_PRIV,
+  MYSQL_DB_FIELD_BACKUP_PRIV,
+  MYSQL_DB_FIELD_RESTORE_PRIV,
   MYSQL_DB_FIELD_COUNT
 };
 

=== modified file 'sql/sql_array.h'
--- a/sql/sql_array.h	2009-09-23 21:32:31 +0000
+++ b/sql/sql_array.h	2009-12-04 22:31:25 +0000
@@ -24,6 +24,7 @@
 
 template <class Elem> class Dynamic_array
 {
+protected:
   DYNAMIC_ARRAY  array;
 public:
   Dynamic_array(uint prealloc=16, uint increment=16)
@@ -31,17 +32,17 @@ public:
     my_init_dynamic_array(&array, sizeof(Elem), prealloc, increment);
   }
 
-  Elem& at(int idx)
+  Elem& at(ulong idx) const
   {
     return *(((Elem*)array.buffer) + idx);
   }
 
-  Elem *front()
+  Elem *front() const
   {
     return (Elem*)array.buffer;
   }
 
-  Elem *back()
+  Elem *back() const
   {
     return ((Elem*)array.buffer) + array.elements;
   }
@@ -51,7 +52,7 @@ public:
     return (insert_dynamic(&array, (uchar*)&el));
   }
 
-  int elements()
+  ulong elements() const
   {
     return array.elements;
   }

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2009-12-03 23:52:05 +0000
+++ b/sql/sql_class.h	2009-12-04 22:31:25 +0000
@@ -434,6 +434,11 @@ struct system_variables
   DATE_TIME_FORMAT *datetime_format;
   DATE_TIME_FORMAT *time_format;
   my_bool sysdate_is_now;
+
+  my_bool restore_disables_events;
+ 
+  /* MySQL Backup privilege variables */
+  my_bool restore_precheck;
 };
 
 
@@ -1872,6 +1877,14 @@ public:
   */
   Parser_state *m_parser_state;
 
+  ulong backup_wait_timeout;
+  /*
+    If online backup/restore in progress, set to SQLCOM_BACKUP or
+    SQLCOM_RESTORE, otherwise 0. This modifies the behavior of some
+    statements, e.g. GRANT.
+  */
+  int backup_in_progress;
+
   Locked_tables_list locked_tables_list;
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2009-12-01 19:07:18 +0000
+++ b/sql/sql_lex.h	2009-12-04 22:31:25 +0000
@@ -120,6 +120,7 @@ enum enum_sql_command {
   SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
   SQLCOM_SHOW_CREATE_TRIGGER,
   SQLCOM_ALTER_DB_UPGRADE,
+  SQLCOM_BACKUP, SQLCOM_RESTORE, SQLCOM_PURGE_BACKUP_LOGS, 
   SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
   SQLCOM_SIGNAL, SQLCOM_RESIGNAL,
   SQLCOM_SHOW_RELAYLOG_EVENTS, 
@@ -127,6 +128,13 @@ enum enum_sql_command {
     When a command is added here, be sure it's also added in mysqld.cc
     in "struct show_var_st status_vars[]= {" ...
   */
+  /*
+    Conditional SQL command codes are not recommended, but if you need to
+    define them, make sure that they are defined immediately before SQLCOM_END.
+  */
+#ifdef BACKUP_TEST
+  SQLCOM_BACKUP_TEST,
+#endif
   /* This should be the last !!! */
   SQLCOM_END
 };

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2009-12-03 23:52:05 +0000
+++ b/sql/sql_parse.cc	2009-12-04 22:31:25 +0000
@@ -27,6 +27,7 @@
 #include "sp.h"
 #include "sp_cache.h"
 #include "events.h"
+#include "bml.h"
 #include "sql_trigger.h"
 #include "transaction.h"
 #include "sql_prepare.h"

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2009-12-03 23:52:05 +0000
+++ b/sql/sql_show.cc	2009-12-04 22:31:25 +0000
@@ -320,6 +320,8 @@ static struct show_privileges_st sys_pri
   {"Create tablespace", "Server Admin", "To create/alter/drop tablespaces"},
   {"Update", "Tables",  "To update existing rows"},
   {"Usage","Server Admin","No privileges - allow connect only"},
+  {"Backup", "Server Admin", "To execute BACKUP commands."},
+  {"Restore", "Server Admin", "To execute RESTORE commands."},
   {NullS, NullS, NullS}
 };
 

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2009-12-03 23:52:05 +0000
+++ b/sql/table.cc	2009-12-04 22:31:25 +0000
@@ -33,6 +33,15 @@ LEX_STRING GENERAL_LOG_NAME= {C_STRING_W
 /* SLOW_LOG name */
 LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
 
+/* BACKUP_HISTORY_LOG name */
+LEX_STRING BACKUP_HISTORY_LOG_NAME= {C_STRING_WITH_LEN("backup_history")};
+
+/* BACKUP_PROGRESS_LOG name */
+LEX_STRING BACKUP_PROGRESS_LOG_NAME= {C_STRING_WITH_LEN("backup_progress")};
+
+/* BACKUP_SETTINGS name */
+LEX_STRING BACKUP_SETTINGS_NAME= {C_STRING_WITH_LEN("backup_settings")};
+
 	/* Functions defined in this file */
 
 void open_table_error(TABLE_SHARE *share, int error, int db_errno,
@@ -238,6 +247,30 @@ TABLE_CATEGORY get_table_category(const 
       return TABLE_CATEGORY_PERFORMANCE;
     }
 
+    if ((name->length == BACKUP_HISTORY_LOG_NAME.length) &&
+        (my_strcasecmp(system_charset_info,
+                      BACKUP_HISTORY_LOG_NAME.str,
+                      name->str) == 0))
+    {
+      return TABLE_CATEGORY_PERFORMANCE;
+    }
+
+    if ((name->length == BACKUP_PROGRESS_LOG_NAME.length) &&
+        (my_strcasecmp(system_charset_info,
+                      BACKUP_PROGRESS_LOG_NAME.str,
+                      name->str) == 0))
+    {
+      return TABLE_CATEGORY_PERFORMANCE;
+    }
+
+    if ((name->length == BACKUP_SETTINGS_NAME.length) &&
+        (my_strcasecmp(system_charset_info,
+                      BACKUP_SETTINGS_NAME.str,
+                      name->str) == 0))
+    {
+      return TABLE_CATEGORY_PERFORMANCE;
+    }
+
     if ((name->length == SLOW_LOG_NAME.length) &&
         (my_strcasecmp(system_charset_info,
                       SLOW_LOG_NAME.str,

=== modified file 'storage/csv/ha_tina.cc'
--- a/storage/csv/ha_tina.cc	2009-11-24 13:54:59 +0000
+++ b/storage/csv/ha_tina.cc	2009-12-04 22:31:25 +0000
@@ -963,8 +963,13 @@ int ha_tina::update_row(const uchar * ol
   temp_file_length+= size;
   rc= 0;
 
-  /* UPDATE should never happen on the log tables */
-  DBUG_ASSERT(!share->is_log_table);
+  /* 
+     DELETE should never happen on the log table
+     UNLESS extra() has been called with HA_EXTRA_ALLOW_LOG_DELETE 
+     which sets allow_log_delete flag. The flag is reset with 
+     HA_EXTRA_MARK_AS_LOG_TABLE.     
+  */
+  DBUG_ASSERT(!share->is_log_table || share->allow_log_delete);
 
 err:
   DBUG_PRINT("info",("rc = %d", rc));
@@ -1186,6 +1191,13 @@ int ha_tina::extra(enum ha_extra_functio
  {
    pthread_mutex_lock(&share->mutex);
    share->is_log_table= TRUE;
+   share->allow_log_delete= FALSE;
+   pthread_mutex_unlock(&share->mutex);
+ }
+ else if (operation == HA_EXTRA_ALLOW_LOG_DELETE)
+ {
+   pthread_mutex_lock(&share->mutex);
+   share->allow_log_delete= TRUE;
    pthread_mutex_unlock(&share->mutex);
  }
   DBUG_RETURN(0);

=== modified file 'storage/csv/ha_tina.h'
--- a/storage/csv/ha_tina.h	2009-10-13 19:53:35 +0000
+++ b/storage/csv/ha_tina.h	2009-12-04 22:31:25 +0000
@@ -50,6 +50,7 @@ typedef struct st_tina_share {
   bool crashed;             /* Meta file is crashed */
   ha_rows rows_recorded;    /* Number of rows in tables */
   uint data_file_version;   /* Version of the data file used */
+  bool allow_log_delete;
 } TINA_SHARE;
 
 struct tina_set {

Attachment: [text/bzr-bundle] bzr/charles.bell@sun.com-20091204223125-mspe5uxazqn57mxa.bundle
Thread
bzr commit into mysql-5.6-next-mr branch (charles.bell:2995) WL#5101Chuck Bell4 Dec